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