/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import ts from 'typescript'; import { addLog, LogType, LogInfo } from './utils'; import { ParentType } from './process_custom_component'; import constantDefine from './constant_define'; function checkLocalBuilderDecoratorCount(node: ts.Node, sourceFileNode: ts.SourceFile, checkDecoratorCount: number, log: LogInfo[]): void { if (checkDecoratorCount > 0) { const message: string = 'The member property or method can not be decorated by multiple decorators.'; addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode, { code: '10905125' }); } } function checkTwoWayComputed(node: ts.PropertyAccessExpression, symbol: ts.Symbol, log: LogInfo[]): void { if (symbol && symbol.declarations) { symbol.declarations.forEach((declaration: ts.Declaration) => { if (ts.isGetAccessor(declaration) && declaration.modifiers && isTagWithDecorator(declaration.modifiers, constantDefine.COMPUTED)) { log.push({ type: LogType.ERROR, message: `A property decorated by '${constantDefine.COMPUTED_DECORATOR}' cannot be used with two-bind syntax.`, pos: node.getStart(), code: '10905129' }); } }); } } function checkComputedGetter(symbol: ts.Symbol, declaration: ts.Declaration, log: LogInfo[]): void { if (ts.isSetAccessor(declaration) && declaration.name && ts.isIdentifier(declaration.name) && symbol.escapedName.toString() === declaration.name.escapedText.toString()) { log.push({ type: LogType.ERROR, message: `A property decorated by '${constantDefine.COMPUTED_DECORATOR}' cannot define a set method.`, pos: declaration.getStart(), code: '10905130' }); } } function checkIfNeedDollarEvent(doubleExclamationCollection: string[], dollarPropertyCollection: string[], node: ts.CallExpression, log: LogInfo[]): void { for (const item of doubleExclamationCollection) { if (dollarPropertyCollection.some((value: string) => value === '$' + item)) { log.push({ type: LogType.ERROR, message: `When the two-way binding syntax is used, do not assign a value to '${constantDefine.EVENT_DECORATOR}'` + ` variable '${'$' + item}' because the framework generates the default assignment.`, pos: node.getStart(), code: '10905358' }); } } } function checkIfAssignToStaticProps(node: ts.ObjectLiteralElementLike, propName: string, staticCollection: Set, log: LogInfo[]): void { if (staticCollection.has(propName)) { log.push({ type: LogType.WARN, message: `Static property '${propName}' can not be initialized through the component constructor.`, pos: node.getStart() }); } } function checkNestedComponents(parentComponentType: ParentType, isRecycleChild: boolean, isReuseV2Child: boolean, node: ts.ExpressionStatement, log: LogInfo[]): void { if (parentComponentType === ParentType.NormalComponentV1 && isReuseV2Child) { log.push({ type: LogType.ERROR, message: `A custom component decorated with '@Component' cannot contain child components decorated with '@ReusableV2'.`, pos: node.getStart(), code: '10905244' }); } if (parentComponentType === ParentType.ReuseComponentV1 && isReuseV2Child) { log.push({ type: LogType.ERROR, message: `A custom component decorated with '@Reusable' cannot contain child components decorated with '@ReusableV2'.`, pos: node.getStart(), code: '10905245' }); } if (parentComponentType === ParentType.ReuseComponentV2 && isRecycleChild) { log.push({ type: LogType.ERROR, message: `A custom component decorated with '@ReusableV2' cannot contain child components decorated with '@Reusable'.`, pos: node.getStart(), code: '10905246' }); } if (parentComponentType === ParentType.NormalComponentV2 && isRecycleChild) { log.push({ type: LogType.WARN, message: `When a custom component is decorated with '@ComponentV2' and contains a child decorated with '@Reusable', ` + `the child component will not create.`, pos: node.getStart() }); } } function checkIfReuseV2InRepeatTemplate(isInRepeatTemplate: boolean, isReuseV2Child: boolean, node: ts.ExpressionStatement, log: LogInfo[]): void { if (isInRepeatTemplate && isReuseV2Child) { log.push({ type: LogType.ERROR, message: `The template attribute of the Repeat component cannot contain any custom component decorated with '@ReusableV2'.`, pos: node.getStart(), code: '10905247' }); } } function checkUsageOfReuseAttribute(node: ts.CallExpression, isReusableV2NodeAttr: boolean, log: LogInfo[]): void { if (!isReusableV2NodeAttr) { log.push({ type: LogType.ERROR, message: `The reuse attribute is only applicable to custom components decorated with both '@ComponentV2' and '@ReusableV2'.`, pos: node.getStart(), code: '10905248' }); } } function checkUsageOfReuseIdAttribute(node: ts.CallExpression, isReusableV2NodeAttr: boolean, log: LogInfo[]): void { if (isReusableV2NodeAttr) { log.push({ type: LogType.ERROR, message: `The reuseId attribute is not applicable to custom components decorated with both '@ComponentV2' and '@ReusableV2'.`, pos: node.getStart(), code: '10905249' }); } } function isTagWithDecorator(node: ts.NodeArray, decoratorName: string): boolean { return node.some((item: ts.Decorator) => ts.isDecorator(item) && ts.isIdentifier(item.expression) && item.expression.escapedText.toString() === decoratorName); } export default { checkLocalBuilderDecoratorCount, checkTwoWayComputed, checkComputedGetter, checkIfNeedDollarEvent, checkIfAssignToStaticProps, checkNestedComponents, checkIfReuseV2InRepeatTemplate, checkUsageOfReuseAttribute, checkUsageOfReuseIdAttribute };