• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import {
17  forEachChild,
18  getLeadingCommentRangesOfNode,
19  isCallExpression,
20  isExpressionStatement,
21  isIdentifier,
22  isStructDeclaration,
23  SyntaxKind,
24  visitEachChild
25} from 'typescript';
26
27import type {
28  CommentRange,
29  Identifier,
30  Node,
31  SourceFile,
32  StructDeclaration,
33  TransformationContext
34} from 'typescript';
35import type { IOptions } from '../configs/IOptions';
36
37export interface ReservedNameInfo {
38  universalReservedArray: RegExp[]; // items contain wildcards
39  specificReservedArray: string[]; // items do not contain wildcards
40}
41
42/**
43 * collect exist identifier names in current source file
44 * @param sourceFile
45 */
46export function collectExistNames(sourceFile: SourceFile): Set<string> {
47  const identifiers: Set<string> = new Set<string>();
48
49  let visit = (node: Node): void => {
50    if (isIdentifier(node)) {
51      identifiers.add(node.text);
52    }
53
54    forEachChild(node, visit);
55  };
56
57  forEachChild(sourceFile, visit);
58  return identifiers;
59}
60
61type IdentifiersAndStructs = {shadowIdentifiers: Identifier[], shadowStructs: StructDeclaration[]};
62
63/**
64 * collect exist identifiers in current source file
65 * @param sourceFile
66 * @param context
67 */
68export function collectIdentifiersAndStructs(sourceFile: SourceFile, context: TransformationContext): IdentifiersAndStructs {
69  const identifiers: Identifier[] = [];
70  const structs: StructDeclaration[] = [];
71
72  let visit = (node: Node): Node => {
73    if (isStructDeclaration(node)) {
74      structs.push(node);
75    }
76    // @ts-ignore
77    if (getOriginalNode(node).virtual) {
78      return node;
79    }
80    if (!isIdentifier(node) || !node.parent) {
81      return visitEachChild(node, visit, context);
82    }
83
84    identifiers.push(node);
85    return node;
86  };
87
88  visit(sourceFile);
89  return {shadowIdentifiers: identifiers, shadowStructs: structs};
90}
91
92export function isCommentedNode(node: Node, sourceFile: SourceFile): boolean {
93  const ranges: CommentRange[] = getLeadingCommentRangesOfNode(node, sourceFile);
94  return ranges !== undefined;
95}
96
97export function isSuperCallStatement(node: Node): boolean {
98  return isExpressionStatement(node) &&
99    isCallExpression(node.expression) &&
100    node.expression.expression.kind === SyntaxKind.SuperKeyword;
101}
102
103/**
104 * separate wildcards from specific items.
105 */
106export function separateUniversalReservedItem(originalArray: string[]): ReservedNameInfo {
107  if (!originalArray) {
108    throw new Error('Unable to handle the empty array.');
109  }
110  const reservedInfo: ReservedNameInfo = {
111    universalReservedArray: [],
112    specificReservedArray: []
113  };
114
115  originalArray.forEach(reservedItem => {
116    if (containWildcards(reservedItem)) {
117      const regexPattern = wildcardTransformer(reservedItem);
118      const regexOperator = new RegExp(`^${regexPattern}$`);
119      reservedInfo.universalReservedArray.push(regexOperator);
120    } else {
121      reservedInfo.specificReservedArray.push(reservedItem);
122    }
123  });
124  return reservedInfo;
125}
126
127/**
128 * check if the item contains '*', '?'.
129 */
130export function containWildcards(item: string): boolean {
131  return /[\*\?]/.test(item);
132}
133
134/**
135 * Convert specific characters into regular expressions.
136 */
137export function wildcardTransformer(wildcard: string, isPath?: boolean): string {
138  // Add an escape character in front of special characters
139  // special characters: '\', '^', '$', '.', '+', '|', '[', ']', '{', '}', '(', ')'
140  let escapedItem = wildcard.replace(/[\\+^${}()|\[\]\.]/g, '\\$&');
141
142  // isPath: containing '**', and '*', '?' can not be matched with '/'.
143  if (isPath) {
144    // before: ../**/a/b/c*/?.ets
145    // after: ../.*/a/b/c[^/]*/[^/].ets
146    return escapedItem.replace(/\*\*/g, '.*').replace(/(?<!\.)\*/g, '[^/]*').replace(/\?/g, '[^/]');
147  }
148  // before: *a?
149  // after: .*a.
150  return escapedItem.replace(/\*/g, '.*').replace(/\?/g, '.');
151}
152
153/**
154 * Determine whether the original name needs to be preserved.
155 */
156export function needToBeReserved(reservedSet: Set<string>, universalArray: RegExp[], originalName: string): boolean {
157  return reservedSet.has(originalName) || isMatchWildcard(universalArray, originalName);
158}
159
160/**
161 * Determine whether it can match the wildcard character in the array.
162 */
163export function isMatchWildcard(wildcardArray: RegExp[], item: string): boolean {
164  for (const wildcard of wildcardArray) {
165    if (wildcard.test(item)) {
166      return true;
167    }
168  }
169  return false;
170}
171
172/**
173 * Separate parts of an array that contain wildcard characters.
174 */
175export function handleReservedConfig(config: IOptions, optionName: string, reservedListName: string,
176  universalLisName: string, enableRemove?: string): void {
177  const reservedConfig = config?.[optionName];
178  let needSeparate: boolean = !!(reservedConfig?.[reservedListName]);
179  if (enableRemove) {
180    needSeparate &&= reservedConfig[enableRemove];
181  }
182  if (needSeparate) {
183    // separate items which contain wildcards from others
184    const reservedInfo: ReservedNameInfo = separateUniversalReservedItem(reservedConfig[reservedListName]);
185    reservedConfig[reservedListName] = reservedInfo.specificReservedArray;
186    reservedConfig[universalLisName] = reservedInfo.universalReservedArray;
187  }
188}
189