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'; 18import fs from 'fs'; 19 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 COMPONENT_ANIMATABLE_EXTEND_DECORATOR, 62 CHECK_EXTEND_DECORATORS, 63 ELMTID, 64 ROUTENAME_NODE, 65 STORAGE_NODE, 66 STORAGE, 67 REGISTER_NAMED_ROUTE, 68 ROUTE_NAME, 69 PAGE_PATH, 70 ISINITIALRENDER, 71 CREATE_ANIMATABLE_PROPERTY, 72 COMPONENT_CREATE_FUNCTION, 73 COMPONENT_POP_FUNCTION, 74 UPDATE_ANIMATABLE_PROPERTY, 75 VIEW_STACK_PROCESSOR, 76 GET_AND_PUSH_FRAME_NODE, 77 COMPONENT_CONSTRUCTOR_PARENT, 78 FINISH_UPDATE_FUNC, 79} from './pre_define'; 80import { 81 componentInfo, 82 LogInfo, 83 LogType, 84 hasDecorator, 85 FileLog, 86 getPossibleBuilderTypeParameter, 87 storedFileInfo, 88 ExtendResult 89} from './utils'; 90import { writeFileSyncByNode } from './process_module_files'; 91import { 92 componentCollection, 93 localStorageLinkCollection, 94 localStoragePropCollection, 95} from './validate_ui_syntax'; 96import { 97 processComponentClass, 98 createParentParameter, 99 processBuildMember 100} from './process_component_class'; 101import processImport, { 102 processImportModule 103} from './process_import'; 104import { 105 processComponentBlock, 106 bindComponentAttr, 107 getName, 108 createViewStackProcessorStatement, 109 createFunction 110} from './process_component_build'; 111import { 112 BUILDIN_STYLE_NAMES, 113 CUSTOM_BUILDER_METHOD, 114 EXTEND_ATTRIBUTE, 115 INNER_STYLE_FUNCTION, 116 GLOBAL_STYLE_FUNCTION, 117 INTERFACE_NODE_SET, 118 ID_ATTRS 119} from './component_map'; 120import { 121 resources, 122 projectConfig, 123 partialUpdateConfig 124} from '../main'; 125import { createCustomComponentNewExpression, createViewCreate } from './process_component_member'; 126import { assignComponentParams } from './process_custom_component'; 127import { ModuleSourceFile } from './fast_build/ark_compiler/module/module_source_file'; 128 129export const transformLog: FileLog = new FileLog(); 130export let contextGlobal: ts.TransformationContext; 131export let resourceFileName: string = ''; 132export const builderTypeParameter: {params: string[]} = {params: []}; 133 134export let hasTsNoCheckOrTsIgnoreFiles: string[] = []; 135export let compilingEtsOrTsFiles: string[] = []; 136 137export function processUISyntax(program: ts.Program, ut = false): Function { 138 let entryNodeKey: ts.Expression; 139 return (context: ts.TransformationContext) => { 140 contextGlobal = context; 141 let pagesDir: string; 142 return (node: ts.SourceFile) => { 143 const hasTsNoCheckOrTsIgnore = ts.hasTsNoCheckOrTsIgnoreFlag(node); 144 compilingEtsOrTsFiles.push(path.normalize(node.fileName)); 145 pagesDir = path.resolve(path.dirname(node.fileName)); 146 resourceFileName = path.resolve(node.fileName); 147 if (process.env.compiler === BUILD_ON || process.env.compileTool === 'rollup') { 148 transformLog.sourceFile = node; 149 preprocessIdAttrs(node.fileName); 150 if (!ut && (process.env.compileMode !== 'moduleJson' && 151 path.resolve(node.fileName) === path.resolve(projectConfig.projectPath, 'app.ets') || 152 /\.ts$/.test(node.fileName))) { 153 node = ts.visitEachChild(node, processResourceNode, context); 154 if (projectConfig.compileMode === ESMODULE) { 155 if (projectConfig.processTs === true) { 156 const processedNode: ts.SourceFile = ts.getTypeExportImportAndConstEnumTransformer(context)(node); 157 if (process.env.compileTool === 'rollup') { 158 hasTsNoCheckOrTsIgnore ? hasTsNoCheckOrTsIgnoreFiles.push(path.normalize(processedNode.fileName)) : 159 ModuleSourceFile.newSourceFile(path.normalize(processedNode.fileName), processedNode); 160 } else { 161 writeFileSyncByNode(processedNode, projectConfig); 162 } 163 } 164 } 165 return node; 166 } 167 const id: number = ++componentInfo.id; 168 node = ts.visitEachChild(node, processAllNodes, context); 169 node = createEntryNode(node, context, entryNodeKey, id); 170 GLOBAL_STYLE_FUNCTION.forEach((block, styleName) => { 171 BUILDIN_STYLE_NAMES.delete(styleName); 172 }); 173 GLOBAL_STYLE_FUNCTION.clear(); 174 const statements: ts.Statement[] = Array.from(node.statements); 175 if (!partialUpdateConfig.partialUpdateMode) { 176 generateId(statements, node); 177 } 178 INTERFACE_NODE_SET.forEach(item => { 179 statements.unshift(item); 180 }); 181 node = ts.factory.updateSourceFile(node, statements); 182 INTERFACE_NODE_SET.clear(); 183 if (projectConfig.compileMode === ESMODULE && projectConfig.processTs === true) { 184 const processedNode: ts.SourceFile = ts.getTypeExportImportAndConstEnumTransformer(context)(node); 185 if (process.env.compileTool === 'rollup') { 186 hasTsNoCheckOrTsIgnore ? hasTsNoCheckOrTsIgnoreFiles.push(path.normalize(processedNode.fileName)) : 187 ModuleSourceFile.newSourceFile(path.normalize(processedNode.fileName), processedNode); 188 } else { 189 writeFileSyncByNode(processedNode, projectConfig); 190 } 191 } 192 return node; 193 } else { 194 return node; 195 } 196 }; 197 198 function entryKeyNode(node: ts.Node): ts.Expression { 199 if (node && node.decorators && node.decorators.length) { 200 node.decorators.forEach(item => { 201 if (item.expression && ts.isCallExpression(item.expression) && ts.isIdentifier(item.expression.expression) && 202 item.expression.expression.escapedText.toString() === 'Entry' && item.expression.arguments && 203 item.expression.arguments.length && ts.isIdentifier(item.expression.arguments[0])) { 204 entryNodeKey = item.expression.arguments[0]; 205 } 206 }); 207 } 208 return entryNodeKey; 209 } 210 211 function isESObjectNode(node: ts.Node): boolean { 212 if (node.kind === ts.SyntaxKind.TypeReference) { 213 const n: TypeReferenceNode = node as TypeReferenceNode; 214 if (n.typeName?.kind === ts.SyntaxKind.Identifier && (n.typeName as ts.Identifier).escapedText === 'ESObject') { 215 return true; 216 } 217 } 218 return false; 219 } 220 221 function processAllNodes(node: ts.Node): ts.Node { 222 if (projectConfig.compileMode === 'esmodule' && process.env.compileTool === 'rollup' && 223 ts.isImportDeclaration(node)) { 224 processImportModule(node); 225 } else if ((projectConfig.compileMode !== 'esmodule' || process.env.compileTool !== 'rollup') && 226 (ts.isImportDeclaration(node) || ts.isImportEqualsDeclaration(node) || 227 ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier))) { 228 processImport(node, pagesDir, transformLog.errors); 229 } 230 if (ts.isStructDeclaration(node)) { 231 componentCollection.currentClassName = node.name.getText(); 232 componentCollection.entryComponent === componentCollection.currentClassName && entryKeyNode(node); 233 node = processComponentClass(node, context, transformLog.errors, program); 234 componentCollection.currentClassName = null; 235 INNER_STYLE_FUNCTION.forEach((block, styleName) => { 236 BUILDIN_STYLE_NAMES.delete(styleName); 237 }); 238 INNER_STYLE_FUNCTION.clear(); 239 } else if (ts.isFunctionDeclaration(node)) { 240 if (hasDecorator(node, COMPONENT_EXTEND_DECORATOR, null, transformLog.errors)) { 241 node = processExtend(node, transformLog.errors, COMPONENT_EXTEND_DECORATOR); 242 } else if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR) && node.name && node.body && 243 ts.isBlock(node.body)) { 244 CUSTOM_BUILDER_METHOD.add(node.name.getText()); 245 builderTypeParameter.params = getPossibleBuilderTypeParameter(node.parameters); 246 let parameters: ts.NodeArray<ts.ParameterDeclaration> = 247 ts.factory.createNodeArray(Array.from(node.parameters)); 248 parameters.push(createParentParameter()); 249 node = ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, 250 node.asteriskToken, node.name, node.typeParameters, parameters, node.type, 251 processComponentBlock(node.body, false, transformLog.errors, false, true, 252 node.name.getText(), undefined, true)); 253 builderTypeParameter.params = []; 254 node = processBuildMember(node, context, transformLog.errors, true); 255 } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { 256 if (node.parameters.length === 0) { 257 node = undefined; 258 } else { 259 transformLog.errors.push({ 260 type: LogType.ERROR, 261 message: `@Styles can't have parameters.`, 262 pos: node.getStart() 263 }); 264 } 265 } else if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) { 266 // ark compiler's feature 267 node = processConcurrent(node); 268 } else if (hasDecorator(node, COMPONENT_ANIMATABLE_EXTEND_DECORATOR, null, transformLog.errors)) { 269 node = processExtend(node, transformLog.errors, COMPONENT_ANIMATABLE_EXTEND_DECORATOR); 270 } 271 } else if (isResource(node)) { 272 node = processResourceData(node as ts.CallExpression); 273 } else if (isWorker(node)) { 274 node = processWorker(node as ts.NewExpression); 275 } else if (isAnimateTo(node)) { 276 node = processAnimateTo(node as ts.CallExpression); 277 } else if (isCustomDialogController(node)) { 278 node = createCustomDialogController(node.parent, node, transformLog.errors); 279 } else if (isESObjectNode(node)) { 280 node = ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword); 281 } 282 return ts.visitEachChild(node, processAllNodes, context); 283 } 284 function processResourceNode(node: ts.Node): ts.Node { 285 if (isResource(node)) { 286 node = processResourceData(node as ts.CallExpression); 287 } 288 return ts.visitEachChild(node, processResourceNode, context); 289 } 290 }; 291} 292 293function generateId(statements: ts.Statement[], node: ts.SourceFile): void { 294 statements.unshift( 295 ts.factory.createVariableStatement( 296 undefined, 297 ts.factory.createVariableDeclarationList( 298 [ts.factory.createVariableDeclaration( 299 ts.factory.createIdentifier(_GENERATE_ID), 300 undefined, 301 ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), 302 ts.factory.createNumericLiteral('0') 303 )], 304 ts.NodeFlags.Let 305 ) 306 ), 307 ts.factory.createFunctionDeclaration( 308 undefined, 309 undefined, 310 undefined, 311 ts.factory.createIdentifier(GENERATE_ID), 312 undefined, 313 [], 314 ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), 315 ts.factory.createBlock( 316 [ts.factory.createReturnStatement(ts.factory.createBinaryExpression( 317 ts.factory.createStringLiteral(path.basename(node.fileName, EXTNAME_ETS) + '_'), 318 ts.factory.createToken(ts.SyntaxKind.PlusToken), ts.factory.createPrefixUnaryExpression( 319 ts.SyntaxKind.PlusPlusToken, 320 ts.factory.createIdentifier(_GENERATE_ID) 321 )))], 322 true 323 ) 324 ) 325 ); 326} 327 328function preprocessIdAttrs(fileName: string): void { 329 for (const [id, idInfo] of ID_ATTRS) { 330 if (fileName === idInfo.get('path')) { 331 ID_ATTRS.delete(id); 332 } 333 } 334} 335 336function isCustomDialogController(node: ts.Expression) { 337 const tempParent: ts.Node = node.parent; 338 // @ts-ignore 339 if (!node.parent && node.original) { 340 // @ts-ignore 341 node.parent = node.original.parent; 342 } 343 if (ts.isNewExpression(node) && node.expression && ts.isIdentifier(node.expression) && 344 node.expression.escapedText.toString() === SET_CONTROLLER_CTR_TYPE) { 345 return true; 346 } else { 347 // @ts-ignore 348 node.parent = tempParent; 349 return false; 350 } 351} 352 353function createCustomDialogController(parent: ts.Expression, node: ts.NewExpression, 354 log: LogInfo[]): ts.NewExpression { 355 if (node.arguments && node.arguments.length === 1 && 356 ts.isObjectLiteralExpression(node.arguments[0]) && node.arguments[0].properties) { 357 const newproperties: ts.ObjectLiteralElementLike[] = node.arguments[0].properties.map((item) => { 358 const componentName: string = isCustomDialogControllerPropertyAssignment(item, log); 359 if (componentName !== null) { 360 item = processCustomDialogControllerPropertyAssignment(parent, 361 item as ts.PropertyAssignment, componentName); 362 } 363 return item; 364 }); 365 return ts.factory.createNewExpression(node.expression, node.typeArguments, 366 [ts.factory.createObjectLiteralExpression(newproperties, true), ts.factory.createThis()]); 367 } else { 368 return node; 369 } 370} 371 372function isCustomDialogControllerPropertyAssignment(node: ts.ObjectLiteralElementLike, 373 log: LogInfo[]): string { 374 if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) && 375 node.name.getText() === CUSTOM_DIALOG_CONTROLLER_BUILDER) { 376 if (node.initializer) { 377 const componentName: string = getName(node.initializer); 378 if (componentCollection.customDialogs.has(componentName)) { 379 return componentName; 380 } 381 } else { 382 validateCustomDialogControllerBuilderInit(node, log); 383 } 384 } 385 return null; 386} 387 388function validateCustomDialogControllerBuilderInit(node: ts.ObjectLiteralElementLike, 389 log: LogInfo[]): void { 390 log.push({ 391 type: LogType.ERROR, 392 message: 'The builder should be initialized with a @CustomDialog Component.', 393 pos: node.getStart() 394 }); 395} 396 397function processCustomDialogControllerPropertyAssignment(parent: ts.Expression, 398 node: ts.PropertyAssignment, componentName: string): ts.PropertyAssignment { 399 if (ts.isCallExpression(node.initializer)) { 400 return ts.factory.updatePropertyAssignment(node, node.name, 401 processCustomDialogControllerBuilder(parent, node.initializer, componentName)); 402 } 403} 404 405function processCustomDialogControllerBuilder(parent: ts.Expression, 406 node: ts.CallExpression, componentName: string): ts.ArrowFunction { 407 const newExp: ts.Expression = createCustomComponentNewExpression(node, componentName, false, false, true); 408 const jsDialog: ts.Identifier = ts.factory.createIdentifier(JS_DIALOG); 409 return createCustomComponentBuilderArrowFunction(node, parent, jsDialog, newExp); 410} 411 412function createCustomComponentBuilderArrowFunction(node: ts.CallExpression, parent: ts.Expression, 413 jsDialog: ts.Identifier, newExp: ts.Expression): ts.ArrowFunction { 414 let mountNodde: ts.PropertyAccessExpression; 415 if (ts.isBinaryExpression(parent)) { 416 mountNodde = parent.left; 417 } else if (ts.isVariableDeclaration(parent) || ts.isPropertyDeclaration(parent)) { 418 mountNodde = ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 419 parent.name as ts.Identifier); 420 } 421 return ts.factory.createArrowFunction( 422 undefined, 423 undefined, 424 [], 425 undefined, 426 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 427 ts.factory.createBlock( 428 [ 429 partialUpdateConfig.partialUpdateMode ? assignComponentParams(node) : undefined, 430 ts.factory.createVariableStatement( 431 undefined, 432 ts.factory.createVariableDeclarationList( 433 [ts.factory.createVariableDeclaration(jsDialog, undefined, undefined, newExp)], 434 ts.NodeFlags.Let 435 ) 436 ), 437 ts.factory.createExpressionStatement( 438 ts.factory.createCallExpression( 439 ts.factory.createPropertyAccessExpression( 440 jsDialog, 441 ts.factory.createIdentifier(SET_CONTROLLER_METHOD) 442 ), 443 undefined, 444 mountNodde ? [mountNodde] : undefined 445 ) 446 ), 447 ts.factory.createExpressionStatement(createViewCreate(jsDialog)) 448 ], 449 true 450 ) 451 ); 452} 453 454export function isResource(node: ts.Node): boolean { 455 return ts.isCallExpression(node) && ts.isIdentifier(node.expression) && 456 (node.expression.escapedText.toString() === RESOURCE || 457 node.expression.escapedText.toString() === RESOURCE_RAWFILE) && node.arguments.length > 0; 458} 459 460export function isAnimateTo(node: ts.Node): boolean { 461 return ts.isCallExpression(node) && ts.isIdentifier(node.expression) && 462 node.expression.escapedText.toString() === ATTRIBUTE_ANIMATETO; 463} 464 465export function processResourceData(node: ts.CallExpression, 466 previewLog: {isAcceleratePreview: boolean, log: LogInfo[]} = {isAcceleratePreview: false, log: []}): ts.Node { 467 if (ts.isStringLiteral(node.arguments[0])) { 468 if (node.expression.getText() === RESOURCE_RAWFILE) { 469 isResourcefile(node, previewLog); 470 return createResourceParam(0, RESOURCE_TYPE.rawfile, [node.arguments[0]]); 471 } else { 472 return getResourceDataNode(node, previewLog); 473 } 474 } 475 return node; 476} 477 478function getResourceDataNode(node: ts.CallExpression, 479 previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): ts.Node { 480 const resourceData: string[] = (node.arguments[0] as ts.StringLiteral).text.trim().split('.'); 481 if (preCheckResourceData(resourceData, resources, node.arguments[0].getStart(), previewLog)) { 482 const resourceType: number = RESOURCE_TYPE[resourceData[1]]; 483 if (resourceType === undefined) { 484 transformLog.errors.push({ 485 type: LogType.ERROR, 486 message: `The resource type ${resourceData[1]} is not supported.`, 487 pos: node.getStart() 488 }); 489 return node; 490 } 491 const resourceValue: number = resources[resourceData[0]][resourceData[1]][resourceData[2]]; 492 return createResourceParam(resourceValue, resourceType, 493 projectConfig.compileHar ? Array.from(node.arguments) : Array.from(node.arguments).slice(1)); 494 } 495 return node; 496} 497 498function isResourcefile(node: ts.CallExpression, previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): void { 499 if (process.env.rawFileResource && !storedFileInfo.resourcesArr.has(node.arguments[0].text) && 500 !previewLog.isAcceleratePreview && process.env.compileMode === 'moduleJson') { 501 transformLog.errors.push({ 502 type: LogType.WARN, 503 message: `No such '${node.arguments[0].text}' resource in current module.`, 504 pos: node.getStart() 505 }); 506 } 507} 508 509function createResourceParam(resourceValue: number, resourceType: number, argsArr: ts.Expression[]): 510 ts.ObjectLiteralExpression { 511 if (projectConfig.compileHar) { 512 projectConfig.bundleName = ''; 513 projectConfig.moduleName = ''; 514 resourceValue = -1; 515 } 516 517 const propertyArray: Array<ts.PropertyAssignment> = [ 518 ts.factory.createPropertyAssignment( 519 ts.factory.createStringLiteral(RESOURCE_NAME_ID), 520 ts.factory.createNumericLiteral(resourceValue) 521 ), 522 ts.factory.createPropertyAssignment( 523 ts.factory.createStringLiteral(RESOURCE_NAME_TYPE), 524 ts.factory.createNumericLiteral(resourceType) 525 ), 526 ts.factory.createPropertyAssignment( 527 ts.factory.createIdentifier(RESOURCE_NAME_PARAMS), 528 ts.factory.createArrayLiteralExpression(argsArr, false) 529 ) 530 ]; 531 532 if (projectConfig.bundleName || projectConfig.bundleName === '') { 533 propertyArray.push(ts.factory.createPropertyAssignment( 534 ts.factory.createStringLiteral(RESOURCE_NAME_BUNDLE), 535 ts.factory.createStringLiteral(projectConfig.bundleName) 536 )); 537 } 538 539 if (projectConfig.moduleName || projectConfig.moduleName === '') { 540 propertyArray.push(ts.factory.createPropertyAssignment( 541 ts.factory.createStringLiteral(RESOURCE_NAME_MODULE), 542 ts.factory.createStringLiteral(projectConfig.moduleName) 543 )); 544 } 545 546 const resourceParams: ts.ObjectLiteralExpression = ts.factory.createObjectLiteralExpression( 547 propertyArray, false); 548 return resourceParams; 549} 550 551function preCheckResourceData(resourceData: string[], resources: object, pos: number, 552 previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): boolean { 553 if (previewLog.isAcceleratePreview) { 554 return validateResourceData(resourceData, resources, pos, previewLog.log); 555 } else { 556 return validateResourceData(resourceData, resources, pos, transformLog.errors); 557 } 558} 559 560function validateResourceData(resourceData: string[], resources: object, pos: number, log: LogInfo[]): boolean { 561 if (resourceData.length !== 3) { 562 log.push({ 563 type: LogType.ERROR, 564 message: 'The input parameter is not supported.', 565 pos: pos 566 }); 567 } else { 568 if (process.env.compileTool === 'rollup' && process.env.compileMode === 'moduleJson') { 569 storedFileInfo.collectResourceInFile(resourceData[1] + '_' + resourceData[2], path.resolve(resourceFileName)); 570 } 571 if (!resources[resourceData[0]]) { 572 log.push({ 573 type: LogType.ERROR, 574 message: `Unknown resource source '${resourceData[0]}'.`, 575 pos: pos 576 }); 577 } else if (!resources[resourceData[0]][resourceData[1]]) { 578 log.push({ 579 type: LogType.ERROR, 580 message: `Unknown resource type '${resourceData[1]}'.`, 581 pos: pos 582 }); 583 } else if (!resources[resourceData[0]][resourceData[1]][resourceData[2]]) { 584 log.push({ 585 type: LogType.ERROR, 586 message: `Unknown resource name '${resourceData[2]}'.`, 587 pos: pos 588 }); 589 } else { 590 return true; 591 } 592 } 593 return false; 594} 595 596function isWorker(node: ts.Node): boolean { 597 return ts.isNewExpression(node) && ts.isPropertyAccessExpression(node.expression) && 598 ts.isIdentifier(node.expression.name) && 599 node.expression.name.escapedText.toString() === WORKER_OBJECT; 600} 601 602function processWorker(node: ts.NewExpression): ts.Node { 603 if (node.arguments.length && ts.isStringLiteral(node.arguments[0])) { 604 const args: ts.Expression[] = Array.from(node.arguments); 605 // @ts-ignore 606 const workerPath: string = node.arguments[0].text; 607 const stringNode: ts.StringLiteral = ts.factory.createStringLiteral( 608 workerPath.replace(/\.ts$/, '.js')); 609 args.splice(0, 1, stringNode); 610 return ts.factory.updateNewExpression(node, node.expression, node.typeArguments, args); 611 } 612 return node; 613} 614 615export function processAnimateTo(node: ts.CallExpression): ts.CallExpression { 616 return ts.factory.updateCallExpression(node, ts.factory.createPropertyAccessExpression( 617 ts.factory.createIdentifier(GLOBAL_CONTEXT), ts.factory.createIdentifier(ATTRIBUTE_ANIMATETO)), 618 node.typeArguments, node.arguments); 619} 620 621function processExtend(node: ts.FunctionDeclaration, log: LogInfo[], 622 decoratorName: string): ts.FunctionDeclaration { 623 const componentName: string = isExtendFunction(node, { decoratorName: '', componentName: '' }, true); 624 if (componentName && node.body && node.body.statements.length) { 625 const statementArray: ts.Statement[] = []; 626 let bodynode: ts.Block; 627 if (decoratorName === COMPONENT_EXTEND_DECORATOR) { 628 const attrSet: ts.CallExpression = node.body.statements[0].expression; 629 if (isOriginalExtend(node.body)) { 630 const changeCompName: ts.ExpressionStatement = ts.factory.createExpressionStatement(processExtendBody(attrSet)); 631 bindComponentAttr(changeCompName as ts.ExpressionStatement, 632 ts.factory.createIdentifier(componentName), statementArray, log); 633 } else { 634 bodynode = ts.visitEachChild(node.body, traverseExtendExpression, contextGlobal); 635 } 636 let extendFunctionName: string; 637 if (node.name.getText().startsWith('__' + componentName + '__')) { 638 extendFunctionName = node.name.getText(); 639 } else { 640 extendFunctionName = '__' + componentName + '__' + node.name.getText(); 641 collectExtend(EXTEND_ATTRIBUTE, componentName, node.name.escapedText.toString()); 642 } 643 return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, 644 ts.factory.createIdentifier(extendFunctionName), node.typeParameters, 645 node.parameters, ts.factory.createToken(ts.SyntaxKind.VoidKeyword), isOriginalExtend(node.body) ? 646 ts.factory.updateBlock(node.body, statementArray) : bodynode); 647 } 648 if (decoratorName === COMPONENT_ANIMATABLE_EXTEND_DECORATOR) { 649 bindComponentAttr(node.body.statements[0], 650 ts.factory.createIdentifier(componentName), statementArray, log); 651 return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, 652 node.name, node.typeParameters, 653 [...node.parameters, ...createAnimatableParameterNode()], ts.factory.createToken(ts.SyntaxKind.VoidKeyword), 654 ts.factory.updateBlock(node.body, createAnimatableBody(componentName, node.name, 655 node.parameters, statementArray))); 656 } 657 } 658 function traverseExtendExpression(node: ts.Node): ts.Node { 659 if (ts.isExpressionStatement(node) && isDollarNode(node, componentName)) { 660 const changeCompName: ts.ExpressionStatement = 661 ts.factory.createExpressionStatement(processExtendBody(node.expression, componentName)); 662 const statementArray: ts.Statement[] = []; 663 bindComponentAttr(changeCompName, ts.factory.createIdentifier(componentName), statementArray, []); 664 return ts.factory.createBlock(statementArray, true); 665 } 666 return ts.visitEachChild(node, traverseExtendExpression, contextGlobal); 667 } 668} 669 670function createAnimatableParameterNode(): ts.ParameterDeclaration[] { 671 return [ 672 ts.factory.createParameterDeclaration( 673 undefined, undefined, undefined, ts.factory.createIdentifier(ELMTID)), 674 ts.factory.createParameterDeclaration( 675 undefined, undefined, undefined, ts.factory.createIdentifier(ISINITIALRENDER)), 676 ts.factory.createParameterDeclaration( 677 undefined, undefined, undefined, ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT)) 678 ]; 679} 680 681function createAnimatableBody(componentName: string, funcName: ts.Identifier, 682 parameters: ts.NodeArray<ts.ParameterDeclaration>, attrArray: ts.Statement[]): ts.Statement[] { 683 const paramNode: ts.Identifier[] = []; 684 parameters.forEach((item: ts.ParameterDeclaration) => { 685 if (item.name && ts.isIdentifier(item.name)) { 686 paramNode.push(item.name); 687 } 688 }); 689 return [ 690 ts.factory.createIfStatement( 691 ts.factory.createIdentifier(ISINITIALRENDER), 692 ts.factory.createBlock([ 693 createAnimatableProperty(componentName, funcName, parameters, paramNode, attrArray), 694 ...attrArray 695 ], true), 696 ts.factory.createBlock([ 697 ts.factory.createExpressionStatement(ts.factory.createCallExpression( 698 ts.factory.createPropertyAccessExpression( 699 ts.factory.createIdentifier(componentName), 700 ts.factory.createIdentifier(UPDATE_ANIMATABLE_PROPERTY) 701 ), undefined, 702 [ ts.factory.createStringLiteral(funcName.escapedText.toString()), ...paramNode ] 703 )) 704 ]) 705 ) 706 ]; 707} 708 709function createAnimatableProperty(componentName: string, funcName: ts.Identifier, 710 parameters: ts.NodeArray<ts.ParameterDeclaration>, 711 paramNode: ts.Identifier[], attrArray: ts.Statement[]) { 712 const componentIdentifier: ts.Identifier = ts.factory.createIdentifier(componentName); 713 return ts.factory.createExpressionStatement( 714 ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( 715 componentIdentifier, 716 ts.factory.createIdentifier(CREATE_ANIMATABLE_PROPERTY)), 717 undefined, [ 718 ts.factory.createStringLiteral(funcName.escapedText.toString()), 719 ...paramNode, 720 ts.factory.createArrowFunction(undefined, undefined, parameters, undefined, 721 ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 722 ts.factory.createBlock([ 723 createViewStackProcessorStatement(STARTGETACCESSRECORDINGFOR, ELMTID), 724 createAnimatableFrameNode(componentName), 725 ...attrArray, 726 createViewStackProcessorStatement(STOPGETACCESSRECORDING), 727 createAnimatableUpdateFunc() 728 ], true)) 729 ] 730 ) 731 ); 732} 733 734function createAnimatableFrameNode(componentName: string): ts.ExpressionStatement { 735 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 736 ts.factory.createPropertyAccessExpression( 737 ts.factory.createIdentifier(VIEW_STACK_PROCESSOR), 738 ts.factory.createIdentifier(GET_AND_PUSH_FRAME_NODE), 739 ), undefined, 740 [ 741 ts.factory.createStringLiteral(componentName), 742 ts.factory.createIdentifier(ELMTID) 743 ] 744 )); 745} 746 747function createAnimatableUpdateFunc(): ts.ExpressionStatement { 748 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 749 ts.factory.createPropertyAccessExpression( 750 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT), 751 ts.factory.createIdentifier(FINISH_UPDATE_FUNC), 752 ), undefined, [ts.factory.createIdentifier(ELMTID)] 753 )); 754} 755 756function processConcurrent(node: ts.FunctionDeclaration): ts.FunctionDeclaration { 757 if (node.body) { 758 const statementArray: ts.Statement[] 759 = [ts.factory.createExpressionStatement(ts.factory.createStringLiteral('use concurrent')), 760 ...node.body.statements]; 761 return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, node.name, 762 node.typeParameters, node.parameters, node.type, ts.factory.updateBlock(node.body, statementArray)); 763 } 764 return node; 765} 766 767export function isOriginalExtend(node: ts.Block): boolean { 768 let innerNode: ts.Node = node.statements[0]; 769 if (node.statements.length === 1 && ts.isExpressionStatement(innerNode)) { 770 while (innerNode.expression) { 771 innerNode = innerNode.expression; 772 } 773 if (ts.isIdentifier(innerNode) && innerNode.pos && innerNode.end && innerNode.pos === innerNode.end && 774 innerNode.escapedText.toString().match(/Instance$/)) { 775 return true; 776 } 777 } 778 return false; 779} 780 781function isDollarNode(node: ts.ExpressionStatement, componentName: string): boolean { 782 let innerNode: ts.Node = node; 783 while (innerNode.expression) { 784 innerNode = innerNode.expression; 785 } 786 let changedIdentifier: string = '$'; 787 if (process.env.compileTool === 'rollup' && storedFileInfo.reUseProgram) { 788 changedIdentifier = `${componentName}Instance`; 789 } 790 if (ts.isIdentifier(innerNode) && innerNode.getText() === changedIdentifier) { 791 return true; 792 } else { 793 return false; 794 } 795} 796 797function processExtendBody(node: ts.Node, componentName?: string): ts.Expression { 798 switch (node.kind) { 799 case ts.SyntaxKind.CallExpression: 800 return ts.factory.createCallExpression(processExtendBody(node.expression, componentName), 801 undefined, node.arguments); 802 case ts.SyntaxKind.PropertyAccessExpression: 803 return ts.factory.createPropertyAccessExpression( 804 processExtendBody(node.expression, componentName), node.name); 805 case ts.SyntaxKind.Identifier: 806 if (!componentName) { 807 return ts.factory.createIdentifier(node.escapedText.toString().replace(INSTANCE, '')); 808 } else { 809 return ts.factory.createIdentifier(componentName); 810 } 811 } 812} 813 814export function collectExtend(collectionSet: Map<string, Set<string>>, component: string, attribute: string): void { 815 if (collectionSet.has(component)) { 816 collectionSet.get(component).add(attribute); 817 } else { 818 collectionSet.set(component, new Set([attribute])); 819 } 820} 821 822export function isExtendFunction(node: ts.FunctionDeclaration, extendResult: ExtendResult, 823 checkArguments: boolean = false): string { 824 if (node.decorators && node.decorators.length) { 825 for (let i = 0, len = node.decorators.length; i < len; i++) { 826 if (ts.isCallExpression(node.decorators[i].expression)) { 827 parseExtendNode(node.decorators[i].expression as ts.CallExpression, extendResult, checkArguments); 828 if (CHECK_EXTEND_DECORATORS.includes(extendResult.decoratorName) && extendResult.componentName) { 829 return extendResult.componentName; 830 } 831 } 832 } 833 } 834 return null; 835} 836 837function parseExtendNode(node: ts.CallExpression, extendResult: ExtendResult, checkArguments: boolean): void { 838 if (ts.isIdentifier(node.expression)) { 839 extendResult.decoratorName = node.expression.escapedText.toString(); 840 if (checkArguments && CHECK_EXTEND_DECORATORS.includes(extendResult.decoratorName) && 841 node.arguments && node.arguments.length !== 1) { 842 transformLog.errors.push({ 843 type: LogType.ERROR, 844 message: `@${extendResult.decoratorName} should have one and only one parameter`, 845 pos: node.getStart() 846 }); 847 } 848 } 849 if (node.arguments.length && ts.isIdentifier(node.arguments[0])) { 850 extendResult.componentName = node.arguments[0].escapedText.toString(); 851 } 852} 853 854function createEntryNode(node: ts.SourceFile, context: ts.TransformationContext, 855 entryNodeKey: ts.Expression, id: number): ts.SourceFile { 856 let cardRelativePath: string; 857 if (projectConfig && projectConfig.cardObj) { 858 cardRelativePath = projectConfig.cardObj[resourceFileName]; 859 } 860 if (componentCollection.previewComponent.length === 0 || !projectConfig.isPreview) { 861 if (componentCollection.entryComponent) { 862 if (!partialUpdateConfig.partialUpdateMode) { 863 const entryNode: ts.ExpressionStatement = 864 createEntryFunction(componentCollection.entryComponent, context, 865 cardRelativePath, entryNodeKey, id) as ts.ExpressionStatement; 866 return context.factory.updateSourceFile(node, [...node.statements, entryNode]); 867 } else { 868 const entryNodes: ts.ExpressionStatement[] = 869 createEntryFunction(componentCollection.entryComponent, context, 870 cardRelativePath, entryNodeKey, id) as ts.ExpressionStatement[]; 871 return entryNodes ? 872 context.factory.updateSourceFile(node, [...node.statements, ...entryNodes]) : 873 context.factory.updateSourceFile(node, [...node.statements]); 874 } 875 } else { 876 return node; 877 } 878 } else { 879 const statementsArray: ts.Statement = 880 createPreviewComponentFunction(componentCollection.entryComponent, context, cardRelativePath, entryNodeKey, id); 881 return context.factory.updateSourceFile(node, [...node.statements, statementsArray]); 882 } 883} 884 885function createEntryFunction(name: string, context: ts.TransformationContext, cardRelativePath: string, 886 entryNodeKey: ts.Expression, id: number): ts.ExpressionStatement | (ts.ExpressionStatement | ts.Block | ts.IfStatement)[] { 887 const newArray: ts.Expression[] = [ 888 context.factory.createStringLiteral(id.toString()), 889 context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 890 context.factory.createObjectLiteralExpression([], false) 891 ]; 892 const [localStorageName, entryOptionNode]: [string, ts.Expression] = addStorageParam(name); 893 if (localStorageName && entryNodeKey) { 894 newArray.push(entryNodeKey); 895 } 896 const newExpressionParams: any[] = [ 897 context.factory.createNewExpression( 898 context.factory.createIdentifier(name), undefined, newArray)]; 899 addCardStringliteral(newExpressionParams, context, cardRelativePath); 900 if (!partialUpdateConfig.partialUpdateMode) { 901 const newExpressionStatement: ts.ExpressionStatement = 902 context.factory.createExpressionStatement(context.factory.createCallExpression( 903 context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME : 904 PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams)); 905 return newExpressionStatement; 906 } else { 907 if (cardRelativePath) { 908 if (entryOptionNode && ts.isObjectLiteralExpression(entryOptionNode)) { 909 transformLog.errors.push({ 910 type: LogType.ERROR, 911 message: `@Entry doesn't support {} parameter in card`, 912 pos: componentCollection.entryComponentPos 913 }); 914 } 915 return [ 916 createStartGetAccessRecording(context), 917 createLoadDocument(context, name, cardRelativePath, localStorageName, entryNodeKey), 918 createStopGetAccessRecording(context) 919 ]; 920 } else { 921 return createLoadPageConditionalJudgMent(context, name, cardRelativePath, localStorageName, entryOptionNode); 922 } 923 } 924} 925 926function createLoadPageConditionalJudgMent(context: ts.TransformationContext, name: string, 927 cardRelativePath: string, localStorageName: string, entryOptionNode: ts.Expression, 928 argsArr: ts.Expression[] = undefined, isComponentPreview: boolean = false) 929 : (ts.ExpressionStatement | ts.Block | ts.IfStatement)[] { 930 let isObject: boolean = false; 931 let routeNameNode: ts.Expression; 932 let storageNode: ts.Expression; 933 if (!entryOptionNode) { 934 const originArray: ts.ExpressionStatement[] = [ 935 createStartGetAccessRecording(context), 936 createLoadDocument(context, name, cardRelativePath, localStorageName, entryOptionNode), 937 createStopGetAccessRecording(context) 938 ]; 939 return originArray; 940 } 941 if (ts.isObjectLiteralExpression(entryOptionNode)) { 942 isObject = true; 943 if (entryOptionNode.properties) { 944 entryOptionNode.properties.forEach((property) => { 945 if (ts.isPropertyAssignment(property) && property.name && ts.isIdentifier(property.name)) { 946 if (property.name.escapedText.toString() === ROUTE_NAME) { 947 routeNameNode = property.initializer; 948 } else if (property.name.escapedText.toString() === STORAGE) { 949 storageNode = property.initializer; 950 } 951 } 952 }); 953 } 954 } else { 955 isObject = false; 956 } 957 if (isObject) { 958 if (routeNameNode && !storageNode) { 959 return isComponentPreview ? [ 960 ...assignRouteNameAndStorage(routeNameNode, storageNode, true, false), 961 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 962 routeNameNode, storageNode, true, false, false, argsArr) 963 ] : [ts.factory.createBlock([ 964 ...assignRouteNameAndStorage(routeNameNode, storageNode, true, false), 965 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 966 routeNameNode, storageNode, true, false, false, argsArr) 967 ])]; 968 } else if (!routeNameNode && !storageNode) { 969 return isComponentPreview ? [ 970 ...assignRouteNameAndStorage(routeNameNode, storageNode, false, false), 971 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 972 routeNameNode, storageNode, false, false, true, argsArr) 973 ] : [ts.factory.createBlock([ 974 ...assignRouteNameAndStorage(routeNameNode, storageNode, false, false), 975 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 976 routeNameNode, storageNode, false, false, true, argsArr) 977 ])]; 978 } else if (!routeNameNode && storageNode) { 979 return isComponentPreview ? [ 980 ...assignRouteNameAndStorage(routeNameNode, storageNode, false, true), 981 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 982 routeNameNode, storageNode, false, true, true, argsArr) 983 ] : [ts.factory.createBlock([ 984 ...assignRouteNameAndStorage(routeNameNode, storageNode, false, true), 985 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 986 routeNameNode, storageNode, false, true, true, argsArr) 987 ])]; 988 } else { 989 return isComponentPreview ? [ 990 ...assignRouteNameAndStorage(routeNameNode, storageNode, true, true), 991 judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode, 992 storageNode, argsArr) 993 ] : [ts.factory.createBlock([ 994 ...assignRouteNameAndStorage(routeNameNode, storageNode, true, true), 995 judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode, 996 storageNode, argsArr) 997 ])]; 998 } 999 } else { 1000 return [ 1001 judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode, 1002 storageNode, argsArr)]; 1003 } 1004} 1005 1006function judgeRouteNameAndStorage(context: ts.TransformationContext, name: string, 1007 cardRelativePath: string, isObject: boolean, entryOptionNode: ts.Expression, routeNameNode: ts.Expression, 1008 storageNode: ts.Expression, argsArr: ts.Expression[] = undefined): ts.IfStatement { 1009 return ts.factory.createIfStatement( 1010 isObject ? judgeRouteAndStorageForObject(true, true) : 1011 judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, true, true), 1012 ts.factory.createBlock( 1013 [ 1014 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 1015 routeNameNode, storageNode, true, true, false, argsArr) 1016 ], 1017 true 1018 ), 1019 ts.factory.createIfStatement( 1020 isObject ? judgeRouteAndStorageForObject(true, false) : 1021 judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, true, false), 1022 ts.factory.createBlock( 1023 [ 1024 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 1025 routeNameNode, storageNode, true, false, false, argsArr) 1026 ], 1027 true 1028 ), 1029 ts.factory.createIfStatement( 1030 isObject ? judgeRouteAndStorageForObject(false, true) : 1031 judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, false, true), 1032 ts.factory.createBlock( 1033 [ 1034 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 1035 routeNameNode, storageNode, false, true, true, argsArr) 1036 ], 1037 true 1038 ), 1039 ts.factory.createBlock( 1040 [ 1041 ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode, 1042 routeNameNode, storageNode, false, false, true, argsArr) 1043 ], 1044 true 1045 ) 1046 ) 1047 ) 1048 ); 1049} 1050 1051function judgeRouteAndStorageForObject(hasRouteName: boolean, hasStorage: boolean): ts.BinaryExpression { 1052 return ts.factory.createBinaryExpression( 1053 ts.factory.createBinaryExpression( 1054 ts.factory.createIdentifier(ROUTENAME_NODE), 1055 ts.factory.createToken(hasRouteName ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken), 1056 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 1057 ), 1058 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 1059 ts.factory.createBinaryExpression( 1060 ts.factory.createIdentifier(STORAGE_NODE), 1061 ts.factory.createToken(hasStorage ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken), 1062 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 1063 ) 1064 ); 1065} 1066 1067function judgeRouteAndStorageForIdentifier(entryOptionNode: ts.Identifier, hasRouteName: boolean, 1068 hasStorage: boolean): ts.BinaryExpression { 1069 return ts.factory.createBinaryExpression( 1070 ts.factory.createBinaryExpression( 1071 entryOptionNode, 1072 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 1073 ts.factory.createBinaryExpression( 1074 ts.factory.createPropertyAccessExpression( 1075 entryOptionNode, 1076 ts.factory.createIdentifier(ROUTE_NAME) 1077 ), 1078 ts.factory.createToken(hasRouteName ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken), 1079 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 1080 ) 1081 ), 1082 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 1083 ts.factory.createBinaryExpression( 1084 ts.factory.createPropertyAccessExpression( 1085 entryOptionNode, 1086 ts.factory.createIdentifier(STORAGE) 1087 ), 1088 ts.factory.createToken(hasStorage ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken), 1089 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED) 1090 ) 1091 ); 1092} 1093 1094function assignRouteNameAndStorage(routeNameNode, storageNode, hasRouteName, hasStorage): ts.ExpressionStatement[] { 1095 const assignOperation: ts.VariableStatement[] = []; 1096 if (hasRouteName) { 1097 assignOperation.push(ts.factory.createVariableStatement( 1098 undefined, 1099 ts.factory.createVariableDeclarationList( 1100 [ts.factory.createVariableDeclaration( 1101 ts.factory.createIdentifier(ROUTENAME_NODE), 1102 undefined, 1103 undefined, 1104 routeNameNode 1105 )], 1106 ts.NodeFlags.Let 1107 ) 1108 )); 1109 } 1110 if (hasStorage) { 1111 assignOperation.push(ts.factory.createVariableStatement( 1112 undefined, 1113 ts.factory.createVariableDeclarationList( 1114 [ts.factory.createVariableDeclaration( 1115 ts.factory.createIdentifier(STORAGE_NODE), 1116 undefined, 1117 undefined, 1118 storageNode 1119 )], 1120 ts.NodeFlags.Let 1121 ) 1122 )); 1123 } 1124 return assignOperation; 1125} 1126 1127function createLoadDocumentWithRoute(context: ts.TransformationContext, name: string, 1128 cardRelativePath: string, isObject: boolean, entryOptionNode: ts.Expression, 1129 routeNameNode: ts.Node, storageNode: ts.Node, hasRouteName: boolean, hasStorage: boolean, 1130 shouldCreateAccsessRecording: boolean, argsArr: ts.Expression[]): ts.ExpressionStatement[] { 1131 const newArray: ts.Expression[] = [ 1132 context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 1133 context.factory.createObjectLiteralExpression([], false) 1134 ]; 1135 if (entryOptionNode) { 1136 if (!isObject) { 1137 if (hasStorage) { 1138 newArray.push(ts.factory.createPropertyAccessExpression( 1139 entryOptionNode, 1140 ts.factory.createIdentifier(STORAGE) 1141 )); 1142 } else if (!hasRouteName) { 1143 newArray.push(entryOptionNode); 1144 } 1145 } else if (storageNode && hasStorage) { 1146 newArray.push(ts.factory.createIdentifier(STORAGE_NODE)); 1147 } 1148 } 1149 const newExpressionParams: any[] = [ 1150 context.factory.createNewExpression( 1151 context.factory.createIdentifier(name), 1152 undefined, newArray)]; 1153 if (argsArr) { 1154 argsArr = []; 1155 componentCollection.previewComponent.forEach((componentName: string) => { 1156 const newExpression: ts.Expression = context.factory.createNewExpression( 1157 context.factory.createIdentifier(componentName), 1158 undefined, 1159 componentName === name ? newArray : newArray.slice(0, newArray.length - 1) 1160 ); 1161 argsArr.push(context.factory.createStringLiteral(componentName), newExpression); 1162 }); 1163 } 1164 if (hasRouteName) { 1165 return [ 1166 shouldCreateAccsessRecording ? createStartGetAccessRecording(context) : undefined, 1167 context.factory.createExpressionStatement(context.factory.createCallExpression( 1168 context.factory.createIdentifier(REGISTER_NAMED_ROUTE), 1169 undefined, 1170 [ 1171 context.factory.createArrowFunction( 1172 undefined, 1173 undefined, 1174 [], 1175 undefined, 1176 context.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), 1177 newExpressionParams[0] 1178 ), 1179 isObject ? ts.factory.createIdentifier(ROUTENAME_NODE) : context.factory.createPropertyAccessExpression( 1180 entryOptionNode, 1181 context.factory.createIdentifier(ROUTE_NAME) 1182 ), 1183 context.factory.createObjectLiteralExpression( 1184 [ 1185 context.factory.createPropertyAssignment( 1186 context.factory.createIdentifier(RESOURCE_NAME_BUNDLE), 1187 context.factory.createStringLiteral(projectConfig.bundleName || '') 1188 ), 1189 context.factory.createPropertyAssignment( 1190 context.factory.createIdentifier(RESOURCE_NAME_MODULE), 1191 context.factory.createStringLiteral(projectConfig.moduleName || '') 1192 ), 1193 context.factory.createPropertyAssignment( 1194 context.factory.createIdentifier(PAGE_PATH), 1195 context.factory.createStringLiteral( 1196 projectConfig.compileHar ? '' : 1197 path.relative(projectConfig.projectPath || '', resourceFileName).replace(/\\/g, '/').replace(/\.ets$/, '') 1198 ) 1199 ) 1200 ], 1201 false 1202 ) 1203 ] 1204 )), 1205 shouldCreateAccsessRecording ? createStopGetAccessRecording(context) : undefined]; 1206 } else { 1207 return [ 1208 shouldCreateAccsessRecording ? createStartGetAccessRecording(context) : undefined, 1209 context.factory.createExpressionStatement( 1210 context.factory.createCallExpression( 1211 context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME : 1212 PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams)), 1213 shouldCreateAccsessRecording ? createStopGetAccessRecording(context) : undefined]; 1214 } 1215} 1216 1217function createStartGetAccessRecording(context: ts.TransformationContext): ts.ExpressionStatement { 1218 return context.factory.createExpressionStatement( 1219 context.factory.createCallExpression( 1220 context.factory.createPropertyAccessExpression( 1221 context.factory.createIdentifier(VIEWSTACKPROCESSOR), 1222 context.factory.createIdentifier(STARTGETACCESSRECORDINGFOR) 1223 ), 1224 undefined, 1225 [context.factory.createCallExpression( 1226 context.factory.createPropertyAccessExpression( 1227 context.factory.createIdentifier(VIEWSTACKPROCESSOR), 1228 context.factory.createIdentifier(ALLOCATENEWELMETIDFORNEXTCOMPONENT) 1229 ), 1230 undefined, 1231 [] 1232 )] 1233 ) 1234 ); 1235} 1236 1237function createLoadDocument(context: ts.TransformationContext, name: string, 1238 cardRelativePath: string, localStorageName: string, entryNodeKey: ts.Expression): ts.ExpressionStatement { 1239 const newArray: ts.Expression[] = [ 1240 context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 1241 context.factory.createObjectLiteralExpression([], false) 1242 ]; 1243 if (localStorageName && entryNodeKey) { 1244 newArray.push(entryNodeKey); 1245 } 1246 const newExpressionParams: any[] = [ 1247 context.factory.createNewExpression( 1248 context.factory.createIdentifier(name), 1249 undefined, newArray)]; 1250 addCardStringliteral(newExpressionParams, context, cardRelativePath); 1251 return context.factory.createExpressionStatement( 1252 context.factory.createCallExpression( 1253 context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME : 1254 PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams) 1255 ); 1256} 1257 1258function createStopGetAccessRecording(context: ts.TransformationContext): ts.ExpressionStatement { 1259 return context.factory.createExpressionStatement( 1260 context.factory.createCallExpression( 1261 context.factory.createPropertyAccessExpression( 1262 context.factory.createIdentifier(VIEWSTACKPROCESSOR), 1263 context.factory.createIdentifier(STOPGETACCESSRECORDING) 1264 ), 1265 undefined, 1266 [] 1267 ) 1268 ); 1269} 1270 1271function addStorageParam(name: string): [string, ts.Expression] { 1272 let localStorageName: string; 1273 let localStorageNode: ts.Identifier | ts.ObjectLiteralExpression; 1274 const localStorageNum: number = (localStorageLinkCollection.get(name) || new Set()).size + 1275 (localStoragePropCollection.get(name) || new Set()).size; 1276 if (componentCollection.entryComponent === name && componentCollection.localStorageNode) { 1277 localStorageNode = componentCollection.localStorageNode; 1278 } 1279 if (componentCollection.entryComponent === name && componentCollection.localStorageName) { 1280 localStorageName = componentCollection.localStorageName; 1281 } else if (componentCollection.entryComponent === name && !componentCollection.localStorageName && 1282 !componentCollection.localStorageNode && localStorageNum) { 1283 transformLog.errors.push({ 1284 type: LogType.WARN, 1285 message: `@Entry should have a parameter, like '@Entry (storage)'.`, 1286 pos: componentCollection.entryComponentPos 1287 }); 1288 } 1289 return [localStorageName, localStorageNode]; 1290} 1291 1292function createPreviewComponentFunction(name: string, context: ts.TransformationContext, 1293 cardRelativePath: string, entryNodeKey: ts.Expression, id: number): ts.Statement { 1294 const newArray: ts.Expression[] = partialUpdateConfig.partialUpdateMode ? 1295 [ 1296 context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 1297 context.factory.createObjectLiteralExpression([], false) 1298 ] : 1299 [ 1300 context.factory.createStringLiteral(id.toString()), 1301 context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 1302 context.factory.createObjectLiteralExpression([], false) 1303 ]; 1304 const [localStorageName, entryOptionNode]: [string, ts.Expression] = addStorageParam(name); 1305 if (entryOptionNode) { 1306 newArray.push(entryOptionNode); 1307 } 1308 const argsArr: ts.Expression[] = []; 1309 componentCollection.previewComponent.forEach(componentName => { 1310 const newExpression: ts.Expression = context.factory.createNewExpression( 1311 context.factory.createIdentifier(componentName), 1312 undefined, 1313 newArray 1314 ); 1315 argsArr.push(context.factory.createStringLiteral(componentName)); 1316 argsArr.push(newExpression); 1317 }); 1318 const newExpressionParams: any[] = name ? [context.factory.createNewExpression( 1319 context.factory.createIdentifier(name), undefined, newArray)] : []; 1320 addCardStringliteral(newExpressionParams, context, cardRelativePath); 1321 const ifStatement: ts.Statement = context.factory.createIfStatement( 1322 context.factory.createCallExpression( 1323 context.factory.createIdentifier(GET_PREVIEW_FLAG_FUNCTION_NAME), 1324 undefined, 1325 [] 1326 ), 1327 context.factory.createBlock( 1328 [...storePreviewComponents(name, entryOptionNode, argsArr), 1329 context.factory.createExpressionStatement(context.factory.createCallExpression( 1330 context.factory.createIdentifier(PREVIEW_COMPONENT_FUNCTION_NAME), 1331 undefined, 1332 [] 1333 ))], 1334 true 1335 ), 1336 context.factory.createBlock( 1337 createPreviewElseBlock(name, context, cardRelativePath, localStorageName, entryOptionNode, 1338 newExpressionParams, argsArr), 1339 true 1340 ) 1341 ); 1342 return ifStatement; 1343} 1344 1345function storePreviewComponents(name: string, entryOptionNode: ts.Expression, argsArr: ts.Expression[]): 1346 (ts.ExpressionStatement|ts.VariableStatement|ts.IfStatement)[] { 1347 let isObject: boolean = false; 1348 let storageNode: ts.Expression; 1349 if (!entryOptionNode) { 1350 return [ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1351 ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS), 1352 undefined, 1353 [ 1354 ts.factory.createNumericLiteral(componentCollection.previewComponent.length), 1355 ...argsArr 1356 ] 1357 ))]; 1358 } 1359 if (ts.isObjectLiteralExpression(entryOptionNode)) { 1360 isObject = true; 1361 if (entryOptionNode.properties) { 1362 entryOptionNode.properties.forEach((property) => { 1363 if (ts.isPropertyAssignment(property) && property.name && ts.isIdentifier(property.name) && 1364 property.name.escapedText.toString() === STORAGE) { 1365 storageNode = property.initializer; 1366 } 1367 }); 1368 } 1369 } else { 1370 isObject = false; 1371 } 1372 const newArray: ts.Expression[] = [ 1373 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 1374 ts.factory.createObjectLiteralExpression([], false) 1375 ]; 1376 const newArgsArr: ts.Expression[] = []; 1377 if (isObject) { 1378 return processObjectStorage(storageNode, newArray, name, newArgsArr); 1379 } else { 1380 return [ts.factory.createIfStatement( 1381 ts.factory.createBinaryExpression( 1382 entryOptionNode, 1383 ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), 1384 ts.factory.createBinaryExpression( 1385 ts.factory.createPropertyAccessExpression( 1386 entryOptionNode, 1387 ts.factory.createIdentifier(STORAGE) 1388 ), 1389 ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsToken), 1390 ts.factory.createIdentifier('undefined') 1391 ) 1392 ), 1393 ts.factory.createBlock( 1394 [returnStorePreview(entryOptionNode, true, name)], 1395 true 1396 ), 1397 ts.factory.createBlock( 1398 [returnStorePreview(entryOptionNode, false, name)], 1399 true 1400 ) 1401 )]; 1402 } 1403} 1404 1405function processObjectStorage(storageNode: ts.Expression, newArray: ts.Expression[], name: string, 1406 newArgsArr: ts.Expression[]): (ts.ExpressionStatement|ts.VariableStatement)[] { 1407 if (storageNode) { 1408 newArray.push(ts.factory.createIdentifier(STORAGE_NODE)); 1409 componentCollection.previewComponent.forEach(componentName => { 1410 const newExpression: ts.Expression = ts.factory.createNewExpression( 1411 ts.factory.createIdentifier(componentName), 1412 undefined, 1413 componentName === name ? newArray : newArray.slice(0, newArray.length - 1) 1414 ); 1415 newArgsArr.push(ts.factory.createStringLiteral(componentName)); 1416 newArgsArr.push(newExpression); 1417 }); 1418 return [ts.factory.createVariableStatement( 1419 undefined, 1420 ts.factory.createVariableDeclarationList( 1421 [ts.factory.createVariableDeclaration( 1422 ts.factory.createIdentifier(STORAGE_NODE), 1423 undefined, 1424 undefined, 1425 storageNode 1426 )], 1427 ts.NodeFlags.Let 1428 ) 1429 ), ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1430 ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS), 1431 undefined, 1432 [ 1433 ts.factory.createNumericLiteral(componentCollection.previewComponent.length), 1434 ...newArgsArr 1435 ] 1436 ))]; 1437 } else { 1438 componentCollection.previewComponent.forEach(componentName => { 1439 const newExpression: ts.Expression = ts.factory.createNewExpression( 1440 ts.factory.createIdentifier(componentName), 1441 undefined, 1442 newArray 1443 ); 1444 newArgsArr.push(ts.factory.createStringLiteral(componentName)); 1445 newArgsArr.push(newExpression); 1446 }); 1447 return [ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1448 ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS), 1449 undefined, 1450 [ 1451 ts.factory.createNumericLiteral(componentCollection.previewComponent.length), 1452 ...newArgsArr 1453 ] 1454 ))]; 1455 } 1456} 1457 1458function returnStorePreview(entryOptionNode: ts.Expression, hasStorage: boolean, name: string): ts.ExpressionStatement { 1459 const newArray: ts.Expression[] = [ 1460 ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED), 1461 ts.factory.createObjectLiteralExpression([], false) 1462 ]; 1463 const newArgsArr: ts.Expression[] = []; 1464 newArray.push(hasStorage ? ts.factory.createPropertyAccessExpression( 1465 entryOptionNode, 1466 ts.factory.createIdentifier(STORAGE) 1467 ) : entryOptionNode); 1468 componentCollection.previewComponent.forEach(componentName => { 1469 const newExpression: ts.Expression = ts.factory.createNewExpression( 1470 ts.factory.createIdentifier(componentName), 1471 undefined, 1472 componentName === name ? newArray : newArray.slice(0, newArray.length - 1) 1473 ); 1474 newArgsArr.push(ts.factory.createStringLiteral(componentName)); 1475 newArgsArr.push(newExpression); 1476 }); 1477 return ts.factory.createExpressionStatement(ts.factory.createCallExpression( 1478 ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS), 1479 undefined, 1480 [ 1481 ts.factory.createNumericLiteral(componentCollection.previewComponent.length), 1482 ...newArgsArr 1483 ] 1484 )); 1485} 1486 1487function createPreviewElseBlock(name: string, context: ts.TransformationContext, cardRelativePath: string, 1488 localStorageName: string, entryOptionNode: ts.Expression, newExpressionParams: ts.Expression[], 1489 argsArr: ts.Expression[]): (ts.ExpressionStatement | ts.IfStatement | ts.Block)[] { 1490 if (name) { 1491 if (!partialUpdateConfig.partialUpdateMode) { 1492 return [context.factory.createExpressionStatement(context.factory.createCallExpression( 1493 context.factory.createIdentifier(STORE_PREVIEW_COMPONENTS), 1494 undefined, 1495 [ 1496 context.factory.createNumericLiteral(componentCollection.previewComponent.length), 1497 ...argsArr 1498 ] 1499 )), 1500 context.factory.createExpressionStatement(context.factory.createCallExpression( 1501 context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME : 1502 PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams 1503 ))]; 1504 } else { 1505 if (cardRelativePath) { 1506 if (entryOptionNode && ts.isObjectLiteralExpression(entryOptionNode)) { 1507 transformLog.errors.push({ 1508 type: LogType.ERROR, 1509 message: `@Entry doesn't support {} parameter in card`, 1510 pos: componentCollection.entryComponentPos 1511 }); 1512 } 1513 return [ 1514 name ? createStartGetAccessRecording(context) : undefined, 1515 name ? context.factory.createExpressionStatement(context.factory.createCallExpression( 1516 context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME : 1517 PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams 1518 )) : undefined, 1519 name ? createStopGetAccessRecording(context) : undefined 1520 ]; 1521 } 1522 return createLoadPageConditionalJudgMent(context, name, cardRelativePath, localStorageName, 1523 entryOptionNode, argsArr, true); 1524 } 1525 } 1526} 1527 1528export function resetLog(): void { 1529 transformLog.errors = []; 1530} 1531 1532function addCardStringliteral(newExpressionParams: any[], context: ts.TransformationContext, 1533 cardRelativePath: string): void { 1534 if (cardRelativePath) { 1535 newExpressionParams.push(context.factory.createStringLiteral( 1536 projectConfig.bundleName + '/' + projectConfig.moduleName + '/' + 1537 cardRelativePath)); 1538 } 1539} 1540 1541export function validatorCard(log: any[], type: number, pos: number, 1542 name: string = ''): void { 1543 if (projectConfig && projectConfig.cardObj && resourceFileName 1544 && projectConfig.cardObj[resourceFileName]) { 1545 const logInfo: object = { 1546 type: LogType.ERROR, 1547 message: '', 1548 pos: pos 1549 }; 1550 switch (type) { 1551 case CARD_LOG_TYPE_COMPONENTS: 1552 logInfo.message = `Card page cannot use the component ${name}.`; 1553 break; 1554 case CARD_LOG_TYPE_DECORATORS: 1555 logInfo.message = `Card page cannot use ${name}`; 1556 break; 1557 case CARD_LOG_TYPE_IMPORT: 1558 logInfo.message = `Card page cannot use import.`; 1559 break; 1560 } 1561 log.push(logInfo); 1562 } 1563} 1564