1/* 2 * Copyright (c) 2024 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 ts from 'typescript'; 17 18import { 19 addLog, 20 LogType, 21 LogInfo 22} from './utils'; 23import { ParentType } from './process_custom_component'; 24import constantDefine from './constant_define'; 25 26function checkLocalBuilderDecoratorCount(node: ts.Node, sourceFileNode: ts.SourceFile, checkDecoratorCount: number, log: LogInfo[]): void { 27 if (checkDecoratorCount > 0) { 28 const message: string = 'The member property or method can not be decorated by multiple decorators.'; 29 addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode, { code: '10905125' }); 30 } 31} 32 33function checkTwoWayComputed(node: ts.PropertyAccessExpression, symbol: ts.Symbol, log: LogInfo[]): void { 34 if (symbol && symbol.declarations) { 35 symbol.declarations.forEach((declaration: ts.Declaration) => { 36 if (ts.isGetAccessor(declaration) && declaration.modifiers && 37 isTagWithDecorator(declaration.modifiers, constantDefine.COMPUTED)) { 38 log.push({ 39 type: LogType.ERROR, 40 message: `A property decorated by '${constantDefine.COMPUTED_DECORATOR}' cannot be used with two-bind syntax.`, 41 pos: node.getStart(), 42 code: '10905129' 43 }); 44 } 45 }); 46 } 47} 48 49function checkComputedGetter(symbol: ts.Symbol, declaration: ts.Declaration, log: LogInfo[]): void { 50 if (ts.isSetAccessor(declaration) && declaration.name && ts.isIdentifier(declaration.name) && 51 symbol.escapedName.toString() === declaration.name.escapedText.toString()) { 52 log.push({ 53 type: LogType.ERROR, 54 message: `A property decorated by '${constantDefine.COMPUTED_DECORATOR}' cannot define a set method.`, 55 pos: declaration.getStart(), 56 code: '10905130' 57 }); 58 } 59} 60 61function checkIfNeedDollarEvent(doubleExclamationCollection: string[], dollarPropertyCollection: string[], 62 node: ts.CallExpression, log: LogInfo[]): void { 63 for (const item of doubleExclamationCollection) { 64 if (dollarPropertyCollection.some((value: string) => value === '$' + item)) { 65 log.push({ 66 type: LogType.ERROR, 67 message: `When the two-way binding syntax is used, do not assign a value to '${constantDefine.EVENT_DECORATOR}'` + 68 ` variable '${'$' + item}' because the framework generates the default assignment.`, 69 pos: node.getStart(), 70 code: '10905358' 71 }); 72 } 73 } 74} 75 76function checkIfAssignToStaticProps(node: ts.ObjectLiteralElementLike, propName: string, 77 staticCollection: Set<string>, log: LogInfo[]): void { 78 if (staticCollection.has(propName)) { 79 log.push({ 80 type: LogType.WARN, 81 message: `Static property '${propName}' can not be initialized through the component constructor.`, 82 pos: node.getStart() 83 }); 84 } 85} 86 87function checkNestedComponents(parentComponentType: ParentType, isRecycleChild: boolean, isReuseV2Child: boolean, 88 node: ts.ExpressionStatement, log: LogInfo[]): void { 89 if (parentComponentType === ParentType.NormalComponentV1 && isReuseV2Child) { 90 log.push({ 91 type: LogType.ERROR, 92 message: `A custom component decorated with '@Component' cannot contain child components decorated with '@ReusableV2'.`, 93 pos: node.getStart(), 94 code: '10905244' 95 }); 96 } 97 if (parentComponentType === ParentType.ReuseComponentV1 && isReuseV2Child) { 98 log.push({ 99 type: LogType.ERROR, 100 message: `A custom component decorated with '@Reusable' cannot contain child components decorated with '@ReusableV2'.`, 101 pos: node.getStart(), 102 code: '10905245' 103 }); 104 } 105 if (parentComponentType === ParentType.ReuseComponentV2 && isRecycleChild) { 106 log.push({ 107 type: LogType.ERROR, 108 message: `A custom component decorated with '@ReusableV2' cannot contain child components decorated with '@Reusable'.`, 109 pos: node.getStart(), 110 code: '10905246' 111 }); 112 } 113 if (parentComponentType === ParentType.NormalComponentV2 && isRecycleChild) { 114 log.push({ 115 type: LogType.WARN, 116 message: `When a custom component is decorated with '@ComponentV2' and contains a child decorated with '@Reusable', ` + 117 `the child component will not create.`, 118 pos: node.getStart() 119 }); 120 } 121} 122 123function checkIfReuseV2InRepeatTemplate(isInRepeatTemplate: boolean, isReuseV2Child: boolean, 124 node: ts.ExpressionStatement, log: LogInfo[]): void { 125 if (isInRepeatTemplate && isReuseV2Child) { 126 log.push({ 127 type: LogType.ERROR, 128 message: `The template attribute of the Repeat component cannot contain any custom component decorated with '@ReusableV2'.`, 129 pos: node.getStart(), 130 code: '10905247' 131 }); 132 } 133} 134 135function checkUsageOfReuseAttribute(node: ts.CallExpression, isReusableV2NodeAttr: boolean, log: LogInfo[]): void { 136 if (!isReusableV2NodeAttr) { 137 log.push({ 138 type: LogType.ERROR, 139 message: `The reuse attribute is only applicable to custom components decorated with both '@ComponentV2' and '@ReusableV2'.`, 140 pos: node.getStart(), 141 code: '10905248' 142 }); 143 } 144} 145 146function checkUsageOfReuseIdAttribute(node: ts.CallExpression, isReusableV2NodeAttr: boolean, log: LogInfo[]): void { 147 if (isReusableV2NodeAttr) { 148 log.push({ 149 type: LogType.ERROR, 150 message: `The reuseId attribute is not applicable to custom components decorated with both '@ComponentV2' and '@ReusableV2'.`, 151 pos: node.getStart(), 152 code: '10905249' 153 }); 154 } 155} 156 157function isTagWithDecorator(node: ts.NodeArray<ts.ModifierLike>, decoratorName: string): boolean { 158 return node.some((item: ts.Decorator) => ts.isDecorator(item) && 159 ts.isIdentifier(item.expression) && item.expression.escapedText.toString() === decoratorName); 160} 161 162export default { 163 checkLocalBuilderDecoratorCount, 164 checkTwoWayComputed, 165 checkComputedGetter, 166 checkIfNeedDollarEvent, 167 checkIfAssignToStaticProps, 168 checkNestedComponents, 169 checkIfReuseV2InRepeatTemplate, 170 checkUsageOfReuseAttribute, 171 checkUsageOfReuseIdAttribute 172}; 173