diff --git a/compiler/components/xcomponent.json b/compiler/components/xcomponent.json index 624aa8e..1183a99 100644 --- a/compiler/components/xcomponent.json +++ b/compiler/components/xcomponent.json @@ -1,6 +1,5 @@ { "name": "XComponent", - "atomic": true, "attrs": [ "id", "type", "libraryname", "onLoad", "onDestroy" ] diff --git a/compiler/src/ets_checker.ts b/compiler/src/ets_checker.ts index 8326ecc..848f234 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)this(\.)/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 0c4f37e..75ecef9 100644 --- a/compiler/src/process_component_build.ts +++ b/compiler/src/process_component_build.ts @@ -94,6 +94,7 @@ import { import { projectConfig } from '../main'; import { transformLog, contextGlobal } from './process_ui_syntax'; import { props } from './compile_info'; +import { CUSTOM_COMPONENT } from '../lib/pre_define'; export function processComponentBuild(node: ts.MethodDeclaration, log: LogInfo[]): ts.MethodDeclaration { @@ -113,10 +114,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 +215,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 +230,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 +249,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 (INNER_CUSTOM_BUILDER_METHOD.has(name)) { newStatements.push(addInnerBuilderParameter(item)); } else { @@ -256,8 +261,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 +370,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 +419,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); } @@ -1148,9 +1160,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)); @@ -1288,7 +1300,8 @@ enum ComponentType { customComponent, forEachComponent, customBuilderMethod, - builderParamMethod + builderParamMethod, + function } function isEtsComponent(node: ts.ExpressionStatement): boolean { @@ -1304,7 +1317,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; @@ -1320,6 +1333,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 ((['Column', 'XComponent'].includes(parent) || 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, @@ -1340,7 +1356,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; } @@ -1350,7 +1366,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 6fc9c2d..f1e5187 100644 --- a/compiler/src/process_custom_component.ts +++ b/compiler/src/process_custom_component.ts @@ -363,16 +363,28 @@ 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, [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( + ts.factory.createThis(), + ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), + ts.factory.createPropertyAccessExpression( + ts.factory.createThis(), + 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(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, [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 createCustomComponentIfStatement(id: string, node: ts.ExpressionStatement, diff --git a/compiler/src/process_ui_syntax.ts b/compiler/src/process_ui_syntax.ts index d7b43a3..f89cbcf 100644 --- a/compiler/src/process_ui_syntax.ts +++ b/compiler/src/process_ui_syntax.ts @@ -141,7 +141,7 @@ export function processUISyntax(program: ts.Program, ut = false): Function { CUSTOM_BUILDER_METHOD.add(node.name.getText()); node = ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, - processComponentBlock(node.body, false, transformLog.errors)); + processComponentBlock(node.body, false, transformLog.errors, false, false, node.name.getText())); } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { if (node.parameters.length === 0) { node = undefined; @@ -425,10 +425,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(); @@ -438,18 +443,68 @@ 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) && isThisNode(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); } } -function processExtendBody(node: ts.Node): ts.Expression { +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 isThisNode(node: ts.ExpressionStatement): boolean { + let innerNode: ts.Node = node; + while (innerNode.expression) { + innerNode = innerNode.expression; + } + if (innerNode.kind === ts.SyntaxKind.ThisKeyword) { + 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, '')); + } + break; + case ts.SyntaxKind.ThisKeyword: + if (componentName) { + return ts.factory.createIdentifier(componentName); + } + break; } }