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