• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021 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 ts from 'typescript';
17import path from 'path';
18import fs from 'fs';
19
20import {
21  INNER_COMPONENT_DECORATORS,
22  COMPONENT_DECORATOR_ENTRY,
23  COMPONENT_DECORATOR_PREVIEW,
24  COMPONENT_DECORATOR_COMPONENT,
25  COMPONENT_DECORATOR_CUSTOM_DIALOG,
26  NATIVE_MODULE,
27  SYSTEM_PLUGIN,
28  OHOS_PLUGIN,
29  INNER_COMPONENT_MEMBER_DECORATORS,
30  COMPONENT_FOREACH,
31  COMPONENT_LAZYFOREACH,
32  COMPONENT_STATE_DECORATOR,
33  COMPONENT_LINK_DECORATOR,
34  COMPONENT_PROP_DECORATOR,
35  COMPONENT_STORAGE_PROP_DECORATOR,
36  COMPONENT_STORAGE_LINK_DECORATOR,
37  COMPONENT_PROVIDE_DECORATOR,
38  COMPONENT_CONSUME_DECORATOR,
39  COMPONENT_OBJECT_LINK_DECORATOR,
40  COMPONENT_OBSERVED_DECORATOR,
41  COMPONENT_LOCAL_STORAGE_LINK_DECORATOR,
42  COMPONENT_LOCAL_STORAGE_PROP_DECORATOR,
43  STYLES,
44  VALIDATE_MODULE,
45  COMPONENT_BUILDER_DECORATOR,
46  COMPONENT_CONCURRENT_DECORATOR,
47  COMPONENT_EXTEND_DECORATOR,
48  COMPONENT_STYLES_DECORATOR,
49  RESOURCE_NAME_TYPE,
50  TTOGGLE_CHECKBOX,
51  TOGGLE_SWITCH,
52  COMPONENT_BUTTON,
53  COMPONENT_TOGGLE,
54  COMPONENT_BUILDERPARAM_DECORATOR,
55  ESMODULE,
56  CARD_ENABLE_DECORATORS,
57  CARD_LOG_TYPE_DECORATORS,
58  JSBUNDLE
59} from './pre_define';
60import {
61  INNER_COMPONENT_NAMES,
62  AUTOMIC_COMPONENT,
63  SINGLE_CHILD_COMPONENT,
64  SPECIFIC_CHILD_COMPONENT,
65  BUILDIN_STYLE_NAMES,
66  EXTEND_ATTRIBUTE,
67  GLOBAL_STYLE_FUNCTION,
68  STYLES_ATTRIBUTE,
69  CUSTOM_BUILDER_METHOD,
70  GLOBAL_CUSTOM_BUILDER_METHOD,
71  INNER_CUSTOM_BUILDER_METHOD,
72  INNER_STYLE_FUNCTION
73} from './component_map';
74import {
75  LogType,
76  LogInfo,
77  componentInfo,
78  addLog,
79  hasDecorator,
80} from './utils';
81import { getPackageInfo } from './ark_utils'
82import { projectConfig, abilityPagesFullPath } from '../main';
83import {
84  collectExtend,
85  isExtendFunction,
86  transformLog,
87  validatorCard
88} from './process_ui_syntax';
89import { logger } from './compile_info';
90
91export interface ComponentCollection {
92  localStorageName: string;
93  entryComponentPos: number;
94  entryComponent: string;
95  previewComponent: Array<string>;
96  customDialogs: Set<string>;
97  customComponents: Set<string>;
98  currentClassName: string;
99}
100
101export interface IComponentSet {
102  properties: Set<string>;
103  regulars: Set<string>;
104  states: Set<string>;
105  links: Set<string>;
106  props: Set<string>;
107  storageProps: Set<string>;
108  storageLinks: Set<string>;
109  provides: Set<string>;
110  consumes: Set<string>;
111  objectLinks: Set<string>;
112  localStorageLink: Map<string, Set<string>>;
113  localStorageProp: Map<string, Set<string>>;
114  builderParams: Set<string>;
115  builderParamData: Set<string>;
116}
117
118export const componentCollection: ComponentCollection = {
119  localStorageName: null,
120  entryComponentPos: null,
121  entryComponent: null,
122  previewComponent: new Array(),
123  customDialogs: new Set([]),
124  customComponents: new Set([]),
125  currentClassName: null
126};
127
128export const observedClassCollection: Set<string> = new Set();
129export const enumCollection: Set<string> = new Set();
130export const classMethodCollection: Map<string, Set<string>> = new Map();
131export const dollarCollection: Set<string> = new Set();
132
133export const propertyCollection: Map<string, Set<string>> = new Map();
134export const stateCollection: Map<string, Set<string>> = new Map();
135export const linkCollection: Map<string, Set<string>> = new Map();
136export const propCollection: Map<string, Set<string>> = new Map();
137export const regularCollection: Map<string, Set<string>> = new Map();
138export const storagePropCollection: Map<string, Set<string>> = new Map();
139export const storageLinkCollection: Map<string, Set<string>> = new Map();
140export const provideCollection: Map<string, Set<string>> = new Map();
141export const consumeCollection: Map<string, Set<string>> = new Map();
142export const objectLinkCollection: Map<string, Set<string>> = new Map();
143export const builderParamObjectCollection: Map<string, Set<string>> = new Map();
144export const localStorageLinkCollection: Map<string, Map<string, Set<string>>> = new Map();
145export const localStoragePropCollection: Map<string, Map<string, Set<string>>> = new Map();
146export const builderParamInitialization: Map<string, Set<string>> = new Map();
147
148export const isStaticViewCollection: Map<string, boolean> = new Map();
149
150export const useOSFiles: Set<string> = new Set();
151export const sourcemapNamesCollection: Map<string, Map<string, string>> = new Map();
152export const originalImportNamesMap: Map<string, string> = new Map();
153
154export function validateUISyntax(source: string, content: string, filePath: string,
155  fileQuery: string): LogInfo[] {
156  let log: LogInfo[] = [];
157  if (process.env.compileMode === 'moduleJson' ||
158    path.resolve(filePath) !== path.resolve(projectConfig.projectPath || '', 'app.ets')) {
159    const res: LogInfo[] = checkComponentDecorator(source, filePath, fileQuery);
160    if (res) {
161      log = log.concat(res);
162    }
163    const allComponentNames: Set<string> =
164      new Set([...INNER_COMPONENT_NAMES, ...componentCollection.customComponents]);
165    checkUISyntax(filePath, allComponentNames, content, log);
166    componentCollection.customComponents.forEach(item => componentInfo.componentNames.add(item));
167  }
168
169  return log;
170}
171
172function checkComponentDecorator(source: string, filePath: string,
173  fileQuery: string): LogInfo[] | null {
174  const log: LogInfo[] = [];
175  const sourceFile: ts.SourceFile = ts.createSourceFile(filePath, source,
176    ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS);
177  if (sourceFile && sourceFile.statements && sourceFile.statements.length) {
178    const result: DecoratorResult = {
179      entryCount: 0,
180      previewCount: 0
181    };
182    sourceFile.statements.forEach((item, index, arr) => {
183      if (isObservedClass(item)) {
184        // @ts-ignore
185        observedClassCollection.add(item.name.getText());
186      }
187      if (ts.isEnumDeclaration(item) && item.name) {
188        enumCollection.add(item.name.getText());
189      }
190      if (ts.isStructDeclaration(item)) {
191        if (item.name && ts.isIdentifier(item.name)) {
192          if (item.decorators && item.decorators.length) {
193            checkDecorators(item.decorators, result, item.name, log, sourceFile, item);
194          } else {
195            const message: string = `A struct should use decorator '@Component'.`;
196            addLog(LogType.WARN, message, item.getStart(), log, sourceFile);
197          }
198        } else {
199          const message: string = `A struct must have a name.`;
200          addLog(LogType.ERROR, message, item.getStart(), log, sourceFile);
201        }
202      }
203      if (ts.isMissingDeclaration(item)) {
204        const decorators: ts.NodeArray<ts.Decorator> = item.decorators;
205        for (let i = 0; i < decorators.length; i++) {
206          if (decorators[i] && /struct/.test(decorators[i].getText())) {
207            const message: string = `Please use a valid decorator.`;
208            addLog(LogType.ERROR, message, item.getStart(), log, sourceFile);
209            break;
210          }
211        }
212      }
213    });
214    validateEntryAndPreviewCount(result, fileQuery, sourceFile.fileName, projectConfig.isPreview,
215      !!projectConfig.checkEntry, log);
216  }
217
218  return log.length ? log : null;
219}
220
221function validateEntryAndPreviewCount(result: DecoratorResult, fileQuery: string,
222  fileName: string, isPreview: boolean, checkEntry: boolean, log: LogInfo[]): void {
223  if (result.previewCount > 10 && fileQuery === '?entry') {
224    log.push({
225      type: LogType.ERROR,
226      message: `A page can contain at most 10 '@Preview' decorators.`,
227      fileName: fileName
228    });
229  }
230  if (result.entryCount > 1 && fileQuery === '?entry') {
231    log.push({
232      type: LogType.ERROR,
233      message: `A page can't contain more than one '@Entry' decorator`,
234      fileName: fileName
235    });
236  }
237  if (isPreview && !checkEntry && result.previewCount < 1 && result.entryCount !== 1 &&
238    fileQuery === '?entry') {
239    log.push({
240      type: LogType.ERROR,
241      message: `A page which is being previewed must have one and only one '@Entry' `
242        + `decorator, or at least one '@Preview' decorator.`,
243      fileName: fileName
244    });
245  } else if ((!isPreview || isPreview && checkEntry) && result.entryCount !== 1 && fileQuery === '?entry' &&
246    !abilityPagesFullPath.includes(fileName)) {
247    log.push({
248      type: LogType.ERROR,
249      message: `A page configured in '${projectConfig.pagesJsonFileName}' must have one and only one '@Entry' `
250        + `decorator.`,
251      fileName: fileName
252    });
253  }
254}
255
256export function isObservedClass(node: ts.Node): boolean {
257  if (ts.isClassDeclaration(node) && hasDecorator(node, COMPONENT_OBSERVED_DECORATOR)) {
258    return true;
259  }
260  return false;
261}
262
263export function isCustomDialogClass(node: ts.Node): boolean {
264  if (ts.isClassDeclaration(node) && hasDecorator(node, COMPONENT_DECORATOR_CUSTOM_DIALOG)) {
265    return true;
266  }
267  return false;
268}
269
270interface DecoratorResult {
271  entryCount: number;
272  previewCount: number;
273}
274
275function checkDecorators(decorators: ts.NodeArray<ts.Decorator>, result: DecoratorResult,
276  component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile, node: ts.StructDeclaration): void {
277  let hasComponentDecorator: boolean = false;
278  const componentName: string = component.getText();
279  decorators.forEach((element) => {
280    const name: string = element.getText().replace(/\([^\(\)]*\)/, '').trim();
281    if (INNER_COMPONENT_DECORATORS.has(name)) {
282      componentCollection.customComponents.add(componentName);
283      switch (name) {
284        case COMPONENT_DECORATOR_ENTRY:
285          checkEntryComponent(node, log, sourceFile);
286          result.entryCount++;
287          componentCollection.entryComponent = componentName;
288          componentCollection.entryComponentPos = node.getStart();
289          collectLocalStorageName(element);
290          break;
291        case COMPONENT_DECORATOR_PREVIEW:
292          result.previewCount++;
293          componentCollection.previewComponent.push(componentName);
294          break;
295        case COMPONENT_DECORATOR_COMPONENT:
296          hasComponentDecorator = true;
297          break;
298        case COMPONENT_DECORATOR_CUSTOM_DIALOG:
299          componentCollection.customDialogs.add(componentName);
300          hasComponentDecorator = true;
301          break;
302      }
303    } else {
304      const pos: number = element.expression ? element.expression.pos : element.pos;
305      const message: string = `The struct '${componentName}' use invalid decorator.`;
306      addLog(LogType.WARN, message, pos, log, sourceFile);
307    }
308  });
309  if (!hasComponentDecorator) {
310    const message: string = `The struct '${componentName}' should use decorator '@Component'.`;
311    addLog(LogType.WARN, message, component.pos, log, sourceFile);
312  }
313  if (BUILDIN_STYLE_NAMES.has(componentName)) {
314    const message: string = `The struct '${componentName}' cannot have the same name ` +
315      `as the built-in attribute '${componentName}'.`;
316    addLog(LogType.ERROR, message, component.pos, log, sourceFile);
317  }
318  if (INNER_COMPONENT_NAMES.has(componentName)) {
319    const message: string = `The struct '${componentName}' cannot have the same name ` +
320      `as the built-in component '${componentName}'.`;
321    addLog(LogType.ERROR, message, component.pos, log, sourceFile);
322  }
323}
324
325function checkConcurrentDecorator(node: ts.FunctionDeclaration | ts.MethodDeclaration, log: LogInfo[],
326  sourceFile: ts.SourceFile): void {
327  if (projectConfig.compileMode === JSBUNDLE) {
328    const message: string = `@Concurrent can only be used in ESMODULE compile mode.`;
329    addLog(LogType.ERROR, message, node.decorators!.pos, log, sourceFile);
330  }
331  if (ts.isMethodDeclaration(node)) {
332    const message: string = `@Concurrent can not be used on method. please use it on function declaration.`;
333    addLog(LogType.ERROR, message, node.decorators!.pos, log, sourceFile);
334  }
335  if (node.asteriskToken) {
336    let hasAsync: boolean = false;
337    const checkAsyncModifier = (modifier: ts.Modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword;
338    node.modifiers && (hasAsync = node.modifiers.some(checkAsyncModifier));
339    const funcKind: string = hasAsync ? 'Async generator' : 'Generator';
340    const message: string = `@Concurrent can not be used on ${funcKind} function declaration.`;
341    addLog(LogType.ERROR, message, node.decorators!.pos, log, sourceFile);
342  }
343}
344
345function collectLocalStorageName(node: ts.Decorator): void {
346  if (node && node.expression && ts.isCallExpression(node.expression)) {
347    if (node.expression.arguments && node.expression.arguments.length) {
348      node.expression.arguments.forEach((item: ts.Node, index: number) => {
349        if (ts.isIdentifier(item) && index === 0) {
350          componentCollection.localStorageName = item.getText();
351        }
352      });
353    }
354  } else {
355    componentCollection.localStorageName = null;
356  }
357}
358
359function checkUISyntax(filePath: string, allComponentNames: Set<string>, content: string,
360  log: LogInfo[]): void {
361  const sourceFile: ts.SourceFile = ts.createSourceFile(filePath, content,
362    ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS);
363  visitAllNode(sourceFile, sourceFile, allComponentNames, log);
364}
365
366function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponentNames: Set<string>,
367  log: LogInfo[]) {
368  if (ts.isStructDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
369    collectComponentProps(node);
370  }
371  if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) {
372    if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) {
373      CUSTOM_BUILDER_METHOD.add(node.name.getText());
374      if (ts.isFunctionDeclaration(node)) {
375        GLOBAL_CUSTOM_BUILDER_METHOD.add(node.name.getText());
376      } else {
377        INNER_CUSTOM_BUILDER_METHOD.add(node.name.getText());
378      }
379    } else if (ts.isFunctionDeclaration(node) && isExtendFunction(node)) {
380      const componentName: string = isExtendFunction(node);
381      collectExtend(EXTEND_ATTRIBUTE, componentName, node.name.getText());
382    } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) {
383      if (ts.isBlock(node.body) && node.body.statements && node.body.statements.length) {
384        if (ts.isFunctionDeclaration(node)) {
385          GLOBAL_STYLE_FUNCTION.set(node.name.getText(), node.body);
386        } else {
387          INNER_STYLE_FUNCTION.set(node.name.getText(), node.body);
388        }
389        STYLES_ATTRIBUTE.add(node.name.getText());
390        BUILDIN_STYLE_NAMES.add(node.name.getText());
391      }
392    }
393    if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) {
394      // ark compiler's feature
395      checkConcurrentDecorator(node, log, sourceFileNode);
396    }
397  }
398  node.getChildren().forEach((item: ts.Node) => visitAllNode(item, sourceFileNode, allComponentNames, log));
399}
400
401export function checkAllNode(
402  node: ts.EtsComponentExpression,
403  allComponentNames: Set<string>,
404  sourceFileNode: ts.SourceFile,
405  log: LogInfo[]
406): void {
407  if (ts.isIdentifier(node.expression)) {
408    checkNoChildComponent(node, sourceFileNode, log);
409    checkOneChildComponent(node, allComponentNames, sourceFileNode, log);
410    checkSpecificChildComponent(node, allComponentNames, sourceFileNode, log);
411  }
412}
413
414interface ParamType {
415  name: string,
416  value: string,
417}
418
419function checkNoChildComponent(node: ts.EtsComponentExpression, sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
420  const isCheckType: ParamType = { name: null, value: null};
421  if (hasChild(node, isCheckType)) {
422    const componentName: string = (node.expression as ts.Identifier).escapedText.toString();
423    const pos: number = node.expression.getStart();
424    const message: string = isCheckType.name === null ?
425      `The component '${componentName}' can't have any child.` :
426      `When the component '${componentName}' set '${isCheckType.name}' is '${isCheckType.value}'` +
427        `, can't have any child.`;
428    addLog(LogType.ERROR, message, pos, log, sourceFileNode);
429  }
430}
431
432function hasChild(node: ts.EtsComponentExpression, isCheckType: ParamType): boolean {
433  const nodeName: ts.Identifier = node.expression as ts.Identifier;
434  if ((AUTOMIC_COMPONENT.has(nodeName.escapedText.toString()) || judgeComponentType(nodeName, node, isCheckType)) &&
435    getNextNode(node)) {
436    return true;
437  }
438  return false;
439}
440
441function judgeComponentType(nodeName: ts.Identifier, etsComponentExpression: ts.EtsComponentExpression,
442  isCheckType: ParamType): boolean {
443  return COMPONENT_TOGGLE === nodeName.escapedText.toString() &&
444    etsComponentExpression.arguments && etsComponentExpression.arguments[0] &&
445    ts.isObjectLiteralExpression(etsComponentExpression.arguments[0]) &&
446    etsComponentExpression.arguments[0].getText() &&
447    judgeToggleComponentParamType(etsComponentExpression.arguments[0].getText(), isCheckType);
448}
449
450function judgeToggleComponentParamType(param: string, isCheckType: ParamType): boolean {
451  if (param.indexOf(RESOURCE_NAME_TYPE) > -1) {
452    isCheckType.name = RESOURCE_NAME_TYPE;
453    const match: string[] = param.match(/\b(Checkbox|Switch|Button)\b/);
454    if (match && match.length) {
455      isCheckType.value = match[0];
456      if (isCheckType.value === COMPONENT_BUTTON) {
457        return false;
458      }
459      return true;
460    }
461  }
462  return false;
463}
464
465function getNextNode(node: ts.EtsComponentExpression): ts.Block {
466  if (node.body && ts.isBlock(node.body)) {
467    const statementsArray: ts.Block = node.body;
468    return statementsArray;
469  }
470}
471
472function checkOneChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set<string>,
473  sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
474  const isCheckType: ParamType = { name: null, value: null};
475  if (hasNonSingleChild(node, allComponentNames, isCheckType)) {
476    const componentName: string = (node.expression as ts.Identifier).escapedText.toString();
477    const pos: number = node.expression.getStart();
478    const message: string = isCheckType.name === null ?
479      `The component '${componentName}' can only have a single child component.` :
480      `When the component '${componentName}' set '${isCheckType.name}' is ` +
481        `'${isCheckType.value}', can only have a single child component.`;
482    addLog(LogType.ERROR, message, pos, log, sourceFileNode);
483  }
484}
485
486function hasNonSingleChild(node: ts.EtsComponentExpression, allComponentNames: Set<string>,
487  isCheckType: ParamType): boolean {
488  const nodeName: ts.Identifier = node.expression as ts.Identifier;
489  const BlockNode: ts.Block = getNextNode(node);
490  if (SINGLE_CHILD_COMPONENT.has(nodeName.escapedText.toString()) || !judgeComponentType(nodeName, node, isCheckType)
491    && isCheckType.value === COMPONENT_BUTTON) {
492    if (!BlockNode) {
493      return false;
494    }
495    if (BlockNode && BlockNode.statements) {
496      const length: number = BlockNode.statements.length;
497      if (!length) {
498        return false;
499      }
500      if (length > 3) {
501        return true;
502      }
503      const childCount: number = getBlockChildrenCount(BlockNode, allComponentNames);
504      if (childCount > 1) {
505        return true;
506      }
507    }
508  }
509  return false;
510}
511
512function getBlockChildrenCount(blockNode: ts.Block, allComponentNames: Set<string>): number {
513  let maxCount: number = 0;
514  const length: number = blockNode.statements.length;
515  for (let i = 0; i < length; ++i) {
516    const item: ts.Node = blockNode.statements[i];
517    if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression) &&
518      isForEachComponent(item.expression)) {
519      maxCount += 2;
520    }
521    if (ts.isIfStatement(item)) {
522      maxCount += getIfChildrenCount(item, allComponentNames);
523    }
524    if (ts.isExpressionStatement(item) && ts.isEtsComponentExpression(item.expression)) {
525      maxCount += 1;
526    }
527    if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression)) {
528      let newNode: any = item.expression;
529      while (newNode.expression) {
530        if (ts.isEtsComponentExpression(newNode) || ts.isCallExpression(newNode) &&
531          isComponent(newNode, allComponentNames)) {
532          maxCount += 1;
533        }
534        newNode = newNode.expression;
535      }
536    }
537    if (maxCount > 1) {
538      break;
539    }
540  }
541  return maxCount;
542}
543
544function isComponent(node: ts.EtsComponentExpression | ts.CallExpression, allComponentNames: Set<string>): boolean {
545  if (ts.isIdentifier(node.expression) &&
546    allComponentNames.has(node.expression.escapedText.toString())) {
547    return true;
548  }
549  return false;
550}
551
552function isForEachComponent(node: ts.EtsComponentExpression | ts.CallExpression): boolean {
553  if (ts.isIdentifier(node.expression)) {
554    const componentName: string = node.expression.escapedText.toString();
555    return componentName === COMPONENT_FOREACH || componentName === COMPONENT_LAZYFOREACH;
556  }
557  return false;
558}
559
560function getIfChildrenCount(ifNode: ts.IfStatement, allComponentNames: Set<string>): number {
561  const maxCount: number =
562    Math.max(getStatementCount(ifNode.thenStatement, allComponentNames),
563      getStatementCount(ifNode.elseStatement, allComponentNames));
564  return maxCount;
565}
566
567function getStatementCount(node: ts.Node, allComponentNames: Set<string>): number {
568  let maxCount: number = 0;
569  if (!node) {
570    return maxCount;
571  } else if (ts.isBlock(node)) {
572    maxCount = getBlockChildrenCount(node, allComponentNames);
573  } else if (ts.isIfStatement(node)) {
574    maxCount = getIfChildrenCount(node, allComponentNames);
575  } else if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) &&
576    isForEachComponent(node.expression)) {
577    maxCount = 2;
578  } else if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) &&
579    !isForEachComponent(node.expression) && isComponent(node.expression, allComponentNames)) {
580    maxCount = 1;
581  }
582  return maxCount;
583}
584
585function checkSpecificChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set<string>,
586  sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
587  if (hasNonspecificChild(node, allComponentNames)) {
588    const componentName: string = (node.expression as ts.Identifier).escapedText.toString();
589    const pos: number = node.expression.getStart();
590    const specificChildArray: string =
591      Array.from(SPECIFIC_CHILD_COMPONENT.get(componentName)).join(' and ');
592    const message: string =
593      `The component '${componentName}' can only have the child component ${specificChildArray}.`;
594    addLog(LogType.ERROR, message, pos, log, sourceFileNode);
595  }
596}
597
598function hasNonspecificChild(node: ts.EtsComponentExpression,
599  allComponentNames: Set<string>): boolean {
600  const nodeName: ts.Identifier = node.expression as ts.Identifier;
601  const nodeNameString: string = nodeName.escapedText.toString();
602  const blockNode: ts.Block = getNextNode(node);
603  let isNonspecific: boolean = false;
604  if (SPECIFIC_CHILD_COMPONENT.has(nodeNameString) && blockNode) {
605    const specificChildSet: Set<string> = SPECIFIC_CHILD_COMPONENT.get(nodeNameString);
606    isNonspecific = isNonspecificChildBlock(blockNode, specificChildSet, allComponentNames);
607    if (isNonspecific) {
608      return isNonspecific;
609    }
610  }
611  return isNonspecific;
612}
613
614function isNonspecificChildBlock(blockNode: ts.Block, specificChildSet: Set<string>,
615  allComponentNames: Set<string>): boolean {
616  if (blockNode.statements) {
617    const length: number = blockNode.statements.length;
618    for (let i = 0; i < length; ++i) {
619      const item: ts.Node = blockNode.statements[i];
620      if (ts.isIfStatement(item) && isNonspecificChildIf(item, specificChildSet, allComponentNames)) {
621        return true;
622      }
623      if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression) &&
624        isForEachComponent(item.expression) &&
625        isNonspecificChildForEach(item.expression, specificChildSet, allComponentNames)) {
626        return true;
627      }
628      if (ts.isBlock(item) && isNonspecificChildBlock(item, specificChildSet, allComponentNames)) {
629        return true;
630      }
631      if (ts.isExpressionStatement(item)) {
632        let newNode: any = item.expression;
633        while (newNode.expression) {
634          if (ts.isEtsComponentExpression(newNode) && ts.isIdentifier(newNode.expression) &&
635          !isForEachComponent(newNode) && isComponent(newNode, allComponentNames)) {
636            const isNonspecific: boolean =
637            isNonspecificChildNonForEach(newNode, specificChildSet);
638            if (isNonspecific) {
639              return isNonspecific;
640            }
641            if (i + 1 < length && ts.isBlock(blockNode.statements[i + 1])) {
642              ++i;
643            }
644          }
645          newNode = newNode.expression;
646        }
647      }
648    }
649  }
650  return false;
651}
652
653function isNonspecificChildIf(node: ts.IfStatement, specificChildSet: Set<string>,
654  allComponentNames: Set<string>): boolean {
655  return isNonspecificChildIfStatement(node.thenStatement, specificChildSet, allComponentNames) ||
656    isNonspecificChildIfStatement(node.elseStatement, specificChildSet, allComponentNames);
657}
658
659function isNonspecificChildForEach(node: ts.EtsComponentExpression, specificChildSet: Set<string>,
660  allComponentNames: Set<string>): boolean {
661  if (ts.isCallExpression(node) && node.arguments &&
662    node.arguments.length > 1 && ts.isArrowFunction(node.arguments[1])) {
663    const arrowFunction: ts.ArrowFunction = node.arguments[1] as ts.ArrowFunction;
664    const body: ts.Block | ts.EtsComponentExpression | ts.IfStatement =
665      arrowFunction.body as ts.Block | ts.EtsComponentExpression | ts.IfStatement;
666    if (!body) {
667      return false;
668    }
669    if (ts.isBlock(body) && isNonspecificChildBlock(body, specificChildSet, allComponentNames)) {
670      return true;
671    }
672    if (ts.isIfStatement(body) && isNonspecificChildIf(body, specificChildSet, allComponentNames)) {
673      return true;
674    }
675    if (ts.isCallExpression(body) && isForEachComponent(body) &&
676      isNonspecificChildForEach(body, specificChildSet, allComponentNames)) {
677      return true;
678    }
679    if (ts.isEtsComponentExpression(body) && !isForEachComponent(body) &&
680      isComponent(body, allComponentNames) &&
681      isNonspecificChildNonForEach(body, specificChildSet)) {
682      return true;
683    }
684  }
685  return false;
686}
687
688function isNonspecificChildNonForEach(node: ts.EtsComponentExpression,
689  specificChildSet: Set<string>): boolean {
690  if (ts.isIdentifier(node.expression) &&
691    !specificChildSet.has(node.expression.escapedText.toString())) {
692    return true;
693  }
694  return false;
695}
696
697function isNonspecificChildIfStatement(node: ts.Node, specificChildSet: Set<string>,
698  allComponentNames: Set<string>): boolean {
699  if (!node) {
700    return false;
701  }
702  if (ts.isBlock(node) && isNonspecificChildBlock(node, specificChildSet, allComponentNames)) {
703    return true;
704  }
705  if (ts.isIfStatement(node) && isNonspecificChildIf(node, specificChildSet, allComponentNames)) {
706    return true;
707  }
708  if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) &&
709    isForEachComponent(node.expression) &&
710    isNonspecificChildForEach(node.expression, specificChildSet, allComponentNames)) {
711    return true;
712  }
713  if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) &&
714    !isForEachComponent(node.expression) && isComponent(node.expression, allComponentNames) &&
715    isNonspecificChildNonForEach(node.expression, specificChildSet)) {
716    return true;
717  }
718  return false;
719}
720
721function collectComponentProps(node: ts.StructDeclaration): void {
722  const componentName: string = node.name.getText();
723  const ComponentSet: IComponentSet = getComponentSet(node);
724  propertyCollection.set(componentName, ComponentSet.properties);
725  stateCollection.set(componentName, ComponentSet.states);
726  linkCollection.set(componentName, ComponentSet.links);
727  propCollection.set(componentName, ComponentSet.props);
728  regularCollection.set(componentName, ComponentSet.regulars);
729  storagePropCollection.set(componentName, ComponentSet.storageProps);
730  storageLinkCollection.set(componentName, ComponentSet.storageLinks);
731  provideCollection.set(componentName, ComponentSet.provides);
732  consumeCollection.set(componentName, ComponentSet.consumes);
733  objectLinkCollection.set(componentName, ComponentSet.objectLinks);
734  localStorageLinkCollection.set(componentName, ComponentSet.localStorageLink);
735  localStoragePropCollection.set(componentName, ComponentSet.localStorageProp);
736  builderParamObjectCollection.set(componentName, ComponentSet.builderParams);
737  builderParamInitialization.set(componentName, ComponentSet.builderParamData);
738}
739
740export function getComponentSet(node: ts.StructDeclaration): IComponentSet {
741  const properties: Set<string> = new Set();
742  const states: Set<string> = new Set();
743  const links: Set<string> = new Set();
744  const props: Set<string> = new Set();
745  const regulars: Set<string> = new Set();
746  const storageProps: Set<string> = new Set();
747  const storageLinks: Set<string> = new Set();
748  const provides: Set<string> = new Set();
749  const consumes: Set<string> = new Set();
750  const objectLinks: Set<string> = new Set();
751  const builderParams: Set<string> = new Set();
752  const localStorageLink: Map<string, Set<string>> = new Map();
753  const localStorageProp: Map<string, Set<string>> = new Map();
754  const builderParamData: Set<string> = new Set();
755  traversalComponentProps(node, properties, regulars, states, links, props, storageProps,
756    storageLinks, provides, consumes, objectLinks, localStorageLink, localStorageProp, builderParams,
757    builderParamData);
758  return {
759    properties, regulars, states, links, props, storageProps, storageLinks, provides,
760    consumes, objectLinks, localStorageLink, localStorageProp, builderParams, builderParamData
761  };
762}
763
764function traversalComponentProps(node: ts.StructDeclaration, properties: Set<string>,
765  regulars: Set<string>, states: Set<string>, links: Set<string>, props: Set<string>,
766  storageProps: Set<string>, storageLinks: Set<string>, provides: Set<string>,
767  consumes: Set<string>, objectLinks: Set<string>,
768  localStorageLink: Map<string, Set<string>>, localStorageProp: Map<string, Set<string>>,
769  builderParams: Set<string>, builderParamData: Set<string>): void {
770  let isStatic: boolean = true;
771  if (node.members) {
772    const currentMethodCollection: Set<string> = new Set();
773    node.members.forEach(item => {
774      if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) {
775        const propertyName: string = item.name.getText();
776        properties.add(propertyName);
777        if (!item.decorators || !item.decorators.length) {
778          regulars.add(propertyName);
779        } else {
780          isStatic = false;
781          for (let i = 0; i < item.decorators.length; i++) {
782            const decoratorName: string = item.decorators[i].getText().replace(/\(.*\)$/, '').trim();
783            if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
784              dollarCollection.add('$' + propertyName);
785              collectionStates(item.decorators[i], decoratorName, propertyName, states, links, props, storageProps,
786                storageLinks, provides, consumes, objectLinks, localStorageLink, localStorageProp,
787                builderParams, item.initializer, builderParamData);
788            }
789          }
790        }
791      }
792      if (ts.isMethodDeclaration(item) && item.name && ts.isIdentifier(item.name)) {
793        validateStateVariable(item);
794        currentMethodCollection.add(item.name.getText());
795      }
796    });
797    classMethodCollection.set(node.name.getText(), currentMethodCollection);
798  }
799  isStaticViewCollection.set(node.name.getText(), isStatic);
800}
801
802function collectionStates(node: ts.Decorator, decorator: string, name: string,
803  states: Set<string>, links: Set<string>, props: Set<string>, storageProps: Set<string>,
804  storageLinks: Set<string>, provides: Set<string>, consumes: Set<string>, objectLinks: Set<string>,
805  localStorageLink: Map<string, Set<string>>, localStorageProp: Map<string, Set<string>>,
806  builderParams: Set<string>, initializationtName: ts.Expression, builderParamData: Set<string>): void {
807  switch (decorator) {
808    case COMPONENT_STATE_DECORATOR:
809      states.add(name);
810      break;
811    case COMPONENT_LINK_DECORATOR:
812      links.add(name);
813      break;
814    case COMPONENT_PROP_DECORATOR:
815      props.add(name);
816      break;
817    case COMPONENT_STORAGE_PROP_DECORATOR:
818      storageProps.add(name);
819      break;
820    case COMPONENT_STORAGE_LINK_DECORATOR:
821      storageLinks.add(name);
822      break;
823    case COMPONENT_PROVIDE_DECORATOR:
824      provides.add(name);
825      break;
826    case COMPONENT_CONSUME_DECORATOR:
827      consumes.add(name);
828      break;
829    case COMPONENT_OBJECT_LINK_DECORATOR:
830      objectLinks.add(name);
831      break;
832    case COMPONENT_BUILDERPARAM_DECORATOR:
833      if (initializationtName) {
834        builderParamData.add(name);
835      }
836      builderParams.add(name);
837      break;
838    case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR :
839      collectionlocalStorageParam(node, name, localStorageLink);
840      break;
841    case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR:
842      collectionlocalStorageParam(node, name, localStorageProp);
843      break;
844  }
845}
846
847function collectionlocalStorageParam(node: ts.Decorator, name: string,
848  localStorage: Map<string, Set<string>>): void {
849  const localStorageParam: Set<string> = new Set();
850  if (node && ts.isCallExpression(node.expression) && node.expression.arguments &&
851    node.expression.arguments.length && ts.isStringLiteral(node.expression.arguments[0])) {
852    localStorage.set(name, localStorageParam.add(
853      node.expression.arguments[0].getText().replace(/\"|'/g, '')));
854  }
855}
856
857export interface ReplaceResult {
858  content: string,
859  log: LogInfo[]
860}
861
862export function sourceReplace(source: string, sourcePath: string): ReplaceResult {
863  let content: string = source;
864  const log: LogInfo[] = [];
865  content = preprocessExtend(content);
866  content = preprocessNewExtend(content);
867  // process @system.
868  content = processSystemApi(content, false, sourcePath);
869  CollectImportNames(content, sourcePath);
870
871  return {
872    content: content,
873    log: log
874  };
875}
876
877export function preprocessExtend(content: string, extendCollection?: Set<string>): string {
878  const REG_EXTEND: RegExp = /@Extend(\s+)([^\.\s]+)\.([^\(]+)\(/gm;
879  return content.replace(REG_EXTEND, (item, item1, item2, item3) => {
880    collectExtend(EXTEND_ATTRIBUTE, item2, '__' + item2 + '__' + item3);
881    collectExtend(EXTEND_ATTRIBUTE, item2, item3);
882    if (extendCollection) {
883      extendCollection.add(item3);
884    }
885    return `@Extend(${item2})${item1}function __${item2}__${item3}(`;
886  });
887}
888
889export function preprocessNewExtend(content: string, extendCollection?: Set<string>): string {
890  const REG_EXTEND: RegExp = /@Extend\s*\([^\)]+\)\s*function\s+([^\(\s]+)\s*\(/gm;
891  return content.replace(REG_EXTEND, (item, item1) => {
892    if (extendCollection) {
893      extendCollection.add(item1);
894    }
895    return item;
896  });
897}
898
899function replaceSystemApi(item: string, systemValue: string, moduleType: string, systemKey: string): string {
900  // if change format, please update regexp in transformModuleSpecifier
901  if (NATIVE_MODULE.has(`${moduleType}.${systemKey}`)) {
902    item = `var ${systemValue} = globalThis.requireNativeModule('${moduleType}.${systemKey}')`;
903  } else if (moduleType === SYSTEM_PLUGIN || moduleType === OHOS_PLUGIN) {
904    item = `var ${systemValue} = globalThis.requireNapi('${systemKey}')`;
905  }
906  return item;
907}
908
909function replaceLibSo(importValue: string, libSoKey: string, sourcePath: string = null): string {
910  if (sourcePath) {
911    useOSFiles.add(sourcePath);
912  }
913  // if change format, please update regexp in transformModuleSpecifier
914  return projectConfig.bundleName && projectConfig.moduleName
915    ? `var ${importValue} = globalThis.requireNapi("${libSoKey}", true, "${projectConfig.bundleName}/${projectConfig.moduleName}");`
916    : `var ${importValue} = globalThis.requireNapi("${libSoKey}", true);`;
917}
918
919function replaceOhmStartsWithBundle(url: string, item: string, importValue: string, moduleRequest: string, sourcePath: string): string {
920  const urlResult: RegExpMatchArray | null = url.match(/^(\S+)\/(\S+)\/(\S+)\/(\S+)$/);
921  if (urlResult) {
922    const moduleKind: string = urlResult[3];
923    if (moduleKind === 'lib') {
924      const libSoKey: string = urlResult[4];
925      item = replaceLibSo(importValue, libSoKey, sourcePath);
926    }
927  }
928  return item;
929}
930
931function replaceOhmStartsWithModule(url: string, item: string, importValue: string, moduleRequest: string, sourcePath: string): string {
932  const urlResult: RegExpMatchArray | null = url.match(/^(\S+)\/(\S+)\/(\S+)$/);
933  if (urlResult && projectConfig.aceModuleJsonPath) {
934    const moduleName: string = urlResult[1];
935    const moduleKind: string = urlResult[2];
936    const modulePath: string = urlResult[3];
937    const bundleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[0];
938    moduleRequest = `@bundle:${bundleName}/${moduleName}/${moduleKind}/${modulePath}`;
939    item = moduleKind === 'lib' ? replaceLibSo(importValue, modulePath, sourcePath) :
940      item.replace(/['"](\S+)['"]/, '\"' + moduleRequest + '\"');
941  }
942  return item;
943}
944
945function replaceOhmStartsWithOhos(url: string, item: string, importValue:string, moduleRequest: string, isSystemModule: boolean): string {
946  url = url.replace('/', '.');
947  const urlResult: RegExpMatchArray | null = url.match(/^system\.(\S+)/);
948  moduleRequest = urlResult ? `@${url}` : `@ohos.${url}`;
949  if (!isSystemModule) {
950    item = item.replace(/['"](\S+)['"]/, '\"' + moduleRequest + '\"');
951  } else {
952    const moduleType: string = urlResult ? 'system' : 'ohos';
953    const systemKey: string = urlResult ? url.substring(7) : url;
954    item = replaceSystemApi(item, importValue, moduleType, systemKey);
955  }
956  return item;
957}
958
959function replaceOhmStartsWithLocal(url: string, item: string, importValue: string, moduleRequest: string, sourcePath: string): string {
960  const result: RegExpMatchArray | null = sourcePath.match(/(\S+)(\/|\\)src(\/|\\)(?:main|ohosTest)(\/|\\)(ets|js)(\/|\\)(\S+)/);
961  if (result && projectConfig.aceModuleJsonPath) {
962    const packageInfo: string[] = getPackageInfo(projectConfig.aceModuleJsonPath);
963    const urlResult: RegExpMatchArray | null = url.match(/^\/(ets|js|lib|node_modules)\/(\S+)$/);
964    if (urlResult) {
965      const moduleKind: string = urlResult[1];
966      const modulePath: string = urlResult[2];
967      if (moduleKind === 'lib') {
968        item = replaceLibSo(importValue, modulePath, sourcePath);
969      } else if (moduleKind === 'node_modules') {
970        moduleRequest = `${modulePath}`;
971        item = item.replace(/['"](\S+)['"]/, '\"' + moduleRequest + '\"');
972      } else {
973        moduleRequest = `@bundle:${packageInfo[0]}/${packageInfo[1]}/${moduleKind}/${modulePath}`;
974        item = item.replace(/['"](\S+)['"]/, '\"' + moduleRequest + '\"');
975      }
976    }
977  }
978  return item;
979}
980
981function replaceOhmUrl(isSystemModule: boolean, item: string, importValue: string, moduleRequest: string, sourcePath: string = null): string {
982  const result: RegExpMatchArray = moduleRequest.match(/^@(\S+):(\S+)$/);
983  const urlType: string = result[1];
984  const url: string = result[2];
985  switch (urlType) {
986    case 'bundle': {
987      item = replaceOhmStartsWithBundle(url, item, importValue, moduleRequest, sourcePath);
988      break;
989    }
990    case 'module': {
991      item = replaceOhmStartsWithModule(url, item, importValue, moduleRequest, sourcePath);
992      break;
993    }
994    case 'ohos': {
995      item = replaceOhmStartsWithOhos(url, item, importValue, moduleRequest, isSystemModule);
996      break;
997    }
998    case 'lib': {
999      item = replaceLibSo(importValue, url, sourcePath);
1000      break;
1001    }
1002    case 'local': {
1003      item = replaceOhmStartsWithLocal(url, item, importValue, moduleRequest, sourcePath);
1004      break;
1005    }
1006    default:
1007      logger.error('\u001b[31m', `ArkTS:ERROR Incorrect OpenHarmony module kind: ${urlType}`, '\u001b[39m');
1008  }
1009  return item;
1010}
1011
1012export function processSystemApi(content: string, isProcessAllowList: boolean = false,
1013  sourcePath: string = null, isSystemModule: boolean = false): string {
1014  if (isProcessAllowList && projectConfig.compileMode === ESMODULE) {
1015    // remove the unused system api import decl like following when compile as [esmodule]
1016    // in the result_process phase
1017    // e.g. import "@ohos.application.xxx"
1018    const REG_UNUSED_SYSTEM_IMPORT: RegExp = /import(?:\s*)['"]@(system|ohos)\.(\S+)['"]/g;
1019    content = content.replace(REG_UNUSED_SYSTEM_IMPORT, '');
1020  }
1021
1022  const REG_IMPORT_DECL: RegExp = isProcessAllowList ? projectConfig.compileMode === ESMODULE ?
1023    /import\s+(.+)\s+from\s+['"]@(system|ohos)\.(\S+)['"]/g :
1024    /(import|const)\s+(.+)\s*=\s*(\_\_importDefault\()?require\(\s*['"]@(system|ohos)\.(\S+)['"]\s*\)(\))?/g :
1025    /(import|export)\s+(?:(.+)|\{([\s\S]+)\})\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g;
1026
1027  const systemValueCollection: Set<string> = new Set();
1028  const processedContent: string = content.replace(REG_IMPORT_DECL, (item, item1, item2, item3, item4, item5, item6) => {
1029    const importValue: string = isProcessAllowList ? projectConfig.compileMode === ESMODULE ? item1 : item2 : item2 || item5;
1030
1031    if (isProcessAllowList) {
1032      systemValueCollection.add(importValue);
1033      if (projectConfig.compileMode !== ESMODULE) {
1034        collectSourcemapNames(sourcePath, importValue, item5);
1035        return replaceSystemApi(item, importValue, item4, item5);
1036      }
1037      collectSourcemapNames(sourcePath, importValue, item3);
1038      return replaceSystemApi(item, importValue, item2, item3);
1039    }
1040
1041    const moduleRequest: string = item4 || item6;
1042    if (/^@(system|ohos)\./.test(moduleRequest)) { // ohos/system.api
1043      // ets & ts file need compile with .d.ts, so do not replace at the phase of pre_process
1044      if (!isSystemModule) {
1045        return item;
1046      }
1047      const result: RegExpMatchArray = moduleRequest.match(/^@(system|ohos)\.(\S+)$/);
1048      const moduleType: string = result[1];
1049      const apiName: string = result[2];
1050      return replaceSystemApi(item, importValue, moduleType, apiName);
1051    } else if (/^lib(\S+)\.so$/.test(moduleRequest)) { // libxxx.so
1052      const result: RegExpMatchArray = moduleRequest.match(/^lib(\S+)\.so$/);
1053      const libSoKey: string = result[1];
1054      return replaceLibSo(importValue, libSoKey, sourcePath);
1055    }
1056    return item;
1057  });
1058  return processInnerModule(processedContent, systemValueCollection);
1059}
1060
1061function collectSourcemapNames(sourcePath: string, changedName: string, originalName: string): void {
1062  if (sourcePath == null) {
1063    return;
1064  }
1065  const cleanSourcePath: string = sourcePath.replace('.ets', '.js').replace('.ts', '.js');
1066  if (!sourcemapNamesCollection.has(cleanSourcePath)) {
1067    return;
1068  }
1069
1070  let map: Map<string, string> = sourcemapNamesCollection.get(cleanSourcePath);
1071  if (map.has(changedName)) {
1072    return;
1073  }
1074
1075  for (let entry of originalImportNamesMap.entries()) {
1076    const key: string = entry[0];
1077    const value: string = entry[1];
1078    if (value === '@ohos.' + originalName || value === '@system.' + originalName) {
1079      map.set(changedName.trim(), key);
1080      sourcemapNamesCollection.set(cleanSourcePath, map);
1081      originalImportNamesMap.delete(key);
1082      break;
1083    }
1084  }
1085}
1086
1087export function CollectImportNames(content: string, sourcePath: string = null): void {
1088  const REG_IMPORT_DECL: RegExp =
1089    /(import|export)\s+(.+)\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g;
1090
1091  const decls: string[] = content.match(REG_IMPORT_DECL);
1092  if (decls != undefined) {
1093    decls.forEach(decl => {
1094      const parts: string[] = decl.split(' ');
1095      if (parts.length === 4 && parts[0] === 'import' && parts[2] === 'from' && !parts[3].includes('.so')) {
1096        originalImportNamesMap.set(parts[1], parts[3].replace(/'/g, ''));
1097      }
1098    })
1099  }
1100
1101  if (sourcePath && sourcePath != null) {
1102    const cleanSourcePath: string = sourcePath.replace('.ets', '.js').replace('.ts', '.js');
1103    if (!sourcemapNamesCollection.has(cleanSourcePath)) {
1104      sourcemapNamesCollection.set(cleanSourcePath, new Map());
1105    }
1106  }
1107}
1108
1109function processInnerModule(content: string, systemValueCollection: Set<string>): string {
1110  systemValueCollection.forEach(element => {
1111    const target: string = element.trim() + '.default';
1112    while (content.includes(target)) {
1113      content = content.replace(target, element.trim());
1114    }
1115  });
1116  return content;
1117}
1118
1119const VALIDATE_MODULE_REG: RegExp = new RegExp('^(' + VALIDATE_MODULE.join('|') + ')');
1120function validateAllowListModule(moduleType: string, systemKey: string): boolean {
1121  return moduleType === 'ohos' && VALIDATE_MODULE_REG.test(systemKey);
1122}
1123
1124export function resetComponentCollection() {
1125  componentCollection.entryComponent = null;
1126  componentCollection.entryComponentPos = null;
1127  componentCollection.previewComponent = new Array();
1128}
1129
1130function checkEntryComponent(node: ts.StructDeclaration, log: LogInfo[], sourceFile: ts.SourceFile): void {
1131  if (node.modifiers) {
1132    for (let i = 0; i < node.modifiers.length; i++) {
1133      if (node.modifiers[i].kind === ts.SyntaxKind.ExportKeyword) {
1134        const message: string = `It's not a recommended way to export struct with @Entry decorator, ` +
1135          `which may cause ACE Engine error in component preview mode.`;
1136        addLog(LogType.WARN, message, node.getStart(), log, sourceFile);
1137        break;
1138      }
1139    }
1140  }
1141}
1142
1143function validateStateVariable(node: ts.MethodDeclaration): void {
1144  if (node.decorators && node.decorators.length) {
1145    for (let i = 0; i < node.decorators.length; i++) {
1146      const decoratorName: string = node.decorators[i].getText().replace(/\(.*\)$/,'').trim();
1147      if (CARD_ENABLE_DECORATORS[decoratorName]) {
1148        validatorCard(transformLog.errors, CARD_LOG_TYPE_DECORATORS,
1149          node.decorators[i].getStart(), decoratorName);
1150      }
1151      if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
1152        transformLog.errors.push({
1153          type: LogType.ERROR,
1154          message: `'${node.decorators[i].getText()}' can not decorate the method.`,
1155          pos: node.decorators[i].getStart()
1156        });
1157      }
1158    }
1159  }
1160}
1161
1162export function getObservedPropertyCollection(className: string): Set<string> {
1163  const observedProperthCollection: Set<string> = new Set([
1164    ...stateCollection.get(className),
1165    ...linkCollection.get(className),
1166    ...propCollection.get(className),
1167    ...storageLinkCollection.get(className),
1168    ...storageLinkCollection.get(className),
1169    ...provideCollection.get(className),
1170    ...consumeCollection.get(className),
1171    ...objectLinkCollection.get(className)
1172  ]);
1173  getLocalStorageCollection(className, observedProperthCollection);
1174  return observedProperthCollection;
1175}
1176
1177export function getLocalStorageCollection(componentName: string, collection: Set<string>): void {
1178  if (localStorageLinkCollection.get(componentName)) {
1179    for (const key of localStorageLinkCollection.get(componentName).keys()) {
1180      collection.add(key);
1181    }
1182  }
1183  if (localStoragePropCollection.get(componentName)) {
1184    for (const key of localStoragePropCollection.get(componentName).keys()) {
1185      collection.add(key);
1186    }
1187  }
1188}
1189