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