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