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;