1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import ts from 'typescript'; 17import path from 'path'; 18 19import { 20 COMPONENT_RENDER_FUNCTION, 21 COMPONENT_CREATE_FUNCTION, 22 COMPONENT_POP_FUNCTION, 23 COMPONENT_BUTTON, 24 COMPONENT_CREATE_LABEL_FUNCTION, 25 COMPONENT_CREATE_CHILD_FUNCTION, 26 COMPONENT_FOREACH, 27 COMPONENT_LAZYFOREACH, 28 IS_RENDERING_IN_PROGRESS, 29 FOREACH_OBSERVED_OBJECT, 30 FOREACH_GET_RAW_OBJECT, 31 COMPONENT_IF, 32 COMPONENT_IF_BRANCH_ID_FUNCTION, 33 COMPONENT_IF_UNDEFINED, 34 ATTRIBUTE_ANIMATION, 35 GLOBAL_CONTEXT, 36 COMPONENT_GESTURE, 37 COMPONENT_GESTURE_GROUP, 38 GESTURE_ATTRIBUTE, 39 PARALLEL_GESTURE_ATTRIBUTE, 40 PRIORITY_GESTURE_ATTRIBUTE, 41 GESTURE_ENUM_KEY, 42 GESTURE_ENUM_VALUE_HIGH, 43 GESTURE_ENUM_VALUE_LOW, 44 GESTURE_ENUM_VALUE_PARALLEL, 45 COMPONENT_TRANSITION_NAME, 46 COMPONENT_DEBUGLINE_FUNCTION, 47 ATTRIBUTE_STATESTYLES, 48 THIS, 49 VISUAL_STATE, 50 VIEW_STACK_PROCESSOR, 51 STYLE_ADD_DOUBLE_DOLLAR, 52 $$_VALUE, 53 $$_CHANGE_EVENT, 54 $$_THIS, 55 $$_NEW_VALUE, 56 BUILDER_ATTR_NAME, 57 BUILDER_ATTR_BIND, 58 CUSTOM_DIALOG_CONTROLLER_BUILDER, 59 BIND_DRAG_SET, 60 BIND_POPUP_SET, 61 $$, 62 PROPERTIES_ADD_DOUBLE_DOLLAR, 63 RESOURCE 64} from './pre_define'; 65import { 66 INNER_COMPONENT_NAMES, 67 BUILDIN_CONTAINER_COMPONENT, 68 BUILDIN_STYLE_NAMES, 69 CUSTOM_BUILDER_METHOD, 70 GESTURE_ATTRS, 71 GESTURE_TYPE_NAMES, 72 EXTEND_ATTRIBUTE, 73 NO_DEBUG_LINE_COMPONENT, 74 NEEDPOP_COMPONENT, 75 INNER_STYLE_FUNCTION, 76 GLOBAL_STYLE_FUNCTION, 77 COMMON_ATTRS, 78 CUSTOM_BUILDER_PROPERTIES 79} from './component_map'; 80import { componentCollection } from './validate_ui_syntax'; 81import { processCustomComponent } from './process_custom_component'; 82import { 83 LogType, 84 LogInfo, 85 componentInfo, 86 createFunction 87} from './utils'; 88import { builderParamObjectCollection } from './process_component_member'; 89import { projectConfig } from '../main'; 90import { transformLog, contextGlobal } from './process_ui_syntax'; 91import { props } from './compile_info'; 92 93export function processComponentBuild(node: ts.MethodDeclaration, 94 log: LogInfo[]): ts.MethodDeclaration { 95 let newNode: ts.MethodDeclaration; 96 const renderNode: ts.Identifier = ts.factory.createIdentifier(COMPONENT_RENDER_FUNCTION); 97 if (node.body && node.body.statements && node.body.statements.length && 98 validateRootNode(node, log)) { 99 newNode = ts.factory.updateMethodDeclaration(node, node.decorators, node.modifiers, 100 node.asteriskToken, renderNode, node.questionToken, node.typeParameters, node.parameters, 101 node.type, processComponentBlock(node.body, false, log)); 102 } else { 103 newNode = ts.factory.updateMethodDeclaration(node, node.decorators, node.modifiers, 104 node.asteriskToken, renderNode, node.questionToken, node.typeParameters, node.parameters, 105 node.type, node.body); 106 } 107 return newNode; 108} 109 110export function processComponentBlock(node: ts.Block, 111 isLazy: boolean, log: LogInfo[], isTransition: boolean = false, 112 forEachParameters: ts.NodeArray<ts.ParameterDeclaration> = undefined): ts.Block { 113 const newStatements: ts.Statement[] = []; 114 processComponentChild(node, newStatements, log, 115 {isAcceleratePreview: false, line: 0, column: 0, fileName: ''}, forEachParameters); 116 if (isLazy) { 117 newStatements.unshift(createRenderingInProgress(true)); 118 } 119 if (isTransition) { 120 newStatements.unshift(ts.factory.createExpressionStatement( 121 createFunction(ts.factory.createIdentifier(COMPONENT_TRANSITION_NAME), 122 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), null))); 123 newStatements.push(ts.factory.createExpressionStatement( 124 createFunction(ts.factory.createIdentifier(COMPONENT_TRANSITION_NAME), 125 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null))); 126 } 127 if (isLazy) { 128 newStatements.push(createRenderingInProgress(false)); 129 } 130 return ts.factory.updateBlock(node, newStatements); 131} 132 133function validateRootNode(node: ts.MethodDeclaration, log: LogInfo[]): boolean { 134 let isValid: boolean = false; 135 if (node.body.statements.length === 1) { 136 const statement: ts.Statement = node.body.statements[0]; 137 if (ts.isIfStatement(statement) || validateFirstNode(statement)) { 138 isValid = true; 139 } 140 } else { 141 isValid = false; 142 } 143 if (!isValid) { 144 log.push({ 145 type: LogType.ERROR, 146 message: `There should have a root container component.`, 147 pos: node.body.statements.pos 148 }); 149 } 150 return isValid; 151} 152 153function validateFirstNode(node: ts.Statement): boolean { 154 const isEntryComponent: boolean = 155 componentCollection.entryComponent === componentCollection.currentClassName; 156 if (isEntryComponent && !validateContainerComponent(node)) { 157 return false; 158 } 159 return true; 160} 161 162function validateContainerComponent(node: ts.Statement): boolean { 163 if (ts.isExpressionStatement(node) && node.expression && 164 (ts.isEtsComponentExpression(node.expression) || ts.isCallExpression(node.expression))) { 165 const nameResult: NameResult = { name: null }; 166 validateEtsComponentNode(node.expression, nameResult); 167 if (nameResult.name && BUILDIN_CONTAINER_COMPONENT.has(nameResult.name)) { 168 return true; 169 } 170 } 171 return false; 172} 173 174interface supplementType { 175 isAcceleratePreview: boolean, 176 line: number, 177 column: number, 178 fileName: string 179} 180 181let newsupplement: supplementType = { 182 isAcceleratePreview: false, 183 line: 0, 184 column: 0, 185 fileName: '' 186}; 187 188type NameResult = { 189 name: string 190} 191 192function validateEtsComponentNode(node: ts.CallExpression | ts.EtsComponentExpression, result?: NameResult) { 193 let childNode: ts.Node = node; 194 result.name = null; 195 while (ts.isCallExpression(childNode) && childNode.expression && 196 ts.isPropertyAccessExpression(childNode.expression) && childNode.expression.expression) { 197 childNode = childNode.expression.expression; 198 } 199 if (ts.isEtsComponentExpression(childNode)) { 200 if (ts.isIdentifier(childNode.expression)) { 201 result.name = childNode.expression.getText(); 202 } 203 return true; 204 } else { 205 return false; 206 } 207} 208 209let sourceNode: ts.SourceFile; 210 211export function processComponentChild(node: ts.Block | ts.SourceFile, newStatements: ts.Statement[], 212 log: LogInfo[], supplement: supplementType = {isAcceleratePreview: false, line: 0, column: 0, fileName: ''}, 213 forEachParameters: ts.NodeArray<ts.ParameterDeclaration> = undefined): void { 214 if (supplement.isAcceleratePreview) { 215 newsupplement = supplement; 216 const compilerOptions = ts.readConfigFile( 217 path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions; 218 Object.assign(compilerOptions, { 219 'sourceMap': false 220 }); 221 sourceNode = ts.createSourceFile('', node.getText(), ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS, compilerOptions); 222 } 223 if (node.statements.length) { 224 node.statements.forEach((item) => { 225 if (ts.isExpressionStatement(item)) { 226 const name: string = getName(item); 227 switch (getComponentType(item, log, name, forEachParameters)) { 228 case ComponentType.innerComponent: 229 processInnerComponent(item, newStatements, log); 230 break; 231 case ComponentType.customComponent: 232 if (!newsupplement.isAcceleratePreview) { 233 if (item.expression && ts.isEtsComponentExpression(item.expression) && item.expression.body) { 234 if (processExpressionStatementChange(item, item.expression.body, log)) { 235 item = processExpressionStatementChange(item, item.expression.body, log); 236 } 237 } 238 processCustomComponent(item as ts.ExpressionStatement, newStatements, log); 239 } 240 break; 241 case ComponentType.forEachComponent: 242 processForEachComponent(item, newStatements, log); 243 break; 244 case ComponentType.customBuilderMethod: 245 case ComponentType.builderParamMethod: 246 newStatements.push(item); 247 break; 248 } 249 } else if (ts.isIfStatement(item)) { 250 processIfStatement(item, newStatements, log); 251 } else if (!ts.isBlock(item)) { 252 log.push({ 253 type: LogType.ERROR, 254 message: `Only UI component syntax can be written in build method.`, 255 pos: item.getStart() 256 }); 257 } 258 }); 259 } 260 if (supplement.isAcceleratePreview) { 261 newsupplement = { 262 isAcceleratePreview: false, 263 line: 0, 264 column: 0, 265 fileName: '' 266 }; 267 } 268} 269 270function processExpressionStatementChange(node: ts.ExpressionStatement, nextNode: ts.Block, 271 log: LogInfo[]): ts.ExpressionStatement { 272 let name: string; 273 // @ts-ignore 274 if (node.expression.expression && ts.isIdentifier(node.expression.expression)) { 275 name = node.expression.expression.escapedText.toString(); 276 } 277 if (builderParamObjectCollection.get(name) && 278 builderParamObjectCollection.get(name).size === 1) { 279 return processBlockToExpression(node, nextNode, log, name); 280 } else { 281 log.push({ 282 type: LogType.ERROR, 283 message: `In the trailing lambda case, '${name}' must have one and only one property decorated with ` 284 + `@BuilderParam, and its @BuilderParam expects no parameter.`, 285 pos: node.getStart() 286 }); 287 } 288} 289 290function processBlockToExpression(node: ts.ExpressionStatement, nextNode: ts.Block, 291 log: LogInfo[], name: string): ts.ExpressionStatement { 292 const childParam: string = [...builderParamObjectCollection.get(name)].slice(-1)[0]; 293 const newBlock: ts.Block = processComponentBlock(nextNode, false, log); 294 const arrowNode: ts.ArrowFunction = ts.factory.createArrowFunction(undefined, undefined, 295 [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), newBlock); 296 const newPropertyAssignment:ts.PropertyAssignment = ts.factory.createPropertyAssignment( 297 ts.factory.createIdentifier(childParam), arrowNode); 298 // @ts-ignore 299 let argumentsArray: ts.ObjectLiteralExpression[] = node.expression.arguments; 300 if (argumentsArray && !argumentsArray.length) { 301 argumentsArray = [ts.factory.createObjectLiteralExpression([newPropertyAssignment], true)]; 302 } else { 303 argumentsArray = [ts.factory.createObjectLiteralExpression( 304 // @ts-ignore 305 node.expression.arguments[0].properties.concat([newPropertyAssignment]), true)]; 306 } 307 node = ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression( 308 // @ts-ignore 309 node.expression, node.expression.expression, node.expression.expression.typeArguments, 310 argumentsArray)); 311 return node; 312} 313 314type EtsComponentResult = { 315 etsComponentNode: ts.EtsComponentExpression; 316 hasAttr: boolean; 317} 318function parseEtsComponentExpression(node: ts.ExpressionStatement): EtsComponentResult { 319 let etsComponentNode: ts.EtsComponentExpression; 320 let hasAttr: boolean = false; 321 let temp: any = node.expression; 322 while (temp) { 323 if (ts.isCallExpression(temp) && temp.expression && 324 ts.isPropertyAccessExpression(temp.expression)) { 325 hasAttr = true; 326 } 327 if (ts.isEtsComponentExpression(temp)) { 328 etsComponentNode = temp; 329 break; 330 } 331 temp = temp.expression; 332 } 333 return { etsComponentNode: etsComponentNode, hasAttr: hasAttr }; 334} 335 336function processInnerComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], log: LogInfo[]): void { 337 const res: CreateResult = createComponent(node, COMPONENT_CREATE_FUNCTION); 338 newStatements.push(res.newNode); 339 const nameResult: NameResult = { name: null }; 340 validateEtsComponentNode(node.expression as ts.EtsComponentExpression, nameResult); 341 if (projectConfig.isPreview && nameResult.name && !NO_DEBUG_LINE_COMPONENT.has(nameResult.name)) { 342 let posOfNode: ts.LineAndCharacter; 343 let curFileName: string; 344 let line: number = 1; 345 let col: number = 1; 346 if (sourceNode && newsupplement.isAcceleratePreview) { 347 posOfNode = sourceNode.getLineAndCharacterOfPosition(getRealNodePos(node) - 22); 348 curFileName = newsupplement.fileName; 349 if (posOfNode.line === 0) { 350 col = newsupplement.column - 1; 351 } 352 line = newsupplement.line; 353 } else { 354 posOfNode = transformLog.sourceFile.getLineAndCharacterOfPosition(getRealNodePos(node)); 355 curFileName = transformLog.sourceFile.fileName.replace(/\.ts$/, ''); 356 } 357 const projectPath: string = projectConfig.projectPath; 358 const debugInfo: string = 359 `${path.relative(projectPath, curFileName).replace(/\\+/g, '/')}` + 360 `(${posOfNode.line + line}:${posOfNode.character + col})`; 361 const debugNode: ts.ExpressionStatement = ts.factory.createExpressionStatement( 362 createFunction(ts.factory.createIdentifier(nameResult.name), 363 ts.factory.createIdentifier(COMPONENT_DEBUGLINE_FUNCTION), 364 ts.factory.createNodeArray([ts.factory.createStringLiteral(debugInfo)]))); 365 newStatements.push(debugNode); 366 } 367 const etsComponentResult: EtsComponentResult = parseEtsComponentExpression(node); 368 if (PROPERTIES_ADD_DOUBLE_DOLLAR.has(res.identifierNode.getText()) && 369 etsComponentResult.etsComponentNode.arguments && etsComponentResult.etsComponentNode.arguments.length) { 370 etsComponentResult.etsComponentNode = processDollarEtsComponent(etsComponentResult.etsComponentNode, 371 res.identifierNode.getText()); 372 } 373 if (etsComponentResult.etsComponentNode.body && ts.isBlock(etsComponentResult.etsComponentNode.body)) { 374 if (res.isButton) { 375 checkButtonParamHasLabel(etsComponentResult.etsComponentNode, log); 376 if (projectConfig.isPreview) { 377 newStatements.splice(-2, 1, createComponent(node, COMPONENT_CREATE_CHILD_FUNCTION).newNode); 378 } else { 379 newStatements.splice(-1, 1, createComponent(node, COMPONENT_CREATE_CHILD_FUNCTION).newNode); 380 } 381 } 382 if (etsComponentResult.hasAttr) { 383 bindComponentAttr(node, res.identifierNode, newStatements, log); 384 } 385 processComponentChild(etsComponentResult.etsComponentNode.body, newStatements, log); 386 } else { 387 bindComponentAttr(node, res.identifierNode, newStatements, log); 388 } 389 if (res.isContainerComponent || res.needPop) { 390 newStatements.push(createComponent(node, COMPONENT_POP_FUNCTION).newNode); 391 } 392} 393 394function getRealNodePos(node: ts.Node): number { 395 // @ts-ignore 396 if (node.pos === -1 && node.expression) { 397 // @ts-ignore 398 return getRealNodePos(node.expression); 399 } else { 400 return node.getStart(); 401 } 402} 403 404function processForEachComponent(node: ts.ExpressionStatement, newStatements: ts.Statement[], 405 log: LogInfo[]): void { 406 const popNode: ts.ExpressionStatement = ts.factory.createExpressionStatement(createFunction( 407 // @ts-ignore 408 node.expression.expression as ts.Identifier, 409 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)); 410 if (ts.isCallExpression(node.expression)) { 411 const propertyNode: ts.PropertyAccessExpression = ts.factory.createPropertyAccessExpression( 412 node.expression.expression as ts.Identifier, 413 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION) 414 ); 415 const argumentsArray: ts.Expression[] = Array.from(node.expression.arguments); 416 let arrayObserveredObject: ts.CallExpression; 417 if (argumentsArray.length) { 418 arrayObserveredObject = ts.factory.createCallExpression( 419 ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(FOREACH_OBSERVED_OBJECT), 420 ts.factory.createIdentifier(FOREACH_GET_RAW_OBJECT)), undefined, [argumentsArray[0]]); 421 } 422 argumentsArray.splice(0, 1, arrayObserveredObject); 423 const newArrowNode: ts.ArrowFunction = processForEachBlock(node.expression, log); 424 if (newArrowNode) { 425 argumentsArray.splice(1, 1, newArrowNode); 426 } 427 node = addForEachId(ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression( 428 node.expression, propertyNode, node.expression.typeArguments, argumentsArray))); 429 } 430 newStatements.push(node, popNode); 431} 432 433function addForEachId(node: ts.ExpressionStatement): ts.ExpressionStatement { 434 const forEachComponent: ts.CallExpression = node.expression as ts.CallExpression; 435 return ts.factory.updateExpressionStatement(node, ts.factory.updateCallExpression( 436 forEachComponent, forEachComponent.expression, forEachComponent.typeArguments, 437 [ts.factory.createStringLiteral((++componentInfo.id).toString()), ts.factory.createThis(), 438 ...forEachComponent.arguments])); 439} 440 441function processForEachBlock(node: ts.CallExpression, log: LogInfo[]): ts.ArrowFunction { 442 if (node.arguments.length > 1 && ts.isArrowFunction(node.arguments[1])) { 443 const isLazy: boolean = node.expression.getText() === COMPONENT_LAZYFOREACH; 444 const arrowNode: ts.ArrowFunction = node.arguments[1] as ts.ArrowFunction; 445 const body: ts.ConciseBody = arrowNode.body; 446 if (node.arguments.length > 2 && !ts.isArrowFunction(node.arguments[2])) { 447 log.push({ 448 type: LogType.ERROR, 449 message: 'There should be wrapped in curly braces in ForEach.', 450 pos: body.getStart() 451 }); 452 } else if (!ts.isBlock(body)) { 453 const statement: ts.Statement = ts.factory.createExpressionStatement(body); 454 const blockNode: ts.Block = ts.factory.createBlock([statement], true); 455 // @ts-ignore 456 statement.parent = blockNode; 457 return ts.factory.updateArrowFunction( 458 arrowNode, arrowNode.modifiers, arrowNode.typeParameters, 459 arrowNode.parameters, arrowNode.type, arrowNode.equalsGreaterThanToken, 460 processComponentBlock(blockNode, isLazy, log, false, arrowNode.parameters)); 461 } else { 462 return ts.factory.updateArrowFunction( 463 arrowNode, arrowNode.modifiers, arrowNode.typeParameters, 464 arrowNode.parameters, arrowNode.type, arrowNode.equalsGreaterThanToken, 465 processComponentBlock(body, isLazy, log, false, arrowNode.parameters)); 466 } 467 } 468 return null; 469} 470 471function createRenderingInProgress(isTrue: boolean): ts.ExpressionStatement { 472 return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 473 ts.factory.createPropertyAccessExpression( 474 ts.factory.createThis(), 475 ts.factory.createIdentifier(IS_RENDERING_IN_PROGRESS) 476 ), 477 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 478 isTrue ? ts.factory.createTrue() : ts.factory.createFalse() 479 )); 480} 481 482function processIfStatement(node: ts.IfStatement, newStatements: ts.Statement[], 483 log: LogInfo[]): void { 484 const ifCreate: ts.ExpressionStatement = createIfCreate(); 485 const newIfNode: ts.IfStatement = processInnerIfStatement(node, 0, log); 486 const ifPop: ts.ExpressionStatement = createIfPop(); 487 newStatements.push(ifCreate, newIfNode, ifPop); 488} 489 490function processInnerIfStatement(node: ts.IfStatement, id: number, log: LogInfo[]): ts.IfStatement { 491 if (ts.isIdentifier(node.expression) && node.expression.originalKeywordKind === undefined && 492 !node.expression.escapedText) { 493 log.push({ 494 type: LogType.ERROR, 495 message: 'Condition expression cannot be null in if statement.', 496 pos: node.expression.getStart() 497 }); 498 node = ts.factory.updateIfStatement(node, ts.factory.createIdentifier(COMPONENT_IF_UNDEFINED), 499 node.thenStatement, node.elseStatement); 500 } 501 const newThenStatement: ts.Statement = processThenStatement(node.thenStatement, id, log); 502 const newElseStatement: ts.Statement = processElseStatement(node.elseStatement, id, log); 503 const newIfNode: ts.IfStatement = ts.factory.updateIfStatement( 504 node, node.expression, newThenStatement, newElseStatement); 505 return newIfNode; 506} 507 508function processThenStatement(thenStatement: ts.Statement, id: number, 509 log: LogInfo[]): ts.Statement { 510 if (ts.isExpressionStatement(thenStatement) && ts.isIdentifier(thenStatement.expression) && 511 thenStatement.expression.originalKeywordKind === undefined && 512 !thenStatement.expression.escapedText) { 513 log.push({ 514 type: LogType.ERROR, 515 message: 'Then statement cannot be null in if statement.', 516 pos: thenStatement.expression.getStart() 517 }); 518 } 519 if (thenStatement) { 520 if (ts.isBlock(thenStatement)) { 521 thenStatement = processIfBlock(thenStatement, id, log); 522 } else if (ts.isIfStatement(thenStatement)) { 523 thenStatement = processInnerIfStatement(thenStatement, 0, log); 524 thenStatement = ts.factory.createBlock( 525 [createIfCreate(), createIfBranchId(id), thenStatement, createIfPop()], true); 526 } else { 527 thenStatement = ts.factory.createBlock([thenStatement], true); 528 thenStatement = processIfBlock(thenStatement as ts.Block, id, log); 529 } 530 } 531 return thenStatement; 532} 533 534function processElseStatement(elseStatement: ts.Statement, id: number, 535 log: LogInfo[]): ts.Statement { 536 if (elseStatement) { 537 if (ts.isBlock(elseStatement)) { 538 elseStatement = processIfBlock(elseStatement, id + 1, log); 539 } else if (ts.isIfStatement(elseStatement)) { 540 elseStatement = processInnerIfStatement(elseStatement, id + 1, log); 541 } else { 542 elseStatement = ts.factory.createBlock([elseStatement], true); 543 elseStatement = processIfBlock(elseStatement as ts.Block, id + 1, log); 544 } 545 } 546 return elseStatement; 547} 548 549function processIfBlock(block: ts.Block, id: number, log: LogInfo[]): ts.Block { 550 return addIfBranchId(id, processComponentBlock(block, false, log)); 551} 552 553function addIfBranchId(id: number, container: ts.Block): ts.Block { 554 return ts.factory.updateBlock(container, [createIfBranchId(id), ...container.statements]); 555} 556 557function createIf(): ts.Identifier { 558 return ts.factory.createIdentifier(COMPONENT_IF); 559} 560 561function createIfCreate(): ts.ExpressionStatement { 562 return ts.factory.createExpressionStatement(createFunction(createIf(), 563 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), ts.factory.createNodeArray([]))); 564} 565 566function createIfPop(): ts.ExpressionStatement { 567 return ts.factory.createExpressionStatement(createFunction(createIf(), 568 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null)); 569} 570 571function createIfBranchId(id: number): ts.ExpressionStatement { 572 return ts.factory.createExpressionStatement(createFunction(createIf(), 573 ts.factory.createIdentifier(COMPONENT_IF_BRANCH_ID_FUNCTION), 574 ts.factory.createNodeArray([ts.factory.createNumericLiteral(id)]))); 575} 576 577interface CreateResult { 578 newNode: ts.ExpressionStatement; 579 identifierNode: ts.Identifier; 580 isContainerComponent: boolean; 581 isButton: boolean; 582 needPop: boolean; 583} 584 585function createComponent(node: ts.ExpressionStatement, type: string): CreateResult { 586 const res: CreateResult = { 587 newNode: node, 588 identifierNode: null, 589 isContainerComponent: false, 590 isButton: false, 591 needPop: false 592 }; 593 let identifierNode: ts.Identifier = ts.factory.createIdentifier(type); 594 let temp: any = node.expression; 595 while (temp && !ts.isIdentifier(temp) && temp.expression) { 596 temp = temp.expression; 597 } 598 if (temp && temp.parent && (ts.isCallExpression(temp.parent) || 599 ts.isEtsComponentExpression(temp.parent)) && ts.isIdentifier(temp)) { 600 if (temp.getText() === COMPONENT_BUTTON && type !== COMPONENT_POP_FUNCTION) { 601 res.isButton = true; 602 identifierNode = type === COMPONENT_CREATE_CHILD_FUNCTION 603 ? ts.factory.createIdentifier(COMPONENT_CREATE_CHILD_FUNCTION) 604 : ts.factory.createIdentifier(COMPONENT_CREATE_LABEL_FUNCTION); 605 } 606 if (NEEDPOP_COMPONENT.has(temp.getText())) { 607 res.needPop = true; 608 } 609 if (BUILDIN_CONTAINER_COMPONENT.has(temp.getText())) { 610 res.isContainerComponent = true; 611 } 612 res.newNode = type === COMPONENT_POP_FUNCTION 613 ? ts.factory.updateExpressionStatement(node, 614 createFunction(temp, identifierNode, null)) 615 : ts.factory.updateExpressionStatement(node, 616 createFunction(temp, identifierNode, temp.parent.arguments)); 617 res.identifierNode = temp; 618 } 619 return res; 620} 621 622interface AnimationInfo { 623 statement: ts.Statement, 624 kind: boolean 625} 626 627export function bindComponentAttr(node: ts.ExpressionStatement, identifierNode: ts.Identifier, 628 newStatements: ts.Statement[], log: LogInfo[], reverse: boolean = true, 629 isStylesAttr: boolean = false): void { 630 let temp: any = node.expression; 631 const statements: ts.Statement[] = []; 632 const lastStatement: AnimationInfo = { statement: null, kind: false }; 633 if (ts.isPropertyAccessExpression(temp)) { 634 log.push({ 635 type: LogType.ERROR, 636 message: `'${node.getText()}' does not meet UI component syntax.`, 637 pos: node.getStart() 638 }); 639 } 640 while (temp && ts.isCallExpression(temp) && temp.expression) { 641 let flag: boolean = false; 642 if (temp.expression && (validatePropertyAccessExpressionWithCustomBuilder(temp.expression) || 643 validateIdentifierWithCustomBuilder(temp.expression))) { 644 let propertyName: string = ''; 645 if (ts.isIdentifier(temp.expression)) { 646 propertyName = temp.expression.escapedText.toString(); 647 } else if (ts.isPropertyAccessExpression(temp.expression)) { 648 propertyName = temp.expression.name.escapedText.toString(); 649 } 650 switch (true) { 651 case BIND_POPUP_SET.has(propertyName): 652 temp = processBindPopupBuilder(temp); 653 break; 654 case BIND_DRAG_SET.has(propertyName): 655 temp = processDragStartBuilder(temp); 656 break; 657 default: 658 temp = processCustomBuilderProperty(temp); 659 } 660 flag = true; 661 } 662 if (ts.isPropertyAccessExpression(temp.expression) && 663 temp.expression.name && ts.isIdentifier(temp.expression.name)) { 664 addComponentAttr(temp, temp.expression.name, lastStatement, statements, identifierNode, log, 665 isStylesAttr); 666 temp = temp.expression.expression; 667 flag = true; 668 } else if (ts.isIdentifier(temp.expression)) { 669 if (!INNER_COMPONENT_NAMES.has(temp.expression.getText()) && 670 !GESTURE_TYPE_NAMES.has(temp.expression.getText())) { 671 addComponentAttr(temp, temp.expression, lastStatement, statements, identifierNode, log, 672 isStylesAttr); 673 } 674 break; 675 } 676 if (!flag) { 677 temp = temp.expression; 678 } 679 } 680 if (lastStatement.statement && lastStatement.kind) { 681 statements.push(lastStatement.statement); 682 } 683 if (statements.length) { 684 reverse ? newStatements.push(...statements.reverse()) : newStatements.push(...statements); 685 } 686} 687 688function processCustomBuilderProperty(node: ts.CallExpression): ts.CallExpression { 689 const newArguments: ts.Expression[] = []; 690 node.arguments.forEach((argument: ts.Expression | ts.Identifier, index: number) => { 691 if (index === 0 && isBuilderChangeNode(argument)) { 692 newArguments.push(parseBuilderNode(argument)); 693 } else { 694 newArguments.push(argument); 695 } 696 }); 697 node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, newArguments); 698 return node; 699} 700 701function isBuilderChangeNode(argument: ts.Node): boolean { 702 return ts.isPropertyAccessExpression(argument) && argument.name && ts.isIdentifier(argument.name) 703 && CUSTOM_BUILDER_METHOD.has(argument.name.getText()) || 704 ts.isCallExpression(argument) && argument.expression && argument.expression.name && 705 ts.isIdentifier(argument.expression.name) && 706 CUSTOM_BUILDER_METHOD.has(argument.expression.name.getText()) || ts.isIdentifier(argument) && 707 argument.escapedText && CUSTOM_BUILDER_METHOD.has(argument.escapedText.toString()); 708} 709 710function parseBuilderNode(node: ts.Node): ts.ObjectLiteralExpression { 711 if (isPropertyAccessExpressionNode(node)) { 712 return processPropertyBuilder(node as ts.PropertyAccessExpression); 713 } else if (ts.isIdentifier(node) && CUSTOM_BUILDER_METHOD.has(node.escapedText.toString())) { 714 return processIdentifierBuilder(node); 715 } else if (ts.isCallExpression(node)) { 716 return getParsedBuilderAttrArgumentWithParams(node); 717 } 718} 719 720function isPropertyAccessExpressionNode(node: ts.Node): boolean { 721 return ts.isPropertyAccessExpression(node) && node.expression && 722 node.expression.kind === ts.SyntaxKind.ThisKeyword && node.name && ts.isIdentifier(node.name) && 723 CUSTOM_BUILDER_METHOD.has(node.name.escapedText.toString()); 724} 725 726function processBindPopupBuilder(node: ts.CallExpression): ts.CallExpression { 727 const newArguments: ts.Expression[] = []; 728 node.arguments.forEach((argument: ts.ObjectLiteralExpression, index: number) => { 729 if (index === 1) { 730 // @ts-ignore 731 newArguments.push(processBindPopupBuilderProperty(argument)); 732 } else { 733 newArguments.push(argument); 734 } 735 }); 736 node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, newArguments); 737 return node; 738} 739 740function processDragStartBuilder(node: ts.CallExpression): ts.CallExpression { 741 const newStatements: ts.Statement[] = []; 742 if (isNodeFunction(node)) { 743 // @ts-ignore 744 for (let i = 0; i < node.arguments[0].body.statements.length; i++) { 745 // @ts-ignore 746 const statement: ts.Statement = node.arguments[0].body.statements[i]; 747 newStatements.push(checkStatement(statement)); 748 } 749 node = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ts.factory.updateArrowFunction( 750 // @ts-ignore 751 node.arguments[0], undefined, undefined, node.arguments[0].parameters, node.arguments[0].type, 752 // @ts-ignore 753 node.arguments[0].equalsGreaterThanToken, ts.factory.updateBlock(node.arguments[0].body, newStatements))]); 754 } 755 return node; 756} 757 758function isNodeFunction(node: ts.CallExpression): boolean { 759 return node.arguments && node.arguments.length && ts.isArrowFunction(node.arguments[0]) && node.arguments[0].body && 760 ts.isBlock(node.arguments[0].body); 761} 762 763function checkStatement(statement: ts.Statement): ts.Statement { 764 if (ts.isReturnStatement(statement)) { 765 if (ts.isObjectLiteralExpression(statement.expression)) { 766 const newProperties: ts.ObjectLiteralElementLike[] = []; 767 for (let j = 0; j < statement.expression.properties.length; j++) { 768 const property: ts.ObjectLiteralElementLike = statement.expression.properties[j]; 769 checkProperty(property); 770 newProperties.push(property); 771 } 772 return ts.factory.createReturnStatement(ts.factory.createObjectLiteralExpression(newProperties)); 773 } else { 774 return ts.factory.updateReturnStatement(statement, parseBuilderNode(statement.expression)); 775 } 776 } else { 777 return statement; 778 } 779} 780 781function checkProperty(property: ts.ObjectLiteralElementLike): void { 782 if (isPropertyFunction(property)) { 783 // @ts-ignore 784 property = ts.factory.createPropertyAssignment(property.name, parseBuilderNode(property.initializer)); 785 } 786} 787 788function isPropertyFunction(property: ts.ObjectLiteralElementLike): boolean { 789 return ts.isPropertyAssignment(property) && property.name && ts.isIdentifier(property.name) && 790 property.name.escapedText.toString() === BUILDER_ATTR_NAME; 791} 792 793function processBindPopupBuilderProperty(node: ts.ObjectLiteralExpression): ts.ObjectLiteralExpression { 794 const newProperties: ts.PropertyAssignment[] = []; 795 node.properties.forEach((property: ts.PropertyAssignment, index: number) => { 796 if (index === 0) { 797 if (property.name && ts.isIdentifier(property.name) && 798 property.name.escapedText.toString() === CUSTOM_DIALOG_CONTROLLER_BUILDER) { 799 newProperties.push(ts.factory.updatePropertyAssignment(property, property.name, 800 parseBuilderNode(property.initializer))); 801 } else { 802 newProperties.push(property); 803 } 804 } else { 805 newProperties.push(property); 806 } 807 }); 808 return ts.factory.updateObjectLiteralExpression(node, newProperties); 809} 810 811function processPropertyBuilder(node: ts.PropertyAccessExpression): ts.ObjectLiteralExpression { 812 return ts.factory.createObjectLiteralExpression([ 813 ts.factory.createPropertyAssignment( 814 ts.factory.createIdentifier(BUILDER_ATTR_NAME), 815 ts.factory.createCallExpression( 816 ts.factory.createPropertyAccessExpression( 817 node, 818 ts.factory.createIdentifier(BUILDER_ATTR_BIND) 819 ), 820 undefined, 821 [ts.factory.createThis()] 822 ) 823 ) 824 ]); 825} 826 827function processIdentifierBuilder(node: ts.Identifier): ts.ObjectLiteralExpression { 828 return ts.factory.createObjectLiteralExpression([ 829 ts.factory.createPropertyAssignment( 830 ts.factory.createIdentifier(BUILDER_ATTR_NAME), 831 node 832 ) 833 ]); 834} 835 836function getParsedBuilderAttrArgumentWithParams(node: ts.CallExpression): 837 ts.ObjectLiteralExpression { 838 return ts.factory.createObjectLiteralExpression([ 839 ts.factory.createPropertyAssignment( 840 ts.factory.createIdentifier(BUILDER_ATTR_NAME), 841 ts.factory.createArrowFunction( 842 undefined, 843 undefined, 844 [], 845 undefined, 846 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 847 ts.factory.createBlock( 848 [ts.factory.createExpressionStatement(node)], 849 true 850 ) 851 ) 852 ) 853 ]); 854} 855 856function validatePropertyAccessExpressionWithCustomBuilder(node: ts.Node): boolean { 857 return ts.isPropertyAccessExpression(node) && node.name && 858 ts.isIdentifier(node.name) && CUSTOM_BUILDER_PROPERTIES.has(node.name.escapedText.toString()); 859} 860 861function validateIdentifierWithCustomBuilder(node: ts.Node): boolean { 862 return ts.isIdentifier(node) && CUSTOM_BUILDER_PROPERTIES.has(node.escapedText.toString()); 863} 864 865function createArrowFunctionFor$$($$varExp: ts.Expression): ts.ArrowFunction { 866 return ts.factory.createArrowFunction( 867 undefined, undefined, 868 [ts.factory.createParameterDeclaration( 869 undefined, undefined, undefined, 870 ts.factory.createIdentifier($$_NEW_VALUE), 871 undefined, undefined, undefined 872 )], 873 undefined, 874 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 875 ts.factory.createBlock( 876 [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression( 877 $$varExp, 878 ts.factory.createToken(ts.SyntaxKind.EqualsToken), 879 ts.factory.createIdentifier($$_NEW_VALUE) 880 ))], 881 false 882 ) 883 ); 884} 885 886function updateArgumentFor$$(argument: any): ts.Expression { 887 if (ts.isElementAccessExpression(argument)) { 888 return ts.factory.updateElementAccessExpression( 889 argument, updateArgumentFor$$(argument.expression), argument.argumentExpression); 890 } else if (ts.isIdentifier(argument)) { 891 props.push(argument.getText()); 892 if (argument.getText() === $$_THIS) { 893 return ts.factory.createThis(); 894 } else if (argument.getText().match(/^\$\$(.|\n)+/)) { 895 return ts.factory.createIdentifier(argument.getText().replace(/\$\$/, '')); 896 } 897 } else if (ts.isPropertyAccessExpression(argument)) { 898 return ts.factory.updatePropertyAccessExpression( 899 argument, updateArgumentFor$$(argument.expression), argument.name); 900 } 901} 902 903function addComponentAttr(temp: any, node: ts.Identifier, lastStatement: any, 904 statements: ts.Statement[], identifierNode: ts.Identifier, log: LogInfo[], 905 isStylesAttr: boolean): void { 906 const propName: string = node.getText(); 907 if (propName === ATTRIBUTE_ANIMATION) { 908 if (!lastStatement.statement) { 909 if (!(temp.arguments.length === 1 && 910 temp.arguments[0].kind === ts.SyntaxKind.NullKeyword)) { 911 statements.push(ts.factory.createExpressionStatement(createFunction( 912 ts.factory.createIdentifier(GLOBAL_CONTEXT), node, 913 // @ts-ignore 914 [ts.factory.createNull()]))); 915 } 916 } else { 917 statements.push(lastStatement.statement); 918 } 919 lastStatement.statement = ts.factory.createExpressionStatement(createFunction( 920 ts.factory.createIdentifier(GLOBAL_CONTEXT), node, temp.arguments)); 921 lastStatement.kind = false; 922 } else if (GESTURE_ATTRS.has(propName)) { 923 parseGesture(temp, propName, statements, log); 924 lastStatement.kind = true; 925 } else if (isExtendFunctionNode(identifierNode, propName)) { 926 if (newsupplement.isAcceleratePreview) { 927 log.push({ 928 type: LogType.ERROR, 929 message: `Doesn't support Extend function now`, 930 pos: temp.getStart() 931 }); 932 } 933 statements.push(ts.factory.createExpressionStatement(ts.factory.createCallExpression( 934 ts.factory.createIdentifier(`__${identifierNode.escapedText.toString()}__${propName}`), 935 undefined, temp.arguments))); 936 lastStatement.kind = true; 937 } else if (propName === ATTRIBUTE_STATESTYLES) { 938 if (temp.arguments.length === 1 && ts.isObjectLiteralExpression(temp.arguments[0])) { 939 statements.push(createViewStackProcessor(temp, true)); 940 traverseStateStylesAttr(temp, statements, identifierNode, log); 941 lastStatement.kind = true; 942 } else { 943 validateStateStyleSyntax(temp, log); 944 } 945 } else if (GLOBAL_STYLE_FUNCTION.has(propName) || INNER_STYLE_FUNCTION.has(propName)) { 946 const styleBlock: ts.Block = 947 INNER_STYLE_FUNCTION.get(propName) || GLOBAL_STYLE_FUNCTION.get(propName); 948 bindComponentAttr(styleBlock.statements[0] as ts.ExpressionStatement, identifierNode, 949 statements, log, false, true); 950 lastStatement.kind = true; 951 } else if (isDoubleDollarToChange(isStylesAttr, identifierNode, propName, temp)) { 952 const argumentsArr: ts.Expression[] = []; 953 classifyArgumentsNum(temp.arguments, argumentsArr, propName, identifierNode); 954 statements.push(ts.factory.createExpressionStatement( 955 createFunction(identifierNode, node, argumentsArr))); 956 lastStatement.kind = true; 957 } else { 958 if (isStylesAttr) { 959 if (!COMMON_ATTRS.has(propName)) { 960 validateStateStyleSyntax(temp, log); 961 } 962 } 963 temp = loopEtscomponent(temp, isStylesAttr); 964 statements.push(ts.factory.createExpressionStatement( 965 createFunction(identifierNode, node, temp.arguments))); 966 lastStatement.kind = true; 967 } 968} 969 970function isDoubleDollarToChange(isStylesAttr: boolean, identifierNode: ts.Identifier, 971 propName: string, temp: any): boolean { 972 return !isStylesAttr && 973 PROPERTIES_ADD_DOUBLE_DOLLAR.has(identifierNode.escapedText.toString()) && 974 PROPERTIES_ADD_DOUBLE_DOLLAR.get(identifierNode.escapedText.toString()).has(propName) || 975 STYLE_ADD_DOUBLE_DOLLAR.has(propName) && temp.arguments.length && temp.arguments[0] ? 976 temp.arguments[0].getText().match(/^\$\$(.|\n)+/) !== null ? true : false 977 : false; 978} 979 980function processDollarEtsComponent(node: ts.EtsComponentExpression, name: string 981 ): ts.EtsComponentExpression { 982 node.arguments.forEach((item: ts.Node, index: number) => { 983 if (ts.isObjectLiteralExpression(item) && item.properties && item.properties.length) { 984 item.properties.forEach((param: ts.PropertyAssignment, paramIndex: number)=>{ 985 if (isHaveDoubleDollar(param, name)) { 986 const varExp: ts.Expression = updateArgumentFor$$(param.initializer); 987 node.arguments[index].properties[paramIndex].initializer = generateObjectFor$$(varExp); 988 } 989 }) 990 } 991 }) 992 return node; 993} 994 995function isHaveDoubleDollar(param: ts.PropertyAssignment, name: string): boolean { 996 return ts.isPropertyAssignment(param) && param.name && ts.isIdentifier(param.name) && 997 PROPERTIES_ADD_DOUBLE_DOLLAR.get(name).has(param.name.getText()) && param.initializer && 998 param.initializer.getText().startsWith($$); 999} 1000 1001function loopEtscomponent(node: any, isStylesAttr: boolean): ts.Node { 1002 node.arguments.forEach((item: ts.Node, index: number) => { 1003 if (ts.isEtsComponentExpression(item)) { 1004 node.arguments[index] = ts.factory.createCallExpression( 1005 item.expression, undefined, item.arguments); 1006 } else if (ts.isCallExpression(item) || ts.isNewExpression(item)) { 1007 node.arguments[index] = ts.visitEachChild(item, 1008 changeEtsComponentKind, contextGlobal); 1009 } 1010 }); 1011 return node; 1012} 1013 1014function changeEtsComponentKind(node: ts.Node): ts.Node { 1015 if (ts.isEtsComponentExpression(node)) { 1016 node.kind = 204; 1017 return node; 1018 } 1019 return ts.visitEachChild(node, changeEtsComponentKind, contextGlobal); 1020} 1021 1022function classifyArgumentsNum(args: any, argumentsArr: ts.Expression[], propName: string, 1023 identifierNode: ts.Identifier): void { 1024 if (STYLE_ADD_DOUBLE_DOLLAR.has(propName) && args.length === 2) { 1025 const varExp: ts.Expression = updateArgumentFor$$(args[0]); 1026 argumentsArr.push(generateObjectFor$$(varExp), args[1]); 1027 } else if (PROPERTIES_ADD_DOUBLE_DOLLAR.has(identifierNode.getText()) && args.length === 1 && 1028 PROPERTIES_ADD_DOUBLE_DOLLAR.get(identifierNode.getText()).has(propName) || 1029 STYLE_ADD_DOUBLE_DOLLAR.has(propName) && args.length === 1) { 1030 const varExp: ts.Expression = updateArgumentFor$$(args[0]); 1031 argumentsArr.push(varExp, createArrowFunctionFor$$(varExp)); 1032 } 1033} 1034 1035function generateObjectFor$$(varExp: ts.Expression): ts.ObjectLiteralExpression { 1036 return ts.factory.createObjectLiteralExpression( 1037 [ 1038 ts.factory.createPropertyAssignment( 1039 ts.factory.createIdentifier($$_VALUE), 1040 varExp 1041 ), 1042 ts.factory.createPropertyAssignment( 1043 ts.factory.createIdentifier($$_CHANGE_EVENT), 1044 createArrowFunctionFor$$(varExp) 1045 ) 1046 ], 1047 false 1048 ); 1049} 1050 1051function createViewStackProcessor(item: any, endViewStack: boolean): ts.ExpressionStatement { 1052 const argument: ts.StringLiteral[] = []; 1053 if (!endViewStack && item.name) { 1054 argument.push(ts.factory.createStringLiteral(item.name.getText())); 1055 } 1056 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1057 ts.factory.createPropertyAccessExpression( 1058 ts.factory.createIdentifier(VIEW_STACK_PROCESSOR), 1059 ts.factory.createIdentifier(VISUAL_STATE) 1060 ), 1061 undefined, 1062 argument 1063 )); 1064} 1065 1066function traverseStateStylesAttr(temp: any, statements: ts.Statement[], 1067 identifierNode: ts.Identifier, log: LogInfo[]): void { 1068 temp.arguments[0].properties.reverse().forEach((item: ts.PropertyAssignment) => { 1069 if (ts.isPropertyAccessExpression(item.initializer) && 1070 item.initializer.expression.getText() === THIS && 1071 INNER_STYLE_FUNCTION.get(item.initializer.name.getText())) { 1072 const name: string = item.initializer.name.getText(); 1073 bindComponentAttr(INNER_STYLE_FUNCTION.get(name).statements[0] as ts.ExpressionStatement, 1074 identifierNode, statements, log, false, true); 1075 } else if (ts.isIdentifier(item.initializer) && 1076 GLOBAL_STYLE_FUNCTION.get(item.initializer.getText())) { 1077 const name: string = item.initializer.getText(); 1078 bindComponentAttr(GLOBAL_STYLE_FUNCTION.get(name).statements[0] as ts.ExpressionStatement, 1079 identifierNode, statements, log, false, true); 1080 } else if (ts.isObjectLiteralExpression(item.initializer) && 1081 item.initializer.properties.length === 1 && 1082 ts.isPropertyAssignment(item.initializer.properties[0])) { 1083 bindComponentAttr(ts.factory.createExpressionStatement( 1084 item.initializer.properties[0].initializer), identifierNode, statements, log, false, true); 1085 } else { 1086 validateStateStyleSyntax(temp, log); 1087 } 1088 if (item.name) { 1089 statements.push(createViewStackProcessor(item, false)); 1090 } 1091 }); 1092} 1093 1094function isExtendFunctionNode(identifierNode: ts.Identifier, propName: string): boolean { 1095 if (identifierNode && EXTEND_ATTRIBUTE.has(identifierNode.escapedText.toString())) { 1096 const attributeArray: string[] = 1097 [...EXTEND_ATTRIBUTE.get(identifierNode.escapedText.toString())]; 1098 if (attributeArray.includes(propName)) { 1099 return true; 1100 } 1101 } 1102 return false; 1103} 1104 1105const gestureMap: Map<string, string> = new Map([ 1106 [PRIORITY_GESTURE_ATTRIBUTE, GESTURE_ENUM_VALUE_HIGH], 1107 [PARALLEL_GESTURE_ATTRIBUTE, GESTURE_ENUM_VALUE_PARALLEL], 1108 [GESTURE_ATTRIBUTE, GESTURE_ENUM_VALUE_LOW] 1109]); 1110 1111function parseGesture(node: ts.CallExpression, propName: string, statements: ts.Statement[], 1112 log: LogInfo[]): void { 1113 statements.push(ts.factory.createExpressionStatement( 1114 createFunction(ts.factory.createIdentifier(COMPONENT_GESTURE), 1115 ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null))); 1116 parseGestureInterface(node, statements, log); 1117 const argumentArr: ts.NodeArray<ts.PropertyAccessExpression> = ts.factory.createNodeArray( 1118 [ts.factory.createPropertyAccessExpression( 1119 ts.factory.createIdentifier(GESTURE_ENUM_KEY), 1120 ts.factory.createIdentifier(gestureMap.get(propName))) 1121 ] 1122 ); 1123 if (node.arguments && node.arguments.length > 1 && 1124 ts.isPropertyAccessExpression(node.arguments[1])) { 1125 // @ts-ignore 1126 argumentArr.push(node.arguments[1]); 1127 } 1128 statements.push(ts.factory.createExpressionStatement( 1129 createFunction(ts.factory.createIdentifier(COMPONENT_GESTURE), 1130 ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), argumentArr))); 1131} 1132 1133function processGestureType(node: ts.CallExpression, statements: ts.Statement[], log: LogInfo[], 1134 reverse: boolean = false): void { 1135 const newStatements: ts.Statement[] = []; 1136 const newNode: ts.ExpressionStatement = ts.factory.createExpressionStatement(node); 1137 let temp: any = node.expression; 1138 while (temp && !ts.isIdentifier(temp) && temp.expression) { 1139 temp = temp.expression; 1140 } 1141 if (temp && temp.parent && ts.isCallExpression(temp.parent) && ts.isIdentifier(temp) && 1142 GESTURE_TYPE_NAMES.has(temp.escapedText.toString())) { 1143 newStatements.push(ts.factory.createExpressionStatement( 1144 createFunction(temp, ts.factory.createIdentifier(COMPONENT_POP_FUNCTION), null))); 1145 if (temp.escapedText.toString() === COMPONENT_GESTURE_GROUP) { 1146 const gestureStatements: ts.Statement[] = []; 1147 parseGestureInterface(temp.parent, gestureStatements, log, true); 1148 newStatements.push(...gestureStatements.reverse()); 1149 bindComponentAttr(newNode, temp, newStatements, log, false); 1150 let argumentArr: ts.NodeArray<ts.Expression> = null; 1151 if (temp.parent.arguments && temp.parent.arguments.length) { 1152 // @ts-ignore 1153 argumentArr = ts.factory.createNodeArray([temp.parent.arguments[0]]); 1154 } 1155 newStatements.push(ts.factory.createExpressionStatement( 1156 createFunction(temp, ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), argumentArr))); 1157 } else { 1158 bindComponentAttr(newNode, temp, newStatements, log, false); 1159 newStatements.push(ts.factory.createExpressionStatement( 1160 createFunction(temp, ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), temp.parent.arguments))); 1161 } 1162 } 1163 if (newStatements.length) { 1164 reverse ? statements.push(...newStatements.reverse()) : statements.push(...newStatements); 1165 } 1166} 1167 1168function parseGestureInterface(node: ts.CallExpression, statements: ts.Statement[], log: LogInfo[], 1169 reverse: boolean = false): void { 1170 if (node.arguments && node.arguments.length) { 1171 node.arguments.forEach((item: ts.Node) => { 1172 if (ts.isCallExpression(item)) { 1173 processGestureType(item, statements, log, reverse); 1174 } 1175 }); 1176 } 1177} 1178 1179export function getName(node: ts.ExpressionStatement): string { 1180 let temp: any = node.expression; 1181 let name: string; 1182 while (temp) { 1183 if (ts.isIdentifier(temp) && temp.parent && (ts.isCallExpression(temp.parent) || 1184 ts.isEtsComponentExpression(temp.parent))) { 1185 name = temp.escapedText.toString(); 1186 break; 1187 } else if (ts.isPropertyAccessExpression(temp) && temp.name && ts.isIdentifier(temp.name) && 1188 !BUILDIN_STYLE_NAMES.has(temp.name.escapedText.toString())) { 1189 name = temp.name.escapedText.toString(); 1190 break; 1191 } 1192 temp = temp.expression; 1193 } 1194 return name; 1195} 1196 1197export function isAttributeNode(node: ts.ExpressionStatement): boolean { 1198 let temp: any = node.expression; 1199 let name: string; 1200 while (temp) { 1201 if (ts.isCallExpression(temp) && temp.expression && ts.isIdentifier(temp.expression)) { 1202 name = temp.expression.escapedText.toString(); 1203 break; 1204 } 1205 temp = temp.expression; 1206 } 1207 return BUILDIN_STYLE_NAMES.has(name); 1208} 1209 1210enum ComponentType { 1211 innerComponent, 1212 customComponent, 1213 forEachComponent, 1214 customBuilderMethod, 1215 builderParamMethod 1216} 1217 1218function isEtsComponent(node: ts.ExpressionStatement): boolean { 1219 let isEtsComponent: boolean = false; 1220 let temp: any = node.expression; 1221 while (temp) { 1222 if (ts.isEtsComponentExpression(temp)) { 1223 isEtsComponent = true; 1224 } 1225 temp = temp.expression; 1226 } 1227 return isEtsComponent; 1228} 1229 1230function isSomeName(forEachParameters: ts.NodeArray<ts.ParameterDeclaration>, name: string): boolean { 1231 return Array.isArray(forEachParameters) && 1232 forEachParameters.some((item)=>{ 1233 return ts.isIdentifier(item.name) ? item.name.escapedText.toString() === name : false; 1234 }); 1235} 1236 1237function isParamFunction(node: ts.ExpressionStatement): boolean { 1238 return node.expression && ts.isCallExpression(node.expression) && 1239 node.expression.expression && ts.isIdentifier(node.expression.expression); 1240} 1241 1242function getComponentType(node: ts.ExpressionStatement, log: LogInfo[], 1243 name: string, forEachParameters: ts.NodeArray<ts.ParameterDeclaration> = undefined): ComponentType { 1244 let isBuilderName: boolean = true; 1245 if(forEachParameters && isSomeName(forEachParameters, name) && isParamFunction(node)) { 1246 isBuilderName = false; 1247 } 1248 if (isEtsComponent(node)) { 1249 if (componentCollection.customComponents.has(name)) { 1250 return ComponentType.customComponent; 1251 } else { 1252 return ComponentType.innerComponent; 1253 } 1254 } else if (componentCollection.customComponents.has(name)) { 1255 return ComponentType.customComponent; 1256 } else if (name === COMPONENT_FOREACH || name === COMPONENT_LAZYFOREACH) { 1257 return ComponentType.forEachComponent; 1258 } else if (CUSTOM_BUILDER_METHOD.has(name) && isBuilderName) { 1259 return ComponentType.customBuilderMethod; 1260 } else if (builderParamObjectCollection.get(componentCollection.currentClassName) && 1261 builderParamObjectCollection.get(componentCollection.currentClassName).has(name)) { 1262 return ComponentType.builderParamMethod; 1263 } else if (!isAttributeNode(node)) { 1264 log.push({ 1265 type: LogType.ERROR, 1266 message: `'${node.getText()}' does not meet UI component syntax.`, 1267 pos: node.getStart() 1268 }); 1269 } 1270 return null; 1271} 1272 1273export function validateStateStyleSyntax(temp: any, log: LogInfo[]): void { 1274 log.push({ 1275 type: LogType.ERROR, 1276 message: `.stateStyles doesn't conform standard.`, 1277 pos: temp.getStart() 1278 }); 1279} 1280 1281function checkButtonParamHasLabel(node: ts.EtsComponentExpression, log: LogInfo[]): void { 1282 if (node.arguments && node.arguments.length !== 0) { 1283 for (let i = 0; i < node.arguments.length; i++) { 1284 let argument: ts.Expression = node.arguments[i]; 1285 if (ts.isStringLiteral(argument) || (ts.isCallExpression(argument) && ts.isIdentifier(argument.expression) && 1286 (argument.expression.escapedText.toString() === RESOURCE))) { 1287 log.push({ 1288 type: LogType.ERROR, 1289 message: "The Button component with a label parameter can not have any child.", 1290 pos: node.getStart(), 1291 }); 1292 return; 1293 } 1294 } 1295 } 1296} 1297