1/*@internal*/ 2namespace ts { 3 export function transformModule(context: TransformationContext) { 4 interface AsynchronousDependencies { 5 aliasedModuleNames: Expression[]; 6 unaliasedModuleNames: Expression[]; 7 importAliasNames: ParameterDeclaration[]; 8 } 9 10 function getTransformModuleDelegate(moduleKind: ModuleKind): (node: SourceFile) => SourceFile { 11 switch (moduleKind) { 12 case ModuleKind.AMD: return transformAMDModule; 13 case ModuleKind.UMD: return transformUMDModule; 14 default: return transformCommonJSModule; 15 } 16 } 17 18 const { 19 factory, 20 getEmitHelperFactory: emitHelpers, 21 startLexicalEnvironment, 22 endLexicalEnvironment, 23 hoistVariableDeclaration 24 } = context; 25 26 const compilerOptions = context.getCompilerOptions(); 27 const resolver = context.getEmitResolver(); 28 const host = context.getEmitHost(); 29 const languageVersion = getEmitScriptTarget(compilerOptions); 30 const moduleKind = getEmitModuleKind(compilerOptions); 31 const previousOnSubstituteNode = context.onSubstituteNode; 32 const previousOnEmitNode = context.onEmitNode; 33 context.onSubstituteNode = onSubstituteNode; 34 context.onEmitNode = onEmitNode; 35 context.enableSubstitution(SyntaxKind.Identifier); // Substitutes expression identifiers with imported/exported symbols. 36 context.enableSubstitution(SyntaxKind.BinaryExpression); // Substitutes assignments to exported symbols. 37 context.enableSubstitution(SyntaxKind.PrefixUnaryExpression); // Substitutes updates to exported symbols. 38 context.enableSubstitution(SyntaxKind.PostfixUnaryExpression); // Substitutes updates to exported symbols. 39 context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes shorthand property assignments for imported/exported symbols. 40 context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. 41 42 const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file. 43 const deferredExports: (Statement[] | undefined)[] = []; // Exports to defer until an EndOfDeclarationMarker is found. 44 45 let currentSourceFile: SourceFile; // The current file. 46 let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file. 47 let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored. 48 let needUMDDynamicImportHelper: boolean; 49 50 return chainBundle(context, transformSourceFile); 51 52 /** 53 * Transforms the module aspects of a SourceFile. 54 * 55 * @param node The SourceFile node. 56 */ 57 function transformSourceFile(node: SourceFile) { 58 if (node.isDeclarationFile || 59 !(isEffectiveExternalModule(node, compilerOptions) || 60 node.transformFlags & TransformFlags.ContainsDynamicImport || 61 (isJsonSourceFile(node) && hasJsonModuleEmitEnabled(compilerOptions) && outFile(compilerOptions)))) { 62 return node; 63 } 64 65 currentSourceFile = node; 66 currentModuleInfo = collectExternalModuleInfo(context, node, resolver, compilerOptions); 67 moduleInfoMap[getOriginalNodeId(node)] = currentModuleInfo; 68 69 // Perform the transformation. 70 const transformModule = getTransformModuleDelegate(moduleKind); 71 const updated = transformModule(node); 72 currentSourceFile = undefined!; 73 currentModuleInfo = undefined!; 74 needUMDDynamicImportHelper = false; 75 return updated; 76 } 77 78 79 function shouldEmitUnderscoreUnderscoreESModule() { 80 if (!currentModuleInfo.exportEquals && isExternalModule(currentSourceFile)) { 81 return true; 82 } 83 return false; 84 } 85 86 /** 87 * Transforms a SourceFile into a CommonJS module. 88 * 89 * @param node The SourceFile node. 90 */ 91 function transformCommonJSModule(node: SourceFile) { 92 startLexicalEnvironment(); 93 94 const statements: Statement[] = []; 95 const ensureUseStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile)); 96 const statementOffset = factory.copyPrologue(node.statements, statements, ensureUseStrict && !isJsonSourceFile(node), sourceElementVisitor); 97 98 if (shouldEmitUnderscoreUnderscoreESModule()) { 99 append(statements, createUnderscoreUnderscoreESModule()); 100 } 101 if (length(currentModuleInfo.exportedNames)) { 102 const chunkSize = 50; 103 for (let i=0; i<currentModuleInfo.exportedNames!.length; i += chunkSize) { 104 append( 105 statements, 106 factory.createExpressionStatement( 107 reduceLeft( 108 currentModuleInfo.exportedNames!.slice(i, i + chunkSize), 109 (prev, nextId) => factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier("exports"), factory.createIdentifier(idText(nextId))), prev), 110 factory.createVoidZero() as Expression 111 ) 112 ) 113 ); 114 } 115 } 116 117 append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement)); 118 addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset)); 119 addExportEqualsIfNeeded(statements, /*emitAsReturn*/ false); 120 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 121 122 const updated = factory.updateSourceFile(node, setTextRange(factory.createNodeArray(statements), node.statements)); 123 addEmitHelpers(updated, context.readEmitHelpers()); 124 return updated; 125 } 126 127 /** 128 * Transforms a SourceFile into an AMD module. 129 * 130 * @param node The SourceFile node. 131 */ 132 function transformAMDModule(node: SourceFile) { 133 const define = factory.createIdentifier("define"); 134 const moduleName = tryGetModuleNameFromFile(factory, node, host, compilerOptions); 135 const jsonSourceFile = isJsonSourceFile(node) && node; 136 137 // An AMD define function has the following shape: 138 // 139 // define(id?, dependencies?, factory); 140 // 141 // This has the shape of the following: 142 // 143 // define(name, ["module1", "module2"], function (module1Alias) { ... } 144 // 145 // The location of the alias in the parameter list in the factory function needs to 146 // match the position of the module name in the dependency list. 147 // 148 // To ensure this is true in cases of modules with no aliases, e.g.: 149 // 150 // import "module" 151 // 152 // or 153 // 154 // /// <amd-dependency path= "a.css" /> 155 // 156 // we need to add modules without alias names to the end of the dependencies list 157 158 const { aliasedModuleNames, unaliasedModuleNames, importAliasNames } = collectAsynchronousDependencies(node, /*includeNonAmdDependencies*/ true); 159 160 // Create an updated SourceFile: 161 // 162 // define(mofactory.updateSourceFile", "module2"], function ... 163 const updated = factory.updateSourceFile(node, 164 setTextRange( 165 factory.createNodeArray([ 166 factory.createExpressionStatement( 167 factory.createCallExpression( 168 define, 169 /*typeArguments*/ undefined, 170 [ 171 // Add the module name (if provided). 172 ...(moduleName ? [moduleName] : []), 173 174 // Add the dependency array argument: 175 // 176 // ["require", "exports", module1", "module2", ...] 177 factory.createArrayLiteralExpression(jsonSourceFile ? emptyArray : [ 178 factory.createStringLiteral("require"), 179 factory.createStringLiteral("exports"), 180 ...aliasedModuleNames, 181 ...unaliasedModuleNames 182 ]), 183 184 // Add the module body function argument: 185 // 186 // function (require, exports, module1, module2) ... 187 jsonSourceFile ? 188 jsonSourceFile.statements.length ? jsonSourceFile.statements[0].expression : factory.createObjectLiteralExpression() : 189 factory.createFunctionExpression( 190 /*modifiers*/ undefined, 191 /*asteriskToken*/ undefined, 192 /*name*/ undefined, 193 /*typeParameters*/ undefined, 194 [ 195 factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "require"), 196 factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "exports"), 197 ...importAliasNames 198 ], 199 /*type*/ undefined, 200 transformAsynchronousModuleBody(node) 201 ) 202 ] 203 ) 204 ) 205 ]), 206 /*location*/ node.statements 207 ) 208 ); 209 210 addEmitHelpers(updated, context.readEmitHelpers()); 211 return updated; 212 } 213 214 /** 215 * Transforms a SourceFile into a UMD module. 216 * 217 * @param node The SourceFile node. 218 */ 219 function transformUMDModule(node: SourceFile) { 220 const { aliasedModuleNames, unaliasedModuleNames, importAliasNames } = collectAsynchronousDependencies(node, /*includeNonAmdDependencies*/ false); 221 const moduleName = tryGetModuleNameFromFile(factory, node, host, compilerOptions); 222 const umdHeader = factory.createFunctionExpression( 223 /*modifiers*/ undefined, 224 /*asteriskToken*/ undefined, 225 /*name*/ undefined, 226 /*typeParameters*/ undefined, 227 [factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "factory")], 228 /*type*/ undefined, 229 setTextRange( 230 factory.createBlock( 231 [ 232 factory.createIfStatement( 233 factory.createLogicalAnd( 234 factory.createTypeCheck(factory.createIdentifier("module"), "object"), 235 factory.createTypeCheck(factory.createPropertyAccessExpression(factory.createIdentifier("module"), "exports"), "object") 236 ), 237 factory.createBlock([ 238 factory.createVariableStatement( 239 /*modifiers*/ undefined, 240 [ 241 factory.createVariableDeclaration( 242 "v", 243 /*exclamationToken*/ undefined, 244 /*type*/ undefined, 245 factory.createCallExpression( 246 factory.createIdentifier("factory"), 247 /*typeArguments*/ undefined, 248 [ 249 factory.createIdentifier("require"), 250 factory.createIdentifier("exports") 251 ] 252 ) 253 ) 254 ] 255 ), 256 setEmitFlags( 257 factory.createIfStatement( 258 factory.createStrictInequality( 259 factory.createIdentifier("v"), 260 factory.createIdentifier("undefined") 261 ), 262 factory.createExpressionStatement( 263 factory.createAssignment( 264 factory.createPropertyAccessExpression(factory.createIdentifier("module"), "exports"), 265 factory.createIdentifier("v") 266 ) 267 ) 268 ), 269 EmitFlags.SingleLine 270 ) 271 ]), 272 factory.createIfStatement( 273 factory.createLogicalAnd( 274 factory.createTypeCheck(factory.createIdentifier("define"), "function"), 275 factory.createPropertyAccessExpression(factory.createIdentifier("define"), "amd") 276 ), 277 factory.createBlock([ 278 factory.createExpressionStatement( 279 factory.createCallExpression( 280 factory.createIdentifier("define"), 281 /*typeArguments*/ undefined, 282 [ 283 // Add the module name (if provided). 284 ...(moduleName ? [moduleName] : []), 285 factory.createArrayLiteralExpression([ 286 factory.createStringLiteral("require"), 287 factory.createStringLiteral("exports"), 288 ...aliasedModuleNames, 289 ...unaliasedModuleNames 290 ]), 291 factory.createIdentifier("factory") 292 ] 293 ) 294 ) 295 ]) 296 ) 297 ) 298 ], 299 /*multiLine*/ true 300 ), 301 /*location*/ undefined 302 ) 303 ); 304 305 // Create an updated SourceFile: 306 // 307 // (function (factory) { 308 // if (typeof module === "object" && typeof module.exports === "object") { 309 // var v = factory(require, exports); 310 // if (v !== undefined) module.exports = v; 311 // } 312 // else if (typeof define === 'function' && define.amd) { 313 // define(["require", "exports"], factory); 314 // } 315 // })(function ...) 316 317 const updated = factory.updateSourceFile( 318 node, 319 setTextRange( 320 factory.createNodeArray([ 321 factory.createExpressionStatement( 322 factory.createCallExpression( 323 umdHeader, 324 /*typeArguments*/ undefined, 325 [ 326 // Add the module body function argument: 327 // 328 // function (require, exports) ... 329 factory.createFunctionExpression( 330 /*modifiers*/ undefined, 331 /*asteriskToken*/ undefined, 332 /*name*/ undefined, 333 /*typeParameters*/ undefined, 334 [ 335 factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "require"), 336 factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "exports"), 337 ...importAliasNames 338 ], 339 /*type*/ undefined, 340 transformAsynchronousModuleBody(node) 341 ) 342 ] 343 ) 344 ) 345 ]), 346 /*location*/ node.statements 347 ) 348 ); 349 350 addEmitHelpers(updated, context.readEmitHelpers()); 351 return updated; 352 } 353 354 /** 355 * Collect the additional asynchronous dependencies for the module. 356 * 357 * @param node The source file. 358 * @param includeNonAmdDependencies A value indicating whether to include non-AMD dependencies. 359 */ 360 function collectAsynchronousDependencies(node: SourceFile, includeNonAmdDependencies: boolean): AsynchronousDependencies { 361 // names of modules with corresponding parameter in the factory function 362 const aliasedModuleNames: Expression[] = []; 363 364 // names of modules with no corresponding parameters in factory function 365 const unaliasedModuleNames: Expression[] = []; 366 367 // names of the parameters in the factory function; these 368 // parameters need to match the indexes of the corresponding 369 // module names in aliasedModuleNames. 370 const importAliasNames: ParameterDeclaration[] = []; 371 372 // Fill in amd-dependency tags 373 for (const amdDependency of node.amdDependencies) { 374 if (amdDependency.name) { 375 aliasedModuleNames.push(factory.createStringLiteral(amdDependency.path)); 376 importAliasNames.push(factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, amdDependency.name)); 377 } 378 else { 379 unaliasedModuleNames.push(factory.createStringLiteral(amdDependency.path)); 380 } 381 } 382 383 for (const importNode of currentModuleInfo.externalImports) { 384 // Find the name of the external module 385 const externalModuleName = getExternalModuleNameLiteral(factory, importNode, currentSourceFile, host, resolver, compilerOptions); 386 387 // Find the name of the module alias, if there is one 388 const importAliasName = getLocalNameForExternalImport(factory, importNode, currentSourceFile); 389 // It is possible that externalModuleName is undefined if it is not string literal. 390 // This can happen in the invalid import syntax. 391 // E.g : "import * from alias from 'someLib';" 392 if (externalModuleName) { 393 if (includeNonAmdDependencies && importAliasName) { 394 // Set emitFlags on the name of the classDeclaration 395 // This is so that when printer will not substitute the identifier 396 setEmitFlags(importAliasName, EmitFlags.NoSubstitution); 397 aliasedModuleNames.push(externalModuleName); 398 importAliasNames.push(factory.createParameterDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, importAliasName)); 399 } 400 else { 401 unaliasedModuleNames.push(externalModuleName); 402 } 403 } 404 } 405 406 return { aliasedModuleNames, unaliasedModuleNames, importAliasNames }; 407 } 408 409 function getAMDImportExpressionForImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration) { 410 if (isImportEqualsDeclaration(node) || isExportDeclaration(node) || !getExternalModuleNameLiteral(factory, node, currentSourceFile, host, resolver, compilerOptions)) { 411 return undefined; 412 } 413 const name = getLocalNameForExternalImport(factory, node, currentSourceFile)!; // TODO: GH#18217 414 const expr = getHelperExpressionForImport(node, name); 415 if (expr === name) { 416 return undefined; 417 } 418 return factory.createExpressionStatement(factory.createAssignment(name, expr)); 419 } 420 421 /** 422 * Transforms a SourceFile into an AMD or UMD module body. 423 * 424 * @param node The SourceFile node. 425 */ 426 function transformAsynchronousModuleBody(node: SourceFile) { 427 startLexicalEnvironment(); 428 429 const statements: Statement[] = []; 430 const statementOffset = factory.copyPrologue(node.statements, statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor); 431 432 if (shouldEmitUnderscoreUnderscoreESModule()) { 433 append(statements, createUnderscoreUnderscoreESModule()); 434 } 435 if (length(currentModuleInfo.exportedNames)) { 436 append(statements, factory.createExpressionStatement(reduceLeft(currentModuleInfo.exportedNames, (prev, nextId) => factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier("exports"), factory.createIdentifier(idText(nextId))), prev), factory.createVoidZero() as Expression))); 437 } 438 439 // Visit each statement of the module body. 440 append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement)); 441 if (moduleKind === ModuleKind.AMD) { 442 addRange(statements, mapDefined(currentModuleInfo.externalImports, getAMDImportExpressionForImport)); 443 } 444 addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset)); 445 446 // Append the 'export =' statement if provided. 447 addExportEqualsIfNeeded(statements, /*emitAsReturn*/ true); 448 449 // End the lexical environment for the module body 450 // and merge any new lexical declarations. 451 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 452 453 const body = factory.createBlock(statements, /*multiLine*/ true); 454 if (needUMDDynamicImportHelper) { 455 addEmitHelper(body, dynamicImportUMDHelper); 456 } 457 458 return body; 459 } 460 461 /** 462 * Adds the down-level representation of `export=` to the statement list if one exists 463 * in the source file. 464 * 465 * @param statements The Statement list to modify. 466 * @param emitAsReturn A value indicating whether to emit the `export=` statement as a 467 * return statement. 468 */ 469 function addExportEqualsIfNeeded(statements: Statement[], emitAsReturn: boolean) { 470 if (currentModuleInfo.exportEquals) { 471 const expressionResult = visitNode(currentModuleInfo.exportEquals.expression, moduleExpressionElementVisitor); 472 if (expressionResult) { 473 if (emitAsReturn) { 474 const statement = factory.createReturnStatement(expressionResult); 475 setTextRange(statement, currentModuleInfo.exportEquals); 476 setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoComments); 477 statements.push(statement); 478 } 479 else { 480 const statement = factory.createExpressionStatement( 481 factory.createAssignment( 482 factory.createPropertyAccessExpression( 483 factory.createIdentifier("module"), 484 "exports" 485 ), 486 expressionResult 487 ) 488 ); 489 490 setTextRange(statement, currentModuleInfo.exportEquals); 491 setEmitFlags(statement, EmitFlags.NoComments); 492 statements.push(statement); 493 } 494 } 495 } 496 } 497 498 // 499 // Top-Level Source Element Visitors 500 // 501 502 /** 503 * Visits a node at the top level of the source file. 504 * 505 * @param node The node to visit. 506 */ 507 function sourceElementVisitor(node: Node): VisitResult<Node> { 508 switch (node.kind) { 509 case SyntaxKind.ImportDeclaration: 510 return visitImportDeclaration(<ImportDeclaration>node); 511 512 case SyntaxKind.ImportEqualsDeclaration: 513 return visitImportEqualsDeclaration(<ImportEqualsDeclaration>node); 514 515 case SyntaxKind.ExportDeclaration: 516 return visitExportDeclaration(<ExportDeclaration>node); 517 518 case SyntaxKind.ExportAssignment: 519 return visitExportAssignment(<ExportAssignment>node); 520 521 case SyntaxKind.VariableStatement: 522 return visitVariableStatement(<VariableStatement>node); 523 524 case SyntaxKind.FunctionDeclaration: 525 return visitFunctionDeclaration(<FunctionDeclaration>node); 526 527 case SyntaxKind.ClassDeclaration: 528 return visitClassDeclaration(<ClassDeclaration>node); 529 530 case SyntaxKind.MergeDeclarationMarker: 531 return visitMergeDeclarationMarker(<MergeDeclarationMarker>node); 532 533 case SyntaxKind.EndOfDeclarationMarker: 534 return visitEndOfDeclarationMarker(<EndOfDeclarationMarker>node); 535 536 default: 537 return visitEachChild(node, moduleExpressionElementVisitor, context); 538 } 539 } 540 541 function moduleExpressionElementVisitor(node: Expression): VisitResult<Expression> { 542 // This visitor does not need to descend into the tree if there is no dynamic import or destructuring assignment, 543 // as export/import statements are only transformed at the top level of a file. 544 if (!(node.transformFlags & TransformFlags.ContainsDynamicImport) && !(node.transformFlags & TransformFlags.ContainsDestructuringAssignment)) { 545 return node; 546 } 547 548 if (isImportCall(node)) { 549 return visitImportCallExpression(node); 550 } 551 else if (isDestructuringAssignment(node)) { 552 return visitDestructuringAssignment(node); 553 } 554 else { 555 return visitEachChild(node, moduleExpressionElementVisitor, context); 556 } 557 } 558 559 function destructuringNeedsFlattening(node: Expression): boolean { 560 if (isObjectLiteralExpression(node)) { 561 for (const elem of node.properties) { 562 switch (elem.kind) { 563 case SyntaxKind.PropertyAssignment: 564 if (destructuringNeedsFlattening(elem.initializer)) { 565 return true; 566 } 567 break; 568 case SyntaxKind.ShorthandPropertyAssignment: 569 if (destructuringNeedsFlattening(elem.name)) { 570 return true; 571 } 572 break; 573 case SyntaxKind.SpreadAssignment: 574 if (destructuringNeedsFlattening(elem.expression)) { 575 return true; 576 } 577 break; 578 case SyntaxKind.MethodDeclaration: 579 case SyntaxKind.GetAccessor: 580 case SyntaxKind.SetAccessor: 581 return false; 582 default: Debug.assertNever(elem, "Unhandled object member kind"); 583 } 584 } 585 } 586 else if (isArrayLiteralExpression(node)) { 587 for (const elem of node.elements) { 588 if (isSpreadElement(elem)) { 589 if (destructuringNeedsFlattening(elem.expression)) { 590 return true; 591 } 592 } 593 else if (destructuringNeedsFlattening(elem)) { 594 return true; 595 } 596 } 597 } 598 else if (isIdentifier(node)) { 599 return length(getExports(node)) > (isExportName(node) ? 1 : 0); 600 } 601 return false; 602 } 603 604 function visitDestructuringAssignment(node: DestructuringAssignment): Expression { 605 if (destructuringNeedsFlattening(node.left)) { 606 return flattenDestructuringAssignment(node, moduleExpressionElementVisitor, context, FlattenLevel.All, /*needsValue*/ false, createAllExportExpressions); 607 } 608 return visitEachChild(node, moduleExpressionElementVisitor, context); 609 } 610 611 function visitImportCallExpression(node: ImportCall): Expression { 612 const externalModuleName = getExternalModuleNameLiteral(factory, node, currentSourceFile, host, resolver, compilerOptions); 613 const firstArgument = visitNode(firstOrUndefined(node.arguments), moduleExpressionElementVisitor); 614 // Only use the external module name if it differs from the first argument. This allows us to preserve the quote style of the argument on output. 615 const argument = externalModuleName && (!firstArgument || !isStringLiteral(firstArgument) || firstArgument.text !== externalModuleName.text) ? externalModuleName : firstArgument; 616 const containsLexicalThis = !!(node.transformFlags & TransformFlags.ContainsLexicalThis); 617 switch (compilerOptions.module) { 618 case ModuleKind.AMD: 619 return createImportCallExpressionAMD(argument, containsLexicalThis); 620 case ModuleKind.UMD: 621 return createImportCallExpressionUMD(argument ?? factory.createVoidZero(), containsLexicalThis); 622 case ModuleKind.CommonJS: 623 default: 624 return createImportCallExpressionCommonJS(argument, containsLexicalThis); 625 } 626 } 627 628 function createImportCallExpressionUMD(arg: Expression, containsLexicalThis: boolean): Expression { 629 // (function (factory) { 630 // ... (regular UMD) 631 // } 632 // })(function (require, exports, useSyncRequire) { 633 // "use strict"; 634 // Object.defineProperty(exports, "__esModule", { value: true }); 635 // var __syncRequire = typeof module === "object" && typeof module.exports === "object"; 636 // var __resolved = new Promise(function (resolve) { resolve(); }); 637 // ..... 638 // __syncRequire 639 // ? __resolved.then(function () { return require(x); }) /*CommonJs Require*/ 640 // : new Promise(function (_a, _b) { require([x], _a, _b); }); /*Amd Require*/ 641 // }); 642 needUMDDynamicImportHelper = true; 643 if (isSimpleCopiableExpression(arg)) { 644 const argClone = isGeneratedIdentifier(arg) ? arg : isStringLiteral(arg) ? factory.createStringLiteralFromNode(arg) : setEmitFlags(setTextRange(factory.cloneNode(arg), arg), EmitFlags.NoComments); 645 return factory.createConditionalExpression( 646 /*condition*/ factory.createIdentifier("__syncRequire"), 647 /*questionToken*/ undefined, 648 /*whenTrue*/ createImportCallExpressionCommonJS(arg, containsLexicalThis), 649 /*colonToken*/ undefined, 650 /*whenFalse*/ createImportCallExpressionAMD(argClone, containsLexicalThis) 651 ); 652 } 653 else { 654 const temp = factory.createTempVariable(hoistVariableDeclaration); 655 return factory.createComma(factory.createAssignment(temp, arg), factory.createConditionalExpression( 656 /*condition*/ factory.createIdentifier("__syncRequire"), 657 /*questionToken*/ undefined, 658 /*whenTrue*/ createImportCallExpressionCommonJS(temp, containsLexicalThis), 659 /*colonToken*/ undefined, 660 /*whenFalse*/ createImportCallExpressionAMD(temp, containsLexicalThis) 661 )); 662 } 663 } 664 665 function createImportCallExpressionAMD(arg: Expression | undefined, containsLexicalThis: boolean): Expression { 666 // improt("./blah") 667 // emit as 668 // define(["require", "exports", "blah"], function (require, exports) { 669 // ... 670 // new Promise(function (_a, _b) { require([x], _a, _b); }); /*Amd Require*/ 671 // }); 672 const resolve = factory.createUniqueName("resolve"); 673 const reject = factory.createUniqueName("reject"); 674 const parameters = [ 675 factory.createParameterDeclaration(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ resolve), 676 factory.createParameterDeclaration(/*decorator*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, /*name*/ reject) 677 ]; 678 const body = factory.createBlock([ 679 factory.createExpressionStatement( 680 factory.createCallExpression( 681 factory.createIdentifier("require"), 682 /*typeArguments*/ undefined, 683 [factory.createArrayLiteralExpression([arg || factory.createOmittedExpression()]), resolve, reject] 684 ) 685 ) 686 ]); 687 688 let func: FunctionExpression | ArrowFunction; 689 if (languageVersion >= ScriptTarget.ES2015) { 690 func = factory.createArrowFunction( 691 /*modifiers*/ undefined, 692 /*typeParameters*/ undefined, 693 parameters, 694 /*type*/ undefined, 695 /*equalsGreaterThanToken*/ undefined, 696 body); 697 } 698 else { 699 func = factory.createFunctionExpression( 700 /*modifiers*/ undefined, 701 /*asteriskToken*/ undefined, 702 /*name*/ undefined, 703 /*typeParameters*/ undefined, 704 parameters, 705 /*type*/ undefined, 706 body); 707 708 // if there is a lexical 'this' in the import call arguments, ensure we indicate 709 // that this new function expression indicates it captures 'this' so that the 710 // es2015 transformer will properly substitute 'this' with '_this'. 711 if (containsLexicalThis) { 712 setEmitFlags(func, EmitFlags.CapturesThis); 713 } 714 } 715 716 const promise = factory.createNewExpression(factory.createIdentifier("Promise"), /*typeArguments*/ undefined, [func]); 717 if (compilerOptions.esModuleInterop) { 718 return factory.createCallExpression(factory.createPropertyAccessExpression(promise, factory.createIdentifier("then")), /*typeArguments*/ undefined, [emitHelpers().createImportStarCallbackHelper()]); 719 } 720 return promise; 721 } 722 723 function createImportCallExpressionCommonJS(arg: Expression | undefined, containsLexicalThis: boolean): Expression { 724 // import("./blah") 725 // emit as 726 // Promise.resolve().then(function () { return require(x); }) /*CommonJs Require*/ 727 // We have to wrap require in then callback so that require is done in asynchronously 728 // if we simply do require in resolve callback in Promise constructor. We will execute the loading immediately 729 const promiseResolveCall = factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("Promise"), "resolve"), /*typeArguments*/ undefined, /*argumentsArray*/ []); 730 let requireCall: Expression = factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, arg ? [arg] : []); 731 if (compilerOptions.esModuleInterop) { 732 requireCall = emitHelpers().createImportStarHelper(requireCall); 733 } 734 735 let func: FunctionExpression | ArrowFunction; 736 if (languageVersion >= ScriptTarget.ES2015) { 737 func = factory.createArrowFunction( 738 /*modifiers*/ undefined, 739 /*typeParameters*/ undefined, 740 /*parameters*/ [], 741 /*type*/ undefined, 742 /*equalsGreaterThanToken*/ undefined, 743 requireCall); 744 } 745 else { 746 func = factory.createFunctionExpression( 747 /*modifiers*/ undefined, 748 /*asteriskToken*/ undefined, 749 /*name*/ undefined, 750 /*typeParameters*/ undefined, 751 /*parameters*/ [], 752 /*type*/ undefined, 753 factory.createBlock([factory.createReturnStatement(requireCall)])); 754 755 // if there is a lexical 'this' in the import call arguments, ensure we indicate 756 // that this new function expression indicates it captures 'this' so that the 757 // es2015 transformer will properly substitute 'this' with '_this'. 758 if (containsLexicalThis) { 759 setEmitFlags(func, EmitFlags.CapturesThis); 760 } 761 } 762 763 return factory.createCallExpression(factory.createPropertyAccessExpression(promiseResolveCall, "then"), /*typeArguments*/ undefined, [func]); 764 } 765 766 function getHelperExpressionForExport(node: ExportDeclaration, innerExpr: Expression) { 767 if (!compilerOptions.esModuleInterop || getEmitFlags(node) & EmitFlags.NeverApplyImportHelper) { 768 return innerExpr; 769 } 770 if (getExportNeedsImportStarHelper(node)) { 771 return emitHelpers().createImportStarHelper(innerExpr); 772 } 773 return innerExpr; 774 } 775 776 function getHelperExpressionForImport(node: ImportDeclaration, innerExpr: Expression) { 777 if (!compilerOptions.esModuleInterop || getEmitFlags(node) & EmitFlags.NeverApplyImportHelper) { 778 return innerExpr; 779 } 780 if (getImportNeedsImportStarHelper(node)) { 781 return emitHelpers().createImportStarHelper(innerExpr); 782 } 783 if (getImportNeedsImportDefaultHelper(node)) { 784 return emitHelpers().createImportDefaultHelper(innerExpr); 785 } 786 return innerExpr; 787 } 788 789 /** 790 * Visits an ImportDeclaration node. 791 * 792 * @param node The node to visit. 793 */ 794 function visitImportDeclaration(node: ImportDeclaration): VisitResult<Statement> { 795 let statements: Statement[] | undefined; 796 const namespaceDeclaration = getNamespaceDeclarationNode(node); 797 if (moduleKind !== ModuleKind.AMD) { 798 if (!node.importClause) { 799 // import "mod"; 800 return setOriginalNode(setTextRange(factory.createExpressionStatement(createRequireCall(node)), node), node); 801 } 802 else { 803 const variables: VariableDeclaration[] = []; 804 if (namespaceDeclaration && !isDefaultImport(node)) { 805 // import * as n from "mod"; 806 variables.push( 807 factory.createVariableDeclaration( 808 factory.cloneNode(namespaceDeclaration.name), 809 /*exclamationToken*/ undefined, 810 /*type*/ undefined, 811 getHelperExpressionForImport(node, createRequireCall(node)) 812 ) 813 ); 814 } 815 else { 816 // import d from "mod"; 817 // import { x, y } from "mod"; 818 // import d, { x, y } from "mod"; 819 // import d, * as n from "mod"; 820 variables.push( 821 factory.createVariableDeclaration( 822 factory.getGeneratedNameForNode(node), 823 /*exclamationToken*/ undefined, 824 /*type*/ undefined, 825 getHelperExpressionForImport(node, createRequireCall(node)) 826 ) 827 ); 828 829 if (namespaceDeclaration && isDefaultImport(node)) { 830 variables.push( 831 factory.createVariableDeclaration( 832 factory.cloneNode(namespaceDeclaration.name), 833 /*exclamationToken*/ undefined, 834 /*type*/ undefined, 835 factory.getGeneratedNameForNode(node) 836 ) 837 ); 838 } 839 } 840 841 statements = append(statements, 842 setOriginalNode( 843 setTextRange( 844 factory.createVariableStatement( 845 /*modifiers*/ undefined, 846 factory.createVariableDeclarationList( 847 variables, 848 languageVersion >= ScriptTarget.ES2015 ? NodeFlags.Const : NodeFlags.None 849 ) 850 ), 851 /*location*/ node), 852 /*original*/ node 853 ) 854 ); 855 } 856 } 857 else if (namespaceDeclaration && isDefaultImport(node)) { 858 // import d, * as n from "mod"; 859 statements = append(statements, 860 factory.createVariableStatement( 861 /*modifiers*/ undefined, 862 factory.createVariableDeclarationList( 863 [ 864 setOriginalNode( 865 setTextRange( 866 factory.createVariableDeclaration( 867 factory.cloneNode(namespaceDeclaration.name), 868 /*exclamationToken*/ undefined, 869 /*type*/ undefined, 870 factory.getGeneratedNameForNode(node) 871 ), 872 /*location*/ node), 873 /*original*/ node 874 ) 875 ], 876 languageVersion >= ScriptTarget.ES2015 ? NodeFlags.Const : NodeFlags.None 877 ) 878 ) 879 ); 880 } 881 882 if (hasAssociatedEndOfDeclarationMarker(node)) { 883 // Defer exports until we encounter an EndOfDeclarationMarker node 884 const id = getOriginalNodeId(node); 885 deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); 886 } 887 else { 888 statements = appendExportsOfImportDeclaration(statements, node); 889 } 890 891 return singleOrMany(statements); 892 } 893 894 /** 895 * Creates a `require()` call to import an external module. 896 * 897 * @param importNode The declararation to import. 898 */ 899 function createRequireCall(importNode: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration) { 900 const moduleName = getExternalModuleNameLiteral(factory, importNode, currentSourceFile, host, resolver, compilerOptions); 901 const args: Expression[] = []; 902 if (moduleName) { 903 args.push(moduleName); 904 } 905 906 return factory.createCallExpression(factory.createIdentifier("require"), /*typeArguments*/ undefined, args); 907 } 908 909 /** 910 * Visits an ImportEqualsDeclaration node. 911 * 912 * @param node The node to visit. 913 */ 914 function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult<Statement> { 915 Debug.assert(isExternalModuleImportEqualsDeclaration(node), "import= for internal module references should be handled in an earlier transformer."); 916 917 let statements: Statement[] | undefined; 918 if (moduleKind !== ModuleKind.AMD) { 919 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 920 statements = append(statements, 921 setOriginalNode( 922 setTextRange( 923 factory.createExpressionStatement( 924 createExportExpression( 925 node.name, 926 createRequireCall(node) 927 ) 928 ), 929 node), 930 node 931 ) 932 ); 933 } 934 else { 935 statements = append(statements, 936 setOriginalNode( 937 setTextRange( 938 factory.createVariableStatement( 939 /*modifiers*/ undefined, 940 factory.createVariableDeclarationList( 941 [ 942 factory.createVariableDeclaration( 943 factory.cloneNode(node.name), 944 /*exclamationToken*/ undefined, 945 /*type*/ undefined, 946 createRequireCall(node) 947 ) 948 ], 949 /*flags*/ languageVersion >= ScriptTarget.ES2015 ? NodeFlags.Const : NodeFlags.None 950 ) 951 ), 952 node), 953 node 954 ) 955 ); 956 } 957 } 958 else { 959 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 960 statements = append(statements, 961 setOriginalNode( 962 setTextRange( 963 factory.createExpressionStatement( 964 createExportExpression(factory.getExportName(node), factory.getLocalName(node)) 965 ), 966 node), 967 node 968 ) 969 ); 970 } 971 } 972 973 if (hasAssociatedEndOfDeclarationMarker(node)) { 974 // Defer exports until we encounter an EndOfDeclarationMarker node 975 const id = getOriginalNodeId(node); 976 deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); 977 } 978 else { 979 statements = appendExportsOfImportEqualsDeclaration(statements, node); 980 } 981 982 return singleOrMany(statements); 983 } 984 985 /** 986 * Visits an ExportDeclaration node. 987 * 988 * @param The node to visit. 989 */ 990 function visitExportDeclaration(node: ExportDeclaration): VisitResult<Statement> { 991 if (!node.moduleSpecifier) { 992 // Elide export declarations with no module specifier as they are handled 993 // elsewhere. 994 return undefined; 995 } 996 997 const generatedName = factory.getGeneratedNameForNode(node); 998 999 if (node.exportClause && isNamedExports(node.exportClause)) { 1000 const statements: Statement[] = []; 1001 // export { x, y } from "mod"; 1002 if (moduleKind !== ModuleKind.AMD) { 1003 statements.push( 1004 setOriginalNode( 1005 setTextRange( 1006 factory.createVariableStatement( 1007 /*modifiers*/ undefined, 1008 factory.createVariableDeclarationList([ 1009 factory.createVariableDeclaration( 1010 generatedName, 1011 /*exclamationToken*/ undefined, 1012 /*type*/ undefined, 1013 createRequireCall(node) 1014 ) 1015 ]) 1016 ), 1017 /*location*/ node), 1018 /* original */ node 1019 ) 1020 ); 1021 } 1022 for (const specifier of node.exportClause.elements) { 1023 if (languageVersion === ScriptTarget.ES3) { 1024 statements.push( 1025 setOriginalNode( 1026 setTextRange( 1027 factory.createExpressionStatement( 1028 emitHelpers().createCreateBindingHelper(generatedName, factory.createStringLiteralFromNode(specifier.propertyName || specifier.name), specifier.propertyName ? factory.createStringLiteralFromNode(specifier.name) : undefined) 1029 ), 1030 specifier), 1031 specifier 1032 ) 1033 ); 1034 } 1035 else { 1036 const exportNeedsImportDefault = 1037 !!compilerOptions.esModuleInterop && 1038 !(getEmitFlags(node) & EmitFlags.NeverApplyImportHelper) && 1039 idText(specifier.propertyName || specifier.name) === "default"; 1040 const exportedValue = factory.createPropertyAccessExpression( 1041 exportNeedsImportDefault ? emitHelpers().createImportDefaultHelper(generatedName) : generatedName, 1042 specifier.propertyName || specifier.name); 1043 statements.push( 1044 setOriginalNode( 1045 setTextRange( 1046 factory.createExpressionStatement( 1047 createExportExpression(factory.getExportName(specifier), exportedValue, /* location */ undefined, /* liveBinding */ true) 1048 ), 1049 specifier), 1050 specifier 1051 ) 1052 ); 1053 } 1054 } 1055 1056 return singleOrMany(statements); 1057 } 1058 else if (node.exportClause) { 1059 const statements: Statement[] = []; 1060 // export * as ns from "mod"; 1061 // export * as default from "mod"; 1062 statements.push( 1063 setOriginalNode( 1064 setTextRange( 1065 factory.createExpressionStatement( 1066 createExportExpression( 1067 factory.cloneNode(node.exportClause.name), 1068 getHelperExpressionForExport(node, moduleKind !== ModuleKind.AMD ? 1069 createRequireCall(node) : 1070 isExportNamespaceAsDefaultDeclaration(node) ? generatedName : 1071 factory.createIdentifier(idText(node.exportClause.name))) 1072 ) 1073 ), 1074 node 1075 ), 1076 node 1077 ) 1078 ); 1079 1080 return singleOrMany(statements); 1081 } 1082 else { 1083 // export * from "mod"; 1084 return setOriginalNode( 1085 setTextRange( 1086 factory.createExpressionStatement( 1087 emitHelpers().createExportStarHelper(moduleKind !== ModuleKind.AMD ? createRequireCall(node) : generatedName) 1088 ), 1089 node), 1090 node 1091 ); 1092 } 1093 } 1094 1095 /** 1096 * Visits an ExportAssignment node. 1097 * 1098 * @param node The node to visit. 1099 */ 1100 function visitExportAssignment(node: ExportAssignment): VisitResult<Statement> { 1101 if (node.isExportEquals) { 1102 return undefined; 1103 } 1104 1105 let statements: Statement[] | undefined; 1106 const original = node.original; 1107 if (original && hasAssociatedEndOfDeclarationMarker(original)) { 1108 // Defer exports until we encounter an EndOfDeclarationMarker node 1109 const id = getOriginalNodeId(node); 1110 deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), visitNode(node.expression, moduleExpressionElementVisitor), /*location*/ node, /*allowComments*/ true); 1111 } 1112 else { 1113 statements = appendExportStatement(statements, factory.createIdentifier("default"), visitNode(node.expression, moduleExpressionElementVisitor), /*location*/ node, /*allowComments*/ true); 1114 } 1115 1116 return singleOrMany(statements); 1117 } 1118 1119 /** 1120 * Visits a FunctionDeclaration node. 1121 * 1122 * @param node The node to visit. 1123 */ 1124 function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> { 1125 let statements: Statement[] | undefined; 1126 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 1127 statements = append(statements, 1128 setOriginalNode( 1129 setTextRange( 1130 factory.createFunctionDeclaration( 1131 /*decorators*/ undefined, 1132 visitNodes(node.modifiers, modifierVisitor, isModifier), 1133 node.asteriskToken, 1134 factory.getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true), 1135 /*typeParameters*/ undefined, 1136 visitNodes(node.parameters, moduleExpressionElementVisitor), 1137 /*type*/ undefined, 1138 visitEachChild(node.body, moduleExpressionElementVisitor, context) 1139 ), 1140 /*location*/ node 1141 ), 1142 /*original*/ node 1143 ) 1144 ); 1145 } 1146 else { 1147 statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context)); 1148 } 1149 1150 if (hasAssociatedEndOfDeclarationMarker(node)) { 1151 // Defer exports until we encounter an EndOfDeclarationMarker node 1152 const id = getOriginalNodeId(node); 1153 deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); 1154 } 1155 else { 1156 statements = appendExportsOfHoistedDeclaration(statements, node); 1157 } 1158 1159 return singleOrMany(statements); 1160 } 1161 1162 /** 1163 * Visits a ClassDeclaration node. 1164 * 1165 * @param node The node to visit. 1166 */ 1167 function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> { 1168 let statements: Statement[] | undefined; 1169 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 1170 statements = append(statements, 1171 setOriginalNode( 1172 setTextRange( 1173 factory.createClassDeclaration( 1174 /*decorators*/ undefined, 1175 visitNodes(node.modifiers, modifierVisitor, isModifier), 1176 factory.getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true), 1177 /*typeParameters*/ undefined, 1178 visitNodes(node.heritageClauses, moduleExpressionElementVisitor), 1179 visitNodes(node.members, moduleExpressionElementVisitor) 1180 ), 1181 node 1182 ), 1183 node 1184 ) 1185 ); 1186 } 1187 else { 1188 statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context)); 1189 } 1190 1191 if (hasAssociatedEndOfDeclarationMarker(node)) { 1192 // Defer exports until we encounter an EndOfDeclarationMarker node 1193 const id = getOriginalNodeId(node); 1194 deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); 1195 } 1196 else { 1197 statements = appendExportsOfHoistedDeclaration(statements, node); 1198 } 1199 1200 return singleOrMany(statements); 1201 } 1202 1203 /** 1204 * Visits a VariableStatement node. 1205 * 1206 * @param node The node to visit. 1207 */ 1208 function visitVariableStatement(node: VariableStatement): VisitResult<Statement> { 1209 let statements: Statement[] | undefined; 1210 let variables: VariableDeclaration[] | undefined; 1211 let expressions: Expression[] | undefined; 1212 1213 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 1214 let modifiers: NodeArray<Modifier> | undefined; 1215 let removeCommentsOnExpressions = false; 1216 1217 // If we're exporting these variables, then these just become assignments to 'exports.x'. 1218 for (const variable of node.declarationList.declarations) { 1219 if (isIdentifier(variable.name) && isLocalName(variable.name)) { 1220 if (!modifiers) { 1221 modifiers = visitNodes(node.modifiers, modifierVisitor, isModifier); 1222 } 1223 1224 variables = append(variables, variable); 1225 } 1226 else if (variable.initializer) { 1227 if (!isBindingPattern(variable.name) && (isArrowFunction(variable.initializer) || isFunctionExpression(variable.initializer) || isClassExpression(variable.initializer))) { 1228 const expression = factory.createAssignment( 1229 setTextRange( 1230 factory.createPropertyAccessExpression( 1231 factory.createIdentifier("exports"), 1232 variable.name 1233 ), 1234 /*location*/ variable.name 1235 ), 1236 factory.createIdentifier(getTextOfIdentifierOrLiteral(variable.name)) 1237 ); 1238 const updatedVariable = factory.createVariableDeclaration( 1239 variable.name, 1240 variable.exclamationToken, 1241 variable.type, 1242 visitNode(variable.initializer, moduleExpressionElementVisitor) 1243 ); 1244 1245 variables = append(variables, updatedVariable); 1246 expressions = append(expressions, expression); 1247 removeCommentsOnExpressions = true; 1248 } 1249 else { 1250 expressions = append(expressions, transformInitializedVariable(variable as InitializedVariableDeclaration)); 1251 } 1252 } 1253 } 1254 1255 if (variables) { 1256 statements = append(statements, factory.updateVariableStatement(node, modifiers, factory.updateVariableDeclarationList(node.declarationList, variables))); 1257 } 1258 1259 if (expressions) { 1260 const statement = setOriginalNode(setTextRange(factory.createExpressionStatement(factory.inlineExpressions(expressions)), node), node); 1261 if (removeCommentsOnExpressions) { 1262 removeAllComments(statement); 1263 } 1264 statements = append(statements, statement); 1265 } 1266 } 1267 else { 1268 statements = append(statements, visitEachChild(node, moduleExpressionElementVisitor, context)); 1269 } 1270 1271 if (hasAssociatedEndOfDeclarationMarker(node)) { 1272 // Defer exports until we encounter an EndOfDeclarationMarker node 1273 const id = getOriginalNodeId(node); 1274 deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node); 1275 } 1276 else { 1277 statements = appendExportsOfVariableStatement(statements, node); 1278 } 1279 1280 return singleOrMany(statements); 1281 } 1282 1283 function createAllExportExpressions(name: Identifier, value: Expression, location?: TextRange) { 1284 const exportedNames = getExports(name); 1285 if (exportedNames) { 1286 // For each additional export of the declaration, apply an export assignment. 1287 let expression: Expression = isExportName(name) ? value : factory.createAssignment(name, value); 1288 for (const exportName of exportedNames) { 1289 // Mark the node to prevent triggering substitution. 1290 setEmitFlags(expression, EmitFlags.NoSubstitution); 1291 expression = createExportExpression(exportName, expression, /*location*/ location); 1292 } 1293 1294 return expression; 1295 } 1296 return factory.createAssignment(name, value); 1297 } 1298 1299 /** 1300 * Transforms an exported variable with an initializer into an expression. 1301 * 1302 * @param node The node to transform. 1303 */ 1304 function transformInitializedVariable(node: InitializedVariableDeclaration): Expression { 1305 if (isBindingPattern(node.name)) { 1306 return flattenDestructuringAssignment( 1307 visitNode(node, moduleExpressionElementVisitor), 1308 /*visitor*/ undefined, 1309 context, 1310 FlattenLevel.All, 1311 /*needsValue*/ false, 1312 createAllExportExpressions 1313 ); 1314 } 1315 else { 1316 return factory.createAssignment( 1317 setTextRange( 1318 factory.createPropertyAccessExpression( 1319 factory.createIdentifier("exports"), 1320 node.name 1321 ), 1322 /*location*/ node.name 1323 ), 1324 node.initializer ? visitNode(node.initializer, moduleExpressionElementVisitor) : factory.createVoidZero() 1325 ); 1326 } 1327 } 1328 1329 /** 1330 * Visits a MergeDeclarationMarker used as a placeholder for the beginning of a merged 1331 * and transformed declaration. 1332 * 1333 * @param node The node to visit. 1334 */ 1335 function visitMergeDeclarationMarker(node: MergeDeclarationMarker): VisitResult<Statement> { 1336 // For an EnumDeclaration or ModuleDeclaration that merges with a preceeding 1337 // declaration we do not emit a leading variable declaration. To preserve the 1338 // begin/end semantics of the declararation and to properly handle exports 1339 // we wrapped the leading variable declaration in a `MergeDeclarationMarker`. 1340 // 1341 // To balance the declaration, add the exports of the elided variable 1342 // statement. 1343 if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) { 1344 const id = getOriginalNodeId(node); 1345 deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], <VariableStatement>node.original); 1346 } 1347 1348 return node; 1349 } 1350 1351 /** 1352 * Determines whether a node has an associated EndOfDeclarationMarker. 1353 * 1354 * @param node The node to test. 1355 */ 1356 function hasAssociatedEndOfDeclarationMarker(node: Node) { 1357 return (getEmitFlags(node) & EmitFlags.HasEndOfDeclarationMarker) !== 0; 1358 } 1359 1360 /** 1361 * Visits a DeclarationMarker used as a placeholder for the end of a transformed 1362 * declaration. 1363 * 1364 * @param node The node to visit. 1365 */ 1366 function visitEndOfDeclarationMarker(node: EndOfDeclarationMarker): VisitResult<Statement> { 1367 // For some transformations we emit an `EndOfDeclarationMarker` to mark the actual 1368 // end of the transformed declaration. We use this marker to emit any deferred exports 1369 // of the declaration. 1370 const id = getOriginalNodeId(node); 1371 const statements = deferredExports[id]; 1372 if (statements) { 1373 delete deferredExports[id]; 1374 return append(statements, node); 1375 } 1376 1377 return node; 1378 } 1379 1380 /** 1381 * Appends the exports of an ImportDeclaration to a statement list, returning the 1382 * statement list. 1383 * 1384 * @param statements A statement list to which the down-level export statements are to be 1385 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1386 * appended. 1387 * @param decl The declaration whose exports are to be recorded. 1388 */ 1389 function appendExportsOfImportDeclaration(statements: Statement[] | undefined, decl: ImportDeclaration): Statement[] | undefined { 1390 if (currentModuleInfo.exportEquals) { 1391 return statements; 1392 } 1393 1394 const importClause = decl.importClause; 1395 if (!importClause) { 1396 return statements; 1397 } 1398 1399 if (importClause.name) { 1400 statements = appendExportsOfDeclaration(statements, importClause); 1401 } 1402 1403 const namedBindings = importClause.namedBindings; 1404 if (namedBindings) { 1405 switch (namedBindings.kind) { 1406 case SyntaxKind.NamespaceImport: 1407 statements = appendExportsOfDeclaration(statements, namedBindings); 1408 break; 1409 1410 case SyntaxKind.NamedImports: 1411 for (const importBinding of namedBindings.elements) { 1412 statements = appendExportsOfDeclaration(statements, importBinding, /* liveBinding */ true); 1413 } 1414 1415 break; 1416 } 1417 } 1418 1419 return statements; 1420 } 1421 1422 /** 1423 * Appends the exports of an ImportEqualsDeclaration to a statement list, returning the 1424 * statement list. 1425 * 1426 * @param statements A statement list to which the down-level export statements are to be 1427 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1428 * appended. 1429 * @param decl The declaration whose exports are to be recorded. 1430 */ 1431 function appendExportsOfImportEqualsDeclaration(statements: Statement[] | undefined, decl: ImportEqualsDeclaration): Statement[] | undefined { 1432 if (currentModuleInfo.exportEquals) { 1433 return statements; 1434 } 1435 1436 return appendExportsOfDeclaration(statements, decl); 1437 } 1438 1439 /** 1440 * Appends the exports of a VariableStatement to a statement list, returning the statement 1441 * list. 1442 * 1443 * @param statements A statement list to which the down-level export statements are to be 1444 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1445 * appended. 1446 * @param node The VariableStatement whose exports are to be recorded. 1447 */ 1448 function appendExportsOfVariableStatement(statements: Statement[] | undefined, node: VariableStatement): Statement[] | undefined { 1449 if (currentModuleInfo.exportEquals) { 1450 return statements; 1451 } 1452 1453 for (const decl of node.declarationList.declarations) { 1454 statements = appendExportsOfBindingElement(statements, decl); 1455 } 1456 1457 return statements; 1458 } 1459 1460 /** 1461 * Appends the exports of a VariableDeclaration or BindingElement to a statement list, 1462 * returning the statement list. 1463 * 1464 * @param statements A statement list to which the down-level export statements are to be 1465 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1466 * appended. 1467 * @param decl The declaration whose exports are to be recorded. 1468 */ 1469 function appendExportsOfBindingElement(statements: Statement[] | undefined, decl: VariableDeclaration | BindingElement): Statement[] | undefined { 1470 if (currentModuleInfo.exportEquals) { 1471 return statements; 1472 } 1473 1474 if (isBindingPattern(decl.name)) { 1475 for (const element of decl.name.elements) { 1476 if (!isOmittedExpression(element)) { 1477 statements = appendExportsOfBindingElement(statements, element); 1478 } 1479 } 1480 } 1481 else if (!isGeneratedIdentifier(decl.name)) { 1482 statements = appendExportsOfDeclaration(statements, decl); 1483 } 1484 1485 return statements; 1486 } 1487 1488 /** 1489 * Appends the exports of a ClassDeclaration or FunctionDeclaration to a statement list, 1490 * returning the statement list. 1491 * 1492 * @param statements A statement list to which the down-level export statements are to be 1493 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1494 * appended. 1495 * @param decl The declaration whose exports are to be recorded. 1496 */ 1497 function appendExportsOfHoistedDeclaration(statements: Statement[] | undefined, decl: ClassDeclaration | FunctionDeclaration): Statement[] | undefined { 1498 if (currentModuleInfo.exportEquals) { 1499 return statements; 1500 } 1501 1502 if (hasSyntacticModifier(decl, ModifierFlags.Export)) { 1503 const exportName = hasSyntacticModifier(decl, ModifierFlags.Default) ? factory.createIdentifier("default") : factory.getDeclarationName(decl); 1504 statements = appendExportStatement(statements, exportName, factory.getLocalName(decl), /*location*/ decl); 1505 } 1506 1507 if (decl.name) { 1508 statements = appendExportsOfDeclaration(statements, decl); 1509 } 1510 1511 return statements; 1512 } 1513 1514 /** 1515 * Appends the exports of a declaration to a statement list, returning the statement list. 1516 * 1517 * @param statements A statement list to which the down-level export statements are to be 1518 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1519 * appended. 1520 * @param decl The declaration to export. 1521 */ 1522 function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration, liveBinding?: boolean): Statement[] | undefined { 1523 const name = factory.getDeclarationName(decl); 1524 const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(idText(name)); 1525 if (exportSpecifiers) { 1526 for (const exportSpecifier of exportSpecifiers) { 1527 statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name, /* allowComments */ undefined, liveBinding); 1528 } 1529 } 1530 return statements; 1531 } 1532 1533 /** 1534 * Appends the down-level representation of an export to a statement list, returning the 1535 * statement list. 1536 * 1537 * @param statements A statement list to which the down-level export statements are to be 1538 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1539 * appended. 1540 * @param exportName The name of the export. 1541 * @param expression The expression to export. 1542 * @param location The location to use for source maps and comments for the export. 1543 * @param allowComments Whether to allow comments on the export. 1544 */ 1545 function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier, expression: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean): Statement[] | undefined { 1546 statements = append(statements, createExportStatement(exportName, expression, location, allowComments, liveBinding)); 1547 return statements; 1548 } 1549 1550 function createUnderscoreUnderscoreESModule() { 1551 let statement: Statement; 1552 if (languageVersion === ScriptTarget.ES3) { 1553 statement = factory.createExpressionStatement( 1554 createExportExpression( 1555 factory.createIdentifier("__esModule"), 1556 factory.createTrue() 1557 ) 1558 ); 1559 } 1560 else { 1561 statement = factory.createExpressionStatement( 1562 factory.createCallExpression( 1563 factory.createPropertyAccessExpression(factory.createIdentifier("Object"), "defineProperty"), 1564 /*typeArguments*/ undefined, 1565 [ 1566 factory.createIdentifier("exports"), 1567 factory.createStringLiteral("__esModule"), 1568 factory.createObjectLiteralExpression([ 1569 factory.createPropertyAssignment("value", factory.createTrue()) 1570 ]) 1571 ] 1572 ) 1573 ); 1574 } 1575 setEmitFlags(statement, EmitFlags.CustomPrologue); 1576 return statement; 1577 } 1578 1579 /** 1580 * Creates a call to the current file's export function to export a value. 1581 * 1582 * @param name The bound name of the export. 1583 * @param value The exported value. 1584 * @param location The location to use for source maps and comments for the export. 1585 * @param allowComments An optional value indicating whether to emit comments for the statement. 1586 */ 1587 function createExportStatement(name: Identifier, value: Expression, location?: TextRange, allowComments?: boolean, liveBinding?: boolean) { 1588 const statement = setTextRange(factory.createExpressionStatement(createExportExpression(name, value, /* location */ undefined, liveBinding)), location); 1589 startOnNewLine(statement); 1590 if (!allowComments) { 1591 setEmitFlags(statement, EmitFlags.NoComments); 1592 } 1593 1594 return statement; 1595 } 1596 1597 /** 1598 * Creates a call to the current file's export function to export a value. 1599 * 1600 * @param name The bound name of the export. 1601 * @param value The exported value. 1602 * @param location The location to use for source maps and comments for the export. 1603 */ 1604 function createExportExpression(name: Identifier, value: Expression, location?: TextRange, liveBinding?: boolean) { 1605 return setTextRange( 1606 liveBinding && languageVersion !== ScriptTarget.ES3 ? factory.createCallExpression( 1607 factory.createPropertyAccessExpression( 1608 factory.createIdentifier("Object"), 1609 "defineProperty" 1610 ), 1611 /*typeArguments*/ undefined, 1612 [ 1613 factory.createIdentifier("exports"), 1614 factory.createStringLiteralFromNode(name), 1615 factory.createObjectLiteralExpression([ 1616 factory.createPropertyAssignment("enumerable", factory.createTrue()), 1617 factory.createPropertyAssignment("get", factory.createFunctionExpression( 1618 /*modifiers*/ undefined, 1619 /*asteriskToken*/ undefined, 1620 /*name*/ undefined, 1621 /*typeParameters*/ undefined, 1622 /*parameters*/ [], 1623 /*type*/ undefined, 1624 factory.createBlock([factory.createReturnStatement(value)]) 1625 )) 1626 ]) 1627 ] 1628 ) : factory.createAssignment( 1629 factory.createPropertyAccessExpression( 1630 factory.createIdentifier("exports"), 1631 factory.cloneNode(name) 1632 ), 1633 value 1634 ), 1635 location 1636 ); 1637 } 1638 1639 // 1640 // Modifier Visitors 1641 // 1642 1643 /** 1644 * Visit nodes to elide module-specific modifiers. 1645 * 1646 * @param node The node to visit. 1647 */ 1648 function modifierVisitor(node: Node): VisitResult<Node> { 1649 // Elide module-specific modifiers. 1650 switch (node.kind) { 1651 case SyntaxKind.ExportKeyword: 1652 case SyntaxKind.DefaultKeyword: 1653 return undefined; 1654 } 1655 1656 return node; 1657 } 1658 1659 // 1660 // Emit Notification 1661 // 1662 1663 /** 1664 * Hook for node emit notifications. 1665 * 1666 * @param hint A hint as to the intended usage of the node. 1667 * @param node The node to emit. 1668 * @param emit A callback used to emit the node in the printer. 1669 */ 1670 function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { 1671 if (node.kind === SyntaxKind.SourceFile) { 1672 currentSourceFile = <SourceFile>node; 1673 currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; 1674 noSubstitution = []; 1675 1676 previousOnEmitNode(hint, node, emitCallback); 1677 1678 currentSourceFile = undefined!; 1679 currentModuleInfo = undefined!; 1680 noSubstitution = undefined!; 1681 } 1682 else { 1683 previousOnEmitNode(hint, node, emitCallback); 1684 } 1685 } 1686 1687 // 1688 // Substitutions 1689 // 1690 1691 /** 1692 * Hooks node substitutions. 1693 * 1694 * @param hint A hint as to the intended usage of the node. 1695 * @param node The node to substitute. 1696 */ 1697 function onSubstituteNode(hint: EmitHint, node: Node) { 1698 node = previousOnSubstituteNode(hint, node); 1699 if (node.id && noSubstitution[node.id]) { 1700 return node; 1701 } 1702 1703 if (hint === EmitHint.Expression) { 1704 return substituteExpression(<Expression>node); 1705 } 1706 else if (isShorthandPropertyAssignment(node)) { 1707 return substituteShorthandPropertyAssignment(node); 1708 } 1709 1710 return node; 1711 } 1712 1713 /** 1714 * Substitution for a ShorthandPropertyAssignment whose declaration name is an imported 1715 * or exported symbol. 1716 * 1717 * @param node The node to substitute. 1718 */ 1719 function substituteShorthandPropertyAssignment(node: ShorthandPropertyAssignment): ObjectLiteralElementLike { 1720 const name = node.name; 1721 const exportedOrImportedName = substituteExpressionIdentifier(name); 1722 if (exportedOrImportedName !== name) { 1723 // A shorthand property with an assignment initializer is probably part of a 1724 // destructuring assignment 1725 if (node.objectAssignmentInitializer) { 1726 const initializer = factory.createAssignment(exportedOrImportedName, node.objectAssignmentInitializer); 1727 return setTextRange(factory.createPropertyAssignment(name, initializer), node); 1728 } 1729 return setTextRange(factory.createPropertyAssignment(name, exportedOrImportedName), node); 1730 } 1731 return node; 1732 } 1733 1734 /** 1735 * Substitution for an Expression that may contain an imported or exported symbol. 1736 * 1737 * @param node The node to substitute. 1738 */ 1739 function substituteExpression(node: Expression) { 1740 switch (node.kind) { 1741 case SyntaxKind.Identifier: 1742 return substituteExpressionIdentifier(<Identifier>node); 1743 case SyntaxKind.BinaryExpression: 1744 return substituteBinaryExpression(<BinaryExpression>node); 1745 case SyntaxKind.PostfixUnaryExpression: 1746 case SyntaxKind.PrefixUnaryExpression: 1747 return substituteUnaryExpression(<PrefixUnaryExpression | PostfixUnaryExpression>node); 1748 } 1749 1750 return node; 1751 } 1752 1753 /** 1754 * Substitution for an Identifier expression that may contain an imported or exported 1755 * symbol. 1756 * 1757 * @param node The node to substitute. 1758 */ 1759 function substituteExpressionIdentifier(node: Identifier): Expression { 1760 if (getEmitFlags(node) & EmitFlags.HelperName) { 1761 const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile); 1762 if (externalHelpersModuleName) { 1763 return factory.createPropertyAccessExpression(externalHelpersModuleName, node); 1764 } 1765 1766 return node; 1767 } 1768 1769 if (!(isGeneratedIdentifier(node) && !(node.autoGenerateFlags & GeneratedIdentifierFlags.AllowNameSubstitution)) && !isLocalName(node)) { 1770 const exportContainer = resolver.getReferencedExportContainer(node, isExportName(node)); 1771 if (exportContainer && exportContainer.kind === SyntaxKind.SourceFile) { 1772 return setTextRange( 1773 factory.createPropertyAccessExpression( 1774 factory.createIdentifier("exports"), 1775 factory.cloneNode(node) 1776 ), 1777 /*location*/ node 1778 ); 1779 } 1780 1781 const importDeclaration = resolver.getReferencedImportDeclaration(node); 1782 if (importDeclaration) { 1783 if (isImportClause(importDeclaration)) { 1784 return setTextRange( 1785 factory.createPropertyAccessExpression( 1786 factory.getGeneratedNameForNode(importDeclaration.parent), 1787 factory.createIdentifier("default") 1788 ), 1789 /*location*/ node 1790 ); 1791 } 1792 else if (isImportSpecifier(importDeclaration)) { 1793 const name = importDeclaration.propertyName || importDeclaration.name; 1794 return setTextRange( 1795 factory.createPropertyAccessExpression( 1796 factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), 1797 factory.cloneNode(name) 1798 ), 1799 /*location*/ node 1800 ); 1801 } 1802 } 1803 } 1804 return node; 1805 } 1806 1807 /** 1808 * Substitution for a BinaryExpression that may contain an imported or exported symbol. 1809 * 1810 * @param node The node to substitute. 1811 */ 1812 function substituteBinaryExpression(node: BinaryExpression): Expression { 1813 // When we see an assignment expression whose left-hand side is an exported symbol, 1814 // we should ensure all exports of that symbol are updated with the correct value. 1815 // 1816 // - We do not substitute generated identifiers for any reason. 1817 // - We do not substitute identifiers tagged with the LocalName flag. 1818 // - We do not substitute identifiers that were originally the name of an enum or 1819 // namespace due to how they are transformed in TypeScript. 1820 // - We only substitute identifiers that are exported at the top level. 1821 if (isAssignmentOperator(node.operatorToken.kind) 1822 && isIdentifier(node.left) 1823 && !isGeneratedIdentifier(node.left) 1824 && !isLocalName(node.left) 1825 && !isDeclarationNameOfEnumOrNamespace(node.left)) { 1826 const exportedNames = getExports(node.left); 1827 if (exportedNames) { 1828 // For each additional export of the declaration, apply an export assignment. 1829 let expression: Expression = node; 1830 for (const exportName of exportedNames) { 1831 // Mark the node to prevent triggering this rule again. 1832 noSubstitution[getNodeId(expression)] = true; 1833 expression = createExportExpression(exportName, expression, /*location*/ node); 1834 } 1835 1836 return expression; 1837 } 1838 } 1839 1840 return node; 1841 } 1842 1843 /** 1844 * Substitution for a UnaryExpression that may contain an imported or exported symbol. 1845 * 1846 * @param node The node to substitute. 1847 */ 1848 function substituteUnaryExpression(node: PrefixUnaryExpression | PostfixUnaryExpression): Expression { 1849 // When we see a prefix or postfix increment expression whose operand is an exported 1850 // symbol, we should ensure all exports of that symbol are updated with the correct 1851 // value. 1852 // 1853 // - We do not substitute generated identifiers for any reason. 1854 // - We do not substitute identifiers tagged with the LocalName flag. 1855 // - We do not substitute identifiers that were originally the name of an enum or 1856 // namespace due to how they are transformed in TypeScript. 1857 // - We only substitute identifiers that are exported at the top level. 1858 if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) 1859 && isIdentifier(node.operand) 1860 && !isGeneratedIdentifier(node.operand) 1861 && !isLocalName(node.operand) 1862 && !isDeclarationNameOfEnumOrNamespace(node.operand)) { 1863 const exportedNames = getExports(node.operand); 1864 if (exportedNames) { 1865 let expression: Expression = node.kind === SyntaxKind.PostfixUnaryExpression 1866 ? setTextRange( 1867 factory.createBinaryExpression( 1868 node.operand, 1869 factory.createToken(node.operator === SyntaxKind.PlusPlusToken ? SyntaxKind.PlusEqualsToken : SyntaxKind.MinusEqualsToken), 1870 factory.createNumericLiteral(1) 1871 ), 1872 /*location*/ node) 1873 : node; 1874 for (const exportName of exportedNames) { 1875 // Mark the node to prevent triggering this rule again. 1876 noSubstitution[getNodeId(expression)] = true; 1877 expression = factory.createParenthesizedExpression(createExportExpression(exportName, expression)); 1878 } 1879 return expression; 1880 } 1881 } 1882 1883 return node; 1884 } 1885 1886 /** 1887 * Gets the additional exports of a name. 1888 * 1889 * @param name The name. 1890 */ 1891 function getExports(name: Identifier): Identifier[] | undefined { 1892 if (!isGeneratedIdentifier(name)) { 1893 const valueDeclaration = resolver.getReferencedImportDeclaration(name) 1894 || resolver.getReferencedValueDeclaration(name); 1895 if (valueDeclaration) { 1896 return currentModuleInfo 1897 && currentModuleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]; 1898 } 1899 } 1900 } 1901 } 1902 1903 // emit helper for dynamic import 1904 const dynamicImportUMDHelper: EmitHelper = { 1905 name: "typescript:dynamicimport-sync-require", 1906 scoped: true, 1907 text: ` 1908 var __syncRequire = typeof module === "object" && typeof module.exports === "object";` 1909 }; 1910} 1911