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 { PresetDecorators, getAnnotationUsage } from '../utils'; 18import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; 19 20function findPropertyDecorator( 21 node: arkts.ClassProperty, 22 decoratorName: string 23): arkts.AnnotationUsage | undefined { 24 const annotation = node.annotations?.find(annotation => 25 annotation.expr && 26 annotation.expr.dumpSrc() === decoratorName 27 ); 28 return annotation; 29} 30 31function paramDecoratorError( 32 context: UISyntaxRuleContext, 33 hasParamDecorator: arkts.AnnotationUsage, 34 hasComponentDecorator: arkts.AnnotationUsage 35): void { 36 context.report({ 37 node: hasParamDecorator, 38 message: rule.messages.paramDecoratorError, 39 fix: () => { 40 const startPosition = arkts.getStartPosition(hasComponentDecorator); 41 const endPosition = arkts.getEndPosition(hasComponentDecorator); 42 return { 43 range: [startPosition, endPosition], 44 code: `@${PresetDecorators.COMPONENT_V2}`, 45 }; 46 }, 47 }); 48} 49 50function stateDecoratorError( 51 context: UISyntaxRuleContext, 52 hasStateDecorator: arkts.AnnotationUsage, 53 hasComponentV2Decorator: arkts.AnnotationUsage 54): void { 55 context.report({ 56 node: hasStateDecorator, 57 message: rule.messages.stateDecoratorError, 58 fix: () => { 59 const startPosition = arkts.getStartPosition(hasComponentV2Decorator); 60 const endPosition = arkts.getEndPosition(hasComponentV2Decorator); 61 return { 62 range: [startPosition, endPosition], 63 code: `@${PresetDecorators.COMPONENT_V1}`, 64 }; 65 }, 66 }); 67} 68 69function findDecoratorError( 70 node: arkts.StructDeclaration, 71 context: UISyntaxRuleContext 72): void { 73 const hasComponentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); 74 const hasComponentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); 75 // Check where @Param and @State are used 76 node.definition.body.forEach((property) => { 77 if (arkts.isClassProperty(property)) { 78 const hasParamDecorator = findPropertyDecorator(property, PresetDecorators.PARAM); 79 const hasStateDecorator = findPropertyDecorator(property, PresetDecorators.STATE); 80 // Check that @Param is in the correct location 81 if (hasParamDecorator && !hasComponentV2Decorator && hasComponentDecorator) { 82 paramDecoratorError(context, hasParamDecorator, hasComponentDecorator); 83 } 84 // Check that @State is in the correct location 85 if (hasStateDecorator && !hasComponentDecorator && hasComponentV2Decorator) { 86 stateDecoratorError(context, hasStateDecorator, hasComponentV2Decorator); 87 } 88 } 89 }); 90} 91 92 93const rule: UISyntaxRule = { 94 name: 'old-new-decorator-mix-use-check', 95 messages: { 96 paramDecoratorError: `The '@Param' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, 97 stateDecoratorError: `The '@State' decorator can only be used in a 'struct' decorated with '@Component'.`, 98 }, 99 setup(context) { 100 return { 101 parsed: (node): void => { 102 if (!arkts.isStructDeclaration(node)) { 103 return; 104 } 105 findDecoratorError(node, context); 106 }, 107 }; 108 }, 109}; 110 111export default rule; 112