• 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';
18
19import {
20  INNER_COMPONENT_DECORATORS,
21  COMPONENT_DECORATOR_ENTRY,
22  COMPONENT_DECORATOR_PREVIEW,
23  COMPONENT_DECORATOR_COMPONENT,
24  COMPONENT_DECORATOR_CUSTOM_DIALOG,
25  NATIVE_MODULE,
26  SYSTEM_PLUGIN,
27  OHOS_PLUGIN,
28  INNER_COMPONENT_MEMBER_DECORATORS,
29  COMPONENT_FOREACH,
30  COMPONENT_LAZYFOREACH,
31  COMPONENT_STATE_DECORATOR,
32  COMPONENT_LINK_DECORATOR,
33  COMPONENT_PROP_DECORATOR,
34  COMPONENT_STORAGE_PROP_DECORATOR,
35  COMPONENT_STORAGE_LINK_DECORATOR,
36  COMPONENT_PROVIDE_DECORATOR,
37  COMPONENT_CONSUME_DECORATOR,
38  COMPONENT_OBJECT_LINK_DECORATOR,
39  COMPONENT_OBSERVED_DECORATOR,
40  COMPONENT_LOCAL_STORAGE_LINK_DECORATOR,
41  COMPONENT_LOCAL_STORAGE_PROP_DECORATOR,
42  COMPONENT_BUILDER_DECORATOR,
43  COMPONENT_CONCURRENT_DECORATOR,
44  CHECK_EXTEND_DECORATORS,
45  COMPONENT_STYLES_DECORATOR,
46  RESOURCE_NAME_TYPE,
47  COMPONENT_BUTTON,
48  COMPONENT_TOGGLE,
49  COMPONENT_BUILDERPARAM_DECORATOR,
50  ESMODULE,
51  CARD_ENABLE_DECORATORS,
52  CARD_LOG_TYPE_DECORATORS,
53  JSBUNDLE,
54  COMPONENT_DECORATOR_REUSEABLE,
55  STRUCT_DECORATORS,
56  STRUCT_CONTEXT_METHOD_DECORATORS,
57  CHECK_COMPONENT_EXTEND_DECORATOR,
58  CHECK_COMPONENT_ANIMATABLE_EXTEND_DECORATOR,
59  CLASS_TRACK_DECORATOR,
60  COMPONENT_REQUIRE_DECORATOR,
61  COMPONENT_SENDABLE_DECORATOR,
62  CLASS_MIN_TRACK_DECORATOR,
63  MIN_OBSERVED,
64  COMPONENT_NON_DECORATOR,
65  COMPONENT_DECORATOR_COMPONENT_V2,
66  OBSERVED
67} from './pre_define';
68import {
69  INNER_COMPONENT_NAMES,
70  AUTOMIC_COMPONENT,
71  SINGLE_CHILD_COMPONENT,
72  SPECIFIC_CHILD_COMPONENT,
73  BUILDIN_STYLE_NAMES,
74  EXTEND_ATTRIBUTE,
75  GLOBAL_STYLE_FUNCTION,
76  STYLES_ATTRIBUTE,
77  CUSTOM_BUILDER_METHOD,
78  GLOBAL_CUSTOM_BUILDER_METHOD,
79  INNER_CUSTOM_BUILDER_METHOD,
80  INNER_STYLE_FUNCTION
81} from './component_map';
82import {
83  LogType,
84  LogInfo,
85  componentInfo,
86  addLog,
87  hasDecorator,
88  storedFileInfo,
89  ExtendResult
90} from './utils';
91import { globalProgram, projectConfig, abilityPagesFullPath } from '../main';
92import {
93  collectExtend,
94  isExtendFunction,
95  transformLog,
96  validatorCard
97} from './process_ui_syntax';
98import { stateObjectCollection } from './process_component_member';
99import { collectSharedModule } from './fast_build/ark_compiler/check_shared_module';
100import constantDefine from './constant_define';
101import processStructComponentV2, { StructInfo } from './process_struct_componentV2';
102
103export class ComponentCollection {
104  localStorageName: string = null;
105  localStorageNode: ts.Identifier | ts.ObjectLiteralExpression = null;
106  localSharedStorage: ts.Node = null;
107  entryComponentPos: number = null;
108  entryComponent: string = null;
109  previewComponent: Array<string> = [];
110  customDialogs: Set<string> = new Set([]);
111  customComponents: Set<string> = new Set([]);
112  currentClassName: string = null;
113}
114
115export class IComponentSet {
116  properties: Set<string> = new Set();
117  regulars: Set<string> = new Set();
118  states: Set<string> = new Set();
119  links: Set<string> = new Set();
120  props: Set<string> = new Set();
121  storageProps: Set<string> = new Set();
122  storageLinks: Set<string> = new Set();
123  provides: Set<string> = new Set();
124  consumes: Set<string> = new Set();
125  objectLinks: Set<string> = new Set();
126  localStorageLink: Map<string, Set<string>> = new Map();
127  localStorageProp: Map<string, Set<string>> = new Map();
128  builderParams: Set<string> = new Set();
129  builderParamData: Set<string> = new Set();
130  propData: Set<string> = new Set();
131  regularInit: Set<string> = new Set();
132  stateInit: Set<string> = new Set();
133  provideInit: Set<string> = new Set();
134  privateCollection: Set<string> = new Set();
135}
136
137export let componentCollection: ComponentCollection = new ComponentCollection();
138
139export const observedClassCollection: Set<string> = new Set();
140export const enumCollection: Set<string> = new Set();
141export const classMethodCollection: Map<string, 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();
159export const regularInitialization: Map<string, Set<string>> = new Map();
160export const stateInitialization: Map<string, Set<string>> = new Map();
161export const provideInitialization: Map<string, Set<string>> = new Map();
162export const privateCollection: Map<string, Set<string>> = new Map();
163
164export const isStaticViewCollection: Map<string, boolean> = new Map();
165
166export const useOSFiles: Set<string> = new Set();
167export const sourcemapNamesCollection: Map<string, Map<string, string>> = new Map();
168export const originalImportNamesMap: Map<string, string> = new Map();
169
170export function validateUISyntax(source: string, content: string, filePath: string,
171  fileQuery: string, sourceFile: ts.SourceFile = null): LogInfo[] {
172  let log: LogInfo[] = [];
173  if (process.env.compileMode === 'moduleJson' ||
174    path.resolve(filePath) !== path.resolve(projectConfig.projectPath || '', 'app.ets')) {
175    componentCollection = new ComponentCollection();
176    const res: LogInfo[] = checkComponentDecorator(source, filePath, fileQuery, sourceFile);
177    if (res) {
178      log = log.concat(res);
179    }
180    const allComponentNames: Set<string> =
181      new Set([...INNER_COMPONENT_NAMES, ...componentCollection.customComponents]);
182    checkUISyntax(filePath, allComponentNames, content, log, sourceFile, fileQuery);
183    componentCollection.customComponents.forEach(item => componentInfo.componentNames.add(item));
184  }
185
186  if (projectConfig.compileMode === ESMODULE) {
187    collectSharedModule(source, filePath, sourceFile);
188  }
189
190  return log;
191}
192
193function checkComponentDecorator(source: string, filePath: string,
194  fileQuery: string, sourceFile: ts.SourceFile | null): LogInfo[] | null {
195  const log: LogInfo[] = [];
196  if (!sourceFile) {
197    sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS);
198  }
199  if (sourceFile && sourceFile.statements && sourceFile.statements.length) {
200    const result: DecoratorResult = {
201      entryCount: 0,
202      previewCount: 0
203    };
204    sourceFile.statements.forEach((item, index, arr) => {
205      if (isObservedClass(item)) {
206        // @ts-ignore
207        observedClassCollection.add(item.name.getText());
208      }
209      if (ts.isEnumDeclaration(item) && item.name) {
210        enumCollection.add(item.name.getText());
211      }
212      if (ts.isStructDeclaration(item)) {
213        validateStructSpec(item, result, log, sourceFile);
214      }
215      if (ts.isMissingDeclaration(item)) {
216        const decorators = ts.getAllDecorators(item);
217        for (let i = 0; i < decorators.length; i++) {
218          if (decorators[i] && /struct/.test(decorators[i].getText())) {
219            const message: string = `Please use a valid decorator.`;
220            addLog(LogType.ERROR, message, item.getStart(), log, sourceFile);
221            break;
222          }
223        }
224      }
225    });
226    if (process.env.compileTool === 'rollup') {
227      if (result.entryCount > 0) {
228        storedFileInfo.wholeFileInfo[filePath].hasEntry = true;
229      } else {
230        storedFileInfo.wholeFileInfo[filePath].hasEntry = false;
231      }
232    }
233    validateEntryAndPreviewCount(result, fileQuery, sourceFile.fileName, projectConfig.isPreview,
234      !!projectConfig.checkEntry, log);
235  }
236
237  return log.length ? log : null;
238}
239
240function validateStructSpec(item: ts.StructDeclaration, result: DecoratorResult, log: LogInfo[],
241  sourceFile: ts.SourceFile | null): void {
242  if (item.name && ts.isIdentifier(item.name)) {
243    const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item);
244    if (decorators && decorators.length) {
245      checkDecorators(decorators, result, item.name, log, sourceFile, item);
246    } else {
247      const message: string = `A struct should use decorator '@Component' or '@ComponentV2'.`;
248      addLog(LogType.WARN, message, item.getStart(), log, sourceFile);
249    }
250  } else {
251    const message: string = `A struct must have a name.`;
252    addLog(LogType.ERROR, message, item.getStart(), log, sourceFile);
253  }
254}
255
256function validateEntryAndPreviewCount(result: DecoratorResult, fileQuery: string,
257  fileName: string, isPreview: boolean, checkEntry: boolean, log: LogInfo[]): void {
258  if (result.previewCount > 10 && (fileQuery === '?entry' || process.env.watchMode === 'true')) {
259    log.push({
260      type: LogType.ERROR,
261      message: `A page can contain at most 10 '@Preview' decorators.`,
262      fileName: fileName
263    });
264  }
265  if (result.entryCount > 1 && fileQuery === '?entry') {
266    log.push({
267      type: LogType.ERROR,
268      message: `A page can't contain more than one '@Entry' decorator`,
269      fileName: fileName
270    });
271  }
272  if (isPreview && !checkEntry && result.previewCount < 1 && result.entryCount !== 1 &&
273    fileQuery === '?entry') {
274    log.push({
275      type: LogType.ERROR,
276      message: `A page which is being previewed must have one and only one '@Entry' ` +
277        `decorator, or at least one '@Preview' decorator.`,
278      fileName: fileName
279    });
280  } else if ((!isPreview || isPreview && checkEntry) && result.entryCount !== 1 && fileQuery === '?entry' &&
281    !abilityPagesFullPath.includes(path.resolve(fileName).toLowerCase())) {
282    log.push({
283      type: LogType.ERROR,
284      message: `A page configured in '${projectConfig.pagesJsonFileName} or build-profile.json5' must have one and only one '@Entry' decorator.` +
285        `Solutions:>Please make sure that the splash page has one and only one '@Entry' decorator.`,
286      fileName: fileName
287    });
288  }
289}
290
291export function isObservedClass(node: ts.Node): boolean {
292  if (ts.isClassDeclaration(node) && hasDecorator(node, COMPONENT_OBSERVED_DECORATOR)) {
293    return true;
294  }
295  return false;
296}
297
298export function isCustomDialogClass(node: ts.Node): boolean {
299  if (ts.isStructDeclaration(node) && hasDecorator(node, COMPONENT_DECORATOR_CUSTOM_DIALOG)) {
300    return true;
301  }
302  return false;
303}
304
305interface DecoratorResult {
306  entryCount: number;
307  previewCount: number;
308}
309
310function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorResult,
311  component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile, node: ts.StructDeclaration): void {
312  let hasComponentDecorator: boolean = false;
313  const componentName: string = component.getText();
314  const structInfo: StructInfo = processStructComponentV2.getOrCreateStructInfo(componentName);
315  decorators.forEach((element) => {
316    let name: string = element.getText().replace(/\([^\(\)]*\)/, '').trim();
317    if (element.expression && element.expression.expression && ts.isIdentifier(element.expression.expression)) {
318      name = '@' + element.expression.expression.getText();
319    }
320    if (INNER_COMPONENT_DECORATORS.has(name)) {
321      componentCollection.customComponents.add(componentName);
322      switch (name) {
323        case COMPONENT_DECORATOR_ENTRY:
324          checkEntryComponent(node, log, sourceFile);
325          result.entryCount++;
326          componentCollection.entryComponent = componentName;
327          componentCollection.entryComponentPos = node.getStart();
328          collectLocalStorageName(element);
329          break;
330        case COMPONENT_DECORATOR_PREVIEW:
331          result.previewCount++;
332          componentCollection.previewComponent.push(componentName);
333          break;
334        case COMPONENT_DECORATOR_COMPONENT_V2:
335          hasComponentDecorator = true;
336          structInfo.isComponentV2 = true;
337          break;
338        case COMPONENT_DECORATOR_COMPONENT:
339          hasComponentDecorator = true;
340          structInfo.isComponentV1 = true;
341          break;
342        case COMPONENT_DECORATOR_CUSTOM_DIALOG:
343          componentCollection.customDialogs.add(componentName);
344          hasComponentDecorator = true;
345          structInfo.isCustomDialog = true;
346          break;
347        case COMPONENT_DECORATOR_REUSEABLE:
348          storedFileInfo.getCurrentArkTsFile().recycleComponents.add(componentName);
349          hasComponentDecorator = true;
350          structInfo.isReusable = true;
351          break;
352      }
353    } else {
354      validateInvalidStructDecorator(element, componentName, log, sourceFile);
355    }
356  });
357  validateStruct(hasComponentDecorator, componentName, component, log, sourceFile, structInfo);
358}
359
360function validateInvalidStructDecorator(element: ts.Decorator, componentName: string, log: LogInfo[],
361  sourceFile: ts.SourceFile): void {
362  const pos: number = element.expression ? element.expression.pos : element.pos;
363  const message: string = `The struct '${componentName}' use invalid decorator.`;
364  addLog(LogType.WARN, message, pos, log, sourceFile);
365}
366
367function validateStruct(hasComponentDecorator: boolean, componentName: string, component: ts.Identifier,
368  log: LogInfo[], sourceFile: ts.SourceFile, structInfo: StructInfo): void {
369  if (!hasComponentDecorator) {
370    const message: string = `The struct '${componentName}' should use decorator '@Component'.`;
371    addLog(LogType.WARN, message, component.pos, log, sourceFile);
372  } else if (structInfo.isComponentV2 && (structInfo.isComponentV1 || structInfo.isReusable || structInfo.isCustomDialog) ) {
373    const message: string = `The struct '${componentName}' can not be decorated with '@ComponentV2' ` +
374      `and '@Component', '@Reusable', '@CustomDialog' at the same time.`;
375    addLog(LogType.ERROR, message, component.pos, log, sourceFile);
376  }
377  if (BUILDIN_STYLE_NAMES.has(componentName)) {
378    const message: string = `The struct '${componentName}' cannot have the same name ` +
379      `as the built-in attribute '${componentName}'.`;
380    addLog(LogType.ERROR, message, component.pos, log, sourceFile);
381  }
382  if (INNER_COMPONENT_NAMES.has(componentName)) {
383    const message: string = `The struct '${componentName}' cannot have the same name ` +
384      `as the built-in component '${componentName}'.`;
385    addLog(LogType.ERROR, message, component.pos, log, sourceFile);
386  }
387}
388
389function checkConcurrentDecorator(node: ts.FunctionDeclaration | ts.MethodDeclaration, log: LogInfo[],
390  sourceFile: ts.SourceFile): void {
391  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
392  if (projectConfig.compileMode === JSBUNDLE) {
393    const message: string = `@Concurrent can only be used in ESMODULE compile mode.`;
394    addLog(LogType.ERROR, message, decorators![0].pos, log, sourceFile);
395  }
396  if (ts.isMethodDeclaration(node)) {
397    const message: string = `@Concurrent can not be used on method. please use it on function declaration.`;
398    addLog(LogType.ERROR, message, decorators![0].pos, log, sourceFile);
399  }
400  if (node.asteriskToken) {
401    let hasAsync: boolean = false;
402    const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
403    const checkAsyncModifier = (modifier: ts.Modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword;
404    modifiers && (hasAsync = modifiers.some(checkAsyncModifier));
405    const funcKind: string = hasAsync ? 'Async generator' : 'Generator';
406    const message: string = `@Concurrent can not be used on ${funcKind} function declaration.`;
407    addLog(LogType.ERROR, message, decorators![0].pos, log, sourceFile);
408  }
409}
410
411function collectLocalStorageName(node: ts.Decorator): void {
412  if (node && node.expression && ts.isCallExpression(node.expression)) {
413    if (node.expression.arguments && node.expression.arguments.length) {
414      node.expression.arguments.forEach((item: ts.Node, index: number) => {
415        if (ts.isIdentifier(item) && index === 0) {
416          componentCollection.localStorageName = item.getText();
417          componentCollection.localStorageNode = item;
418        } else if (ts.isObjectLiteralExpression(item) && index === 0) {
419          componentCollection.localStorageName = null;
420          componentCollection.localStorageNode = item;
421        } else {
422          componentCollection.localSharedStorage = item;
423        }
424      });
425    }
426  } else {
427    componentCollection.localStorageName = null;
428    componentCollection.localStorageNode = null;
429  }
430}
431
432function checkUISyntax(filePath: string, allComponentNames: Set<string>, content: string,
433  log: LogInfo[], sourceFile: ts.SourceFile | null, fileQuery: string): void {
434  if (!sourceFile) {
435    sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS);
436  }
437  visitAllNode(sourceFile, sourceFile, allComponentNames, log, false, false, false, false, fileQuery);
438}
439
440function propertyInitializeInEntry(fileQuery: string, name: string): boolean {
441  return fileQuery === '?entry' && name === componentCollection.entryComponent;
442}
443
444function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponentNames: Set<string>,
445  log: LogInfo[], structContext: boolean, classContext: boolean, isObservedClass: boolean,
446  isComponentV2: boolean, fileQuery: string): void {
447  if (ts.isStructDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
448    structContext = true;
449    const structName: string = node.name.escapedText.toString();
450    const structInfo: StructInfo = processStructComponentV2.getOrCreateStructInfo(structName);
451    if (structInfo.isComponentV2) {
452      processStructComponentV2.parseComponentProperty(node, structInfo, log, sourceFileNode);
453      isComponentV2 = true;
454    } else {
455      collectComponentProps(node, propertyInitializeInEntry(fileQuery, structName), structInfo);
456    }
457  }
458  if (ts.isClassDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
459    classContext = true;
460    isObservedClass = parseClassDecorator(node, sourceFileNode, log);
461  }
462  if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) {
463    methodDecoratorCollect(node);
464    if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) {
465      // ark compiler's feature
466      checkConcurrentDecorator(node, log, sourceFileNode);
467    }
468  }
469  checkDecoratorCount(node, sourceFileNode, log);
470  checkDecorator(sourceFileNode, node, log, structContext, classContext, isObservedClass, isComponentV2);
471  node.getChildren().forEach((item: ts.Node) => visitAllNode(item, sourceFileNode, allComponentNames,
472    log, structContext, classContext, isObservedClass, isComponentV2, fileQuery));
473  structContext = false;
474  classContext = false;
475  isObservedClass = false;
476}
477
478function checkDecoratorCount(node: ts.Node, sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
479  if (ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isMethodDeclaration(node)) {
480    const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
481    let innerDecoratorCount: number = 0;
482    const exludeDecorators: string[] = ['@Require', '@Once'];
483    decorators.forEach((item: ts.Decorator) => {
484      const decoratorName: string = item.getText().replace(/\([^\(\)]*\)/, '');
485      if (!exludeDecorators.includes(decoratorName) && (constantDefine.DECORATOR_V2.includes(decoratorName) ||
486        decoratorName === '@BuilderParam')) {
487        innerDecoratorCount++;
488      }
489    });
490    if (innerDecoratorCount > 1) {
491      const message: string = 'The member property or method can not be decorated by multiple built-in decorators.';
492      addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode);
493    }
494  }
495}
496
497function methodDecoratorCollect(node: ts.MethodDeclaration | ts.FunctionDeclaration): void {
498  const extendResult: ExtendResult = { decoratorName: '', componentName: '' };
499  if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) {
500    CUSTOM_BUILDER_METHOD.add(node.name.getText());
501    if (ts.isFunctionDeclaration(node)) {
502      GLOBAL_CUSTOM_BUILDER_METHOD.add(node.name.getText());
503    } else {
504      INNER_CUSTOM_BUILDER_METHOD.add(node.name.getText());
505    }
506  } else if (ts.isFunctionDeclaration(node) && isExtendFunction(node, extendResult)) {
507    if (extendResult.decoratorName === CHECK_COMPONENT_EXTEND_DECORATOR) {
508      collectExtend(EXTEND_ATTRIBUTE, extendResult.componentName, node.name.getText());
509    }
510    if (extendResult.decoratorName === CHECK_COMPONENT_ANIMATABLE_EXTEND_DECORATOR) {
511      collectExtend(storedFileInfo.getCurrentArkTsFile().animatableExtendAttribute,
512        extendResult.componentName, node.name.getText());
513    }
514  } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) {
515    collectStyles(node);
516  }
517}
518
519function collectStyles(node: ts.FunctionLikeDeclarationBase): void {
520  if (ts.isBlock(node.body) && node.body.statements) {
521    if (ts.isFunctionDeclaration(node)) {
522      GLOBAL_STYLE_FUNCTION.set(node.name.getText(), node.body);
523    } else {
524      INNER_STYLE_FUNCTION.set(node.name.getText(), node.body);
525    }
526    STYLES_ATTRIBUTE.add(node.name.getText());
527    BUILDIN_STYLE_NAMES.add(node.name.getText());
528  }
529}
530
531function checkDecorator(sourceFileNode: ts.SourceFile, node: ts.Node,
532  log: LogInfo[], structContext: boolean, classContext: boolean, isObservedClass: boolean,
533  isComponentV2: boolean): void {
534  if (ts.isIdentifier(node) && (ts.isDecorator(node.parent) ||
535    (ts.isCallExpression(node.parent) && ts.isDecorator(node.parent.parent)))) {
536    const decoratorName: string = node.escapedText.toString();
537    validateStructDecorator(sourceFileNode, node, log, structContext, decoratorName, isComponentV2);
538    validateMethodDecorator(sourceFileNode, node, log, structContext, decoratorName);
539    validateClassDecorator(sourceFileNode, node, log, classContext, decoratorName, isObservedClass);
540    return;
541  }
542  if (ts.isDecorator(node)) {
543    validateSingleDecorator(node, sourceFileNode, log);
544  }
545}
546
547function validateSingleDecorator(node: ts.Decorator, sourceFileNode: ts.SourceFile,
548  log: LogInfo[]): void {
549  const decoratorName: string = node.getText().replace(/\([^\(\)]*\)/, '');
550  if (decoratorName === constantDefine.COMPUTED_DECORATOR && node.parent && !ts.isGetAccessor(node.parent)) {
551    const message: string = `@Computed can only decorate 'GetAccessor'.`;
552    addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode);
553    return;
554  }
555  if (decoratorName === constantDefine.MONITOR_DECORATOR && node.parent &&
556    !ts.isMethodDeclaration(node.parent)) {
557    const message: string = '@Monitor can only decorate method.';
558    addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode);
559    return;
560  }
561  if (constantDefine.COMPONENT_MEMBER_DECORATOR_V2.includes(decoratorName) && node.parent &&
562    !ts.isPropertyDeclaration(node.parent)) {
563    const message: string = `'${decoratorName}' can only decorate member property.`;
564    addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode);
565    return;
566  }
567}
568
569const classDecorators: string[] = [CLASS_TRACK_DECORATOR, CLASS_MIN_TRACK_DECORATOR, MIN_OBSERVED];
570const classMemberDecorators: string[] = [CLASS_TRACK_DECORATOR, CLASS_MIN_TRACK_DECORATOR,
571  constantDefine.MONITOR, constantDefine.COMPUTED];
572
573function validateClassDecorator(sourceFileNode: ts.SourceFile, node: ts.Identifier, log: LogInfo[],
574  classContext: boolean, decoratorName: string, isObservedClass: boolean): void {
575  if (!classContext && classDecorators.includes(decoratorName)) {
576    const message: string = `The '@${decoratorName}' decorator can only be used in 'class'.`;
577    addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
578  } else if ('@' + decoratorName === COMPONENT_SENDABLE_DECORATOR &&
579      (!node.parent || !node.parent.parent || !ts.isClassDeclaration(node.parent.parent))) {
580    const message: string = 'The \'@Sendable\' decorator can only be added to \'class\'.';
581    addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
582  } else if (classContext && classMemberDecorators.includes(decoratorName)) {
583    validateMemberInClass(isObservedClass, decoratorName, node, log, sourceFileNode);
584  }
585}
586
587function validateMemberInClass(isObservedClass: boolean, decoratorName: string, node: ts.Identifier,
588  log: LogInfo[], sourceFileNode: ts.SourceFile): void {
589  if (decoratorName === CLASS_TRACK_DECORATOR) {
590    if (isObservedClass) {
591      const message: string = `The '@${decoratorName}' decorator can not be used in a 'class' decorated with ObservedV2.`;
592      addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
593    }
594    return;
595  }
596  if (!isObservedClass || !isPropertyForTrace(node, decoratorName)) {
597    const info: string = decoratorName === CLASS_MIN_TRACK_DECORATOR ? 'variables' : 'method';
598    const message: string = `The '@${decoratorName}' can decorate only member ${info} within a 'class' decorated with ObservedV2.`;
599    addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
600    return;
601  }
602}
603
604function isPropertyForTrace(node: ts.Identifier, decoratorName: string): boolean {
605  if (decoratorName === CLASS_MIN_TRACK_DECORATOR && ts.isDecorator(node.parent) &&
606    !ts.isPropertyDeclaration(node.parent.parent)) {
607    return false;
608  }
609  return true;
610}
611
612class ClassDecoratorResult {
613  hasObserved: boolean = false;
614  hasObservedV2: boolean = false;
615}
616
617function parseClassDecorator(node: ts.ClassDeclaration, sourceFileNode: ts.SourceFile,
618  log: LogInfo[]): boolean {
619  const classResult: ClassDecoratorResult = getClassDecoratorResult(node);
620  validateMutilObserved(node, classResult, sourceFileNode, log);
621  if (classResult.hasObserved || classResult.hasObservedV2) {
622    parseInheritClass(node, classResult, sourceFileNode, log);
623  }
624  return classResult.hasObservedV2;
625}
626
627function getClassDecoratorResult(node: ts.ClassDeclaration): ClassDecoratorResult {
628  const classResult: ClassDecoratorResult = new ClassDecoratorResult();
629  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
630  decorators.forEach((item: ts.Decorator) => {
631    if (ts.isIdentifier(item.expression)) {
632      const decoratorName: string = item.expression.escapedText.toString();
633      switch (decoratorName) {
634        case MIN_OBSERVED:
635          classResult.hasObservedV2 = true;
636          break;
637        case OBSERVED:
638          classResult.hasObserved = true;
639          break;
640      }
641    }
642  });
643  return classResult;
644}
645
646function validateMutilObserved(node: ts.ClassDeclaration, classResult: ClassDecoratorResult,
647  sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
648  if (classResult.hasObserved && classResult.hasObservedV2) {
649    const message: string = `A class can not be decorated by '@Observed' and '@ObservedV2' at the same time.`;
650    addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode);
651  }
652}
653
654function parseInheritClass(node: ts.ClassDeclaration, childClassResult: ClassDecoratorResult,
655  sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
656  if (globalProgram.checker && process.env.compileTool === 'rollup' && node.heritageClauses) {
657    for (const heritageClause of node.heritageClauses) {
658      if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword && heritageClause.types &&
659        heritageClause.types.length) {
660        getClassNode(heritageClause.types[0].expression, childClassResult, node, sourceFileNode, log);
661      }
662    }
663  }
664}
665
666function getClassNode(parentType: ts.Node, childClassResult: ClassDecoratorResult,
667  childClass: ts.ClassDeclaration, sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
668  const symbol: ts.Symbol = parentType && getSymbolIfAliased(parentType);
669  if (symbol && symbol.valueDeclaration) {
670    if (ts.isClassDeclaration(symbol.valueDeclaration)) {
671      validateInheritClassDecorator(symbol.valueDeclaration, childClassResult, childClass, sourceFileNode, log);
672      return;
673    }
674    if (ts.isPropertyAssignment(symbol.valueDeclaration)) {
675      getClassNode(symbol.valueDeclaration.initializer, childClassResult, childClass, sourceFileNode, log);
676      return;
677    }
678    if (ts.isShorthandPropertyAssignment(symbol.valueDeclaration)) {
679      parseShorthandPropertyForClass(symbol.valueDeclaration, childClassResult, childClass, sourceFileNode, log);
680      return;
681    }
682  }
683}
684
685function parseShorthandPropertyForClass(node: ts.ShorthandPropertyAssignment, childClassResult: ClassDecoratorResult,
686  childClass: ts.ClassDeclaration, sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
687  const shortSymbol: ts.Symbol = globalProgram.checker.getShorthandAssignmentValueSymbol(node);
688  if (shortSymbol && shortSymbol.valueDeclaration && ts.isClassDeclaration(shortSymbol.valueDeclaration)) {
689    validateInheritClassDecorator(shortSymbol.valueDeclaration, childClassResult, childClass, sourceFileNode, log);
690  }
691}
692
693export function getSymbolIfAliased(node: ts.Node): ts.Symbol {
694  const symbol: ts.Symbol = globalProgram.checker.getSymbolAtLocation(node);
695  if (symbol && (symbol.getFlags() & ts.SymbolFlags.Alias) !== 0) {
696    return globalProgram.checker.getAliasedSymbol(symbol);
697  }
698  return symbol;
699}
700
701function validateInheritClassDecorator(parentNode: ts.ClassDeclaration, childClassResult: ClassDecoratorResult,
702  childClass: ts.ClassDeclaration, sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
703  const parentClassResult: ClassDecoratorResult = getClassDecoratorResult(parentNode);
704  if (childClassResult.hasObservedV2 && parentClassResult.hasObserved) {
705    const message: string = `Because the current class is decorated by '@ObservedV2', ` +
706      `it can not inherit a class decorated by '@Observed'.`;
707    addLog(LogType.ERROR, message, childClass.getStart(), log, sourceFileNode);
708    return;
709  }
710  if (childClassResult.hasObserved && parentClassResult.hasObservedV2) {
711    const message: string = `Because the current class is decorated by '@Observed', ` +
712      `it can not inherit a class decorated by '@ObservedV2'.`;
713    addLog(LogType.ERROR, message, childClass.getStart(), log, sourceFileNode);
714    return;
715  }
716}
717
718function validateStructDecorator(sourceFileNode: ts.SourceFile, node: ts.Identifier, log: LogInfo[],
719  structContext: boolean, decoratorName: string, isComponentV2: boolean): void {
720  const name: string = `@${decoratorName}`;
721  if (structContext) {
722    if (isComponentV2) {
723      if (constantDefine.COMPONENT_MEMBER_DECORATOR_V1.includes(name)) {
724        const message: string = `The '@${decoratorName}' decorator can only be used in a 'struct' decorated with '@Component'.`;
725        addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
726      }
727    } else if (constantDefine.DECORATOR_V2.includes(name)) {
728      const message: string = `The '@${decoratorName}' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`;
729      addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
730    }
731  } else if (STRUCT_DECORATORS.has(name) || constantDefine.COMPONENT_MEMBER_DECORATOR_V2.includes(name)) {
732    const message: string = `The '@${decoratorName}' decorator can only be used with 'struct'.`;
733    addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
734  }
735}
736
737function validateMethodDecorator(sourceFileNode: ts.SourceFile, node: ts.Identifier, log: LogInfo[],
738  structContext: boolean, decoratorName: string): void {
739  let message: string;
740  if (ts.isMethodDeclaration(node.parent.parent) ||
741    (ts.isDecorator(node.parent.parent) && ts.isMethodDeclaration(node.parent.parent.parent))) {
742    if (!structContext && STRUCT_CONTEXT_METHOD_DECORATORS.has(`@${decoratorName}`)) {
743      message = `The '@${decoratorName}' decorator can only be used in 'struct'.`;
744    }
745    if (CHECK_EXTEND_DECORATORS.includes(decoratorName)) {
746      message = `The '@${decoratorName}' decorator can not be a member property method of a 'class' or 'struct'.`;
747    }
748    if (message) {
749      addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
750    }
751  }
752}
753
754export function isSendableClassDeclaration(classDeclarationNode: ts.ClassDeclaration): boolean {
755  return hasDecorator(classDeclarationNode, COMPONENT_SENDABLE_DECORATOR) ||
756      (classDeclarationNode.members && classDeclarationNode.members.some((member: ts.Node) => {
757        // Check if the constructor has "use sendable" as the first statement
758        // (Sendable classes already transformed by process_ui_syntax.ts)
759        if (ts.isConstructorDeclaration(member)) {
760          if (!(member as ts.ConstructorDeclaration).body ||
761              !(member as ts.ConstructorDeclaration).body.statements) {
762            return false;
763          }
764          const constructorStatements: ts.Statement[] = (member as ts.ConstructorDeclaration).body.statements;
765          if (constructorStatements && constructorStatements[0] &&
766              ts.isExpressionStatement(constructorStatements[0])) {
767            const expression: ts.Node = (constructorStatements[0] as ts.ExpressionStatement).expression;
768            return expression && ts.isStringLiteral(expression) &&
769                (expression as ts.StringLiteral).text === 'use sendable';
770          }
771        }
772        return false;
773      }));
774}
775
776export function checkAllNode(
777  node: ts.EtsComponentExpression,
778  allComponentNames: Set<string>,
779  sourceFileNode: ts.SourceFile,
780  log: LogInfo[]
781): void {
782  if (ts.isIdentifier(node.expression)) {
783    checkNoChildComponent(node, sourceFileNode, log);
784    checkOneChildComponent(node, allComponentNames, sourceFileNode, log);
785    checkSpecificChildComponent(node, allComponentNames, sourceFileNode, log);
786  }
787}
788
789interface ParamType {
790  name: string,
791  value: string,
792}
793
794function checkNoChildComponent(node: ts.EtsComponentExpression, sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
795  const isCheckType: ParamType = { name: null, value: null};
796  if (hasChild(node, isCheckType)) {
797    const componentName: string = (node.expression as ts.Identifier).escapedText.toString();
798    const pos: number = node.expression.getStart();
799    const message: string = isCheckType.name === null ?
800      `The component '${componentName}' can't have any child.` :
801      `When the component '${componentName}' set '${isCheckType.name}' is '${isCheckType.value}'` +
802        `, can't have any child.`;
803    addLog(LogType.ERROR, message, pos, log, sourceFileNode);
804  }
805}
806
807function hasChild(node: ts.EtsComponentExpression, isCheckType: ParamType): boolean {
808  const nodeName: ts.Identifier = node.expression as ts.Identifier;
809  if ((AUTOMIC_COMPONENT.has(nodeName.escapedText.toString()) || judgeComponentType(nodeName, node, isCheckType)) &&
810    getNextNode(node)) {
811    return true;
812  }
813  return false;
814}
815
816function judgeComponentType(nodeName: ts.Identifier, etsComponentExpression: ts.EtsComponentExpression,
817  isCheckType: ParamType): boolean {
818  return COMPONENT_TOGGLE === nodeName.escapedText.toString() &&
819    etsComponentExpression.arguments && etsComponentExpression.arguments[0] &&
820    ts.isObjectLiteralExpression(etsComponentExpression.arguments[0]) &&
821    etsComponentExpression.arguments[0].getText() &&
822    judgeToggleComponentParamType(etsComponentExpression.arguments[0].getText(), isCheckType);
823}
824
825function judgeToggleComponentParamType(param: string, isCheckType: ParamType): boolean {
826  if (param.indexOf(RESOURCE_NAME_TYPE) > -1) {
827    isCheckType.name = RESOURCE_NAME_TYPE;
828    const match: string[] = param.match(/\b(Checkbox|Switch|Button)\b/);
829    if (match && match.length) {
830      isCheckType.value = match[0];
831      if (isCheckType.value === COMPONENT_BUTTON) {
832        return false;
833      }
834      return true;
835    }
836  }
837  return false;
838}
839
840function getNextNode(node: ts.EtsComponentExpression): ts.Block {
841  if (node.body && ts.isBlock(node.body)) {
842    const statementsArray: ts.Block = node.body;
843    return statementsArray;
844  }
845}
846
847function checkOneChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set<string>,
848  sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
849  const isCheckType: ParamType = { name: null, value: null};
850  if (hasNonSingleChild(node, allComponentNames, isCheckType)) {
851    const componentName: string = (node.expression as ts.Identifier).escapedText.toString();
852    const pos: number = node.expression.getStart();
853    const message: string = isCheckType.name === null ?
854      `The component '${componentName}' can only have a single child component.` :
855      `When the component '${componentName}' set '${isCheckType.name}' is ` +
856        `'${isCheckType.value}', can only have a single child component.`;
857    addLog(LogType.ERROR, message, pos, log, sourceFileNode);
858  }
859}
860
861function hasNonSingleChild(node: ts.EtsComponentExpression, allComponentNames: Set<string>,
862  isCheckType: ParamType): boolean {
863  const nodeName: ts.Identifier = node.expression as ts.Identifier;
864  const BlockNode: ts.Block = getNextNode(node);
865  if (SINGLE_CHILD_COMPONENT.has(nodeName.escapedText.toString()) || !judgeComponentType(nodeName, node, isCheckType) &&
866    isCheckType.value === COMPONENT_BUTTON) {
867    if (!BlockNode) {
868      return false;
869    }
870    if (BlockNode && BlockNode.statements) {
871      const length: number = BlockNode.statements.length;
872      if (!length) {
873        return false;
874      }
875      if (length > 3) {
876        return true;
877      }
878      const childCount: number = getBlockChildrenCount(BlockNode, allComponentNames);
879      if (childCount > 1) {
880        return true;
881      }
882    }
883  }
884  return false;
885}
886
887function getBlockChildrenCount(blockNode: ts.Block, allComponentNames: Set<string>): number {
888  let maxCount: number = 0;
889  const length: number = blockNode.statements.length;
890  for (let i = 0; i < length; ++i) {
891    const item: ts.Node = blockNode.statements[i];
892    if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression) &&
893      isForEachComponent(item.expression)) {
894      maxCount += 2;
895    }
896    if (ts.isIfStatement(item)) {
897      maxCount += getIfChildrenCount(item, allComponentNames);
898    }
899    if (ts.isExpressionStatement(item) && ts.isEtsComponentExpression(item.expression)) {
900      maxCount += 1;
901    }
902    if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression)) {
903      let newNode: any = item.expression;
904      while (newNode.expression) {
905        if (ts.isEtsComponentExpression(newNode) || ts.isCallExpression(newNode) &&
906          isComponent(newNode, allComponentNames)) {
907          maxCount += 1;
908        }
909        newNode = newNode.expression;
910      }
911    }
912    if (maxCount > 1) {
913      break;
914    }
915  }
916  return maxCount;
917}
918
919function isComponent(node: ts.EtsComponentExpression | ts.CallExpression, allComponentNames: Set<string>): boolean {
920  if (ts.isIdentifier(node.expression) &&
921    allComponentNames.has(node.expression.escapedText.toString())) {
922    return true;
923  }
924  return false;
925}
926
927function isForEachComponent(node: ts.EtsComponentExpression | ts.CallExpression): boolean {
928  if (ts.isIdentifier(node.expression)) {
929    const componentName: string = node.expression.escapedText.toString();
930    return componentName === COMPONENT_FOREACH || componentName === COMPONENT_LAZYFOREACH;
931  }
932  return false;
933}
934
935function getIfChildrenCount(ifNode: ts.IfStatement, allComponentNames: Set<string>): number {
936  const maxCount: number =
937    Math.max(getStatementCount(ifNode.thenStatement, allComponentNames),
938      getStatementCount(ifNode.elseStatement, allComponentNames));
939  return maxCount;
940}
941
942function getStatementCount(node: ts.Node, allComponentNames: Set<string>): number {
943  let maxCount: number = 0;
944  if (!node) {
945    return maxCount;
946  } else if (ts.isBlock(node)) {
947    maxCount = getBlockChildrenCount(node, allComponentNames);
948  } else if (ts.isIfStatement(node)) {
949    maxCount = getIfChildrenCount(node, allComponentNames);
950  } else if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) &&
951    isForEachComponent(node.expression)) {
952    maxCount = 2;
953  } else if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) &&
954    !isForEachComponent(node.expression) && isComponent(node.expression, allComponentNames)) {
955    maxCount = 1;
956  }
957  return maxCount;
958}
959
960function checkSpecificChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set<string>,
961  sourceFileNode: ts.SourceFile, log: LogInfo[]): void {
962  if (hasNonspecificChild(node, allComponentNames)) {
963    const componentName: string = (node.expression as ts.Identifier).escapedText.toString();
964    const pos: number = node.expression.getStart();
965    const specificChildArray: string =
966      Array.from(SPECIFIC_CHILD_COMPONENT.get(componentName)).join(' and ');
967    const message: string =
968      `The component '${componentName}' can only have the child component ${specificChildArray}.`;
969    addLog(LogType.ERROR, message, pos, log, sourceFileNode);
970  }
971}
972
973function hasNonspecificChild(node: ts.EtsComponentExpression,
974  allComponentNames: Set<string>): boolean {
975  const nodeName: ts.Identifier = node.expression as ts.Identifier;
976  const nodeNameString: string = nodeName.escapedText.toString();
977  const blockNode: ts.Block = getNextNode(node);
978  let isNonspecific: boolean = false;
979  if (SPECIFIC_CHILD_COMPONENT.has(nodeNameString) && blockNode) {
980    const specificChildSet: Set<string> = SPECIFIC_CHILD_COMPONENT.get(nodeNameString);
981    isNonspecific = isNonspecificChildBlock(blockNode, specificChildSet, allComponentNames);
982    if (isNonspecific) {
983      return isNonspecific;
984    }
985  }
986  return isNonspecific;
987}
988
989function isNonspecificChildBlock(blockNode: ts.Block, specificChildSet: Set<string>,
990  allComponentNames: Set<string>): boolean {
991  if (blockNode.statements) {
992    const length: number = blockNode.statements.length;
993    for (let i = 0; i < length; ++i) {
994      const item: ts.Node = blockNode.statements[i];
995      if (ts.isIfStatement(item) && isNonspecificChildIf(item, specificChildSet, allComponentNames)) {
996        return true;
997      }
998      if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression) &&
999        isForEachComponent(item.expression) &&
1000        isNonspecificChildForEach(item.expression, specificChildSet, allComponentNames)) {
1001        return true;
1002      }
1003      if (ts.isBlock(item) && isNonspecificChildBlock(item, specificChildSet, allComponentNames)) {
1004        return true;
1005      }
1006      if (ts.isExpressionStatement(item)) {
1007        let newNode: any = item.expression;
1008        while (newNode.expression) {
1009          if (ts.isEtsComponentExpression(newNode) && ts.isIdentifier(newNode.expression) &&
1010          !isForEachComponent(newNode) && isComponent(newNode, allComponentNames)) {
1011            const isNonspecific: boolean =
1012            isNonspecificChildNonForEach(newNode, specificChildSet);
1013            if (isNonspecific) {
1014              return isNonspecific;
1015            }
1016            if (i + 1 < length && ts.isBlock(blockNode.statements[i + 1])) {
1017              ++i;
1018            }
1019          }
1020          newNode = newNode.expression;
1021        }
1022      }
1023    }
1024  }
1025  return false;
1026}
1027
1028function isNonspecificChildIf(node: ts.IfStatement, specificChildSet: Set<string>,
1029  allComponentNames: Set<string>): boolean {
1030  return isNonspecificChildIfStatement(node.thenStatement, specificChildSet, allComponentNames) ||
1031    isNonspecificChildIfStatement(node.elseStatement, specificChildSet, allComponentNames);
1032}
1033
1034function isNonspecificChildForEach(node: ts.EtsComponentExpression, specificChildSet: Set<string>,
1035  allComponentNames: Set<string>): boolean {
1036  if (ts.isCallExpression(node) && node.arguments &&
1037    node.arguments.length > 1 && ts.isArrowFunction(node.arguments[1])) {
1038    const arrowFunction: ts.ArrowFunction = node.arguments[1] as ts.ArrowFunction;
1039    const body: ts.Block | ts.EtsComponentExpression | ts.IfStatement =
1040      arrowFunction.body as ts.Block | ts.EtsComponentExpression | ts.IfStatement;
1041    if (!body) {
1042      return false;
1043    }
1044    if (ts.isBlock(body) && isNonspecificChildBlock(body, specificChildSet, allComponentNames)) {
1045      return true;
1046    }
1047    if (ts.isIfStatement(body) && isNonspecificChildIf(body, specificChildSet, allComponentNames)) {
1048      return true;
1049    }
1050    if (ts.isCallExpression(body) && isForEachComponent(body) &&
1051      isNonspecificChildForEach(body, specificChildSet, allComponentNames)) {
1052      return true;
1053    }
1054    if (ts.isEtsComponentExpression(body) && !isForEachComponent(body) &&
1055      isComponent(body, allComponentNames) &&
1056      isNonspecificChildNonForEach(body, specificChildSet)) {
1057      return true;
1058    }
1059  }
1060  return false;
1061}
1062
1063function isNonspecificChildNonForEach(node: ts.EtsComponentExpression,
1064  specificChildSet: Set<string>): boolean {
1065  if (ts.isIdentifier(node.expression) &&
1066    !specificChildSet.has(node.expression.escapedText.toString())) {
1067    return true;
1068  }
1069  return false;
1070}
1071
1072function isNonspecificChildIfStatement(node: ts.Node, specificChildSet: Set<string>,
1073  allComponentNames: Set<string>): boolean {
1074  if (!node) {
1075    return false;
1076  }
1077  if (ts.isBlock(node) && isNonspecificChildBlock(node, specificChildSet, allComponentNames)) {
1078    return true;
1079  }
1080  if (ts.isIfStatement(node) && isNonspecificChildIf(node, specificChildSet, allComponentNames)) {
1081    return true;
1082  }
1083  if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) &&
1084    isForEachComponent(node.expression) &&
1085    isNonspecificChildForEach(node.expression, specificChildSet, allComponentNames)) {
1086    return true;
1087  }
1088  if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) &&
1089    !isForEachComponent(node.expression) && isComponent(node.expression, allComponentNames) &&
1090    isNonspecificChildNonForEach(node.expression, specificChildSet)) {
1091    return true;
1092  }
1093  return false;
1094}
1095
1096function collectComponentProps(node: ts.StructDeclaration, judgeInitializeInEntry: boolean,
1097  structInfo: StructInfo): void {
1098  const componentName: string = node.name.getText();
1099  const componentSet: IComponentSet = getComponentSet(node, judgeInitializeInEntry, true);
1100  propertyCollection.set(componentName, componentSet.properties);
1101  stateCollection.set(componentName, componentSet.states);
1102  linkCollection.set(componentName, componentSet.links);
1103  storedFileInfo.overallLinkCollection.set(componentName, componentSet.links);
1104  propCollection.set(componentName, componentSet.props);
1105  regularCollection.set(componentName, componentSet.regulars);
1106  storagePropCollection.set(componentName, componentSet.storageProps);
1107  storageLinkCollection.set(componentName, componentSet.storageLinks);
1108  provideCollection.set(componentName, componentSet.provides);
1109  consumeCollection.set(componentName, componentSet.consumes);
1110  objectLinkCollection.set(componentName, componentSet.objectLinks);
1111  storedFileInfo.overallObjectLinkCollection.set(componentName, componentSet.objectLinks);
1112  localStorageLinkCollection.set(componentName, componentSet.localStorageLink);
1113  localStoragePropCollection.set(componentName, componentSet.localStorageProp);
1114  builderParamObjectCollection.set(componentName, componentSet.builderParams);
1115  builderParamInitialization.set(componentName, componentSet.builderParamData);
1116  propInitialization.set(componentName, componentSet.propData);
1117  regularInitialization.set(componentName, componentSet.regularInit);
1118  stateInitialization.set(componentName, componentSet.stateInit);
1119  provideInitialization.set(componentName, componentSet.provideInit);
1120  privateCollection.set(componentName, componentSet.privateCollection);
1121  structInfo.updatePropsDecoratorsV1.push(
1122    ...componentSet.states, ...componentSet.props, ...componentSet.links,
1123    ...componentSet.provides, ...componentSet.objectLinks
1124  );
1125}
1126
1127export function getComponentSet(node: ts.StructDeclaration, judgeInitializeInEntry: boolean,
1128  uiCheck: boolean = false): IComponentSet {
1129  const componentSet: IComponentSet = new IComponentSet();
1130  traversalComponentProps(node, judgeInitializeInEntry, componentSet, uiCheck);
1131  return componentSet;
1132}
1133
1134class RecordRequire {
1135  hasRequire: boolean = false;
1136  hasProp: boolean = false;
1137  hasBuilderParam: boolean = false;
1138  hasRegular: boolean = false;
1139  hasState: boolean = false;
1140  hasProvide: boolean = false;
1141}
1142
1143function traversalComponentProps(node: ts.StructDeclaration, judgeInitializeInEntry: boolean,
1144  componentSet: IComponentSet, uiCheck: boolean = false): void {
1145  let isStatic: boolean = true;
1146  if (node.members) {
1147    const currentMethodCollection: Set<string> = new Set();
1148    node.members.forEach(item => {
1149      if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) {
1150        const propertyName: string = item.name.getText();
1151        componentSet.properties.add(propertyName);
1152        const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item);
1153        const accessQualifierResult: AccessQualifierResult = getAccessQualifier(item, uiCheck);
1154        if (!decorators || !decorators.length) {
1155          componentSet.regulars.add(propertyName);
1156          setPrivateCollection(componentSet, accessQualifierResult, propertyName, COMPONENT_NON_DECORATOR);
1157        } else {
1158          isStatic = false;
1159          let hasValidatePrivate: boolean = false;
1160          const recordRequire: RecordRequire = new RecordRequire();
1161          for (let i = 0; i < decorators.length; i++) {
1162            const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim();
1163            if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
1164              dollarCollection.add('$' + propertyName);
1165              collectionStates(decorators[i], judgeInitializeInEntry, decoratorName, propertyName,
1166                componentSet, recordRequire);
1167              setPrivateCollection(componentSet, accessQualifierResult, propertyName, decoratorName);
1168              validateAccessQualifier(item, propertyName, decoratorName, accessQualifierResult,
1169                recordRequire, uiCheck, hasValidatePrivate);
1170              hasValidatePrivate = true;
1171            }
1172          }
1173          regularAndRequire(decorators, componentSet, recordRequire, propertyName, accessQualifierResult);
1174          checkRequire(propertyName, componentSet, recordRequire);
1175        }
1176      }
1177      if (ts.isMethodDeclaration(item) && item.name && ts.isIdentifier(item.name)) {
1178        validateStateVariable(item);
1179        currentMethodCollection.add(item.name.getText());
1180      }
1181    });
1182    collectCurrentClassMethod(node, currentMethodCollection);
1183  }
1184  isStaticViewCollection.set(node.name.getText(), isStatic);
1185}
1186
1187function collectCurrentClassMethod(node: ts.StructDeclaration, currentMethodCollection: Set<string>): void {
1188  const componentMethodCollection: Map<string, Set<string>> = new Map();
1189  componentMethodCollection.set(node.name.getText(), currentMethodCollection);
1190  const sourceFile: ts.SourceFile = node.getSourceFile();
1191  const filePath: string = sourceFile ? sourceFile.fileName : undefined;
1192  if (filePath) {
1193    const pageMethodCollection: Map<string, Set<string>> = classMethodCollection.get(filePath);
1194    if (!pageMethodCollection) {
1195      classMethodCollection.set(filePath, componentMethodCollection);
1196    } else if (!pageMethodCollection.get(node.name.getText())) {
1197      pageMethodCollection.set(node.name.getText(), currentMethodCollection);
1198    }
1199  }
1200}
1201
1202const FORBIDDEN_PUBLIC_ACCESS: string[] = [COMPONENT_STORAGE_PROP_DECORATOR,
1203  COMPONENT_STORAGE_LINK_DECORATOR, COMPONENT_LOCAL_STORAGE_LINK_DECORATOR,
1204  COMPONENT_LOCAL_STORAGE_PROP_DECORATOR, COMPONENT_CONSUME_DECORATOR
1205];
1206const FORBIDDEN_PRIVATE_ACCESS: string[] = [COMPONENT_LINK_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR];
1207
1208function validateAccessQualifier(node: ts.PropertyDeclaration, propertyName: string,
1209  decoratorName: string, accessQualifierResult: AccessQualifierResult,
1210  recordRequire: RecordRequire, uiCheck: boolean = false, hasValidatePrivate: boolean): void {
1211  if (uiCheck) {
1212    if (accessQualifierResult.hasPublic && FORBIDDEN_PUBLIC_ACCESS.includes(decoratorName)) {
1213      transformLog.errors.push({
1214        type: LogType.WARN,
1215        message: `Property '${propertyName}' can not be decorated with both ${decoratorName} and public.`,
1216        pos: node.getStart()
1217      });
1218    }
1219    if (accessQualifierResult.hasPrivate) {
1220      if (FORBIDDEN_PRIVATE_ACCESS.includes(decoratorName)) {
1221        transformLog.errors.push({
1222          type: LogType.WARN,
1223          message: `Property '${propertyName}' can not be decorated with both ${decoratorName} and private.`,
1224          pos: node.getStart()
1225        });
1226      }
1227      if (recordRequire.hasRequire && !hasValidatePrivate) {
1228        transformLog.errors.push({
1229          type: LogType.WARN,
1230          message: `Property '${propertyName}' can not be decorated with both @Require and private.`,
1231          pos: node.getStart()
1232        });
1233      }
1234    }
1235  }
1236}
1237
1238const SUPPORT_PRIVATE_PROPS: string[] = [COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR,
1239  COMPONENT_PROP_DECORATOR, COMPONENT_PROVIDE_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR
1240];
1241
1242function setPrivateCollection(componentSet: IComponentSet, accessQualifierResult: AccessQualifierResult,
1243  propertyName: string, decoratorName: string): void {
1244  if (accessQualifierResult.hasPrivate && SUPPORT_PRIVATE_PROPS.includes(decoratorName)) {
1245    componentSet.privateCollection.add(propertyName);
1246  }
1247}
1248
1249class AccessQualifierResult {
1250  hasPrivate: boolean = false;
1251  hasPublic: boolean = false;
1252}
1253
1254function getAccessQualifier(node: ts.PropertyDeclaration, uiCheck: boolean = false): AccessQualifierResult {
1255  const modifiers: readonly ts.Modifier[] = ts.getModifiers(node);
1256  const accessQualifierResult: AccessQualifierResult = new AccessQualifierResult();
1257  if (modifiers && modifiers.length) {
1258    modifiers.forEach((item) => {
1259      if (item.kind === ts.SyntaxKind.PrivateKeyword) {
1260        accessQualifierResult.hasPrivate = true;
1261      }
1262      if (item.kind === ts.SyntaxKind.PublicKeyword) {
1263        accessQualifierResult.hasPublic = true;
1264      }
1265      if (uiCheck && item.kind === ts.SyntaxKind.ProtectedKeyword) {
1266        transformLog.errors.push({
1267          type: LogType.WARN,
1268          message: `The member attributes of a struct can not be protected.`,
1269          pos: node.getStart()
1270        });
1271      }
1272    });
1273  }
1274  return accessQualifierResult;
1275}
1276
1277function regularAndRequire(decorators: readonly ts.Decorator[], componentSet: IComponentSet,
1278  recordRequire: RecordRequire, propertyName: string, accessQualifierResult: AccessQualifierResult): void {
1279  if (decorators && decorators.length === 1 && decorators[0].getText() === COMPONENT_REQUIRE_DECORATOR) {
1280    componentSet.regulars.add(propertyName);
1281    recordRequire.hasRegular = true;
1282    setPrivateCollection(componentSet, accessQualifierResult, propertyName, COMPONENT_NON_DECORATOR);
1283  }
1284}
1285
1286function checkRequire(name: string, componentSet: IComponentSet, recordRequire: RecordRequire): void {
1287  if (recordRequire.hasRequire) {
1288    setInitValue('hasProp', 'propData', name, componentSet, recordRequire);
1289    setInitValue('hasBuilderParam', 'builderParamData', name, componentSet, recordRequire);
1290    setInitValue('hasRegular', 'regularInit', name, componentSet, recordRequire);
1291    setInitValue('hasState', 'stateInit', name, componentSet, recordRequire);
1292    setInitValue('hasProvide', 'provideInit', name, componentSet, recordRequire);
1293  }
1294}
1295
1296function setInitValue(requirekey: string, initKey: string, name: string, componentSet: IComponentSet,
1297  recordRequire: RecordRequire): void {
1298  if (recordRequire[requirekey]) {
1299    componentSet[initKey].add(name);
1300  }
1301}
1302
1303function collectionStates(node: ts.Decorator, judgeInitializeInEntry: boolean, decorator: string, name: string,
1304  componentSet: IComponentSet, recordRequire: RecordRequire): void {
1305  switch (decorator) {
1306    case COMPONENT_STATE_DECORATOR:
1307      componentSet.states.add(name);
1308      recordRequire.hasState = true;
1309      break;
1310    case COMPONENT_LINK_DECORATOR:
1311      componentSet.links.add(name);
1312      break;
1313    case COMPONENT_PROP_DECORATOR:
1314      recordRequire.hasProp = true;
1315      componentSet.props.add(name);
1316      break;
1317    case COMPONENT_STORAGE_PROP_DECORATOR:
1318      componentSet.storageProps.add(name);
1319      break;
1320    case COMPONENT_STORAGE_LINK_DECORATOR:
1321      componentSet.storageLinks.add(name);
1322      break;
1323    case COMPONENT_PROVIDE_DECORATOR:
1324      recordRequire.hasProvide = true;
1325      componentSet.provides.add(name);
1326      break;
1327    case COMPONENT_CONSUME_DECORATOR:
1328      componentSet.consumes.add(name);
1329      break;
1330    case COMPONENT_OBJECT_LINK_DECORATOR:
1331      componentSet.objectLinks.add(name);
1332      break;
1333    case COMPONENT_BUILDERPARAM_DECORATOR:
1334      if (judgeInitializeInEntry) {
1335        validateInitializeInEntry(node, name);
1336      }
1337      recordRequire.hasBuilderParam = true;
1338      componentSet.builderParams.add(name);
1339      break;
1340    case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR :
1341      collectionlocalStorageParam(node, name, componentSet.localStorageLink);
1342      break;
1343    case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR:
1344      collectionlocalStorageParam(node, name, componentSet.localStorageProp);
1345      break;
1346    case COMPONENT_REQUIRE_DECORATOR:
1347      recordRequire.hasRequire = true;
1348      break;
1349  }
1350}
1351
1352function validateInitializeInEntry(node: ts.Decorator, name: string): void {
1353  transformLog.errors.push({
1354    type: LogType.WARN,
1355    message: `'${name}' should be initialized in @Entry Component`,
1356    pos: node.getStart()
1357  });
1358}
1359
1360function collectionlocalStorageParam(node: ts.Decorator, name: string,
1361  localStorage: Map<string, Set<string>>): void {
1362  const localStorageParam: Set<string> = new Set();
1363  if (node && ts.isCallExpression(node.expression) && node.expression.arguments &&
1364    node.expression.arguments.length) {
1365    localStorage.set(name, localStorageParam.add(
1366      node.expression.arguments[0].getText()));
1367  }
1368}
1369
1370export interface ReplaceResult {
1371  content: string,
1372  log: LogInfo[]
1373}
1374
1375export function sourceReplace(source: string, sourcePath: string): ReplaceResult {
1376  let content: string = source;
1377  const log: LogInfo[] = [];
1378  content = preprocessExtend(content);
1379  content = preprocessNewExtend(content);
1380  // process @system.
1381  content = processSystemApi(content, false, sourcePath);
1382  collectImportNames(content, sourcePath);
1383
1384  return {
1385    content: content,
1386    log: log
1387  };
1388}
1389
1390export function preprocessExtend(content: string, extendCollection?: Set<string>): string {
1391  const REG_EXTEND: RegExp = /@Extend(\s+)([^\.\s]+)\.([^\(]+)\(/gm;
1392  return content.replace(REG_EXTEND, (item, item1, item2, item3) => {
1393    collectExtend(EXTEND_ATTRIBUTE, item2, '__' + item2 + '__' + item3);
1394    collectExtend(EXTEND_ATTRIBUTE, item2, item3);
1395    if (extendCollection) {
1396      extendCollection.add(item3);
1397    }
1398    return `@Extend(${item2})${item1}function __${item2}__${item3}(`;
1399  });
1400}
1401
1402export function preprocessNewExtend(content: string, extendCollection?: Set<string>): string {
1403  const REG_EXTEND: RegExp = /@Extend\s*\([^\)]+\)\s*function\s+([^\(\s]+)\s*\(/gm;
1404  return content.replace(REG_EXTEND, (item, item1) => {
1405    if (extendCollection) {
1406      extendCollection.add(item1);
1407    }
1408    return item;
1409  });
1410}
1411
1412function replaceSystemApi(item: string, systemValue: string, moduleType: string, systemKey: string): string {
1413  // if change format, please update regexp in transformModuleSpecifier
1414  if (NATIVE_MODULE.has(`${moduleType}.${systemKey}`)) {
1415    item = `var ${systemValue} = globalThis.requireNativeModule('${moduleType}.${systemKey}')`;
1416  } else if (moduleType === SYSTEM_PLUGIN || moduleType === OHOS_PLUGIN) {
1417    item = `var ${systemValue} = globalThis.requireNapi('${systemKey}')`;
1418  }
1419  return item;
1420}
1421
1422function replaceLibSo(importValue: string, libSoKey: string, sourcePath: string = null): string {
1423  if (sourcePath) {
1424    useOSFiles.add(sourcePath);
1425  }
1426  // if change format, please update regexp in transformModuleSpecifier
1427  return projectConfig.bundleName && projectConfig.moduleName ?
1428    `var ${importValue} = globalThis.requireNapi("${libSoKey}", true, "${projectConfig.bundleName}/${projectConfig.moduleName}");` :
1429    `var ${importValue} = globalThis.requireNapi("${libSoKey}", true);`;
1430}
1431
1432export function processSystemApi(content: string, isProcessAllowList: boolean = false,
1433  sourcePath: string = null, isSystemModule: boolean = false): string {
1434  if (isProcessAllowList && projectConfig.compileMode === ESMODULE) {
1435    // remove the unused system api import decl like following when compile as [esmodule]
1436    // in the result_process phase
1437    // e.g. import "@ohos.application.xxx"
1438    const REG_UNUSED_SYSTEM_IMPORT: RegExp = /import(?:\s*)['"]@(system|ohos)\.(\S+)['"]/g;
1439    content = content.replace(REG_UNUSED_SYSTEM_IMPORT, '');
1440  }
1441
1442  const REG_IMPORT_DECL: RegExp = isProcessAllowList ? projectConfig.compileMode === ESMODULE ?
1443    /import\s+(.+)\s+from\s+['"]@(system|ohos)\.(\S+)['"]/g :
1444    /(import|const)\s+(.+)\s*=\s*(\_\_importDefault\()?require\(\s*['"]@(system|ohos)\.(\S+)['"]\s*\)(\))?/g :
1445    /(import|export)\s+(?:(.+)|\{([\s\S]+)\})\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g;
1446
1447  const systemValueCollection: Set<string> = new Set();
1448  const processedContent: string = content.replace(REG_IMPORT_DECL, (item, item1, item2, item3, item4, item5, item6) => {
1449    const importValue: string = isProcessAllowList ? projectConfig.compileMode === ESMODULE ? item1 : item2 : item2 || item5;
1450
1451    if (isProcessAllowList) {
1452      systemValueCollection.add(importValue);
1453      if (projectConfig.compileMode !== ESMODULE) {
1454        collectSourcemapNames(sourcePath, importValue, item5);
1455        return replaceSystemApi(item, importValue, item4, item5);
1456      }
1457      collectSourcemapNames(sourcePath, importValue, item3);
1458      return replaceSystemApi(item, importValue, item2, item3);
1459    }
1460
1461    const moduleRequest: string = item4 || item6;
1462    if (/^@(system|ohos)\./.test(moduleRequest)) { // ohos/system.api
1463      // ets & ts file need compile with .d.ts, so do not replace at the phase of pre_process
1464      if (!isSystemModule) {
1465        return item;
1466      }
1467      const result: RegExpMatchArray = moduleRequest.match(/^@(system|ohos)\.(\S+)$/);
1468      const moduleType: string = result[1];
1469      const apiName: string = result[2];
1470      return replaceSystemApi(item, importValue, moduleType, apiName);
1471    } else if (/^lib(\S+)\.so$/.test(moduleRequest)) { // libxxx.so
1472      const result: RegExpMatchArray = moduleRequest.match(/^lib(\S+)\.so$/);
1473      const libSoKey: string = result[1];
1474      return replaceLibSo(importValue, libSoKey, sourcePath);
1475    }
1476    return item;
1477  });
1478  return processInnerModule(processedContent, systemValueCollection);
1479}
1480
1481function collectSourcemapNames(sourcePath: string, changedName: string, originalName: string): void {
1482  if (sourcePath == null) {
1483    return;
1484  }
1485  const cleanSourcePath: string = sourcePath.replace('.ets', '.js').replace('.ts', '.js');
1486  if (!sourcemapNamesCollection.has(cleanSourcePath)) {
1487    return;
1488  }
1489
1490  const map: Map<string, string> = sourcemapNamesCollection.get(cleanSourcePath);
1491  if (map.has(changedName)) {
1492    return;
1493  }
1494
1495  for (const entry of originalImportNamesMap.entries()) {
1496    const key: string = entry[0];
1497    const value: string = entry[1];
1498    if (value === '@ohos.' + originalName || value === '@system.' + originalName) {
1499      map.set(changedName.trim(), key);
1500      sourcemapNamesCollection.set(cleanSourcePath, map);
1501      originalImportNamesMap.delete(key);
1502      break;
1503    }
1504  }
1505}
1506
1507export function collectImportNames(content: string, sourcePath: string = null): void {
1508  const REG_IMPORT_DECL: RegExp =
1509    /(import|export)\s+(.+)\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g;
1510
1511  const decls: string[] = content.match(REG_IMPORT_DECL);
1512  if (decls != undefined) {
1513    decls.forEach(decl => {
1514      const parts: string[] = decl.split(' ');
1515      if (parts.length === 4 && parts[0] === 'import' && parts[2] === 'from' && !parts[3].includes('.so')) {
1516        originalImportNamesMap.set(parts[1], parts[3].replace(/'/g, ''));
1517      }
1518    });
1519  }
1520
1521  if (sourcePath && sourcePath !== null) {
1522    const cleanSourcePath: string = sourcePath.replace('.ets', '.js').replace('.ts', '.js');
1523    if (!sourcemapNamesCollection.has(cleanSourcePath)) {
1524      sourcemapNamesCollection.set(cleanSourcePath, new Map());
1525    }
1526  }
1527}
1528
1529function processInnerModule(content: string, systemValueCollection: Set<string>): string {
1530  systemValueCollection.forEach(element => {
1531    const target: string = element.trim() + '.default';
1532    while (content.includes(target)) {
1533      content = content.replace(target, element.trim());
1534    }
1535  });
1536  return content;
1537}
1538
1539export function resetComponentCollection(): void {
1540  componentCollection.entryComponent = null;
1541  componentCollection.entryComponentPos = null;
1542  componentCollection.previewComponent = [];
1543  stateObjectCollection.clear();
1544  builderParamInitialization.clear();
1545  propInitialization.clear();
1546  propCollection.clear();
1547  objectLinkCollection.clear();
1548  linkCollection.clear();
1549  storedFileInfo.overallLinkCollection.clear();
1550  storedFileInfo.overallObjectLinkCollection.clear();
1551  regularInitialization.clear();
1552  stateInitialization.clear();
1553  provideInitialization.clear();
1554  privateCollection.clear();
1555}
1556
1557function checkEntryComponent(node: ts.StructDeclaration, log: LogInfo[], sourceFile: ts.SourceFile): void {
1558  const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
1559  if (modifiers) {
1560    for (let i = 0; i < modifiers.length; i++) {
1561      if (modifiers[i].kind === ts.SyntaxKind.ExportKeyword) {
1562        const message: string = `It's not a recommended way to export struct with @Entry decorator, ` +
1563          `which may cause ACE Engine error in component preview mode.`;
1564        addLog(LogType.WARN, message, node.getStart(), log, sourceFile);
1565        break;
1566      }
1567    }
1568  }
1569}
1570
1571function validateStateVariable(node: ts.MethodDeclaration): void {
1572  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
1573  if (decorators && decorators.length) {
1574    for (let i = 0; i < decorators.length; i++) {
1575      const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim();
1576      if (CARD_ENABLE_DECORATORS[decoratorName]) {
1577        validatorCard(transformLog.errors, CARD_LOG_TYPE_DECORATORS,
1578          decorators[i].getStart(), decoratorName);
1579      }
1580      if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
1581        transformLog.errors.push({
1582          type: LogType.ERROR,
1583          message: `'${decorators[i].getText()}' can not decorate the method.`,
1584          pos: decorators[i].getStart()
1585        });
1586      }
1587    }
1588  }
1589}
1590
1591export function getObservedPropertyCollection(className: string): Set<string> {
1592  const observedProperthCollection: Set<string> = new Set([
1593    ...stateCollection.get(className),
1594    ...linkCollection.get(className),
1595    ...propCollection.get(className),
1596    ...storageLinkCollection.get(className),
1597    ...storageLinkCollection.get(className),
1598    ...provideCollection.get(className),
1599    ...consumeCollection.get(className),
1600    ...objectLinkCollection.get(className)
1601  ]);
1602  getLocalStorageCollection(className, observedProperthCollection);
1603  return observedProperthCollection;
1604}
1605
1606export function getLocalStorageCollection(componentName: string, collection: Set<string>): void {
1607  if (localStorageLinkCollection.get(componentName)) {
1608    for (const key of localStorageLinkCollection.get(componentName).keys()) {
1609      collection.add(key);
1610    }
1611  }
1612  if (localStoragePropCollection.get(componentName)) {
1613    for (const key of localStoragePropCollection.get(componentName).keys()) {
1614      collection.add(key);
1615    }
1616  }
1617}
1618
1619export function resetValidateUiSyntax(): void {
1620  observedClassCollection.clear();
1621  enumCollection.clear();
1622  classMethodCollection.clear();
1623  dollarCollection.clear();
1624  stateCollection.clear();
1625  regularCollection.clear();
1626  storagePropCollection.clear();
1627  storageLinkCollection.clear();
1628  provideCollection.clear();
1629  consumeCollection.clear();
1630  builderParamObjectCollection.clear();
1631  localStorageLinkCollection.clear();
1632  localStoragePropCollection.clear();
1633  isStaticViewCollection.clear();
1634  useOSFiles.clear();
1635  sourcemapNamesCollection.clear();
1636  originalImportNamesMap.clear();
1637}
1638