• 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 } from '../utils/index';
19
20// Helper function to find the '@Component' decorator in a ClassDeclaration and report errors.
21function findComponentDecorator(context: UISyntaxRuleContext, node: arkts.ClassDeclaration): void {
22  const componentDecorator = node.definition?.annotations?.find(
23    (annotation) =>
24      annotation.expr &&
25      arkts.isIdentifier(annotation.expr) &&
26      annotation.expr.name === PresetDecorators.COMPONENT_V1
27  );
28  if (componentDecorator) {
29    reportDecoratorError(context, componentDecorator, rule.messages.invalidComponentDecorator);
30  }
31}
32
33// Helper function to find the '@Prop' decorator in a MethodDefinition or ClassProperty.
34const findPropDecorator = (node: arkts.MethodDefinition | arkts.ClassProperty): arkts.AnnotationUsage | undefined => {
35  const annotations = 'scriptFunction' in node ? node.scriptFunction.annotations : node.annotations;
36  return annotations?.find(
37    (annotation) =>
38      annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.PROP
39  );
40};
41
42// Rule 2: Check for '@Prop' on MethodDefinition
43function checkPropOnMethod(context: UISyntaxRuleContext, node: arkts.MethodDefinition): void {
44  const propDecorator = findPropDecorator(node);
45  if (propDecorator) {
46    reportDecoratorError(context, propDecorator, rule.messages.propOnMethod);
47  }
48};
49
50// Rule 3: Check for '@Prop' on ClassProperty within a ClassDeclaration
51function checkPropOnClassProperty(context: UISyntaxRuleContext, node: arkts.ClassProperty, currentNode: arkts.AstNode)
52  : void {
53  const propDecorator = findPropDecorator(node);
54  while (arkts.nodeType(currentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) {
55    currentNode = currentNode.parent;
56    if (propDecorator && arkts.isClassDeclaration(currentNode)) {
57      reportDecoratorError(context, propDecorator, rule.messages.propOnMethod);
58    }
59  }
60};
61
62function reportDecoratorError(context: UISyntaxRuleContext, Decorator: arkts.AnnotationUsage, message: string
63): void {
64  context.report({
65    node: Decorator,
66    message: message,
67    fix: () => {
68      const startPosition = arkts.getStartPosition(Decorator);
69      const endPosition = arkts.getEndPosition(Decorator);
70      return {
71        range: [startPosition, endPosition],
72        code: '',
73      };
74    },
75  });
76}
77
78const rule: UISyntaxRule = {
79  name: 'no-prop-on-method',
80  messages: {
81    invalidComponentDecorator: `'@Component' can decorate only custom components.`,
82    propOnMethod: `'@Prop' can decorate only member variables of custom components.`,
83  },
84  setup(context) {
85    return {
86      parsed: (node: arkts.AstNode): void => {
87        if (arkts.isClassDeclaration(node)) {
88          findComponentDecorator(context, node);
89        }
90        if (arkts.isMethodDefinition(node)) {
91          checkPropOnMethod(context, node);
92        }
93        let currentNode = node;
94        if (arkts.isClassProperty(node)) {
95          checkPropOnClassProperty(context, node, currentNode);
96        }
97      },
98    };
99  },
100};
101
102export default rule;