diff --git a/compiler/src/ets_checker.ts b/compiler/src/ets_checker.ts index 8326ecc..dd335d0 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 } 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 (/(?{ + let subStr: string = content.substring(item.start,item.end); + let 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,27 @@ 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)); +} + +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 traverseBuild(node: ts.Node, index: number): void { diff --git a/compiler/src/process_ui_syntax.ts b/compiler/src/process_ui_syntax.ts index d7b43a3..97291f0 100644 --- a/compiler/src/process_ui_syntax.ts +++ b/compiler/src/process_ui_syntax.ts @@ -80,6 +80,7 @@ import { projectConfig } from '../main'; import { createCustomComponentNewExpression, createViewCreate } from './process_component_member'; +import { isOriginalExtend } from './ets_checker' export const transformLog: FileLog = new FileLog(); export let contextGlobal: ts.TransformationContext; @@ -425,10 +426,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 +444,56 @@ 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)) { + let 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 { + + +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; } }