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 validateReExportType from './validate_module_syntax'; 20import { 21 PAGE_ENTRY_FUNCTION_NAME, 22 PREVIEW_COMPONENT_FUNCTION_NAME, 23 STORE_PREVIEW_COMPONENTS, 24 GET_PREVIEW_FLAG_FUNCTION_NAME, 25 COMPONENT_CONSTRUCTOR_UNDEFINED, 26 BUILD_ON, 27 COMPONENT_BUILDER_DECORATOR, 28 COMPONENT_CONCURRENT_DECORATOR, 29 COMPONENT_EXTEND_DECORATOR, 30 COMPONENT_STYLES_DECORATOR, 31 RESOURCE, 32 RESOURCE_TYPE, 33 WORKER_OBJECT, 34 RESOURCE_NAME_ID, 35 RESOURCE_NAME_TYPE, 36 RESOURCE_NAME_PARAMS, 37 RESOURCE_RAWFILE, 38 RESOURCE_NAME_BUNDLE, 39 RESOURCE_NAME_MODULE, 40 ATTRIBUTE_ANIMATETO, 41 GLOBAL_CONTEXT, 42 CHECK_COMPONENT_EXTEND_DECORATOR, 43 INSTANCE, 44 SET_CONTROLLER_CTR_TYPE, 45 SET_CONTROLLER_METHOD, 46 JS_DIALOG, 47 CUSTOM_DIALOG_CONTROLLER_BUILDER, 48 ESMODULE, 49 ARK, 50 EXTNAME_ETS, 51 GENERATE_ID, 52 _GENERATE_ID, 53 VIEWSTACKPROCESSOR, 54 STARTGETACCESSRECORDINGFOR, 55 ALLOCATENEWELMETIDFORNEXTCOMPONENT, 56 STOPGETACCESSRECORDING, 57 CARD_ENTRY_FUNCTION_NAME, 58 CARD_LOG_TYPE_COMPONENTS, 59 CARD_LOG_TYPE_DECORATORS, 60 CARD_LOG_TYPE_IMPORT 61} from './pre_define'; 62import { 63 componentInfo, 64 LogInfo, 65 LogType, 66 hasDecorator, 67 FileLog, 68 getPossibleBuilderTypeParameter 69} from './utils'; 70import { writeFileSyncByNode } from './process_module_files'; 71import { 72 componentCollection, 73 localStorageLinkCollection, 74 localStoragePropCollection, 75} from './validate_ui_syntax'; 76import { 77 processComponentClass, 78 createParentParameter, 79 processBuildMember 80} from './process_component_class'; 81import processImport from './process_import'; 82import { 83 processComponentBlock, 84 bindComponentAttr, 85 getName 86} from './process_component_build'; 87import { 88 BUILDIN_STYLE_NAMES, 89 CUSTOM_BUILDER_METHOD, 90 EXTEND_ATTRIBUTE, 91 INNER_STYLE_FUNCTION, 92 GLOBAL_STYLE_FUNCTION, 93 INTERFACE_NODE_SET, 94 ID_ATTRS 95} from './component_map'; 96import { 97 resources, 98 projectConfig, 99 partialUpdateConfig 100} from '../main'; 101import { createCustomComponentNewExpression, createViewCreate } from './process_component_member'; 102import { ModuleSourceFile } from './fast_build/ark_compiler/module/module_source_file'; 103 104export const transformLog: FileLog = new FileLog(); 105export let contextGlobal: ts.TransformationContext; 106export let resourceFileName: string = ''; 107export const builderTypeParameter: { params: string[] } = { params: [] }; 108 109export function processUISyntax(program: ts.Program, ut = false): Function { 110 let entryNodeKey: ts.Expression; 111 return (context: ts.TransformationContext) => { 112 contextGlobal = context; 113 let pagesDir: string; 114 return (node: ts.SourceFile) => { 115 pagesDir = path.resolve(path.dirname(node.fileName)); 116 resourceFileName = path.resolve(node.fileName); 117 if (process.env.compiler === BUILD_ON || process.env.compileTool === 'rollup') { 118 transformLog.sourceFile = node; 119 preprocessIdAttrs(node.fileName); 120 if (!ut && (process.env.compileMode !== 'moduleJson' && 121 path.resolve(node.fileName) === path.resolve(projectConfig.projectPath, 'app.ets') || 122 /\.ts$/.test(node.fileName))) { 123 node = ts.visitEachChild(node, processResourceNode, context); 124 if (projectConfig.compileMode === ESMODULE) { 125 validateReExportType(node, pagesDir, transformLog.errors); 126 if (projectConfig.processTs === true) { 127 process.env.compileTool === 'rollup' ? 128 ModuleSourceFile.newSourceFile(path.normalize(node.fileName), node) : 129 writeFileSyncByNode(node, true, projectConfig); 130 } 131 } 132 return node; 133 } 134 const id: number = ++componentInfo.id; 135 node = ts.visitEachChild(node, processAllNodes, context); 136 node = createEntryNode(node, context, entryNodeKey, id); 137 if (projectConfig.compileMode === ESMODULE) { 138 validateReExportType(node, pagesDir, transformLog.errors); 139 } 140 GLOBAL_STYLE_FUNCTION.forEach((block, styleName) => { 141 BUILDIN_STYLE_NAMES.delete(styleName); 142 }); 143 GLOBAL_STYLE_FUNCTION.clear(); 144 const statements: ts.Statement[] = Array.from(node.statements); 145 if (!partialUpdateConfig.partialUpdateMode) { 146 generateId(statements, node); 147 } 148 INTERFACE_NODE_SET.forEach(item => { 149 statements.unshift(item); 150 }); 151 node = ts.factory.updateSourceFile(node, statements); 152 INTERFACE_NODE_SET.clear(); 153 if (projectConfig.compileMode === ESMODULE && projectConfig.processTs === true) { 154 process.env.compileTool === 'rollup' ? ModuleSourceFile.newSourceFile(path.normalize(node.fileName), node) : 155 writeFileSyncByNode(node, true, projectConfig); 156 } 157 return node; 158 } else { 159 return node; 160 } 161 }; 162 163 function entryKeyNode(node: ts.Node): ts.Expression { 164 if (node && node.decorators && node.decorators.length) { 165 node.decorators.forEach(item => { 166 if (item.expression && ts.isCallExpression(item.expression) && ts.isIdentifier(item.expression.expression) && 167 item.expression.expression.escapedText.toString() === 'Entry' && item.expression.arguments && 168 item.expression.arguments.length && ts.isIdentifier(item.expression.arguments[0])) { 169 entryNodeKey = item.expression.arguments[0]; 170 } 171 }); 172 } 173 return entryNodeKey; 174 } 175 176 function processAllNodes(node: ts.Node): ts.Node { 177 if (ts.isImportDeclaration(node) || ts.isImportEqualsDeclaration(node) || 178 ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) { 179 processImport(node, pagesDir, transformLog.errors); 180 } else if (ts.isStructDeclaration(node)) { 181 componentCollection.currentClassName = node.name.getText(); 182 componentCollection.entryComponent === componentCollection.currentClassName && entryKeyNode(node); 183 node = processComponentClass(node, context, transformLog.errors, program); 184 componentCollection.currentClassName = null; 185 INNER_STYLE_FUNCTION.forEach((block, styleName) => { 186 BUILDIN_STYLE_NAMES.delete(styleName); 187 }); 188 INNER_STYLE_FUNCTION.clear(); 189 } else if (ts.isFunctionDeclaration(node)) { 190 if (hasDecorator(node, COMPONENT_EXTEND_DECORATOR)) { 191 node = processExtend(node, transformLog.errors); 192 } else if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR) && node.name && node.body && 193 ts.isBlock(node.body)) { 194 CUSTOM_BUILDER_METHOD.add(node.name.getText()); 195 builderTypeParameter.params = getPossibleBuilderTypeParameter(node.parameters); 196 node.parameters.push(createParentParameter()); 197 node = ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, 198 node.asteriskToken, node.name, node.typeParameters, node.parameters, node.type, 199 processComponentBlock(node.body, false, transformLog.errors, false, true, 200 node.name.getText(), undefined, true)); 201 builderTypeParameter.params = []; 202 node = processBuildMember(node, context, transformLog.errors, true); 203 } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { 204 if (node.parameters.length === 0) { 205 node = undefined; 206 } else { 207 transformLog.errors.push({ 208 type: LogType.ERROR, 209 message: `@Styles can't have parameters.`, 210 pos: node.getStart() 211 }); 212 } 213 } else if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) { 214 // ark compiler's feature 215 node = processConcurrent(node); 216 } 217 } else if (isResource(node)) { 218 node = processResourceData(node as ts.CallExpression); 219 } else if (isWorker(node)) { 220 node = processWorker(node as ts.NewExpression); 221 } else if (isAnimateTo(node)) { 222 node = processAnimateTo(node as ts.CallExpression); 223 } else if (isCustomDialogController(node)) { 224 node = createCustomDialogController(node.parent, node, transformLog.errors); 225 } 226 return ts.visitEachChild(node, processAllNodes, context); 227 } 228 function processResourceNode(node: ts.Node): ts.Node { 229 if (isResource(node)) { 230 node = processResourceData(node as ts.CallExpression); 231 } 232 return ts.visitEachChild(node, processResourceNode, context); 233 } 234 }; 235} 236 237function generateId(statements: ts.Statement[], node: ts.SourceFile): void { 238 statements.unshift( 239 ts.factory.createVariableStatement( 240 undefined, 241 ts.factory.createVariableDeclarationList( 242 [ts.factory.createVariableDeclaration( 243 ts.factory.createIdentifier(_GENERATE_ID), 244 undefined, 245 ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), 246 ts.factory.createNumericLiteral('0') 247 )], 248 ts.NodeFlags.Let 249 ) 250 ), 251 ts.factory.createFunctionDeclaration( 252 undefined, 253 undefined, 254 undefined, 255 ts.factory.createIdentifier(GENERATE_ID), 256 undefined, 257 [], 258 ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), 259 ts.factory.createBlock( 260 [ts.factory.createReturnStatement(ts.factory.createBinaryExpression( 261 ts.factory.createStringLiteral(path.basename(node.fileName, EXTNAME_ETS) + '_'), 262 ts.factory.createToken(ts.SyntaxKind.PlusToken), ts.factory.createPrefixUnaryExpression( 263 ts.SyntaxKind.PlusPlusToken, 264 ts.factory.createIdentifier(_GENERATE_ID) 265 )))], 266 true 267 ) 268 ) 269 ); 270} 271 272function preprocessIdAttrs(fileName: string): void { 273 for (const [id, idInfo] of ID_ATTRS) { 274 if (fileName === idInfo.get('path')) { 275 ID_ATTRS.delete(id); 276 } 277 } 278} 279 280function isCustomDialogController(node: ts.Expression) { 281 const tempParent: ts.Node = node.parent; 282 // @ts-ignore 283 if (!node.parent && node.original) { 284 // @ts-ignore 285 node.parent = node.original.parent; 286 } 287 if (ts.isNewExpression(node) && node.expression && ts.isIdentifier(node.expression) && 288 node.expression.escapedText.toString() === SET_CONTROLLER_CTR_TYPE) { 289 return true; 290 } else { 291 // @ts-ignore 292 node.parent = tempParent; 293 return false; 294 } 295} 296 297function createCustomDialogController(parent: ts.Expression, node: ts.NewExpression, 298 log: LogInfo[]): ts.NewExpression { 299 if (node.arguments && node.arguments.length === 1 && 300 ts.isObjectLiteralExpression(node.arguments[0]) && node.arguments[0].properties) { 301 const newproperties: ts.ObjectLiteralElementLike[] = node.arguments[0].properties.map((item) => { 302 const componentName: string = isCustomDialogControllerPropertyAssignment(item, log); 303 if (componentName !== null) { 304 item = processCustomDialogControllerPropertyAssignment(parent, 305 item as ts.PropertyAssignment, componentName); 306 } 307 return item; 308 }); 309 return ts.factory.createNewExpression(node.expression, node.typeArguments, 310 [ts.factory.createObjectLiteralExpression(newproperties, true), ts.factory.createThis()]); 311 } 312} 313 314function isCustomDialogControllerPropertyAssignment(node: ts.ObjectLiteralElementLike, 315 log: LogInfo[]): string { 316 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 317 node.name.getText() === CUSTOM_DIALOG_CONTROLLER_BUILDER) { 318 if (node.initializer) { 319 const componentName: string = getName(node.initializer); 320 if (componentCollection.customDialogs.has(componentName)) { 321 return componentName; 322 } 323 } else { 324 validateCustomDialogControllerBuilderInit(node, log); 325 } 326 } 327 return null; 328} 329 330function validateCustomDialogControllerBuilderInit(node: ts.ObjectLiteralElementLike, 331 log: LogInfo[]): void { 332 log.push({ 333 type: LogType.ERROR, 334 message: 'The builder should be initialized with a @CustomDialog Component.', 335 pos: node.getStart() 336 }); 337} 338 339function processCustomDialogControllerPropertyAssignment(parent: ts.Expression, 340 node: ts.PropertyAssignment, componentName: string): ts.PropertyAssignment { 341 if (ts.isCallExpression(node.initializer)) { 342 return ts.factory.updatePropertyAssignment(node, node.name, 343 processCustomDialogControllerBuilder(parent, node.initializer, componentName)); 344 } 345} 346 347function processCustomDialogControllerBuilder(parent: ts.Expression, 348 node: ts.CallExpression, componentName: string): ts.ArrowFunction { 349 const newExp: ts.Expression = createCustomComponentNewExpression(node, componentName, false, false, true); 350 const jsDialog: ts.Identifier = ts.factory.createIdentifier(JS_DIALOG); 351 return createCustomComponentBuilderArrowFunction(parent, jsDialog, newExp); 352} 353 354function createCustomComponentBuilderArrowFunction(parent: ts.Expression, 355 jsDialog: ts.Identifier, newExp: ts.Expression): ts.ArrowFunction { 356 let mountNodde: ts.PropertyAccessExpression; 357 if (ts.isBinaryExpression(parent)) { 358 mountNodde = parent.left; 359 } else if (ts.isVariableDeclaration(parent) || ts.isPropertyDeclaration(parent)) { 360 mountNodde = ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 361 parent.name as ts.Identifier); 362 } 363 return ts.factory.createArrowFunction( 364 undefined, 365 undefined, 366 [], 367 undefined, 368 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 369 ts.factory.createBlock( 370 [ 371 ts.factory.createVariableStatement( 372 undefined, 373 ts.factory.createVariableDeclarationList( 374 [ts.factory.createVariableDeclaration(jsDialog, undefined, undefined, newExp)], 375 ts.NodeFlags.Let 376 ) 377 ), 378 ts.factory.createExpressionStatement( 379 ts.factory.createCallExpression( 380 ts.factory.createPropertyAccessExpression( 381 jsDialog, 382 ts.factory.createIdentifier(SET_CONTROLLER_METHOD) 383 ), 384 undefined, 385 [mountNodde] 386 ) 387 ), 388 ts.factory.createExpressionStatement(createViewCreate(jsDialog)) 389 ], 390 true 391 ) 392 ); 393} 394 395export function isResource(node: ts.Node): boolean { 396 return ts.isCallExpression(node) && ts.isIdentifier(node.expression) && 397 (node.expression.escapedText.toString() === RESOURCE || 398 node.expression.escapedText.toString() === RESOURCE_RAWFILE) && node.arguments.length > 0; 399} 400 401export function isAnimateTo(node: ts.Node): boolean { 402 return ts.isCallExpression(node) && ts.isIdentifier(node.expression) && 403 node.expression.escapedText.toString() === ATTRIBUTE_ANIMATETO; 404} 405 406export function processResourceData(node: ts.CallExpression, 407 previewLog: {isAcceleratePreview: boolean, log: LogInfo[]} = {isAcceleratePreview: false, log: []}): ts.Node { 408 if (ts.isStringLiteral(node.arguments[0])) { 409 if (node.expression.getText() === RESOURCE_RAWFILE) { 410 return createResourceParam(0, RESOURCE_TYPE.rawfile, [node.arguments[0]]); 411 } else { 412 return getResourceDataNode(node, previewLog); 413 } 414 } 415 return node; 416} 417 418function getResourceDataNode(node: ts.CallExpression, 419 previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): ts.Node { 420 const resourceData: string[] = (node.arguments[0] as ts.StringLiteral).text.trim().split('.'); 421 if (preCheckResourceData(resourceData, resources, node.arguments[0].getStart(), previewLog)) { 422 const resourceType: number = RESOURCE_TYPE[resourceData[1]]; 423 if (resourceType === undefined) { 424 transformLog.errors.push({ 425 type: LogType.ERROR, 426 message: `The resource type ${resourceData[1]} is not supported.`, 427 pos: node.getStart() 428 }); 429 return node; 430 } 431 const resourceValue: number = resources[resourceData[0]][resourceData[1]][resourceData[2]]; 432 return createResourceParam(resourceValue, resourceType, 433 projectConfig.compileHar ? Array.from(node.arguments) : Array.from(node.arguments).slice(1)); 434 } 435 return node; 436} 437 438function createResourceParam(resourceValue: number, resourceType: number, argsArr: ts.Expression[]): 439 ts.ObjectLiteralExpression { 440 if (projectConfig.compileHar) { 441 projectConfig.bundleName = ''; 442 projectConfig.moduleName = ''; 443 resourceValue = -1; 444 } 445 446 const propertyArray: Array<ts.PropertyAssignment> = [ 447 ts.factory.createPropertyAssignment( 448 ts.factory.createStringLiteral(RESOURCE_NAME_ID), 449 ts.factory.createNumericLiteral(resourceValue) 450 ), 451 ts.factory.createPropertyAssignment( 452 ts.factory.createStringLiteral(RESOURCE_NAME_TYPE), 453 ts.factory.createNumericLiteral(resourceType) 454 ), 455 ts.factory.createPropertyAssignment( 456 ts.factory.createIdentifier(RESOURCE_NAME_PARAMS), 457 ts.factory.createArrayLiteralExpression(argsArr, false) 458 ) 459 ]; 460 461 if (projectConfig.bundleName || projectConfig.bundleName === '') { 462 propertyArray.push(ts.factory.createPropertyAssignment( 463 ts.factory.createStringLiteral(RESOURCE_NAME_BUNDLE), 464 ts.factory.createStringLiteral(projectConfig.bundleName) 465 )); 466 } 467 468 if (projectConfig.moduleName || projectConfig.moduleName === '') { 469 propertyArray.push(ts.factory.createPropertyAssignment( 470 ts.factory.createStringLiteral(RESOURCE_NAME_MODULE), 471 ts.factory.createStringLiteral(projectConfig.moduleName) 472 )); 473 } 474 475 const resourceParams: ts.ObjectLiteralExpression = ts.factory.createObjectLiteralExpression( 476 propertyArray, false); 477 return resourceParams; 478} 479 480function preCheckResourceData(resourceData: string[], resources: object, pos: number, 481 previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): boolean { 482 if (previewLog.isAcceleratePreview) { 483 return validateResourceData(resourceData, resources, pos, previewLog.log); 484 } else { 485 return validateResourceData(resourceData, resources, pos, transformLog.errors); 486 } 487} 488 489function validateResourceData(resourceData: string[], resources: object, pos: number, log: LogInfo[]): boolean { 490 if (resourceData.length !== 3) { 491 log.push({ 492 type: LogType.ERROR, 493 message: 'The input parameter is not supported.', 494 pos: pos 495 }); 496 } else if (!resources[resourceData[0]]) { 497 log.push({ 498 type: LogType.ERROR, 499 message: `Unknown resource source '${resourceData[0]}'.`, 500 pos: pos 501 }); 502 } else if (!resources[resourceData[0]][resourceData[1]]) { 503 log.push({ 504 type: LogType.ERROR, 505 message: `Unknown resource type '${resourceData[1]}'.`, 506 pos: pos 507 }); 508 } else if (!resources[resourceData[0]][resourceData[1]][resourceData[2]]) { 509 log.push({ 510 type: LogType.ERROR, 511 message: `Unknown resource name '${resourceData[2]}'.`, 512 pos: pos 513 }); 514 } else { 515 return true; 516 } 517 return false; 518} 519 520function isWorker(node: ts.Node): boolean { 521 return ts.isNewExpression(node) && ts.isPropertyAccessExpression(node.expression) && 522 ts.isIdentifier(node.expression.name) && 523 node.expression.name.escapedText.toString() === WORKER_OBJECT; 524} 525 526function processWorker(node: ts.NewExpression): ts.Node { 527 if (node.arguments.length && ts.isStringLiteral(node.arguments[0])) { 528 const args: ts.Expression[] = Array.from(node.arguments); 529 // @ts-ignore 530 const workerPath: string = node.arguments[0].text; 531 const stringNode: ts.StringLiteral = ts.factory.createStringLiteral( 532 workerPath.replace(/\.ts$/, '.js')); 533 args.splice(0, 1, stringNode); 534 return ts.factory.updateNewExpression(node, node.expression, node.typeArguments, args); 535 } 536 return node; 537} 538 539export function processAnimateTo(node: ts.CallExpression): ts.CallExpression { 540 return ts.factory.updateCallExpression(node, ts.factory.createPropertyAccessExpression( 541 ts.factory.createIdentifier(GLOBAL_CONTEXT), ts.factory.createIdentifier(ATTRIBUTE_ANIMATETO)), 542 node.typeArguments, node.arguments); 543} 544 545function processExtend(node: ts.FunctionDeclaration, log: LogInfo[]): ts.FunctionDeclaration { 546 const componentName: string = isExtendFunction(node); 547 if (componentName && node.body && node.body.statements.length) { 548 const statementArray: ts.Statement[] = []; 549 let bodynode: ts.Block; 550 const attrSet: ts.CallExpression = node.body.statements[0].expression; 551 if (isOriginalExtend(node.body)) { 552 const changeCompName: ts.ExpressionStatement = ts.factory.createExpressionStatement(processExtendBody(attrSet)); 553 bindComponentAttr(changeCompName as ts.ExpressionStatement, 554 ts.factory.createIdentifier(componentName), statementArray, log); 555 } else { 556 bodynode = ts.visitEachChild(node.body, traverseExtendExpression, contextGlobal); 557 } 558 let extendFunctionName: string; 559 if (node.name.getText().startsWith('__' + componentName + '__')) { 560 extendFunctionName = node.name.getText(); 561 } else { 562 extendFunctionName = '__' + componentName + '__' + node.name.getText(); 563 collectExtend(EXTEND_ATTRIBUTE, componentName, node.name.escapedText.toString()); 564 } 565 return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, 566 ts.factory.createIdentifier(extendFunctionName), node.typeParameters, 567 node.parameters, ts.factory.createToken(ts.SyntaxKind.VoidKeyword), isOriginalExtend(node.body) ? 568 ts.factory.updateBlock(node.body, statementArray) : bodynode); 569 } 570 function traverseExtendExpression(node: ts.Node): ts.Node { 571 if (ts.isExpressionStatement(node) && isDollarNode(node)) { 572 const changeCompName: ts.ExpressionStatement = 573 ts.factory.createExpressionStatement(processExtendBody(node.expression, componentName)); 574 const statementArray: ts.Statement[] = []; 575 bindComponentAttr(changeCompName, ts.factory.createIdentifier(componentName), statementArray, []); 576 return ts.factory.createBlock(statementArray, true); 577 } 578 return ts.visitEachChild(node, traverseExtendExpression, contextGlobal); 579 } 580} 581 582function processConcurrent(node: ts.FunctionDeclaration): ts.FunctionDeclaration { 583 if (node.body) { 584 const statementArray: ts.Statement[] 585 = [ts.factory.createExpressionStatement(ts.factory.createStringLiteral('use concurrent')), 586 ...node.body.statements]; 587 return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, node.name, 588 node.typeParameters, node.parameters, node.type, ts.factory.updateBlock(node.body, statementArray)); 589 } 590 return node; 591} 592 593export function isOriginalExtend(node: ts.Block): boolean { 594 let innerNode: ts.Node = node.statements[0]; 595 if (node.statements.length === 1 && ts.isExpressionStatement(innerNode)) { 596 while (innerNode.expression) { 597 innerNode = innerNode.expression; 598 } 599 if (ts.isIdentifier(innerNode) && innerNode.pos && innerNode.end && innerNode.pos === innerNode.end && 600 innerNode.escapedText.toString().match(/Instance$/)) { 601 return true; 602 } 603 } 604 return false; 605} 606 607function isDollarNode(node: ts.ExpressionStatement): boolean { 608 let innerNode: ts.Node = node; 609 while (innerNode.expression) { 610 innerNode = innerNode.expression; 611 } 612 if (ts.isIdentifier(innerNode) && innerNode.getText() === '$') { 613 return true; 614 } else { 615 return false; 616 } 617} 618 619function processExtendBody(node: ts.Node, componentName?: string): ts.Expression { 620 switch (node.kind) { 621 case ts.SyntaxKind.CallExpression: 622 return ts.factory.createCallExpression(processExtendBody(node.expression, componentName), 623 undefined, node.arguments); 624 case ts.SyntaxKind.PropertyAccessExpression: 625 return ts.factory.createPropertyAccessExpression( 626 processExtendBody(node.expression, componentName), node.name); 627 case ts.SyntaxKind.Identifier: 628 if (!componentName) { 629 return ts.factory.createIdentifier(node.escapedText.toString().replace(INSTANCE, '')); 630 } else { 631 return ts.factory.createIdentifier(componentName); 632 } 633 } 634} 635 636export function collectExtend(collectionSet: Map<string, Set<string>>, component: string, attribute: string): void { 637 if (collectionSet.has(component)) { 638 collectionSet.get(component).add(attribute); 639 } else { 640 collectionSet.set(component, new Set([attribute])); 641 } 642} 643 644export function isExtendFunction(node: ts.FunctionDeclaration): string { 645 if (node.decorators && node.decorators.length) { 646 for (let i = 0, len = node.decorators.length; i < len; i++) { 647 if (node.decorators[i].expression && node.decorators[i].expression.expression && 648 node.decorators[i].expression.expression.escapedText.toString() === CHECK_COMPONENT_EXTEND_DECORATOR && 649 node.decorators[i].expression.arguments) { 650 return node.decorators[i].expression.arguments[0].escapedText.toString(); 651 } 652 } 653 } 654 return null; 655} 656 657function createEntryNode(node: ts.SourceFile, context: ts.TransformationContext, 658 entryNodeKey: ts.Expression, id: number): ts.SourceFile { 659 let cardRelativePath: string = undefined; 660 if (projectConfig && projectConfig.cardObj) { 661 cardRelativePath = projectConfig.cardObj[resourceFileName]; 662 } 663 if (componentCollection.previewComponent.length === 0 || !projectConfig.isPreview) { 664 if (componentCollection.entryComponent) { 665 if (!partialUpdateConfig.partialUpdateMode) { 666 const entryNode: ts.ExpressionStatement = 667 createEntryFunction(componentCollection.entryComponent, context, 668 cardRelativePath, entryNodeKey, id) as ts.ExpressionStatement; 669 return context.factory.updateSourceFile(node, [...node.statements, entryNode]); 670 } else { 671 const entryNodes: ts.ExpressionStatement[] = 672 createEntryFunction(componentCollection.entryComponent, context, 673 cardRelativePath, entryNodeKey, id) as ts.ExpressionStatement[]; 674 return entryNodes ? 675 context.factory.updateSourceFile(node, [...node.statements, ...entryNodes]) : 676 context.factory.updateSourceFile(node, [...node.statements]); 677 } 678 } else { 679 return node; 680 } 681 } else { 682 const statementsArray: ts.Statement = 683 createPreviewComponentFunction(componentCollection.entryComponent, context, cardRelativePath, entryNodeKey, id); 684 return context.factory.updateSourceFile(node, [...node.statements, statementsArray]); 685 } 686} 687 688function createEntryFunction(name: string, context: ts.TransformationContext, cardRelativePath: string, 689 entryNodeKey: ts.Expression, id: number): ts.ExpressionStatement | ts.ExpressionStatement[] { 690 const newArray: ts.Expression[] = [ 691 context.factory.createStringLiteral(id.toString()), 692 context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 693 context.factory.createObjectLiteralExpression([], false) 694 ]; 695 const localStorageName: string = addStorageParam(name); 696 if (localStorageName) { 697 newArray.push(entryNodeKey); 698 } 699 const newExpressionParams: any[] = [ 700 context.factory.createNewExpression( 701 context.factory.createIdentifier(name),undefined, newArray)]; 702 addCardStringliteral(newExpressionParams, context, cardRelativePath); 703 if (!partialUpdateConfig.partialUpdateMode) { 704 const newExpressionStatement: ts.ExpressionStatement = 705 context.factory.createExpressionStatement(context.factory.createCallExpression( 706 context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME : 707 PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams)); 708 return newExpressionStatement; 709 } else { 710 return [ 711 createStartGetAccessRecording(context), 712 createLoadDocument(context, name, cardRelativePath, localStorageName, entryNodeKey), 713 createStopGetAccessRecording(context) 714 ]; 715 } 716} 717 718function createStartGetAccessRecording(context: ts.TransformationContext): ts.ExpressionStatement { 719 return context.factory.createExpressionStatement( 720 context.factory.createCallExpression( 721 context.factory.createPropertyAccessExpression( 722 context.factory.createIdentifier(VIEWSTACKPROCESSOR), 723 context.factory.createIdentifier(STARTGETACCESSRECORDINGFOR) 724 ), 725 undefined, 726 [context.factory.createCallExpression( 727 context.factory.createPropertyAccessExpression( 728 context.factory.createIdentifier(VIEWSTACKPROCESSOR), 729 context.factory.createIdentifier(ALLOCATENEWELMETIDFORNEXTCOMPONENT) 730 ), 731 undefined, 732 [] 733 )] 734 ) 735 ); 736} 737 738function createLoadDocument(context: ts.TransformationContext, name: string, 739 cardRelativePath: string, localStorageName: string, entryNodeKey: ts.Expression): ts.ExpressionStatement { 740 const newArray: ts.Expression[] = [ 741 context.factory.createIdentifier('undefined'), 742 context.factory.createObjectLiteralExpression([], false) 743 ]; 744 if (localStorageName) { 745 newArray.push(entryNodeKey); 746 } 747 const newExpressionParams: any[] = [ 748 context.factory.createNewExpression( 749 context.factory.createIdentifier(name), 750 undefined, newArray)]; 751 addCardStringliteral(newExpressionParams, context, cardRelativePath); 752 return context.factory.createExpressionStatement( 753 context.factory.createCallExpression( 754 context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME : 755 PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams) 756 ); 757} 758 759function createStopGetAccessRecording(context: ts.TransformationContext): ts.ExpressionStatement { 760 return context.factory.createExpressionStatement( 761 context.factory.createCallExpression( 762 context.factory.createPropertyAccessExpression( 763 context.factory.createIdentifier(VIEWSTACKPROCESSOR), 764 context.factory.createIdentifier(STOPGETACCESSRECORDING) 765 ), 766 undefined, 767 [] 768 ) 769 ); 770} 771 772function addStorageParam(name: string): string { 773 let localStorageName: string; 774 const localStorageNum: number = (localStorageLinkCollection.get(name) || new Set()).size + 775 (localStoragePropCollection.get(name) || new Set()).size; 776 if (componentCollection.entryComponent === name && componentCollection.localStorageName) { 777 localStorageName = componentCollection.localStorageName; 778 } else if (componentCollection.entryComponent === name && !componentCollection.localStorageName 779 && localStorageNum) { 780 transformLog.errors.push({ 781 type: LogType.WARN, 782 message: `@Entry should have a parameter, like '@Entry (storage)'.`, 783 pos: componentCollection.entryComponentPos 784 }); 785 return; 786 } 787 return localStorageName; 788} 789 790function createPreviewComponentFunction(name: string, context: ts.TransformationContext, 791 cardRelativePath: string, entryNodeKey: ts.Expression, id: number): ts.Statement { 792 const newArray: ts.Expression[] = partialUpdateConfig.partialUpdateMode ? 793 [ 794 context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 795 context.factory.createObjectLiteralExpression([], false) 796 ] : 797 [ 798 context.factory.createStringLiteral(id.toString()), 799 context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 800 context.factory.createObjectLiteralExpression([], false) 801 ]; 802 if (addStorageParam(name)) { 803 newArray.push(entryNodeKey); 804 } 805 const argsArr: ts.Expression[] = []; 806 componentCollection.previewComponent.forEach(componentName => { 807 const newExpression: ts.Expression = context.factory.createNewExpression( 808 context.factory.createIdentifier(componentName), 809 undefined, 810 newArray 811 ); 812 argsArr.push(context.factory.createStringLiteral(componentName)); 813 argsArr.push(newExpression); 814 }); 815 const newExpressionParams: any[] = name ? [context.factory.createNewExpression( 816 context.factory.createIdentifier(name), undefined, newArray)] : []; 817 addCardStringliteral(newExpressionParams, context, cardRelativePath); 818 const ifStatement: ts.Statement = context.factory.createIfStatement( 819 context.factory.createCallExpression( 820 context.factory.createIdentifier(GET_PREVIEW_FLAG_FUNCTION_NAME), 821 undefined, 822 [] 823 ), 824 context.factory.createBlock( 825 [context.factory.createExpressionStatement(context.factory.createCallExpression( 826 context.factory.createIdentifier(PREVIEW_COMPONENT_FUNCTION_NAME), 827 undefined, 828 [] 829 ))], 830 true 831 ), 832 context.factory.createBlock( 833 [ 834 context.factory.createExpressionStatement(context.factory.createCallExpression( 835 context.factory.createIdentifier(STORE_PREVIEW_COMPONENTS), 836 undefined, 837 [ 838 context.factory.createNumericLiteral(componentCollection.previewComponent.length), 839 ...argsArr 840 ] 841 )), 842 name && partialUpdateConfig.partialUpdateMode ? createStartGetAccessRecording(context) : undefined, 843 name ? context.factory.createExpressionStatement(context.factory.createCallExpression( 844 context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME : 845 PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams 846 )) : undefined, 847 name && partialUpdateConfig.partialUpdateMode ? createStopGetAccessRecording(context) : undefined 848 ], 849 true 850 ) 851 ); 852 return ifStatement; 853} 854 855export function resetLog(): void { 856 transformLog.errors = []; 857} 858 859function addCardStringliteral(newExpressionParams: any[], context: ts.TransformationContext, 860 cardRelativePath: string): void { 861 if (cardRelativePath) { 862 newExpressionParams.push(context.factory.createStringLiteral( 863 projectConfig.bundleName + '/' + projectConfig.moduleName + '/' + 864 cardRelativePath)); 865 } 866} 867 868export function validatorCard(log: any[], type: number, pos: number, 869 name: string = ''): void { 870 if (projectConfig && projectConfig.cardObj && resourceFileName 871 && projectConfig.cardObj[resourceFileName]) { 872 const logInfo: object = { 873 type: LogType.ERROR, 874 message: '', 875 pos: pos 876 }; 877 switch (type) { 878 case CARD_LOG_TYPE_COMPONENTS: 879 logInfo.message = `Card page cannot use the component ${name}.`; 880 break; 881 case CARD_LOG_TYPE_DECORATORS: 882 logInfo.message = `Card page cannot use ${name}`; 883 break; 884 case CARD_LOG_TYPE_IMPORT: 885 logInfo.message = `Card page cannot use import.`; 886 break; 887 } 888 log.push(logInfo); 889 } 890} 891