diff --git a/compiler/src/process_component_build.ts b/compiler/src/process_component_build.ts index 50f39ac..0cebbd2 100644 --- a/compiler/src/process_component_build.ts +++ b/compiler/src/process_component_build.ts @@ -81,7 +81,8 @@ import { } from './component_map'; import { componentCollection, - builderParamObjectCollection + builderParamObjectCollection, + checkAllNode } from './validate_ui_syntax'; import { processCustomComponent } from './process_custom_component'; import { @@ -134,8 +135,17 @@ export function processComponentBlock(node: ts.Block, isLazy: boolean, log: LogI function validateRootNode(node: ts.MethodDeclaration, log: LogInfo[]): boolean { let isValid: boolean = false; - if (node.body.statements.length === 1) { - const statement: ts.Statement = node.body.statements[0]; + const bodyStatements: ts.NodeArray = node.body.statements; + let length: number = bodyStatements.length; + bodyStatements.map(node => { + const nodeName: string = node.expression && node.expression.expression && + node.expression.expression.escapedText || undefined; + if (BUILDER_MIX_STYLES.has(nodeName) || BUILDER_MIX_EXTEND.has(nodeName)) { + length--; + } + }); + if (length === 1) { + const statement: ts.Statement = bodyStatements[0]; if (ts.isIfStatement(statement) || validateFirstNode(statement)) { isValid = true; } @@ -146,7 +156,7 @@ function validateRootNode(node: ts.MethodDeclaration, log: LogInfo[]): boolean { log.push({ type: LogType.ERROR, message: `There should have a root container component.`, - pos: node.body.statements.pos + pos: bodyStatements.pos }); } return isValid; @@ -226,6 +236,7 @@ export function processComponentChild(node: ts.Block | ts.SourceFile, newStateme let lastExpression: ts.Statement; node.statements.forEach((item, index) => { if (ts.isExpressionStatement(item)) { + checkEtsComponent(item, log); const name: string = getName(item); switch (getComponentType(item, log, name)) { case ComponentType.innerComponent: @@ -1355,3 +1366,26 @@ export function validateStateStyleSyntax(temp: any, log: LogInfo[]): void { pos: temp.getStart() }); } + +function getEtsComponentExpression(node:ts.ExpressionStatement): ts.EtsComponentExpression { + let current: any = node.expression; + while(current) { + if (ts.isEtsComponentExpression(current)) { + return current; + } + current = current.expression; + } + return null; +} + +function checkEtsComponent(node: ts.ExpressionStatement, log: LogInfo[]): void { + const etsComponentExpression: ts.EtsComponentExpression = getEtsComponentExpression(node); + if (etsComponentExpression) { + checkAllNode( + etsComponentExpression, + new Set([...INNER_COMPONENT_NAMES, ...componentCollection.customComponents]), + transformLog.sourceFile, + log + ); + } +} \ No newline at end of file diff --git a/compiler/src/validate_ui_syntax.ts b/compiler/src/validate_ui_syntax.ts index 76272bb..0307dba 100644 --- a/compiler/src/validate_ui_syntax.ts +++ b/compiler/src/validate_ui_syntax.ts @@ -333,7 +333,6 @@ function checkUISyntax(filePath: string, allComponentNames: Set, content function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponentNames: Set, log: LogInfo[]) { - checkAllNode(node, allComponentNames, sourceFileNode, log); if (ts.isStructDeclaration(node) && node.name && ts.isIdentifier(node.name)) { collectComponentProps(node); } @@ -359,18 +358,17 @@ function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponent node.getChildren().forEach((item: ts.Node) => visitAllNode(item, sourceFileNode, allComponentNames, log)); } -function checkAllNode(node: ts.Node, allComponentNames: Set, sourceFileNode: ts.SourceFile, - log: LogInfo[]): void { - if (ts.isExpressionStatement(node) && node.expression && ts.isIdentifier(node.expression) && - allComponentNames.has(node.expression.escapedText.toString())) { - const pos: number = node.expression.getStart(); - const message: string = - `The component name must be followed by parentheses, like '${node.expression.getText()}()'.`; - addLog(LogType.ERROR, message, pos, log, sourceFileNode); +export function checkAllNode( + node: ts.EtsComponentExpression, + allComponentNames: Set, + sourceFileNode: ts.SourceFile, + log: LogInfo[] +): void { + if (ts.isIdentifier(node.expression)) { + checkNoChildComponent(node, sourceFileNode, log); + checkOneChildComponent(node, allComponentNames, sourceFileNode, log); + checkSpecificChildComponent(node, allComponentNames, sourceFileNode, log); } - checkNoChildComponent(node, sourceFileNode, log); - checkOneChildComponent(node, allComponentNames, sourceFileNode, log); - checkSpecificChildComponent(node, allComponentNames, sourceFileNode, log); } interface ParamType { @@ -378,12 +376,11 @@ interface ParamType { value: string, } -function checkNoChildComponent(node: ts.Node, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { +function checkNoChildComponent(node: ts.EtsComponentExpression, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { const isCheckType: ParamType = { name: null, value: null}; - if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && - ts.isIdentifier(node.expression.expression) && hasChild(node, isCheckType)) { - const componentName: string = node.expression.expression.escapedText.toString(); - const pos: number = node.expression.expression.getStart(); + if (hasChild(node, isCheckType)) { + const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); + const pos: number = node.expression.getStart(); const message: string = isCheckType.name === null ? `The component '${componentName}' can't have any child.` : `When the component '${componentName}' set '${isCheckType.name}' is '${isCheckType.value}'` + @@ -392,11 +389,10 @@ function checkNoChildComponent(node: ts.Node, sourceFileNode: ts.SourceFile, log } } -function hasChild(node: ts.ExpressionStatement, isCheckType: ParamType): boolean { - const etsComponentExpression: ts.EtsComponentExpression = node.expression as ts.EtsComponentExpression; - const nodeName: ts.Identifier = etsComponentExpression.expression as ts.Identifier; - if (AUTOMIC_COMPONENT.has(nodeName.escapedText.toString()) || judgeComponentType( - nodeName, etsComponentExpression, isCheckType) && getNextNode(etsComponentExpression)) { +function hasChild(node: ts.EtsComponentExpression, isCheckType: ParamType): boolean { + const nodeName: ts.Identifier = node.expression as ts.Identifier; + if ((AUTOMIC_COMPONENT.has(nodeName.escapedText.toString()) || judgeComponentType(nodeName, node, isCheckType)) && + getNextNode(node)) { return true; } return false; @@ -433,13 +429,12 @@ function getNextNode(node: ts.EtsComponentExpression): ts.Block { } } -function checkOneChildComponent(node: ts.Node, allComponentNames: Set, +function checkOneChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { const isCheckType: ParamType = { name: null, value: null}; - if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && - ts.isIdentifier(node.expression.expression) && hasNonSingleChild(node, allComponentNames, isCheckType)) { - const componentName: string = node.expression.expression.escapedText.toString(); - const pos: number = node.expression.expression.getStart(); + if (hasNonSingleChild(node, allComponentNames, isCheckType)) { + const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); + const pos: number = node.expression.getStart(); const message: string = isCheckType.name === null ? `The component '${componentName}' can only have a single child component.` : `When the component '${componentName}' set '${isCheckType.name}' is ` + @@ -448,21 +443,29 @@ function checkOneChildComponent(node: ts.Node, allComponentNames: Set, } } -function hasNonSingleChild(node: ts.ExpressionStatement, allComponentNames: Set, +function hasNonSingleChild(node: ts.EtsComponentExpression, allComponentNames: Set, isCheckType: ParamType): boolean { - const etsComponentExpression: ts.EtsComponentExpression = node.expression as ts.EtsComponentExpression; - const nodeName: ts.Identifier = etsComponentExpression.expression as ts.Identifier; - const BlockNode: ts.Block = getNextNode(etsComponentExpression); - if (SINGLE_CHILD_COMPONENT.has(nodeName.escapedText.toString()) || !judgeComponentType( - nodeName, etsComponentExpression, isCheckType) && isCheckType.value === COMPONENT_BUTTON) { + const nodeName: ts.Identifier = node.expression as ts.Identifier; + const BlockNode: ts.Block = getNextNode(node); + if ((SINGLE_CHILD_COMPONENT.has(nodeName.escapedText.toString()) || !judgeComponentType(nodeName, node, isCheckType)) + && isCheckType.value === COMPONENT_BUTTON) { if (!BlockNode) { return false; } if (BlockNode && BlockNode.statements) { - const length: number = BlockNode.statements.length; + let length: number = BlockNode.statements.length; if (!length) { return false; } + BlockNode.statements.map(node => { + const nodeName: string = ts.isExpressionStatement(node) && + ts.isCallExpression(node.expression) && + ts.isIdentifier(node.expression.expression) ? + node.expression.expression.escapedText.toString() : ''; + if (BUILDER_MIX_STYLES.has(nodeName) || BUILDER_MIX_EXTEND.has(nodeName)) { + length--; + } + }); if (length > 3) { return true; } @@ -507,7 +510,7 @@ function getBlockChildrenCount(blockNode: ts.Block, allComponentNames: Set): boolean { +function isComponent(node: ts.EtsComponentExpression | ts.CallExpression, allComponentNames: Set): boolean { if (ts.isIdentifier(node.expression) && allComponentNames.has(node.expression.escapedText.toString())) { return true; @@ -515,7 +518,7 @@ function isComponent(node: ts.EtsComponentExpression, allComponentNames: Set): numbe return maxCount; } -function checkSpecificChildComponent(node: ts.Node, allComponentNames: Set, +function checkSpecificChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { - if (ts.isExpressionStatement(node) && ts.isCallExpression(node.expression) && - ts.isIdentifier(node.expression.expression) && hasNonspecificChild(node, allComponentNames)) { - const componentName: string = node.expression.expression.escapedText.toString(); - const pos: number = node.expression.expression.getStart(); + if (hasNonspecificChild(node, allComponentNames)) { + const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); + const pos: number = node.expression.getStart(); const specificChildArray: string = Array.from(SPECIFIC_CHILD_COMPONENT.get(componentName)).join(' and '); const message: string = @@ -562,12 +564,11 @@ function checkSpecificChildComponent(node: ts.Node, allComponentNames: Set): boolean { - const etsComponentExpression: ts.EtsComponentExpression = node.expression as ts.EtsComponentExpression; - const nodeName: ts.Identifier = etsComponentExpression.expression as ts.Identifier; + const nodeName: ts.Identifier = node.expression as ts.Identifier; const nodeNameString: string = nodeName.escapedText.toString(); - const blockNode: ts.Block = getNextNode(etsComponentExpression); + const blockNode: ts.Block = getNextNode(node); let isNonspecific: boolean = false; if (SPECIFIC_CHILD_COMPONENT.has(nodeNameString) && blockNode) { const specificChildSet: Set = SPECIFIC_CHILD_COMPONENT.get(nodeNameString); @@ -1070,4 +1071,4 @@ export function resetComponentCollection() { componentCollection.entryComponent = null; componentCollection.entryComponentPos = null; componentCollection.previewComponent = new Set([]); -} +}