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 { getIdentifierName } from '../utils'; 18import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; 19 20const NOT_PARAM_LENGTH: number = 0; 21const BUILD_NAME: string = 'build'; 22const BUILD_FUNCTION_COUNT_INI: number = 0; 23const BUILD_FUNCTION_COUNT: number = 1; 24const NOT_STATEMENT_LENGTH: number = 0; 25 26// rule1: Check if the build function contains arguments and report an error 27function validateBuildFunctionParameters(buildFunction: arkts.MethodDefinition, context: UISyntaxRuleContext): void { 28 const paramsNodes = buildFunction.scriptFunction.params; 29 if (paramsNodes.length > NOT_PARAM_LENGTH) { 30 paramsNodes.forEach((param) => { 31 if (arkts.isEtsParameterExpression(param)) { 32 reportBuildParamNotAllowed(param, context); 33 } 34 }); 35 } 36} 37 38// Report an error with an unallowed parameter in the build function 39function reportBuildParamNotAllowed( 40 param: arkts.ETSParameterExpression, 41 context: UISyntaxRuleContext 42): void { 43 context.report({ 44 node: param, 45 message: rule.messages.invalidComponet, 46 fix: (param) => { 47 const startPosition = arkts.getStartPosition(param); 48 const endPosition = arkts.getEndPosition(param); 49 return { 50 range: [startPosition, endPosition], 51 code: '' 52 }; 53 } 54 }); 55} 56 57function validateConstructorForBuildFunction( 58 node: arkts.StructDeclaration, 59 member: arkts.MethodDefinition, 60 buildFunctionCount: number, 61 context: UISyntaxRuleContext 62): void { 63 const blockStatement = member.scriptFunction.body; 64 if (!blockStatement || !arkts.isBlockStatement(blockStatement)) { 65 return; 66 } 67 const statements = blockStatement.statements; 68 const structName = node.definition.ident; 69 if (buildFunctionCount !== BUILD_FUNCTION_COUNT && 70 statements.length === NOT_STATEMENT_LENGTH) { 71 reportMissingBuildInStruct(structName, blockStatement, context); 72 } 73} 74 75function reportMissingBuildInStruct( 76 structName: arkts.Identifier | undefined, 77 blockStatement: arkts.BlockStatement, 78 context: UISyntaxRuleContext 79): void { 80 if (!structName) { 81 return; 82 } 83 context.report({ 84 node: structName, 85 message: rule.messages.invalidBuild, 86 fix: (structName) => { 87 const startPosition = arkts.getStartPosition(blockStatement); 88 const endPosition = startPosition; 89 return { 90 range: [startPosition, endPosition], 91 code: '{\nbuild {\n}' 92 }; 93 } 94 }); 95} 96 97function validateBuild( 98 node: arkts.StructDeclaration, 99 buildFunctionCount: number, 100 context: UISyntaxRuleContext, 101): void { 102 node.definition.body.forEach((member) => { 103 // Check if the member is defined for the method and the method name is 'build' 104 if (arkts.isMethodDefinition(member) && getIdentifierName(member.name) === BUILD_NAME) { 105 buildFunctionCount++; 106 validateBuildFunctionParameters(member, context); 107 } 108 // rule2: This rule validates the use of the 'build' function 109 if (arkts.isMethodDefinition(member) && 110 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR === member.kind) { 111 validateConstructorForBuildFunction(node, member, buildFunctionCount, context); 112 } 113 }); 114} 115 116const rule: UISyntaxRule = { 117 name: 'validate-build-in-struct', 118 messages: { 119 invalidComponet: `A custom component can have only one 'build' function, which does not require parameters.`, 120 invalidBuild: `This rule validates the use of the 'build' function`, 121 }, 122 setup(context) { 123 return { 124 parsed: (node: arkts.AstNode): void => { 125 if (!arkts.isStructDeclaration(node)) { 126 return; 127 } 128 let buildFunctionCount: number = BUILD_FUNCTION_COUNT_INI; 129 validateBuild(node, buildFunctionCount, context); 130 }, 131 }; 132 }, 133}; 134 135export default rule;