From 0affaba06322e67f3e8ab1ed932df3004512508c Mon Sep 17 00:00:00 2001 From: lihong Date: Sat, 11 May 2024 17:40:15 +0800 Subject: [PATCH] lihong67@huawei.com update validate for struct/@BuilderParam. Signed-off-by: lihong Change-Id: Ia9af1515366f7f49507d8fc9f6a6e5994f0d50f5 --- .gitignore | 1 + BUILD.gn | 11 +++- build_ets_loader_library.py | 3 + compiler/build_declarations_file.js | 10 +++- compiler/src/pre_define.ts | 3 +- compiler/src/process_import.ts | 2 +- compiler/src/validate_ui_syntax.ts | 59 +++++++------------ compiler/test/error.json | 8 +-- .../vaildate_ui_syntax/BuilderParamNoInit.ts | 24 -------- 9 files changed, 48 insertions(+), 73 deletions(-) delete mode 100644 compiler/test/utForValidate/Decorators/vaildate_ui_syntax/BuilderParamNoInit.ts diff --git a/.gitignore b/.gitignore index 251de8b7..94ac3336 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ compiler/declarations/ compiler/sample/build/ compiler/component_config.json compiler/form_config.json +compiler/build_config.json compiler/syntax_parser/dist/ compiler/.nyc_output compiler/.test_output diff --git a/BUILD.gn b/BUILD.gn index 0f7a4222..486d9cf1 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -31,6 +31,9 @@ ets_loader_component_config_file = ets_loader_form_config_file = get_label_info(":build_ets_loader_library", "target_out_dir") + "/form_config.json" +ets_loader_build_config_file = + get_label_info(":build_ets_loader_library", "target_out_dir") + + "/build_config.json" ets_sysResource = get_label_info(":build_ets_sysResource", "target_out_dir") + "/sysResource.js" ets_loader_kit_configs_dir = get_label_info(":build_ets_loader_library", @@ -51,6 +54,7 @@ action("build_ets_loader_library") { ets_loader_component_config_file, ets_loader_form_config_file, ets_loader_kit_configs_dir, + ets_loader_build_config_file, ] _ets_loader_dir = "compiler" @@ -114,6 +118,8 @@ action("build_ets_loader_library") { rebase_path(ets_loader_component_config_file, root_build_dir), "--output-form-config-file", rebase_path(ets_loader_form_config_file, root_build_dir), + "--output-build-config-file", + rebase_path(ets_loader_build_config_file, root_build_dir), "--kit-configs-file-dir", rebase_path(_kit_configs_file_dir, root_build_dir), "--build-kit-configs-file-js", @@ -177,7 +183,10 @@ ohos_copy("ets_loader") { ohos_copy("ets_loader_component_config") { deps = [ ":build_ets_loader_library" ] - sources = [ ets_loader_component_config_file ] + sources = [ + ets_loader_build_config_file, + ets_loader_component_config_file, + ] outputs = [ target_out_dir + "/$target_name/{{source_file_part}}" ] module_source_dir = target_out_dir + "/$target_name" diff --git a/build_ets_loader_library.py b/build_ets_loader_library.py index c37c8973..147a8173 100755 --- a/build_ets_loader_library.py +++ b/build_ets_loader_library.py @@ -52,6 +52,8 @@ def parse_args(): help='path component config file to output') parser.add_argument('--output-form-config-file', help='path form config file to output') + parser.add_argument('--output-build-config-file', + help='path form config file to output') parser.add_argument('--kit-configs-file-dir', help='path kit configs file') parser.add_argument('--build-kit-configs-file-js', @@ -115,6 +117,7 @@ def main(): options.output_declarations_dir, options.output_component_config_file, options.output_form_config_file, + options.output_build_config_file, options.output_kit_configs_dir])) if __name__ == '__main__': diff --git a/compiler/build_declarations_file.js b/compiler/build_declarations_file.js index 934ced3e..b265d7ba 100644 --- a/compiler/build_declarations_file.js +++ b/compiler/build_declarations_file.js @@ -221,11 +221,19 @@ generateComponentConfig(process.argv[4], process.argv[5]); function generateComponentConfig(dir, buildPublicSDK) { const configFile = path.resolve(dir, 'component_map.js'); if (fs.existsSync(configFile)) { - const { COMPONENT_MAP, FORM_MAP } = require(configFile); + const { COMPONENT_MAP, FORM_MAP, forbiddenUseStateType } = require(configFile); + const buildConfig = { + forbiddenUseStateTypeForDecorators: [ + '@State', '@Prop', '@Link', '@Provide', '@Consume', '@ObjectLink', '@BuilderParam', + '@LocalStorageLink', '@LocalStorageProp', '@StorageLink', '@StorageProp' + ], + forbiddenUseStateType: [...forbiddenUseStateType] + }; try { removeSystemApiComp(buildPublicSDK, COMPONENT_MAP, FORM_MAP); fs.writeFileSync(path.resolve(dir, '../component_config.json'), JSON.stringify(COMPONENT_MAP)); fs.writeFileSync(path.resolve(dir, '../form_config.json'), JSON.stringify(FORM_MAP)); + fs.writeFileSync(path.resolve(dir, '../build_config.json'), JSON.stringify(buildConfig)); } catch (error) { console.error(error); } diff --git a/compiler/src/pre_define.ts b/compiler/src/pre_define.ts index 626e74ac..c9d51ff3 100644 --- a/compiler/src/pre_define.ts +++ b/compiler/src/pre_define.ts @@ -122,7 +122,8 @@ export const XCOMPONENTTYPE: string = 'XComponentType'; export const XCOMPONENTTYPE_CONTAINER: string = 'COMPONENT'; export const COMPONENT_DECORATOR_NAME_CUSTOMDIALOG: string = 'CustomDialog'; export const CUSTOM_DECORATOR_NAME: Set = new Set([ - COMPONENT_DECORATOR_NAME_COMPONENT, COMPONENT_DECORATOR_NAME_CUSTOMDIALOG + COMPONENT_DECORATOR_NAME_COMPONENT, COMPONENT_DECORATOR_NAME_CUSTOMDIALOG, + DECORATOR_REUSEABLE, 'Entry', 'Preview' ]); export const EXTNAME_ETS: string = '.ets'; diff --git a/compiler/src/process_import.ts b/compiler/src/process_import.ts index b6ee01ed..29f4e20e 100644 --- a/compiler/src/process_import.ts +++ b/compiler/src/process_import.ts @@ -872,6 +872,7 @@ function setComponentCollectionInfo(name: string, componentSet: IComponentSet, i function parseComponentInImportNode(originNode: ts.StructDeclaration, name: string, asComponentName: string, structDecorator: structDecoratorResult, originFile: string): void { + componentCollection.customComponents.add(name); const structInfo: StructInfo = asComponentName ? processStructComponentV2.getOrCreateStructInfo(asComponentName) : processStructComponentV2.getOrCreateStructInfo(name); @@ -900,7 +901,6 @@ function parseComponentInImportNode(originNode: ts.StructDeclaration, name: stri function parseComponentV2InImportNode(node: ts.StructDeclaration, name: string, originFile: string, structInfo: StructInfo): void { structInfo.isComponentV2 = true; - componentCollection.customComponents.add(name); const isDETS: boolean = originFile && /\.d\.ets$/.test(originFile); if (isDETS) { storedFileInfo.getCurrentArkTsFile().compFromDETS.add(name); diff --git a/compiler/src/validate_ui_syntax.ts b/compiler/src/validate_ui_syntax.ts index ab000483..3466237d 100644 --- a/compiler/src/validate_ui_syntax.ts +++ b/compiler/src/validate_ui_syntax.ts @@ -240,12 +240,14 @@ function checkComponentDecorator(source: string, filePath: string, function validateStructSpec(item: ts.StructDeclaration, result: DecoratorResult, log: LogInfo[], sourceFile: ts.SourceFile | null): void { if (item.name && ts.isIdentifier(item.name)) { + const componentName: string = item.name.getText(); + componentCollection.customComponents.add(componentName); const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item); if (decorators && decorators.length) { checkDecorators(decorators, result, item.name, log, sourceFile, item); } else { - const message: string = `A struct should use decorator '@Component' or '@ComponentV2'.`; - addLog(LogType.WARN, message, item.getStart(), log, sourceFile); + const message: string = `Decorator '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct '${componentName}'.`; + addLog(LogType.ERROR, message, item.getStart(), log, sourceFile); } } else { const message: string = `A struct must have a name.`; @@ -309,16 +311,16 @@ interface DecoratorResult { function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorResult, component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile, node: ts.StructDeclaration): void { - let hasComponentDecorator: boolean = false; const componentName: string = component.getText(); const structInfo: StructInfo = processStructComponentV2.getOrCreateStructInfo(componentName); + let hasInnerComponentDecorator: boolean = false; decorators.forEach((element) => { let name: string = element.getText().replace(/\([^\(\)]*\)/, '').trim(); if (element.expression && element.expression.expression && ts.isIdentifier(element.expression.expression)) { name = '@' + element.expression.expression.getText(); } if (INNER_COMPONENT_DECORATORS.has(name)) { - componentCollection.customComponents.add(componentName); + hasInnerComponentDecorator = true; switch (name) { case COMPONENT_DECORATOR_ENTRY: checkEntryComponent(node, log, sourceFile); @@ -332,21 +334,17 @@ function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorR componentCollection.previewComponent.push(componentName); break; case COMPONENT_DECORATOR_COMPONENT_V2: - hasComponentDecorator = true; structInfo.isComponentV2 = true; break; case COMPONENT_DECORATOR_COMPONENT: - hasComponentDecorator = true; structInfo.isComponentV1 = true; break; case COMPONENT_DECORATOR_CUSTOM_DIALOG: componentCollection.customDialogs.add(componentName); - hasComponentDecorator = true; structInfo.isCustomDialog = true; break; case COMPONENT_DECORATOR_REUSEABLE: storedFileInfo.getCurrentArkTsFile().recycleComponents.add(componentName); - hasComponentDecorator = true; structInfo.isReusable = true; break; } @@ -354,7 +352,7 @@ function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorR validateInvalidStructDecorator(element, componentName, log, sourceFile); } }); - validateStruct(hasComponentDecorator, componentName, component, log, sourceFile, structInfo); + validateStruct(hasInnerComponentDecorator, componentName, component, log, sourceFile, structInfo); } function validateInvalidStructDecorator(element: ts.Decorator, componentName: string, log: LogInfo[], @@ -364,11 +362,11 @@ function validateInvalidStructDecorator(element: ts.Decorator, componentName: st addLog(LogType.WARN, message, pos, log, sourceFile); } -function validateStruct(hasComponentDecorator: boolean, componentName: string, component: ts.Identifier, +function validateStruct(hasInnerComponentDecorator: boolean, componentName: string, component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile, structInfo: StructInfo): void { - if (!hasComponentDecorator) { - const message: string = `The struct '${componentName}' should use decorator '@Component'.`; - addLog(LogType.WARN, message, component.pos, log, sourceFile); + if (!hasInnerComponentDecorator) { + const message: string = `Decorator '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct '${componentName}'.`; + addLog(LogType.ERROR, message, component.pos, log, sourceFile); } else if (structInfo.isComponentV2 && (structInfo.isComponentV1 || structInfo.isReusable || structInfo.isCustomDialog) ) { const message: string = `The struct '${componentName}' can not be decorated with '@ComponentV2' ` + `and '@Component', '@Reusable', '@CustomDialog' at the same time.`; @@ -437,10 +435,6 @@ function checkUISyntax(filePath: string, allComponentNames: Set, content visitAllNode(sourceFile, sourceFile, allComponentNames, log, false, false, false, false, fileQuery); } -function propertyInitializeInEntry(fileQuery: string, name: string): boolean { - return fileQuery === '?entry' && name === componentCollection.entryComponent; -} - function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponentNames: Set, log: LogInfo[], structContext: boolean, classContext: boolean, isObservedClass: boolean, isComponentV2: boolean, fileQuery: string): void { @@ -452,7 +446,7 @@ function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponent processStructComponentV2.parseComponentProperty(node, structInfo, log, sourceFileNode); isComponentV2 = true; } else { - collectComponentProps(node, propertyInitializeInEntry(fileQuery, structName), structInfo); + collectComponentProps(node, structInfo); } } if (ts.isClassDeclaration(node) && node.name && ts.isIdentifier(node.name)) { @@ -1093,10 +1087,9 @@ function isNonspecificChildIfStatement(node: ts.Node, specificChildSet: Set = new Set(); @@ -1162,7 +1154,7 @@ function traversalComponentProps(node: ts.StructDeclaration, judgeInitializeInEn const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim(); if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { dollarCollection.add('$' + propertyName); - collectionStates(decorators[i], judgeInitializeInEntry, decoratorName, propertyName, + collectionStates(decorators[i], decoratorName, propertyName, componentSet, recordRequire); setPrivateCollection(componentSet, accessQualifierResult, propertyName, decoratorName); validateAccessQualifier(item, propertyName, decoratorName, accessQualifierResult, @@ -1300,7 +1292,7 @@ function setInitValue(requirekey: string, initKey: string, name: string, compone } } -function collectionStates(node: ts.Decorator, judgeInitializeInEntry: boolean, decorator: string, name: string, +function collectionStates(node: ts.Decorator, decorator: string, name: string, componentSet: IComponentSet, recordRequire: RecordRequire): void { switch (decorator) { case COMPONENT_STATE_DECORATOR: @@ -1331,9 +1323,6 @@ function collectionStates(node: ts.Decorator, judgeInitializeInEntry: boolean, d componentSet.objectLinks.add(name); break; case COMPONENT_BUILDERPARAM_DECORATOR: - if (judgeInitializeInEntry) { - validateInitializeInEntry(node, name); - } recordRequire.hasBuilderParam = true; componentSet.builderParams.add(name); break; @@ -1349,14 +1338,6 @@ function collectionStates(node: ts.Decorator, judgeInitializeInEntry: boolean, d } } -function validateInitializeInEntry(node: ts.Decorator, name: string): void { - transformLog.errors.push({ - type: LogType.WARN, - message: `'${name}' should be initialized in @Entry Component`, - pos: node.getStart() - }); -} - function collectionlocalStorageParam(node: ts.Decorator, name: string, localStorage: Map>): void { const localStorageParam: Set = new Set(); diff --git a/compiler/test/error.json b/compiler/test/error.json index 38fb8380..0a443ccd 100644 --- a/compiler/test/error.json +++ b/compiler/test/error.json @@ -180,8 +180,8 @@ "type": "ERROR" }, "notComponent": { - "message": "A struct should use decorator '@Component' or '@ComponentV2'.", - "type": "WARN" + "message": "Decorator '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct 'EntryComponent'.", + "type": "ERROR" }, "notConcurrent": { "message": "The struct 'IndexDecorator' use invalid decorator.", @@ -287,10 +287,6 @@ "message": "'@State' can not decorate the method.", "type": "ERROR" }, - "BuilderParamNoInit": { - "message": "'closer' should be initialized in @Entry Component", - "type": "WARN" - }, "checkNonspecificParents": { "message": "The 'Blank' component can only be nested in the 'Row,Column,Flex' parent component.", "type": "ERROR" diff --git a/compiler/test/utForValidate/Decorators/vaildate_ui_syntax/BuilderParamNoInit.ts b/compiler/test/utForValidate/Decorators/vaildate_ui_syntax/BuilderParamNoInit.ts deleted file mode 100644 index ff6bd932..00000000 --- a/compiler/test/utForValidate/Decorators/vaildate_ui_syntax/BuilderParamNoInit.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2023 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. - */ - -exports.source = ` -@Entry -@Component -struct sp { - @BuilderParam closer:() => void; - build() { - } -} -` \ No newline at end of file