• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 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';
17
18import {
19  addLog,
20  LogType,
21  LogInfo
22} from './utils';
23import { ParentType } from './process_custom_component';
24import constantDefine from './constant_define';
25
26function checkLocalBuilderDecoratorCount(node: ts.Node, sourceFileNode: ts.SourceFile, checkDecoratorCount: number, log: LogInfo[]): void {
27  if (checkDecoratorCount > 0) {
28    const message: string = 'The member property or method can not be decorated by multiple decorators.';
29    addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode, { code: '10905125' });
30  }
31}
32
33function checkTwoWayComputed(node: ts.PropertyAccessExpression, symbol: ts.Symbol, log: LogInfo[]): void {
34  if (symbol && symbol.declarations) {
35    symbol.declarations.forEach((declaration: ts.Declaration) => {
36      if (ts.isGetAccessor(declaration) && declaration.modifiers &&
37        isTagWithDecorator(declaration.modifiers, constantDefine.COMPUTED)) {
38        log.push({
39          type: LogType.ERROR,
40          message: `A property decorated by '${constantDefine.COMPUTED_DECORATOR}' cannot be used with two-bind syntax.`,
41          pos: node.getStart(),
42          code: '10905129'
43        });
44      }
45    });
46  }
47}
48
49function checkComputedGetter(symbol: ts.Symbol, declaration: ts.Declaration, log: LogInfo[]): void {
50  if (ts.isSetAccessor(declaration) && declaration.name && ts.isIdentifier(declaration.name) &&
51    symbol.escapedName.toString() === declaration.name.escapedText.toString()) {
52    log.push({
53      type: LogType.ERROR,
54      message: `A property decorated by '${constantDefine.COMPUTED_DECORATOR}' cannot define a set method.`,
55      pos: declaration.getStart(),
56      code: '10905130'
57    });
58  }
59}
60
61function checkIfNeedDollarEvent(doubleExclamationCollection: string[], dollarPropertyCollection: string[],
62  node: ts.CallExpression, log: LogInfo[]): void {
63  for (const item of doubleExclamationCollection) {
64    if (dollarPropertyCollection.some((value: string) => value === '$' + item)) {
65      log.push({
66        type: LogType.ERROR,
67        message: `When the two-way binding syntax is used, do not assign a value to '${constantDefine.EVENT_DECORATOR}'` +
68          ` variable '${'$' + item}' because the framework generates the default assignment.`,
69        pos: node.getStart(),
70        code: '10905358'
71      });
72    }
73  }
74}
75
76function checkIfAssignToStaticProps(node: ts.ObjectLiteralElementLike, propName: string,
77  staticCollection: Set<string>, log: LogInfo[]): void {
78  if (staticCollection.has(propName)) {
79    log.push({
80      type: LogType.WARN,
81      message: `Static property '${propName}' can not be initialized through the component constructor.`,
82      pos: node.getStart()
83    });
84  }
85}
86
87function checkNestedComponents(parentComponentType: ParentType, isRecycleChild: boolean, isReuseV2Child: boolean,
88  node: ts.ExpressionStatement, log: LogInfo[]): void {
89  if (parentComponentType === ParentType.NormalComponentV1 && isReuseV2Child) {
90    log.push({
91      type: LogType.ERROR,
92      message: `A custom component decorated with '@Component' cannot contain child components decorated with '@ReusableV2'.`,
93      pos: node.getStart(),
94      code: '10905244'
95    });
96  }
97  if (parentComponentType === ParentType.ReuseComponentV1 && isReuseV2Child) {
98    log.push({
99      type: LogType.ERROR,
100      message: `A custom component decorated with '@Reusable' cannot contain child components decorated with '@ReusableV2'.`,
101      pos: node.getStart(),
102      code: '10905245'
103    });
104  }
105  if (parentComponentType === ParentType.ReuseComponentV2 && isRecycleChild) {
106    log.push({
107      type: LogType.ERROR,
108      message: `A custom component decorated with '@ReusableV2' cannot contain child components decorated with '@Reusable'.`,
109      pos: node.getStart(),
110      code: '10905246'
111    });
112  }
113  if (parentComponentType === ParentType.NormalComponentV2 && isRecycleChild) {
114    log.push({
115      type: LogType.WARN,
116      message: `When a custom component is decorated with '@ComponentV2' and contains a child decorated with '@Reusable', ` +
117        `the child component will not create.`,
118      pos: node.getStart()
119    });
120  }
121}
122
123function checkIfReuseV2InRepeatTemplate(isInRepeatTemplate: boolean, isReuseV2Child: boolean,
124  node: ts.ExpressionStatement, log: LogInfo[]): void {
125  if (isInRepeatTemplate && isReuseV2Child) {
126    log.push({
127      type: LogType.ERROR,
128      message: `The template attribute of the Repeat component cannot contain any custom component decorated with '@ReusableV2'.`,
129      pos: node.getStart(),
130      code: '10905247'
131    });
132  }
133}
134
135function checkUsageOfReuseAttribute(node: ts.CallExpression, isReusableV2NodeAttr: boolean, log: LogInfo[]): void {
136  if (!isReusableV2NodeAttr) {
137    log.push({
138      type: LogType.ERROR,
139      message: `The reuse attribute is only applicable to custom components decorated with both '@ComponentV2' and '@ReusableV2'.`,
140      pos: node.getStart(),
141      code: '10905248'
142    });
143  }
144}
145
146function checkUsageOfReuseIdAttribute(node: ts.CallExpression, isReusableV2NodeAttr: boolean, log: LogInfo[]): void {
147  if (isReusableV2NodeAttr) {
148    log.push({
149      type: LogType.ERROR,
150      message: `The reuseId attribute is not applicable to custom components decorated with both '@ComponentV2' and '@ReusableV2'.`,
151      pos: node.getStart(),
152      code: '10905249'
153    });
154  }
155}
156
157function isTagWithDecorator(node: ts.NodeArray<ts.ModifierLike>, decoratorName: string): boolean {
158  return node.some((item: ts.Decorator) => ts.isDecorator(item) &&
159    ts.isIdentifier(item.expression) && item.expression.escapedText.toString() === decoratorName);
160}
161
162export default {
163  checkLocalBuilderDecoratorCount,
164  checkTwoWayComputed,
165  checkComputedGetter,
166  checkIfNeedDollarEvent,
167  checkIfAssignToStaticProps,
168  checkNestedComponents,
169  checkIfReuseV2InRepeatTemplate,
170  checkUsageOfReuseAttribute,
171  checkUsageOfReuseIdAttribute
172};
173