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