diff --git a/compiler/package-lock.json b/compiler/package-lock.json index 5940092f451a0e686a53c139b3de1dd77d3748fb..61a331eee9e1d1b96e2ca63a305009b6d4aac862 100644 --- a/compiler/package-lock.json +++ b/compiler/package-lock.json @@ -2732,6 +2732,11 @@ "resolved": "https://registry.npmmirror.com/ignore/download/ignore-5.2.0.tgz", "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" }, + "ignore-loader": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz", + "integrity": "sha1-2B8kA3bQuk8Nd4lyw60lh0EXpGM=" + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npm.taobao.org/import-fresh/download/import-fresh-3.3.0.tgz?cache=0&sync_timestamp=1608469520474&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimport-fresh%2Fdownload%2Fimport-fresh-3.3.0.tgz", diff --git a/compiler/sample/pages/index.ets b/compiler/sample/pages/index.ets index 2dd5eb53dad6ada0716d6ee513b0cbad26eff259..8cf437be7112d07bd7c7381eeddacc4bf349fdbf 100644 --- a/compiler/sample/pages/index.ets +++ b/compiler/sample/pages/index.ets @@ -13,35 +13,27 @@ * limitations under the License. */ +@Entry @Component struct Banner { - private value1: string = "hello world 4" - private value2: string = "hello world 5" - private value3: string = "hello world 6" - - build() { + @Builder textBuilder() { + Text("文本") + .fontSize(30) + } + @Builder NavigationTitle() { Column() { - Text(this.value1) - Text(this.value2) - Text(this.value3) + Text("title") + .width(10) + .bindMenu(this.textBuilder) } } -} - -@Component -@Entry -struct MyComponent { - private value1: string = "hello world 1" - private value2: string = "hello world 2" - private value3: string = "hello world 3" - @State private idx:number = 0 - build() { Column() { - Text(this.value1) - Text(this.value2) - Text(this.value3) - Banner() + Text("111") + .bindMenu(this.NavigationTitle()) + Tabs(){ + TabContent(){}.tabBar("1") + } } } } diff --git a/compiler/src/component_map.ts b/compiler/src/component_map.ts index 550a0886d17f0f04a312cb72aa8e0a7486071473..f15b95483dadef86a3bea2a2a7829a0329df114e 100644 --- a/compiler/src/component_map.ts +++ b/compiler/src/component_map.ts @@ -89,6 +89,9 @@ export const JS_BIND_COMPONENTS: Set = new Set([ export const NEEDPOP_COMPONENT: Set = new Set(['Blank', 'Search']); +export const CUSTOM_BUILDER_PROPERTIES: Set = new Set(['bindPopup', 'bindMenu', 'bindContextMenu', 'title', + 'menus', 'toolBar', 'tabBar']); + (function initComponent() { Object.keys(COMPONENT_MAP).forEach((componentName) => { INNER_COMPONENT_NAMES.add(componentName); diff --git a/compiler/src/process_component_build.ts b/compiler/src/process_component_build.ts index ea2b141bc62a671b7b97d64755b37a988f80b578..c66da1729311e8e337c1a34b8fe566497a15dc52 100644 --- a/compiler/src/process_component_build.ts +++ b/compiler/src/process_component_build.ts @@ -52,7 +52,10 @@ import { $$_VALUE, $$_CHANGE_EVENT, $$_THIS, - $$_NEW_VALUE + $$_NEW_VALUE, + BUILDER_ATTR_NAME, + BUILDER_ATTR_BIND, + CUSTOM_DIALOG_CONTROLLER_BUILDER } from './pre_define'; import { INNER_COMPONENT_NAMES, @@ -66,7 +69,8 @@ import { NEEDPOP_COMPONENT, INNER_STYLE_FUNCTION, GLOBAL_STYLE_FUNCTION, - COMMON_ATTRS + COMMON_ATTRS, + CUSTOM_BUILDER_PROPERTIES } from './component_map'; import { componentCollection } from './validate_ui_syntax'; import { processCustomComponent } from './process_custom_component'; @@ -516,6 +520,16 @@ export function bindComponentAttr(node: ts.ExpressionStatement, identifierNode: const statements: ts.Statement[] = []; const lastStatement: AnimationInfo = { statement: null, kind: false }; while (temp && ts.isCallExpression(temp) && temp.expression) { + if (temp.expression && (validatePropertyAccessExpressionWithCustomBuilder(temp.expression) || + validateIdentifierWithCustomBuilder(temp.expression))) { + let propertyName: string = ''; + if (ts.isIdentifier(temp.expression)) { + propertyName = temp.expression.escapedText.toString(); + } else if (ts.isPropertyAccessExpression(temp.expression)) { + propertyName = temp.expression.name.escapedText.toString(); + } + temp = propertyName === BIND_POPUP ? processBindPopupBuilder(temp) : processCustomBuilderProperty(temp); + } if (ts.isPropertyAccessExpression(temp.expression) && temp.expression.name && ts.isIdentifier(temp.expression.name)) { addComponentAttr(temp, temp.expression.name, lastStatement, statements, identifierNode, log, @@ -538,6 +552,122 @@ export function bindComponentAttr(node: ts.ExpressionStatement, identifierNode: } } +function processCustomBuilderProperty(node: ts.CallExpression): ts.CallExpression { + const newArguments: ts.Expression[] = []; + node.arguments.forEach((argument: ts.Expression | ts.Identifier, index: number) => { + if (index === 0 && (ts.isPropertyAccessExpression(argument) || ts.isCallExpression(argument) || + ts.isIdentifier(argument))) { + newArguments.push(parseBuilderNode(argument)); + } else { + newArguments.push(argument); + } + }); + node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, newArguments); + return node; +} + +function parseBuilderNode(node: ts.Node): ts.ObjectLiteralExpression { + if (isPropertyAccessExpressionNode(node)) { + return processPropertyBuilder(node as ts.PropertyAccessExpression); + } else if (ts.isIdentifier(node) && CUSTOM_BUILDER_METHOD.has(node.escapedText.toString())) { + return processIdentifierBuilder(node); + } else if (ts.isCallExpression(node)) { + return getParsedBuilderAttrArgumentWithParams(node); + } +} + +function isPropertyAccessExpressionNode(node: ts.Node): boolean { + return ts.isPropertyAccessExpression(node) && node.expression && + node.expression.kind === ts.SyntaxKind.ThisKeyword && node.name && ts.isIdentifier(node.name) && + CUSTOM_BUILDER_METHOD.has(node.name.escapedText.toString()); +} + +function processBindPopupBuilder(node: ts.CallExpression): ts.CallExpression { + const newArguments: ts.Expression[] = []; + node.arguments.forEach((argument: ts.ObjectLiteralExpression, index: number) => { + if (index === 1) { + // @ts-ignore + newArguments.push(processBindPopupBuilderProperty(argument)); + } else { + newArguments.push(argument); + } + }); + node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, newArguments); + return node; +} + +function processBindPopupBuilderProperty(node: ts.ObjectLiteralExpression): ts.ObjectLiteralExpression { + const newProperties: ts.PropertyAssignment[] = []; + node.properties.forEach((property: ts.PropertyAssignment, index: number) => { + if (index === 0) { + if (property.name && ts.isIdentifier(property.name) && + property.name.escapedText.toString() === CUSTOM_DIALOG_CONTROLLER_BUILDER) { + newProperties.push(ts.factory.updatePropertyAssignment(property, property.name, + parseBuilderNode(property.initializer))); + } else { + newProperties.push(property); + } + } else { + newProperties.push(property); + } + }); + return ts.factory.updateObjectLiteralExpression(node, newProperties); +} + +function processPropertyBuilder(node: ts.PropertyAccessExpression): ts.ObjectLiteralExpression { + return ts.factory.createObjectLiteralExpression([ + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier(BUILDER_ATTR_NAME), + ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + node, + ts.factory.createIdentifier(BUILDER_ATTR_BIND) + ), + undefined, + [ts.factory.createThis()] + ) + ) + ]); +} + +function processIdentifierBuilder(node: ts.Identifier): ts.ObjectLiteralExpression { + return ts.factory.createObjectLiteralExpression([ + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier(BUILDER_ATTR_NAME), + node + ) + ]); +} + +function getParsedBuilderAttrArgumentWithParams(node: ts.CallExpression): + ts.ObjectLiteralExpression { + return ts.factory.createObjectLiteralExpression([ + ts.factory.createPropertyAssignment( + ts.factory.createIdentifier(BUILDER_ATTR_NAME), + ts.factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + ts.factory.createBlock( + [ts.factory.createExpressionStatement(node)], + true + ) + ) + ) + ]); +} + +function validatePropertyAccessExpressionWithCustomBuilder(node: ts.Node): boolean { + return ts.isPropertyAccessExpression(node) && node.name && + ts.isIdentifier(node.name) && CUSTOM_BUILDER_PROPERTIES.has(node.name.escapedText.toString()); +} + +function validateIdentifierWithCustomBuilder(node: ts.Node): boolean { + return ts.isIdentifier(node) && CUSTOM_BUILDER_PROPERTIES.has(node.escapedText.toString()); +} + function createArrowFunctionFor$$($$varExp: ts.Expression): ts.ArrowFunction { return ts.factory.createArrowFunction( undefined, undefined, @@ -561,8 +691,8 @@ function createArrowFunctionFor$$($$varExp: ts.Expression): ts.ArrowFunction { function updateArgumentFor$$(argument: any): ts.Expression { if (ts.isElementAccessExpression(argument)) { - return ts.factory.updateElementAccessExpression - (argument, updateArgumentFor$$(argument.expression), argument.argumentExpression); + return ts.factory.updateElementAccessExpression( + argument, updateArgumentFor$$(argument.expression), argument.argumentExpression); } else if (ts.isIdentifier(argument)) { props.push(argument.getText()); if (argument.getText() === $$_THIS) { @@ -571,8 +701,8 @@ function updateArgumentFor$$(argument: any): ts.Expression { return ts.factory.createIdentifier(argument.getText().replace(/\$\$/, '')); } } else if (ts.isPropertyAccessExpression(argument)) { - return ts.factory.updatePropertyAccessExpression - (argument, updateArgumentFor$$(argument.expression), argument.name); + return ts.factory.updatePropertyAccessExpression( + argument, updateArgumentFor$$(argument.expression), argument.name); } } @@ -706,8 +836,8 @@ function traverseStateStylesAttr(temp: any, statements: ts.Statement[], } else if (ts.isObjectLiteralExpression(item.initializer) && item.initializer.properties.length === 1 && ts.isPropertyAssignment(item.initializer.properties[0])) { - bindComponentAttr(ts.factory.createExpressionStatement - (item.initializer.properties[0].initializer), identifierNode, statements, log, false, true); + bindComponentAttr(ts.factory.createExpressionStatement( + item.initializer.properties[0].initializer), identifierNode, statements, log, false, true); } else { validateStateStyleSyntax(temp, log); } diff --git a/compiler/src/process_component_class.ts b/compiler/src/process_component_class.ts index 0e759bfc42367fe637a2433c19a0e237629daf7b..2eaa0dbfcf94b3630ca4caae02ebbcce7ad9cd33 100644 --- a/compiler/src/process_component_class.ts +++ b/compiler/src/process_component_class.ts @@ -34,11 +34,8 @@ import { COMPONENT_TRANSITION_FUNCTION, COMPONENT_CREATE_FUNCTION, GEOMETRY_VIEW, - BUILDER_ATTR_NAME, - BUILDER_ATTR_BIND, COMPONENT_STYLES_DECORATOR, - STYLES, - CUSTOM_COMPONENT_EARLIER_CREATE_CHILD + STYLES } from './pre_define'; import { BUILDIN_STYLE_NAMES, @@ -210,8 +207,7 @@ function processBuildMember(node: ts.MethodDeclaration, context: ts.Transformati }); } const buildNode: ts.MethodDeclaration = processComponentBuild(node, log); - const firstParseBuildNode = ts.visitNode(buildNode, visitBuild); - return ts.visitNode(firstParseBuildNode, visitBuildSecond); + return ts.visitNode(buildNode, visitBuild); function visitBuild(node: ts.Node): ts.Node { if (isGeometryView(node)) { node = processGeometryView(node as ts.ExpressionStatement, log); @@ -229,90 +225,6 @@ function processBuildMember(node: ts.MethodDeclaration, context: ts.Transformati } return ts.visitEachChild(node, visitBuild, context); } - function visitBuildSecond(node: ts.Node): ts.Node { - if (isCustomComponentNode(node) || isCustomBuilderNode(node)) { - return node; - } - if ((ts.isIdentifier(node) || ts.isPropertyAccessExpression(node)) && - validateBuilderFunctionNode(node)) { - return getParsedBuilderAttrArgument(node); - } - return ts.visitEachChild(node, visitBuildSecond, context); - } -} - -function validateBuilderFunctionNode(node: ts.PropertyAccessExpression | ts.Identifier): boolean { - if ((ts.isPropertyAccessExpression(node) && node.expression && node.name && - node.expression.kind === ts.SyntaxKind.ThisKeyword && ts.isIdentifier(node.name) && - CUSTOM_BUILDER_METHOD.has(node.name.escapedText.toString()) || - ts.isIdentifier(node) && CUSTOM_BUILDER_METHOD.has(node.escapedText.toString())) && - !(ts.isPropertyAccessExpression(node) && validateBuilderParam(node) || - ts.isIdentifier(node) && node.parent && ts.isPropertyAccessExpression(node.parent) && - validateBuilderParam(node.parent))) { - return true; - } else { - return false; - } -} - -function validateBuilderParam(node: ts.PropertyAccessExpression): boolean { - if (node.parent && ts.isCallExpression(node.parent) && node.parent.expression === node) { - return true; - } else { - return false; - } -} - -function getParsedBuilderAttrArgument(node: ts.PropertyAccessExpression | ts.Identifier): - ts.ObjectLiteralExpression { - let newObjectNode: ts.ObjectLiteralExpression = null; - if (ts.isPropertyAccessExpression(node)) { - newObjectNode = ts.factory.createObjectLiteralExpression([ - ts.factory.createPropertyAssignment( - ts.factory.createIdentifier(BUILDER_ATTR_NAME), - ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - node, - ts.factory.createIdentifier(BUILDER_ATTR_BIND) - ), - undefined, - [ts.factory.createThis()] - ) - ) - ]); - } else if (ts.isIdentifier(node)) { - newObjectNode = ts.factory.createObjectLiteralExpression([ - ts.factory.createPropertyAssignment( - ts.factory.createIdentifier(BUILDER_ATTR_NAME), - node - ) - ]); - } - return newObjectNode; -} - -function isCustomComponentNode(node:ts.NewExpression | ts.ExpressionStatement): boolean { - if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.escapedText - && componentCollection.customComponents.has(node.expression.escapedText.toString()) || - // @ts-ignore - ts.isExpressionStatement(node) && node.expression && node.expression.expression && - // @ts-ignore - node.expression.expression.expression && node.expression.expression.expression.escapedText && - // @ts-ignore - node.expression.expression.expression.escapedText.toString().startsWith( - CUSTOM_COMPONENT_EARLIER_CREATE_CHILD)) { - return true; - } else { - return false; - } -} - -function isCustomBuilderNode(node: ts.ExpressionStatement): boolean { - return ts.isExpressionStatement(node) && node.expression && - // @ts-ignore - node.expression.expression && node.expression.expression.escapedText && - // @ts-ignore - CUSTOM_BUILDER_METHOD.has(node.expression.expression.escapedText.toString()); } function isGeometryView(node: ts.Node): boolean { diff --git a/compiler/src/validate_ui_syntax.ts b/compiler/src/validate_ui_syntax.ts index 148178cb25abb75e3ef2a6f7d820688fe23850a3..04ee994fe395d1b1d57b3e575d167bf39c792bf1 100644 --- a/compiler/src/validate_ui_syntax.ts +++ b/compiler/src/validate_ui_syntax.ts @@ -41,10 +41,10 @@ import { COMPONENT_CONSTRUCTOR_ID, COMPONENT_CONSTRUCTOR_PARENT, COMPONENT_CONSTRUCTOR_PARAMS, - COMPONENT_EXTEND_DECORATOR, COMPONENT_OBSERVED_DECORATOR, STYLES, - VALIDATE_MODULE + VALIDATE_MODULE, + COMPONENT_BUILDER_DECORATOR } from './pre_define'; import { INNER_COMPONENT_NAMES, @@ -54,7 +54,8 @@ import { BUILDIN_STYLE_NAMES, EXTEND_ATTRIBUTE, GLOBAL_STYLE_FUNCTION, - STYLES_ATTRIBUTE + STYLES_ATTRIBUTE, + CUSTOM_BUILDER_METHOD } from './component_map'; import { LogType, @@ -324,6 +325,9 @@ function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponent if (ts.isClassDeclaration(node) && node.name && ts.isIdentifier(node.name)) { collectComponentProps(node); } + if (ts.isMethodDeclaration(node) && hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) { + CUSTOM_BUILDER_METHOD.add(node.name.getText()); + } node.getChildren().forEach((item: ts.Node) => visitAllNode(item, sourceFileNode, allComponentNames, log)); }