diff --git a/compiler/src/ets_checker.ts b/compiler/src/ets_checker.ts index 8326ecc621e9ddfb2cf886305c9ca0ca4e9f66ab..6dab74f59c053d3ce741f3222e021f35d05d1a39 100644 --- a/compiler/src/ets_checker.ts +++ b/compiler/src/ets_checker.ts @@ -33,7 +33,8 @@ import { STYLE_ADD_DOUBLE_DOLLAR, $$, PROPERTIES_ADD_DOUBLE_DOLLAR, - $$_BLOCK_INTERFACE + $$_BLOCK_INTERFACE, + COMPONENT_EXTEND_DECORATOR } from './pre_define'; import { getName } from './process_component_build'; import { INNER_COMPONENT_NAMES } from './component_map'; @@ -43,6 +44,8 @@ import { CacheFileName, cache } from './compile_info'; +import { hasDecorator } from './utils'; +import { isExtendFunction, isOriginalExtend } from './process_ui_syntax'; function readDeaclareFiles(): string[] { const declarationsFileNames: string[] = []; @@ -85,6 +88,12 @@ function setCompilerOptions() { }); } +interface extendInfo { + start: number, + end: number, + compName: string +} + export function createLanguageService(rootFileNames: string[]): ts.LanguageService { setCompilerOptions(); const files: ts.MapLike<{ version: number }> = {}; @@ -97,8 +106,9 @@ export function createLanguageService(rootFileNames: string[]): ts.LanguageServi return undefined; } if (/(? { + const subStr: string = content.substring(item.start, item.end); + const insert: string = subStr.replace(/(\s)\$(\.)/g, (origin, item1, item2) => { + return item1 + item.compName + 'Instance' + item2; + }); + content = content.slice(0, item.start) + insert + content.slice(item.end); + }); + return content; +} + function getResolveModule(modulePath: string, type): ts.ResolvedModuleFull { return { resolvedFileName: modulePath, @@ -255,18 +278,18 @@ export const decoratorParamsCollection: Set = new Set(); export const extendCollection: Set = new Set(); export const importModuleCollection: Set = new Set(); -function checkUISyntax(source: string, fileName: string): void { +function checkUISyntax(source: string, fileName: string, extendFunctionInfo: extendInfo[]): void { if (/\.ets$/.test(fileName)) { if (path.basename(fileName) !== 'app.ets') { const sourceFile: ts.SourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); - parseAllNode(sourceFile, sourceFile); + parseAllNode(sourceFile, sourceFile, extendFunctionInfo); props.push(...dollarCollection, ...decoratorParamsCollection, ...extendCollection); } } } -function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile): void { +function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, extendFunctionInfo: extendInfo[]): void { if (ts.isStructDeclaration(node)) { if (node.members) { node.members.forEach(item => { @@ -295,7 +318,13 @@ function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile): void { }); } } - node.getChildren().forEach((item: ts.Node) => parseAllNode(item, sourceFileNode)); + if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_EXTEND_DECORATOR)) { + if (node.body && node.body.statements && node.body.statements.length && + !isOriginalExtend(node.body)) { + extendFunctionInfo.push({start: node.pos, end: node.end, compName: isExtendFunction(node)}); + } + } + node.getChildren().forEach((item: ts.Node) => parseAllNode(item, sourceFileNode, extendFunctionInfo)); } function traverseBuild(node: ts.Node, index: number): void { diff --git a/compiler/src/process_component_build.ts b/compiler/src/process_component_build.ts index 4a46a41c3b86fec06fe707af451d01f1548f4088..9b496a3a10d00e4ebae8eeff8b7219dc9e3faee8 100644 --- a/compiler/src/process_component_build.ts +++ b/compiler/src/process_component_build.ts @@ -94,6 +94,8 @@ import { import { projectConfig } from '../main'; import { transformLog, contextGlobal } from './process_ui_syntax'; import { props } from './compile_info'; +import { CUSTOM_COMPONENT } from './pre_define'; + export function processComponentBuild(node: ts.MethodDeclaration, log: LogInfo[]): ts.MethodDeclaration { @@ -113,10 +115,10 @@ export function processComponentBuild(node: ts.MethodDeclaration, } export function processComponentBlock(node: ts.Block, isLazy: boolean, log: LogInfo[], - isTransition: boolean = false, isInnerBuilder: boolean = false): ts.Block { + isTransition: boolean = false, isInnerBuilder: boolean = false, parent: string = undefined): ts.Block { const newStatements: ts.Statement[] = []; processComponentChild(node, newStatements, log, - {isAcceleratePreview: false, line: 0, column: 0, fileName: ''}, isInnerBuilder); + {isAcceleratePreview: false, line: 0, column: 0, fileName: ''}, isInnerBuilder, parent); if (isLazy) { newStatements.unshift(createRenderingInProgress(true)); } @@ -214,7 +216,7 @@ let sourceNode: ts.SourceFile; export function processComponentChild(node: ts.Block | ts.SourceFile, newStatements: ts.Statement[], log: LogInfo[], supplement: supplementType = {isAcceleratePreview: false, line: 0, column: 0, fileName: ''}, - isInnerBuilder: boolean = false): void { + isInnerBuilder: boolean = false, parent: string = undefined): void { if (supplement.isAcceleratePreview) { newsupplement = supplement; const compilerOptions = ts.readConfigFile( @@ -229,11 +231,13 @@ export function processComponentChild(node: ts.Block | ts.SourceFile, newStateme if (ts.isExpressionStatement(item)) { checkEtsComponent(item, log); const name: string = getName(item); - switch (getComponentType(item, log, name)) { + switch (getComponentType(item, log, name, parent)) { case ComponentType.innerComponent: - processInnerComponent(item, newStatements, log); + parent = name; + processInnerComponent(item, newStatements, log, parent); break; case ComponentType.customComponent: + parent = undefined; if (!newsupplement.isAcceleratePreview) { if (item.expression && ts.isEtsComponentExpression(item.expression) && item.expression.body) { const expressionResult: ts.ExpressionStatement = @@ -246,9 +250,11 @@ export function processComponentChild(node: ts.Block | ts.SourceFile, newStateme } break; case ComponentType.forEachComponent: + parent = undefined; processForEachComponent(item, newStatements, log, isInnerBuilder); break; case ComponentType.customBuilderMethod: + parent = undefined; if (CUSTOM_BUILDER_METHOD.has(name)) { newStatements.push(addInnerBuilderParameter(item)); } else { @@ -256,8 +262,13 @@ export function processComponentChild(node: ts.Block | ts.SourceFile, newStateme } break; case ComponentType.builderParamMethod: + parent = undefined; newStatements.push(addInnerBuilderParameter(item)); break; + case ComponentType.function: + parent = undefined; + newStatements.push(item); + break; } } else if (ts.isIfStatement(item)) { processIfStatement(item, newStatements, log, isInnerBuilder); @@ -360,7 +371,8 @@ function parseEtsComponentExpression(node: ts.ExpressionStatement): EtsComponent return { etsComponentNode: etsComponentNode, hasAttr: hasAttr }; } -function processInnerComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], log: LogInfo[]): void { +function processInnerComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], + log: LogInfo[], parent: string = undefined): void { const res: CreateResult = createComponent(node, COMPONENT_CREATE_FUNCTION); newStatements.push(res.newNode); const nameResult: NameResult = { name: null }; @@ -408,7 +420,8 @@ function processInnerComponent(node: ts.ExpressionStatement, newStatements: ts.S if (etsComponentResult.hasAttr) { bindComponentAttr(node, res.identifierNode, newStatements, log); } - processComponentChild(etsComponentResult.etsComponentNode.body, newStatements, log); + processComponentChild(etsComponentResult.etsComponentNode.body, newStatements, log, + {isAcceleratePreview: false, line: 0, column: 0, fileName: ''}, false, parent); } else { bindComponentAttr(node, res.identifierNode, newStatements, log); } @@ -1154,9 +1167,9 @@ function traverseStateStylesAttr(temp: any, statements: ts.Statement[], bindComponentAttr(ts.factory.createExpressionStatement( item.initializer.properties[0].initializer), identifierNode, statements, log, false, true); } else { - if (!(ts.isObjectLiteralExpression(item.initializer) && item.initializer.properties.length === 0)) { - validateStateStyleSyntax(temp, log); - } + if (!(ts.isObjectLiteralExpression(item.initializer) && item.initializer.properties.length === 0)) { + validateStateStyleSyntax(temp, log); + } } if (item.name) { statements.push(createViewStackProcessor(item, false)); @@ -1294,7 +1307,8 @@ enum ComponentType { customComponent, forEachComponent, customBuilderMethod, - builderParamMethod + builderParamMethod, + function } function isEtsComponent(node: ts.ExpressionStatement): boolean { @@ -1310,7 +1324,7 @@ function isEtsComponent(node: ts.ExpressionStatement): boolean { } function getComponentType(node: ts.ExpressionStatement, log: LogInfo[], - name: string): ComponentType { + name: string, parent: string): ComponentType { if (isEtsComponent(node)) { if (componentCollection.customComponents.has(name)) { return ComponentType.customComponent; @@ -1326,6 +1340,9 @@ function getComponentType(node: ts.ExpressionStatement, log: LogInfo[], } else if (builderParamObjectCollection.get(componentCollection.currentClassName) && builderParamObjectCollection.get(componentCollection.currentClassName).has(name)) { return ComponentType.builderParamMethod; + } else if (CUSTOM_BUILDER_METHOD.has(parent) && ts.isCallExpression(node.expression) && + ts.isIdentifier(node.expression.expression)) { + return ComponentType.function; } else if (!isAttributeNode(node)) { log.push({ type: LogType.ERROR, @@ -1346,7 +1363,7 @@ export function validateStateStyleSyntax(temp: any, log: LogInfo[]): void { function getEtsComponentExpression(node:ts.ExpressionStatement): ts.EtsComponentExpression { let current: any = node.expression; - while(current) { + while (current) { if (ts.isEtsComponentExpression(current)) { return current; } @@ -1356,7 +1373,7 @@ function getEtsComponentExpression(node:ts.ExpressionStatement): ts.EtsComponent } function checkEtsComponent(node: ts.ExpressionStatement, log: LogInfo[]): void { - const etsComponentExpression: ts.EtsComponentExpression = getEtsComponentExpression(node); + const etsComponentExpression: ts.EtsComponentExpression = getEtsComponentExpression(node); if (etsComponentExpression) { checkAllNode( etsComponentExpression, diff --git a/compiler/src/process_custom_component.ts b/compiler/src/process_custom_component.ts index 2b63cf00491f1424d1afcfc3ffa7d05c3fcaa2b4..6c69be9d16537fa721917bc71fb7e2d4d3aee578 100644 --- a/compiler/src/process_custom_component.ts +++ b/compiler/src/process_custom_component.ts @@ -364,17 +364,31 @@ function createFindChildById(id: string, name: string, isInnerBuilder: boolean = return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList( [ts.factory.createVariableDeclaration(ts.factory.createIdentifier( `${CUSTOM_COMPONENT_EARLIER_CREATE_CHILD}${id}`), undefined, ts.factory.createTypeReferenceNode( - ts.factory.createIdentifier(name)), ts.factory.createAsExpression(ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression(isInnerBuilder ? - ts.factory.createParenthesizedExpression(ts.factory.createConditionalExpression( - ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT), - ts.factory.createToken(ts.SyntaxKind.QuestionToken), - ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT), - ts.factory.createToken(ts.SyntaxKind.ColonToken), ts.factory.createThis() - )) : ts.factory.createThis(), ts.factory.createIdentifier( - `${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined, [isInnerBuilder ? ts.factory.createCallExpression( - ts.factory.createIdentifier(GENERATE_ID), undefined, []) : ts.factory.createStringLiteral(id)]), - ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))))], ts.NodeFlags.Let)); + ts.factory.createIdentifier(name)), + ts.factory.createConditionalExpression( + ts.factory.createParenthesizedExpression( + ts.factory.createBinaryExpression( + createConditionParent(isInnerBuilder), + ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), + ts.factory.createPropertyAccessExpression( + createConditionParent(isInnerBuilder), + ts.factory.createIdentifier(CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID) + ))), ts.factory.createToken(ts.SyntaxKind.QuestionToken), + ts.factory.createAsExpression(ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(createConditionParent(isInnerBuilder), + ts.factory.createIdentifier(`${CUSTOM_COMPONENT_FUNCTION_FIND_CHILD_BY_ID}`)), undefined, + [isInnerBuilder ? ts.factory.createCallExpression(ts.factory.createIdentifier(GENERATE_ID), + undefined, []) : ts.factory.createStringLiteral(id)]), + ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(name))), + ts.factory.createToken(ts.SyntaxKind.ColonToken), + ts.factory.createIdentifier('undefined')))], ts.NodeFlags.Let)); +} + +function createConditionParent(isInnerBuilder: boolean): ts.ParenthesizedExpression | ts.ThisExpression { + return isInnerBuilder ? ts.factory.createParenthesizedExpression(ts.factory.createConditionalExpression( + ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT), ts.factory.createToken(ts.SyntaxKind.QuestionToken), + ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT), ts.factory.createToken(ts.SyntaxKind.ColonToken), + ts.factory.createThis())) : ts.factory.createThis(); } function createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement, diff --git a/compiler/src/process_ui_syntax.ts b/compiler/src/process_ui_syntax.ts index 13b4b2cc47339b9ef3c08096b5eab74f3997f394..3f0b604a7f732647d3310189881329a37e3a5be1 100644 --- a/compiler/src/process_ui_syntax.ts +++ b/compiler/src/process_ui_syntax.ts @@ -152,7 +152,7 @@ export function processUISyntax(program: ts.Program, ut = false): Function { node.parameters.push(createParentParameter()); node = ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, - processComponentBlock(node.body, false, transformLog.errors, false, true)); + processComponentBlock(node.body, false, transformLog.errors, false, true, node.name.getText())); } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { if (node.parameters.length === 0) { node = undefined; @@ -479,10 +479,15 @@ function processExtend(node: ts.FunctionDeclaration, log: LogInfo[]): ts.Functio const componentName: string = isExtendFunction(node); if (componentName && node.body && node.body.statements.length) { const statementArray: ts.Statement[] = []; + let bodynode: ts.Block; const attrSet: ts.CallExpression = node.body.statements[0].expression; - const changeCompName: ts.ExpressionStatement = ts.factory.createExpressionStatement(processExtendBody(attrSet)); - bindComponentAttr(changeCompName as ts.ExpressionStatement, - ts.factory.createIdentifier(componentName), statementArray, log); + if (isOriginalExtend(node.body)) { + const changeCompName: ts.ExpressionStatement = ts.factory.createExpressionStatement(processExtendBody(attrSet)); + bindComponentAttr(changeCompName as ts.ExpressionStatement, + ts.factory.createIdentifier(componentName), statementArray, log); + } else { + bodynode = ts.visitEachChild(node.body, traverseExtendExpression, contextGlobal); + } let extendFunctionName: string; if (node.name.getText().startsWith('__' + componentName + '__')) { extendFunctionName = node.name.getText(); @@ -492,18 +497,64 @@ function processExtend(node: ts.FunctionDeclaration, log: LogInfo[]): ts.Functio } return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, ts.factory.createIdentifier(extendFunctionName), node.typeParameters, - node.parameters, node.type, ts.factory.updateBlock(node.body, statementArray)); + node.parameters, node.type, isOriginalExtend(node.body) ? + ts.factory.updateBlock(node.body, statementArray) : bodynode); + } + function traverseExtendExpression(node: ts.Node): ts.Node { + if (ts.isExpressionStatement(node) && isDollarNode(node)) { + const changeCompName: ts.ExpressionStatement = + ts.factory.createExpressionStatement(processExtendBody(node.expression, componentName)); + const statementArray: ts.Statement[] = []; + bindComponentAttr(changeCompName, ts.factory.createIdentifier(componentName), statementArray, []); + return ts.factory.createCallExpression( + ts.factory.createParenthesizedExpression(ts.factory.createFunctionExpression( + undefined, undefined, undefined, undefined, [], undefined, + ts.factory.createBlock(statementArray, true))), undefined, []); + } + return ts.visitEachChild(node, traverseExtendExpression, contextGlobal); + } +} + +export function isOriginalExtend(node: ts.Block): boolean { + let innerNode: ts.Node = node.statements[0]; + if (node.statements.length === 1 && ts.isExpressionStatement(innerNode)) { + while (innerNode.expression) { + innerNode = innerNode.expression; + } + if (ts.isIdentifier(innerNode) && innerNode.pos && innerNode.end && innerNode.pos === innerNode.end && + innerNode.escapedText.toString().match(/Instance$/)) { + return true; + } } + return false; } -function processExtendBody(node: ts.Node): ts.Expression { +function isDollarNode(node: ts.ExpressionStatement): boolean { + let innerNode: ts.Node = node; + while (innerNode.expression) { + innerNode = innerNode.expression; + } + if (ts.isIdentifier(innerNode) && innerNode.getText() === '$') { + return true; + } else { + return false; + } +} + +function processExtendBody(node: ts.Node, componentName?: string): ts.Expression { switch (node.kind) { case ts.SyntaxKind.CallExpression: - return ts.factory.createCallExpression(processExtendBody(node.expression), undefined, node.arguments); + return ts.factory.createCallExpression(processExtendBody(node.expression, componentName), + undefined, node.arguments); case ts.SyntaxKind.PropertyAccessExpression: - return ts.factory.createPropertyAccessExpression(processExtendBody(node.expression), node.name); + return ts.factory.createPropertyAccessExpression( + processExtendBody(node.expression, componentName), node.name); case ts.SyntaxKind.Identifier: - return ts.factory.createIdentifier(node.escapedText.toString().replace(INSTANCE, '')); + if (!componentName) { + return ts.factory.createIdentifier(node.escapedText.toString().replace(INSTANCE, '')); + } else { + return ts.factory.createIdentifier(componentName); + } } }