update validate for struct/@BuilderParam.

Signed-off-by: lihong <lihong67@huawei.com>
Change-Id: Ia9af1515366f7f49507d8fc9f6a6e5994f0d50f5
This commit is contained in:
lihong 2024-05-11 17:40:15 +08:00
parent 228411823f
commit 0affaba063
9 changed files with 48 additions and 73 deletions

1
.gitignore vendored
View File

@ -6,6 +6,7 @@ compiler/declarations/
compiler/sample/build/ compiler/sample/build/
compiler/component_config.json compiler/component_config.json
compiler/form_config.json compiler/form_config.json
compiler/build_config.json
compiler/syntax_parser/dist/ compiler/syntax_parser/dist/
compiler/.nyc_output compiler/.nyc_output
compiler/.test_output compiler/.test_output

View File

@ -31,6 +31,9 @@ ets_loader_component_config_file =
ets_loader_form_config_file = ets_loader_form_config_file =
get_label_info(":build_ets_loader_library", "target_out_dir") + get_label_info(":build_ets_loader_library", "target_out_dir") +
"/form_config.json" "/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") + ets_sysResource = get_label_info(":build_ets_sysResource", "target_out_dir") +
"/sysResource.js" "/sysResource.js"
ets_loader_kit_configs_dir = get_label_info(":build_ets_loader_library", 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_component_config_file,
ets_loader_form_config_file, ets_loader_form_config_file,
ets_loader_kit_configs_dir, ets_loader_kit_configs_dir,
ets_loader_build_config_file,
] ]
_ets_loader_dir = "compiler" _ets_loader_dir = "compiler"
@ -114,6 +118,8 @@ action("build_ets_loader_library") {
rebase_path(ets_loader_component_config_file, root_build_dir), rebase_path(ets_loader_component_config_file, root_build_dir),
"--output-form-config-file", "--output-form-config-file",
rebase_path(ets_loader_form_config_file, root_build_dir), 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", "--kit-configs-file-dir",
rebase_path(_kit_configs_file_dir, root_build_dir), rebase_path(_kit_configs_file_dir, root_build_dir),
"--build-kit-configs-file-js", "--build-kit-configs-file-js",
@ -177,7 +183,10 @@ ohos_copy("ets_loader") {
ohos_copy("ets_loader_component_config") { ohos_copy("ets_loader_component_config") {
deps = [ ":build_ets_loader_library" ] 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}}" ] outputs = [ target_out_dir + "/$target_name/{{source_file_part}}" ]
module_source_dir = target_out_dir + "/$target_name" module_source_dir = target_out_dir + "/$target_name"

View File

@ -52,6 +52,8 @@ def parse_args():
help='path component config file to output') help='path component config file to output')
parser.add_argument('--output-form-config-file', parser.add_argument('--output-form-config-file',
help='path form config file to output') 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', parser.add_argument('--kit-configs-file-dir',
help='path kit configs file') help='path kit configs file')
parser.add_argument('--build-kit-configs-file-js', parser.add_argument('--build-kit-configs-file-js',
@ -115,6 +117,7 @@ def main():
options.output_declarations_dir, options.output_declarations_dir,
options.output_component_config_file, options.output_component_config_file,
options.output_form_config_file, options.output_form_config_file,
options.output_build_config_file,
options.output_kit_configs_dir])) options.output_kit_configs_dir]))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -221,11 +221,19 @@ generateComponentConfig(process.argv[4], process.argv[5]);
function generateComponentConfig(dir, buildPublicSDK) { function generateComponentConfig(dir, buildPublicSDK) {
const configFile = path.resolve(dir, 'component_map.js'); const configFile = path.resolve(dir, 'component_map.js');
if (fs.existsSync(configFile)) { 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 { try {
removeSystemApiComp(buildPublicSDK, COMPONENT_MAP, FORM_MAP); removeSystemApiComp(buildPublicSDK, COMPONENT_MAP, FORM_MAP);
fs.writeFileSync(path.resolve(dir, '../component_config.json'), JSON.stringify(COMPONENT_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, '../form_config.json'), JSON.stringify(FORM_MAP));
fs.writeFileSync(path.resolve(dir, '../build_config.json'), JSON.stringify(buildConfig));
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }

View File

@ -122,7 +122,8 @@ export const XCOMPONENTTYPE: string = 'XComponentType';
export const XCOMPONENTTYPE_CONTAINER: string = 'COMPONENT'; export const XCOMPONENTTYPE_CONTAINER: string = 'COMPONENT';
export const COMPONENT_DECORATOR_NAME_CUSTOMDIALOG: string = 'CustomDialog'; export const COMPONENT_DECORATOR_NAME_CUSTOMDIALOG: string = 'CustomDialog';
export const CUSTOM_DECORATOR_NAME: Set<string> = new Set([ export const CUSTOM_DECORATOR_NAME: Set<string> = 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'; export const EXTNAME_ETS: string = '.ets';

View File

@ -872,6 +872,7 @@ function setComponentCollectionInfo(name: string, componentSet: IComponentSet, i
function parseComponentInImportNode(originNode: ts.StructDeclaration, name: string, function parseComponentInImportNode(originNode: ts.StructDeclaration, name: string,
asComponentName: string, structDecorator: structDecoratorResult, originFile: string): void { asComponentName: string, structDecorator: structDecoratorResult, originFile: string): void {
componentCollection.customComponents.add(name);
const structInfo: StructInfo = asComponentName ? const structInfo: StructInfo = asComponentName ?
processStructComponentV2.getOrCreateStructInfo(asComponentName) : processStructComponentV2.getOrCreateStructInfo(asComponentName) :
processStructComponentV2.getOrCreateStructInfo(name); processStructComponentV2.getOrCreateStructInfo(name);
@ -900,7 +901,6 @@ function parseComponentInImportNode(originNode: ts.StructDeclaration, name: stri
function parseComponentV2InImportNode(node: ts.StructDeclaration, name: string, originFile: string, function parseComponentV2InImportNode(node: ts.StructDeclaration, name: string, originFile: string,
structInfo: StructInfo): void { structInfo: StructInfo): void {
structInfo.isComponentV2 = true; structInfo.isComponentV2 = true;
componentCollection.customComponents.add(name);
const isDETS: boolean = originFile && /\.d\.ets$/.test(originFile); const isDETS: boolean = originFile && /\.d\.ets$/.test(originFile);
if (isDETS) { if (isDETS) {
storedFileInfo.getCurrentArkTsFile().compFromDETS.add(name); storedFileInfo.getCurrentArkTsFile().compFromDETS.add(name);

View File

@ -240,12 +240,14 @@ function checkComponentDecorator(source: string, filePath: string,
function validateStructSpec(item: ts.StructDeclaration, result: DecoratorResult, log: LogInfo[], function validateStructSpec(item: ts.StructDeclaration, result: DecoratorResult, log: LogInfo[],
sourceFile: ts.SourceFile | null): void { sourceFile: ts.SourceFile | null): void {
if (item.name && ts.isIdentifier(item.name)) { 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); const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item);
if (decorators && decorators.length) { if (decorators && decorators.length) {
checkDecorators(decorators, result, item.name, log, sourceFile, item); checkDecorators(decorators, result, item.name, log, sourceFile, item);
} else { } else {
const message: string = `A struct should use decorator '@Component' or '@ComponentV2'.`; const message: string = `Decorator '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct '${componentName}'.`;
addLog(LogType.WARN, message, item.getStart(), log, sourceFile); addLog(LogType.ERROR, message, item.getStart(), log, sourceFile);
} }
} else { } else {
const message: string = `A struct must have a name.`; const message: string = `A struct must have a name.`;
@ -309,16 +311,16 @@ interface DecoratorResult {
function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorResult, function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorResult,
component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile, node: ts.StructDeclaration): void { component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile, node: ts.StructDeclaration): void {
let hasComponentDecorator: boolean = false;
const componentName: string = component.getText(); const componentName: string = component.getText();
const structInfo: StructInfo = processStructComponentV2.getOrCreateStructInfo(componentName); const structInfo: StructInfo = processStructComponentV2.getOrCreateStructInfo(componentName);
let hasInnerComponentDecorator: boolean = false;
decorators.forEach((element) => { decorators.forEach((element) => {
let name: string = element.getText().replace(/\([^\(\)]*\)/, '').trim(); let name: string = element.getText().replace(/\([^\(\)]*\)/, '').trim();
if (element.expression && element.expression.expression && ts.isIdentifier(element.expression.expression)) { if (element.expression && element.expression.expression && ts.isIdentifier(element.expression.expression)) {
name = '@' + element.expression.expression.getText(); name = '@' + element.expression.expression.getText();
} }
if (INNER_COMPONENT_DECORATORS.has(name)) { if (INNER_COMPONENT_DECORATORS.has(name)) {
componentCollection.customComponents.add(componentName); hasInnerComponentDecorator = true;
switch (name) { switch (name) {
case COMPONENT_DECORATOR_ENTRY: case COMPONENT_DECORATOR_ENTRY:
checkEntryComponent(node, log, sourceFile); checkEntryComponent(node, log, sourceFile);
@ -332,21 +334,17 @@ function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorR
componentCollection.previewComponent.push(componentName); componentCollection.previewComponent.push(componentName);
break; break;
case COMPONENT_DECORATOR_COMPONENT_V2: case COMPONENT_DECORATOR_COMPONENT_V2:
hasComponentDecorator = true;
structInfo.isComponentV2 = true; structInfo.isComponentV2 = true;
break; break;
case COMPONENT_DECORATOR_COMPONENT: case COMPONENT_DECORATOR_COMPONENT:
hasComponentDecorator = true;
structInfo.isComponentV1 = true; structInfo.isComponentV1 = true;
break; break;
case COMPONENT_DECORATOR_CUSTOM_DIALOG: case COMPONENT_DECORATOR_CUSTOM_DIALOG:
componentCollection.customDialogs.add(componentName); componentCollection.customDialogs.add(componentName);
hasComponentDecorator = true;
structInfo.isCustomDialog = true; structInfo.isCustomDialog = true;
break; break;
case COMPONENT_DECORATOR_REUSEABLE: case COMPONENT_DECORATOR_REUSEABLE:
storedFileInfo.getCurrentArkTsFile().recycleComponents.add(componentName); storedFileInfo.getCurrentArkTsFile().recycleComponents.add(componentName);
hasComponentDecorator = true;
structInfo.isReusable = true; structInfo.isReusable = true;
break; break;
} }
@ -354,7 +352,7 @@ function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorR
validateInvalidStructDecorator(element, componentName, log, sourceFile); 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[], 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); 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 { log: LogInfo[], sourceFile: ts.SourceFile, structInfo: StructInfo): void {
if (!hasComponentDecorator) { if (!hasInnerComponentDecorator) {
const message: string = `The struct '${componentName}' should use decorator '@Component'.`; const message: string = `Decorator '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct '${componentName}'.`;
addLog(LogType.WARN, message, component.pos, log, sourceFile); addLog(LogType.ERROR, message, component.pos, log, sourceFile);
} else if (structInfo.isComponentV2 && (structInfo.isComponentV1 || structInfo.isReusable || structInfo.isCustomDialog) ) { } else if (structInfo.isComponentV2 && (structInfo.isComponentV1 || structInfo.isReusable || structInfo.isCustomDialog) ) {
const message: string = `The struct '${componentName}' can not be decorated with '@ComponentV2' ` + const message: string = `The struct '${componentName}' can not be decorated with '@ComponentV2' ` +
`and '@Component', '@Reusable', '@CustomDialog' at the same time.`; `and '@Component', '@Reusable', '@CustomDialog' at the same time.`;
@ -437,10 +435,6 @@ function checkUISyntax(filePath: string, allComponentNames: Set<string>, content
visitAllNode(sourceFile, sourceFile, allComponentNames, log, false, false, false, false, fileQuery); 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<string>, function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponentNames: Set<string>,
log: LogInfo[], structContext: boolean, classContext: boolean, isObservedClass: boolean, log: LogInfo[], structContext: boolean, classContext: boolean, isObservedClass: boolean,
isComponentV2: boolean, fileQuery: string): void { isComponentV2: boolean, fileQuery: string): void {
@ -452,7 +446,7 @@ function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponent
processStructComponentV2.parseComponentProperty(node, structInfo, log, sourceFileNode); processStructComponentV2.parseComponentProperty(node, structInfo, log, sourceFileNode);
isComponentV2 = true; isComponentV2 = true;
} else { } else {
collectComponentProps(node, propertyInitializeInEntry(fileQuery, structName), structInfo); collectComponentProps(node, structInfo);
} }
} }
if (ts.isClassDeclaration(node) && node.name && ts.isIdentifier(node.name)) { if (ts.isClassDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
@ -1093,10 +1087,9 @@ function isNonspecificChildIfStatement(node: ts.Node, specificChildSet: Set<stri
return false; return false;
} }
function collectComponentProps(node: ts.StructDeclaration, judgeInitializeInEntry: boolean, function collectComponentProps(node: ts.StructDeclaration, structInfo: StructInfo): void {
structInfo: StructInfo): void {
const componentName: string = node.name.getText(); const componentName: string = node.name.getText();
const componentSet: IComponentSet = getComponentSet(node, judgeInitializeInEntry, true); const componentSet: IComponentSet = getComponentSet(node, true);
propertyCollection.set(componentName, componentSet.properties); propertyCollection.set(componentName, componentSet.properties);
stateCollection.set(componentName, componentSet.states); stateCollection.set(componentName, componentSet.states);
linkCollection.set(componentName, componentSet.links); linkCollection.set(componentName, componentSet.links);
@ -1124,10 +1117,9 @@ function collectComponentProps(node: ts.StructDeclaration, judgeInitializeInEntr
); );
} }
export function getComponentSet(node: ts.StructDeclaration, judgeInitializeInEntry: boolean, export function getComponentSet(node: ts.StructDeclaration, uiCheck: boolean = false): IComponentSet {
uiCheck: boolean = false): IComponentSet {
const componentSet: IComponentSet = new IComponentSet(); const componentSet: IComponentSet = new IComponentSet();
traversalComponentProps(node, judgeInitializeInEntry, componentSet, uiCheck); traversalComponentProps(node, componentSet, uiCheck);
return componentSet; return componentSet;
} }
@ -1140,8 +1132,8 @@ class RecordRequire {
hasProvide: boolean = false; hasProvide: boolean = false;
} }
function traversalComponentProps(node: ts.StructDeclaration, judgeInitializeInEntry: boolean, function traversalComponentProps(node: ts.StructDeclaration, componentSet: IComponentSet,
componentSet: IComponentSet, uiCheck: boolean = false): void { uiCheck: boolean = false): void {
let isStatic: boolean = true; let isStatic: boolean = true;
if (node.members) { if (node.members) {
const currentMethodCollection: Set<string> = new Set(); const currentMethodCollection: Set<string> = new Set();
@ -1162,7 +1154,7 @@ function traversalComponentProps(node: ts.StructDeclaration, judgeInitializeInEn
const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim(); const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim();
if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
dollarCollection.add('$' + propertyName); dollarCollection.add('$' + propertyName);
collectionStates(decorators[i], judgeInitializeInEntry, decoratorName, propertyName, collectionStates(decorators[i], decoratorName, propertyName,
componentSet, recordRequire); componentSet, recordRequire);
setPrivateCollection(componentSet, accessQualifierResult, propertyName, decoratorName); setPrivateCollection(componentSet, accessQualifierResult, propertyName, decoratorName);
validateAccessQualifier(item, propertyName, decoratorName, accessQualifierResult, 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 { componentSet: IComponentSet, recordRequire: RecordRequire): void {
switch (decorator) { switch (decorator) {
case COMPONENT_STATE_DECORATOR: case COMPONENT_STATE_DECORATOR:
@ -1331,9 +1323,6 @@ function collectionStates(node: ts.Decorator, judgeInitializeInEntry: boolean, d
componentSet.objectLinks.add(name); componentSet.objectLinks.add(name);
break; break;
case COMPONENT_BUILDERPARAM_DECORATOR: case COMPONENT_BUILDERPARAM_DECORATOR:
if (judgeInitializeInEntry) {
validateInitializeInEntry(node, name);
}
recordRequire.hasBuilderParam = true; recordRequire.hasBuilderParam = true;
componentSet.builderParams.add(name); componentSet.builderParams.add(name);
break; 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, function collectionlocalStorageParam(node: ts.Decorator, name: string,
localStorage: Map<string, Set<string>>): void { localStorage: Map<string, Set<string>>): void {
const localStorageParam: Set<string> = new Set(); const localStorageParam: Set<string> = new Set();

View File

@ -180,8 +180,8 @@
"type": "ERROR" "type": "ERROR"
}, },
"notComponent": { "notComponent": {
"message": "A struct should use decorator '@Component' or '@ComponentV2'.", "message": "Decorator '@Component', '@ComponentV2', or '@CustomDialog' is missing for struct 'EntryComponent'.",
"type": "WARN" "type": "ERROR"
}, },
"notConcurrent": { "notConcurrent": {
"message": "The struct 'IndexDecorator' use invalid decorator.", "message": "The struct 'IndexDecorator' use invalid decorator.",
@ -287,10 +287,6 @@
"message": "'@State' can not decorate the method.", "message": "'@State' can not decorate the method.",
"type": "ERROR" "type": "ERROR"
}, },
"BuilderParamNoInit": {
"message": "'closer' should be initialized in @Entry Component",
"type": "WARN"
},
"checkNonspecificParents": { "checkNonspecificParents": {
"message": "The 'Blank' component can only be nested in the 'Row,Column,Flex' parent component.", "message": "The 'Blank' component can only be nested in the 'Row,Column,Flex' parent component.",
"type": "ERROR" "type": "ERROR"

View File

@ -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() {
}
}
`