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