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;