• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 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 * as arkts from '@koalaui/libarkts';
17import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule';
18import { PresetDecorators, getAnnotationUsage } from '../utils';
19
20function findStructsWithReusableAndComponentV2(node: arkts.AstNode, reusableV2ComponentV2Struct: string[]): void {
21  //Go through all the children of Program
22  for (const childNode of node.getChildren()) {
23    // Check whether the type is struct
24    if (!arkts.isStructDeclaration(childNode)) {
25      continue;
26    }
27    // Check that the current component has @ComponentV2 and @ReusableV2 decorators
28    const hasReusableV2Decorator = getAnnotationUsage(childNode, PresetDecorators.REUSABLE_V2);
29    const hasComponentV2Decorator = getAnnotationUsage(childNode, PresetDecorators.COMPONENT_V2);
30    if (hasReusableV2Decorator && hasComponentV2Decorator) {
31      const struceName = childNode.definition?.ident?.name ?? '';
32      reusableV2ComponentV2Struct.push(struceName);
33    }
34  }
35}
36
37function validateReuseOrReuseIdUsage(context: UISyntaxRuleContext, node: arkts.MemberExpression,
38  reusableV2ComponentV2Struct: string[]): void {
39  const structNode = node.object;
40  // Gets the reuse or reuseId attribute
41  const decoratedNode = node.property;
42  if (arkts.isCallExpression(structNode)) {
43    const Node = structNode.expression;
44    if (decoratedNode.dumpSrc() === 'reuse' && !reusableV2ComponentV2Struct.includes(Node.dumpSrc())) {
45      reportInvalidReuseUsage(context, node, structNode, rule);
46    }
47    else if (decoratedNode.dumpSrc() === 'reuseId' && reusableV2ComponentV2Struct.includes(Node.dumpSrc())) {
48      reportInvalidReuseIdUsage(context, node, structNode, rule);
49    }
50  }
51}
52
53function reportInvalidReuseUsage(context: UISyntaxRuleContext, node: arkts.AstNode, structNode: arkts.AstNode,
54  rule: any): void {
55  context.report({
56    node: node,
57    message: rule.messages.invalidReuseUsage,
58    fix: (node) => {
59      const startPosition = arkts.getStartPosition(node);
60      const endPosition = arkts.getEndPosition(node);
61      return {
62        range: [startPosition, endPosition],
63        code: `${structNode.dumpSrc()}.reuseId`,
64      };
65    },
66  });
67}
68
69function reportInvalidReuseIdUsage(context: UISyntaxRuleContext, node: arkts.AstNode, structNode: arkts.AstNode,
70  rule: any): void {
71  context.report({
72    node: node,
73    message: rule.messages.invalidReuseIdUsage,
74    fix: (node) => {
75      const startPosition = arkts.getStartPosition(node);
76      const endPosition = arkts.getEndPosition(node);
77      return {
78        range: [startPosition, endPosition],
79        code: `${structNode.dumpSrc()}.reuse`,
80      };
81    },
82  });
83}
84
85const rule: UISyntaxRule = {
86  name: 'reuse-attribute-check',
87  messages: {
88    invalidReuseUsage: `The reuse attribute is only applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`,
89    invalidReuseIdUsage: `The reuseId attribute is not applicable to custom components decorated with both @ComponentV2 and @ReusableV2.`,
90  },
91  setup(context) {
92    const reusableV2ComponentV2Struct: string[] = [];
93    return {
94      parsed: (node): void => {
95        // Check whether the type is "Program"
96        if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) {
97          findStructsWithReusableAndComponentV2(node, reusableV2ComponentV2Struct);
98        }
99        if (arkts.isMemberExpression(node)) {
100          validateReuseOrReuseIdUsage(context, node, reusableV2ComponentV2Struct);
101        }
102      },
103    };
104  },
105};
106
107export default rule;