1import { 2 addRange, append, BinaryExpression, BindingElement, Block, Bundle, CaseBlock, CaseClause, CaseOrDefaultClause, CatchClause, 3 chainBundle, ClassDeclaration, collectExternalModuleInfo, Debug, Declaration, DefaultClause, 4 DestructuringAssignment, DoStatement, EmitFlags, EmitHint, EndOfDeclarationMarker, ExportAssignment, 5 ExportDeclaration, Expression, ExpressionStatement, ExternalModuleInfo, firstOrUndefined, 6 flattenDestructuringAssignment, FlattenLevel, forEach, ForInitializer, ForInStatement, ForOfStatement, ForStatement, 7 FunctionDeclaration, getEmitFlags, getExternalHelpersModuleName, getExternalModuleNameLiteral, 8 getLocalNameForExternalImport, getNodeId, getOriginalNode, getOriginalNodeId, getStrictOptionValue, 9 getTextOfIdentifierOrLiteral, hasSyntacticModifier, Identifier, idText, ImportCall, ImportDeclaration, 10 ImportEqualsDeclaration, insertStatementsAfterStandardPrologue, isArrayLiteralExpression, isAssignmentExpression, 11 isAssignmentOperator, isBindingPattern, isBlock, isCaseBlock, isCaseOrDefaultClause, isClassElement, 12 isDeclarationNameOfEnumOrNamespace, isDestructuringAssignment, isEffectiveExternalModule, isExpression, 13 isExternalModule, isExternalModuleImportEqualsDeclaration, isForInitializer, isGeneratedIdentifier, 14 isHeritageClause, isIdentifier, isImportCall, isImportClause, isImportMeta, isImportSpecifier, isLocalName, 15 isModifierLike, isModuleOrEnumDeclaration, isNamedExports, isObjectLiteralExpression, isOmittedExpression, 16 isParameterDeclaration, isPrefixUnaryExpression, isPropertyAssignment, isShorthandPropertyAssignment, 17 isSpreadElement, isStatement, isStringLiteral, isVariableDeclarationList, LabeledStatement, map, Map, 18 MergeDeclarationMarker, MetaProperty, ModifierFlags, moveEmitHelpers, Node, NodeFlags, ObjectLiteralElementLike, 19 outFile, ParenthesizedExpression, PartiallyEmittedExpression, PostfixUnaryExpression, PrefixUnaryExpression, 20 PropertyAssignment, setCommentRange, setEmitFlags, setTextRange, ShorthandPropertyAssignment, singleOrMany, some, 21 SourceFile, startOnNewLine, Statement, StringLiteral, SwitchStatement, SyntaxKind, TextRange, TransformationContext, 22 TransformFlags, tryGetModuleNameFromFile, TryStatement, VariableDeclaration, VariableDeclarationList, 23 VariableStatement, visitEachChild, visitIterationBody, visitNode, visitNodes, VisitResult, WhileStatement, 24 WithStatement, 25} from "../../_namespaces/ts"; 26 27/** @internal */ 28export function transformSystemModule(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { 29 interface DependencyGroup { 30 name: StringLiteral; 31 externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; 32 } 33 34 const { 35 factory, 36 startLexicalEnvironment, 37 endLexicalEnvironment, 38 hoistVariableDeclaration 39 } = context; 40 41 const compilerOptions = context.getCompilerOptions(); 42 const resolver = context.getEmitResolver(); 43 const host = context.getEmitHost(); 44 const previousOnSubstituteNode = context.onSubstituteNode; 45 const previousOnEmitNode = context.onEmitNode; 46 context.onSubstituteNode = onSubstituteNode; 47 context.onEmitNode = onEmitNode; 48 context.enableSubstitution(SyntaxKind.Identifier); // Substitutes expression identifiers for imported symbols. 49 context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes expression identifiers for imported symbols 50 context.enableSubstitution(SyntaxKind.BinaryExpression); // Substitutes assignments to exported symbols. 51 context.enableSubstitution(SyntaxKind.MetaProperty); // Substitutes 'import.meta' 52 context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. 53 54 const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file. 55 const deferredExports: (Statement[] | undefined)[] = []; // Exports to defer until an EndOfDeclarationMarker is found. 56 const exportFunctionsMap: Identifier[] = []; // The export function associated with a source file. 57 const noSubstitutionMap: boolean[][] = []; // Set of nodes for which substitution rules should be ignored for each file. 58 const contextObjectMap: Identifier[] = []; // The context object associated with a source file. 59 60 let currentSourceFile: SourceFile; // The current file. 61 let moduleInfo: ExternalModuleInfo; // ExternalModuleInfo for the current file. 62 let exportFunction: Identifier; // The export function for the current file. 63 let contextObject: Identifier; // The context object for the current file. 64 let hoistedStatements: Statement[] | undefined; 65 let enclosingBlockScopedContainer: Node; 66 let noSubstitution: boolean[] | undefined; // Set of nodes for which substitution rules should be ignored. 67 68 return chainBundle(context, transformSourceFile); 69 70 /** 71 * Transforms the module aspects of a SourceFile. 72 * 73 * @param node The SourceFile node. 74 */ 75 function transformSourceFile(node: SourceFile) { 76 if (node.isDeclarationFile || !(isEffectiveExternalModule(node, compilerOptions) || node.transformFlags & TransformFlags.ContainsDynamicImport)) { 77 return node; 78 } 79 80 const id = getOriginalNodeId(node); 81 currentSourceFile = node; 82 enclosingBlockScopedContainer = node; 83 84 // System modules have the following shape: 85 // 86 // System.register(['dep-1', ... 'dep-n'], function(exports) {/* module body function */}) 87 // 88 // The parameter 'exports' here is a callback '<T>(name: string, value: T) => T' that 89 // is used to publish exported values. 'exports' returns its 'value' argument so in 90 // most cases expressions that mutate exported values can be rewritten as: 91 // 92 // expr -> exports('name', expr) 93 // 94 // The only exception in this rule is postfix unary operators, 95 // see comment to 'substitutePostfixUnaryExpression' for more details 96 97 // Collect information about the external module and dependency groups. 98 moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(context, node, resolver, compilerOptions); 99 100 // Make sure that the name of the 'exports' function does not conflict with 101 // existing identifiers. 102 exportFunction = factory.createUniqueName("exports"); 103 exportFunctionsMap[id] = exportFunction; 104 contextObject = contextObjectMap[id] = factory.createUniqueName("context"); 105 106 // Add the body of the module. 107 const dependencyGroups = collectDependencyGroups(moduleInfo.externalImports); 108 const moduleBodyBlock = createSystemModuleBody(node, dependencyGroups); 109 const moduleBodyFunction = factory.createFunctionExpression( 110 /*modifiers*/ undefined, 111 /*asteriskToken*/ undefined, 112 /*name*/ undefined, 113 /*typeParameters*/ undefined, 114 [ 115 factory.createParameterDeclaration(/*modifiers*/ undefined, /*dotDotDotToken*/ undefined, exportFunction), 116 factory.createParameterDeclaration(/*modifiers*/ undefined, /*dotDotDotToken*/ undefined, contextObject) 117 ], 118 /*type*/ undefined, 119 moduleBodyBlock 120 ); 121 122 // Write the call to `System.register` 123 // Clear the emit-helpers flag for later passes since we'll have already used it in the module body 124 // So the helper will be emit at the correct position instead of at the top of the source-file 125 const moduleName = tryGetModuleNameFromFile(factory, node, host, compilerOptions); 126 const dependencies = factory.createArrayLiteralExpression(map(dependencyGroups, dependencyGroup => dependencyGroup.name)); 127 const updated = setEmitFlags( 128 factory.updateSourceFile( 129 node, 130 setTextRange( 131 factory.createNodeArray([ 132 factory.createExpressionStatement( 133 factory.createCallExpression( 134 factory.createPropertyAccessExpression(factory.createIdentifier("System"), "register"), 135 /*typeArguments*/ undefined, 136 moduleName 137 ? [moduleName, dependencies, moduleBodyFunction] 138 : [dependencies, moduleBodyFunction] 139 ) 140 ) 141 ]), 142 node.statements 143 ) 144 ), EmitFlags.NoTrailingComments); 145 146 if (!outFile(compilerOptions)) { 147 moveEmitHelpers(updated, moduleBodyBlock, helper => !helper.scoped); 148 } 149 150 if (noSubstitution) { 151 noSubstitutionMap[id] = noSubstitution; 152 noSubstitution = undefined; 153 } 154 155 currentSourceFile = undefined!; 156 moduleInfo = undefined!; 157 exportFunction = undefined!; 158 contextObject = undefined!; 159 hoistedStatements = undefined; 160 enclosingBlockScopedContainer = undefined!; 161 return updated; 162 } 163 164 /** 165 * Collects the dependency groups for this files imports. 166 * 167 * @param externalImports The imports for the file. 168 */ 169 function collectDependencyGroups(externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]) { 170 const groupIndices = new Map<string, number>(); 171 const dependencyGroups: DependencyGroup[] = []; 172 for (const externalImport of externalImports) { 173 const externalModuleName = getExternalModuleNameLiteral(factory, externalImport, currentSourceFile, host, resolver, compilerOptions); 174 if (externalModuleName) { 175 const text = externalModuleName.text; 176 const groupIndex = groupIndices.get(text); 177 if (groupIndex !== undefined) { 178 // deduplicate/group entries in dependency list by the dependency name 179 dependencyGroups[groupIndex].externalImports.push(externalImport); 180 } 181 else { 182 groupIndices.set(text, dependencyGroups.length); 183 dependencyGroups.push({ 184 name: externalModuleName, 185 externalImports: [externalImport] 186 }); 187 } 188 } 189 } 190 191 return dependencyGroups; 192 } 193 194 /** 195 * Adds the statements for the module body function for the source file. 196 * 197 * @param node The source file for the module. 198 * @param dependencyGroups The grouped dependencies of the module. 199 */ 200 function createSystemModuleBody(node: SourceFile, dependencyGroups: DependencyGroup[]) { 201 // Shape of the body in system modules: 202 // 203 // function (exports) { 204 // <list of local aliases for imports> 205 // <hoisted variable declarations> 206 // <hoisted function declarations> 207 // return { 208 // setters: [ 209 // <list of setter function for imports> 210 // ], 211 // execute: function() { 212 // <module statements> 213 // } 214 // } 215 // <temp declarations> 216 // } 217 // 218 // i.e: 219 // 220 // import {x} from 'file1' 221 // var y = 1; 222 // export function foo() { return y + x(); } 223 // console.log(y); 224 // 225 // Will be transformed to: 226 // 227 // function(exports) { 228 // function foo() { return y + file_1.x(); } 229 // exports("foo", foo); 230 // var file_1, y; 231 // return { 232 // setters: [ 233 // function(v) { file_1 = v } 234 // ], 235 // execute(): function() { 236 // y = 1; 237 // console.log(y); 238 // } 239 // }; 240 // } 241 242 const statements: Statement[] = []; 243 244 // We start a new lexical environment in this function body, but *not* in the 245 // body of the execute function. This allows us to emit temporary declarations 246 // only in the outer module body and not in the inner one. 247 startLexicalEnvironment(); 248 249 // Add any prologue directives. 250 const ensureUseStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") || (!compilerOptions.noImplicitUseStrict && isExternalModule(currentSourceFile)); 251 const statementOffset = factory.copyPrologue(node.statements, statements, ensureUseStrict, topLevelVisitor); 252 253 // var __moduleName = context_1 && context_1.id; 254 statements.push( 255 factory.createVariableStatement( 256 /*modifiers*/ undefined, 257 factory.createVariableDeclarationList([ 258 factory.createVariableDeclaration( 259 "__moduleName", 260 /*exclamationToken*/ undefined, 261 /*type*/ undefined, 262 factory.createLogicalAnd( 263 contextObject, 264 factory.createPropertyAccessExpression(contextObject, "id") 265 ) 266 ) 267 ]) 268 ) 269 ); 270 271 // Visit the synthetic external helpers import declaration if present 272 visitNode(moduleInfo.externalHelpersImportDeclaration, topLevelVisitor, isStatement); 273 274 // Visit the statements of the source file, emitting any transformations into 275 // the `executeStatements` array. We do this *before* we fill the `setters` array 276 // as we both emit transformations as well as aggregate some data used when creating 277 // setters. This allows us to reduce the number of times we need to loop through the 278 // statements of the source file. 279 const executeStatements = visitNodes(node.statements, topLevelVisitor, isStatement, statementOffset); 280 281 // Emit early exports for function declarations. 282 addRange(statements, hoistedStatements); 283 284 // We emit hoisted variables early to align roughly with our previous emit output. 285 // Two key differences in this approach are: 286 // - Temporary variables will appear at the top rather than at the bottom of the file 287 insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment()); 288 289 const exportStarFunction = addExportStarIfNeeded(statements)!; // TODO: GH#18217 290 const modifiers = node.transformFlags & TransformFlags.ContainsAwait ? 291 factory.createModifiersFromModifierFlags(ModifierFlags.Async) : 292 undefined; 293 const moduleObject = factory.createObjectLiteralExpression([ 294 factory.createPropertyAssignment("setters", 295 createSettersArray(exportStarFunction, dependencyGroups) 296 ), 297 factory.createPropertyAssignment("execute", 298 factory.createFunctionExpression( 299 modifiers, 300 /*asteriskToken*/ undefined, 301 /*name*/ undefined, 302 /*typeParameters*/ undefined, 303 /*parameters*/ [], 304 /*type*/ undefined, 305 factory.createBlock(executeStatements, /*multiLine*/ true) 306 ) 307 ) 308 ], /*multiLine*/ true); 309 310 statements.push(factory.createReturnStatement(moduleObject)); 311 return factory.createBlock(statements, /*multiLine*/ true); 312 } 313 314 /** 315 * Adds an exportStar function to a statement list if it is needed for the file. 316 * 317 * @param statements A statement list. 318 */ 319 function addExportStarIfNeeded(statements: Statement[]) { 320 if (!moduleInfo.hasExportStarsToExportValues) { 321 return; 322 } 323 324 // when resolving exports local exported entries/indirect exported entries in the module 325 // should always win over entries with similar names that were added via star exports 326 // to support this we store names of local/indirect exported entries in a set. 327 // this set is used to filter names brought by star expors. 328 329 // local names set should only be added if we have anything exported 330 if (!moduleInfo.exportedNames && moduleInfo.exportSpecifiers.size === 0) { 331 // no exported declarations (export var ...) or export specifiers (export {x}) 332 // check if we have any non star export declarations. 333 let hasExportDeclarationWithExportClause = false; 334 for (const externalImport of moduleInfo.externalImports) { 335 if (externalImport.kind === SyntaxKind.ExportDeclaration && externalImport.exportClause) { 336 hasExportDeclarationWithExportClause = true; 337 break; 338 } 339 } 340 341 if (!hasExportDeclarationWithExportClause) { 342 // we still need to emit exportStar helper 343 const exportStarFunction = createExportStarFunction(/*localNames*/ undefined); 344 statements.push(exportStarFunction); 345 return exportStarFunction.name; 346 } 347 } 348 349 const exportedNames: ObjectLiteralElementLike[] = []; 350 if (moduleInfo.exportedNames) { 351 for (const exportedLocalName of moduleInfo.exportedNames) { 352 if (exportedLocalName.escapedText === "default") { 353 continue; 354 } 355 356 // write name of exported declaration, i.e 'export var x...' 357 exportedNames.push( 358 factory.createPropertyAssignment( 359 factory.createStringLiteralFromNode(exportedLocalName), 360 factory.createTrue() 361 ) 362 ); 363 } 364 } 365 366 const exportedNamesStorageRef = factory.createUniqueName("exportedNames"); 367 statements.push( 368 factory.createVariableStatement( 369 /*modifiers*/ undefined, 370 factory.createVariableDeclarationList([ 371 factory.createVariableDeclaration( 372 exportedNamesStorageRef, 373 /*exclamationToken*/ undefined, 374 /*type*/ undefined, 375 factory.createObjectLiteralExpression(exportedNames, /*multiline*/ true) 376 ) 377 ]) 378 ) 379 ); 380 381 const exportStarFunction = createExportStarFunction(exportedNamesStorageRef); 382 statements.push(exportStarFunction); 383 return exportStarFunction.name; 384 } 385 386 /** 387 * Creates an exportStar function for the file, with an optional set of excluded local 388 * names. 389 * 390 * @param localNames An optional reference to an object containing a set of excluded local 391 * names. 392 */ 393 function createExportStarFunction(localNames: Identifier | undefined) { 394 const exportStarFunction = factory.createUniqueName("exportStar"); 395 const m = factory.createIdentifier("m"); 396 const n = factory.createIdentifier("n"); 397 const exports = factory.createIdentifier("exports"); 398 let condition: Expression = factory.createStrictInequality(n, factory.createStringLiteral("default")); 399 if (localNames) { 400 condition = factory.createLogicalAnd( 401 condition, 402 factory.createLogicalNot( 403 factory.createCallExpression( 404 factory.createPropertyAccessExpression(localNames, "hasOwnProperty"), 405 /*typeArguments*/ undefined, 406 [n] 407 ) 408 ) 409 ); 410 } 411 412 return factory.createFunctionDeclaration( 413 /*modifiers*/ undefined, 414 /*asteriskToken*/ undefined, 415 exportStarFunction, 416 /*typeParameters*/ undefined, 417 [factory.createParameterDeclaration(/*modifiers*/ undefined, /*dotDotDotToken*/ undefined, m)], 418 /*type*/ undefined, 419 factory.createBlock([ 420 factory.createVariableStatement( 421 /*modifiers*/ undefined, 422 factory.createVariableDeclarationList([ 423 factory.createVariableDeclaration( 424 exports, 425 /*exclamationToken*/ undefined, 426 /*type*/ undefined, 427 factory.createObjectLiteralExpression([]) 428 ) 429 ]) 430 ), 431 factory.createForInStatement( 432 factory.createVariableDeclarationList([ 433 factory.createVariableDeclaration(n) 434 ]), 435 m, 436 factory.createBlock([ 437 setEmitFlags( 438 factory.createIfStatement( 439 condition, 440 factory.createExpressionStatement( 441 factory.createAssignment( 442 factory.createElementAccessExpression(exports, n), 443 factory.createElementAccessExpression(m, n) 444 ) 445 ) 446 ), 447 EmitFlags.SingleLine 448 ) 449 ]) 450 ), 451 factory.createExpressionStatement( 452 factory.createCallExpression( 453 exportFunction, 454 /*typeArguments*/ undefined, 455 [exports] 456 ) 457 ) 458 ], /*multiline*/ true) 459 ); 460 } 461 462 /** 463 * Creates an array setter callbacks for each dependency group. 464 * 465 * @param exportStarFunction A reference to an exportStarFunction for the file. 466 * @param dependencyGroups An array of grouped dependencies. 467 */ 468 function createSettersArray(exportStarFunction: Identifier, dependencyGroups: DependencyGroup[]) { 469 const setters: Expression[] = []; 470 for (const group of dependencyGroups) { 471 // derive a unique name for parameter from the first named entry in the group 472 const localName = forEach(group.externalImports, i => getLocalNameForExternalImport(factory, i, currentSourceFile)); 473 const parameterName = localName ? factory.getGeneratedNameForNode(localName) : factory.createUniqueName(""); 474 const statements: Statement[] = []; 475 for (const entry of group.externalImports) { 476 const importVariableName = getLocalNameForExternalImport(factory, entry, currentSourceFile)!; // TODO: GH#18217 477 switch (entry.kind) { 478 case SyntaxKind.ImportDeclaration: 479 if (!entry.importClause) { 480 // 'import "..."' case 481 // module is imported only for side-effects, no emit required 482 break; 483 } 484 // falls through 485 486 case SyntaxKind.ImportEqualsDeclaration: 487 Debug.assert(importVariableName !== undefined); 488 // save import into the local 489 statements.push( 490 factory.createExpressionStatement( 491 factory.createAssignment(importVariableName, parameterName) 492 ) 493 ); 494 if (hasSyntacticModifier(entry, ModifierFlags.Export)) { 495 statements.push( 496 factory.createExpressionStatement( 497 factory.createCallExpression( 498 exportFunction, 499 /*typeArguments*/ undefined, 500 [ 501 factory.createStringLiteral(idText(importVariableName)), 502 parameterName, 503 ] 504 ) 505 ) 506 ); 507 } 508 break; 509 510 case SyntaxKind.ExportDeclaration: 511 Debug.assert(importVariableName !== undefined); 512 if (entry.exportClause) { 513 if (isNamedExports(entry.exportClause)) { 514 // export {a, b as c} from 'foo' 515 // 516 // emit as: 517 // 518 // exports_({ 519 // "a": _["a"], 520 // "c": _["b"] 521 // }); 522 const properties: PropertyAssignment[] = []; 523 for (const e of entry.exportClause.elements) { 524 properties.push( 525 factory.createPropertyAssignment( 526 factory.createStringLiteral(idText(e.name)), 527 factory.createElementAccessExpression( 528 parameterName, 529 factory.createStringLiteral(idText(e.propertyName || e.name)) 530 ) 531 ) 532 ); 533 } 534 535 statements.push( 536 factory.createExpressionStatement( 537 factory.createCallExpression( 538 exportFunction, 539 /*typeArguments*/ undefined, 540 [factory.createObjectLiteralExpression(properties, /*multiline*/ true)] 541 ) 542 ) 543 ); 544 } 545 else { 546 statements.push( 547 factory.createExpressionStatement( 548 factory.createCallExpression( 549 exportFunction, 550 /*typeArguments*/ undefined, 551 [ 552 factory.createStringLiteral(idText(entry.exportClause.name)), 553 parameterName 554 ] 555 ) 556 ) 557 ); 558 } 559 } 560 else { 561 // export * from 'foo' 562 // 563 // emit as: 564 // 565 // exportStar(foo_1_1); 566 statements.push( 567 factory.createExpressionStatement( 568 factory.createCallExpression( 569 exportStarFunction, 570 /*typeArguments*/ undefined, 571 [parameterName] 572 ) 573 ) 574 ); 575 } 576 break; 577 } 578 } 579 580 setters.push( 581 factory.createFunctionExpression( 582 /*modifiers*/ undefined, 583 /*asteriskToken*/ undefined, 584 /*name*/ undefined, 585 /*typeParameters*/ undefined, 586 [factory.createParameterDeclaration(/*modifiers*/ undefined, /*dotDotDotToken*/ undefined, parameterName)], 587 /*type*/ undefined, 588 factory.createBlock(statements, /*multiLine*/ true) 589 ) 590 ); 591 } 592 593 return factory.createArrayLiteralExpression(setters, /*multiLine*/ true); 594 } 595 596 // 597 // Top-level Source Element Visitors 598 // 599 600 /** 601 * Visit source elements at the top-level of a module. 602 * 603 * @param node The node to visit. 604 */ 605 function topLevelVisitor(node: Node): VisitResult<Node> { 606 switch (node.kind) { 607 case SyntaxKind.ImportDeclaration: 608 return visitImportDeclaration(node as ImportDeclaration); 609 610 case SyntaxKind.ImportEqualsDeclaration: 611 return visitImportEqualsDeclaration(node as ImportEqualsDeclaration); 612 613 case SyntaxKind.ExportDeclaration: 614 return visitExportDeclaration(node as ExportDeclaration); 615 616 case SyntaxKind.ExportAssignment: 617 return visitExportAssignment(node as ExportAssignment); 618 619 default: 620 return topLevelNestedVisitor(node); 621 } 622 } 623 624 /** 625 * Visits an ImportDeclaration node. 626 * 627 * @param node The node to visit. 628 */ 629 function visitImportDeclaration(node: ImportDeclaration): VisitResult<Statement> { 630 let statements: Statement[] | undefined; 631 if (node.importClause) { 632 hoistVariableDeclaration(getLocalNameForExternalImport(factory, node, currentSourceFile)!); // TODO: GH#18217 633 } 634 635 if (hasAssociatedEndOfDeclarationMarker(node)) { 636 // Defer exports until we encounter an EndOfDeclarationMarker node 637 const id = getOriginalNodeId(node); 638 deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); 639 } 640 else { 641 statements = appendExportsOfImportDeclaration(statements, node); 642 } 643 644 return singleOrMany(statements); 645 } 646 647 function visitExportDeclaration(node: ExportDeclaration): VisitResult<Statement> { 648 Debug.assertIsDefined(node); 649 return undefined; 650 } 651 652 /** 653 * Visits an ImportEqualsDeclaration node. 654 * 655 * @param node The node to visit. 656 */ 657 function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult<Statement> { 658 Debug.assert(isExternalModuleImportEqualsDeclaration(node), "import= for internal module references should be handled in an earlier transformer."); 659 660 let statements: Statement[] | undefined; 661 hoistVariableDeclaration(getLocalNameForExternalImport(factory, node, currentSourceFile)!); // TODO: GH#18217 662 663 if (hasAssociatedEndOfDeclarationMarker(node)) { 664 // Defer exports until we encounter an EndOfDeclarationMarker node 665 const id = getOriginalNodeId(node); 666 deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); 667 } 668 else { 669 statements = appendExportsOfImportEqualsDeclaration(statements, node); 670 } 671 672 return singleOrMany(statements); 673 } 674 675 /** 676 * Visits an ExportAssignment node. 677 * 678 * @param node The node to visit. 679 */ 680 function visitExportAssignment(node: ExportAssignment): VisitResult<Statement> { 681 if (node.isExportEquals) { 682 // Elide `export=` as it is illegal in a SystemJS module. 683 return undefined; 684 } 685 686 const expression = visitNode(node.expression, visitor, isExpression); 687 const original = node.original; 688 if (original && hasAssociatedEndOfDeclarationMarker(original)) { 689 // Defer exports until we encounter an EndOfDeclarationMarker node 690 const id = getOriginalNodeId(node); 691 deferredExports[id] = appendExportStatement(deferredExports[id], factory.createIdentifier("default"), expression, /*allowComments*/ true); 692 } 693 else { 694 return createExportStatement(factory.createIdentifier("default"), expression, /*allowComments*/ true); 695 } 696 } 697 698 /** 699 * Visits a FunctionDeclaration, hoisting it to the outer module body function. 700 * 701 * @param node The node to visit. 702 */ 703 function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> { 704 if (hasSyntacticModifier(node, ModifierFlags.Export)) { 705 hoistedStatements = append(hoistedStatements, 706 factory.updateFunctionDeclaration( 707 node, 708 visitNodes(node.modifiers, modifierVisitor, isModifierLike), 709 node.asteriskToken, 710 factory.getDeclarationName(node, /*allowComments*/ true, /*allowSourceMaps*/ true), 711 /*typeParameters*/ undefined, 712 visitNodes(node.parameters, visitor, isParameterDeclaration), 713 /*type*/ undefined, 714 visitNode(node.body, visitor, isBlock))); 715 } 716 else { 717 hoistedStatements = append(hoistedStatements, visitEachChild(node, visitor, context)); 718 } 719 720 if (hasAssociatedEndOfDeclarationMarker(node)) { 721 // Defer exports until we encounter an EndOfDeclarationMarker node 722 const id = getOriginalNodeId(node); 723 deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); 724 } 725 else { 726 hoistedStatements = appendExportsOfHoistedDeclaration(hoistedStatements, node); 727 } 728 729 return undefined; 730 } 731 732 /** 733 * Visits a ClassDeclaration, hoisting its name to the outer module body function. 734 * 735 * @param node The node to visit. 736 */ 737 function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> { 738 let statements: Statement[] | undefined; 739 740 // Hoist the name of the class declaration to the outer module body function. 741 const name = factory.getLocalName(node); 742 hoistVariableDeclaration(name); 743 744 // Rewrite the class declaration into an assignment of a class expression. 745 statements = append(statements, 746 setTextRange( 747 factory.createExpressionStatement( 748 factory.createAssignment( 749 name, 750 setTextRange( 751 factory.createClassExpression( 752 visitNodes(node.modifiers, modifierVisitor, isModifierLike), 753 node.name, 754 /*typeParameters*/ undefined, 755 visitNodes(node.heritageClauses, visitor, isHeritageClause), 756 visitNodes(node.members, visitor, isClassElement) 757 ), 758 node 759 ) 760 ) 761 ), 762 node 763 ) 764 ); 765 766 if (hasAssociatedEndOfDeclarationMarker(node)) { 767 // Defer exports until we encounter an EndOfDeclarationMarker node 768 const id = getOriginalNodeId(node); 769 deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); 770 } 771 else { 772 statements = appendExportsOfHoistedDeclaration(statements, node); 773 } 774 775 return singleOrMany(statements); 776 } 777 778 /** 779 * Visits a variable statement, hoisting declared names to the top-level module body. 780 * Each declaration is rewritten into an assignment expression. 781 * 782 * @param node The node to visit. 783 */ 784 function visitVariableStatement(node: VariableStatement): VisitResult<Statement> { 785 if (!shouldHoistVariableDeclarationList(node.declarationList)) { 786 return visitNode(node, visitor, isStatement); 787 } 788 789 let expressions: Expression[] | undefined; 790 const isExportedDeclaration = hasSyntacticModifier(node, ModifierFlags.Export); 791 const isMarkedDeclaration = hasAssociatedEndOfDeclarationMarker(node); 792 for (const variable of node.declarationList.declarations) { 793 if (variable.initializer) { 794 expressions = append(expressions, transformInitializedVariable(variable, isExportedDeclaration && !isMarkedDeclaration)); 795 } 796 else { 797 hoistBindingElement(variable); 798 } 799 } 800 801 let statements: Statement[] | undefined; 802 if (expressions) { 803 statements = append(statements, setTextRange(factory.createExpressionStatement(factory.inlineExpressions(expressions)), node)); 804 } 805 806 if (isMarkedDeclaration) { 807 // Defer exports until we encounter an EndOfDeclarationMarker node 808 const id = getOriginalNodeId(node); 809 deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node, isExportedDeclaration); 810 } 811 else { 812 statements = appendExportsOfVariableStatement(statements, node, /*exportSelf*/ false); 813 } 814 815 return singleOrMany(statements); 816 } 817 818 /** 819 * Hoists the declared names of a VariableDeclaration or BindingElement. 820 * 821 * @param node The declaration to hoist. 822 */ 823 function hoistBindingElement(node: VariableDeclaration | BindingElement): void { 824 if (isBindingPattern(node.name)) { 825 for (const element of node.name.elements) { 826 if (!isOmittedExpression(element)) { 827 hoistBindingElement(element); 828 } 829 } 830 } 831 else { 832 hoistVariableDeclaration(factory.cloneNode(node.name)); 833 } 834 } 835 836 /** 837 * Determines whether a VariableDeclarationList should be hoisted. 838 * 839 * @param node The node to test. 840 */ 841 function shouldHoistVariableDeclarationList(node: VariableDeclarationList) { 842 // hoist only non-block scoped declarations or block scoped declarations parented by source file 843 return (getEmitFlags(node) & EmitFlags.NoHoisting) === 0 844 && (enclosingBlockScopedContainer.kind === SyntaxKind.SourceFile 845 || (getOriginalNode(node).flags & NodeFlags.BlockScoped) === 0); 846 } 847 848 /** 849 * Transform an initialized variable declaration into an expression. 850 * 851 * @param node The node to transform. 852 * @param isExportedDeclaration A value indicating whether the variable is exported. 853 */ 854 function transformInitializedVariable(node: VariableDeclaration, isExportedDeclaration: boolean): Expression { 855 const createAssignment = isExportedDeclaration ? createExportedVariableAssignment : createNonExportedVariableAssignment; 856 return isBindingPattern(node.name) 857 ? flattenDestructuringAssignment( 858 node, 859 visitor, 860 context, 861 FlattenLevel.All, 862 /*needsValue*/ false, 863 createAssignment 864 ) 865 : node.initializer ? createAssignment(node.name, visitNode(node.initializer, visitor, isExpression)) : node.name; 866 } 867 868 /** 869 * Creates an assignment expression for an exported variable declaration. 870 * 871 * @param name The name of the variable. 872 * @param value The value of the variable's initializer. 873 * @param location The source map location for the assignment. 874 */ 875 function createExportedVariableAssignment(name: Identifier, value: Expression, location?: TextRange) { 876 return createVariableAssignment(name, value, location, /*isExportedDeclaration*/ true); 877 } 878 879 /** 880 * Creates an assignment expression for a non-exported variable declaration. 881 * 882 * @param name The name of the variable. 883 * @param value The value of the variable's initializer. 884 * @param location The source map location for the assignment. 885 */ 886 function createNonExportedVariableAssignment(name: Identifier, value: Expression, location?: TextRange) { 887 return createVariableAssignment(name, value, location, /*isExportedDeclaration*/ false); 888 } 889 890 /** 891 * Creates an assignment expression for a variable declaration. 892 * 893 * @param name The name of the variable. 894 * @param value The value of the variable's initializer. 895 * @param location The source map location for the assignment. 896 * @param isExportedDeclaration A value indicating whether the variable is exported. 897 */ 898 function createVariableAssignment(name: Identifier, value: Expression, location: TextRange | undefined, isExportedDeclaration: boolean) { 899 hoistVariableDeclaration(factory.cloneNode(name)); 900 return isExportedDeclaration 901 ? createExportExpression(name, preventSubstitution(setTextRange(factory.createAssignment(name, value), location))) 902 : preventSubstitution(setTextRange(factory.createAssignment(name, value), location)); 903 } 904 905 /** 906 * Visits a MergeDeclarationMarker used as a placeholder for the beginning of a merged 907 * and transformed declaration. 908 * 909 * @param node The node to visit. 910 */ 911 function visitMergeDeclarationMarker(node: MergeDeclarationMarker): VisitResult<Statement> { 912 // For an EnumDeclaration or ModuleDeclaration that merges with a preceeding 913 // declaration we do not emit a leading variable declaration. To preserve the 914 // begin/end semantics of the declararation and to properly handle exports 915 // we wrapped the leading variable declaration in a `MergeDeclarationMarker`. 916 // 917 // To balance the declaration, we defer the exports of the elided variable 918 // statement until we visit this declaration's `EndOfDeclarationMarker`. 919 if (hasAssociatedEndOfDeclarationMarker(node) && node.original!.kind === SyntaxKind.VariableStatement) { 920 const id = getOriginalNodeId(node); 921 const isExportedDeclaration = hasSyntacticModifier(node.original!, ModifierFlags.Export); 922 deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original as VariableStatement, isExportedDeclaration); 923 } 924 925 return node; 926 } 927 928 /** 929 * Determines whether a node has an associated EndOfDeclarationMarker. 930 * 931 * @param node The node to test. 932 */ 933 function hasAssociatedEndOfDeclarationMarker(node: Node) { 934 return (getEmitFlags(node) & EmitFlags.HasEndOfDeclarationMarker) !== 0; 935 } 936 937 /** 938 * Visits a DeclarationMarker used as a placeholder for the end of a transformed 939 * declaration. 940 * 941 * @param node The node to visit. 942 */ 943 function visitEndOfDeclarationMarker(node: EndOfDeclarationMarker): VisitResult<Statement> { 944 // For some transformations we emit an `EndOfDeclarationMarker` to mark the actual 945 // end of the transformed declaration. We use this marker to emit any deferred exports 946 // of the declaration. 947 const id = getOriginalNodeId(node); 948 const statements = deferredExports[id]; 949 if (statements) { 950 delete deferredExports[id]; 951 return append(statements, node); 952 } 953 else { 954 const original = getOriginalNode(node); 955 if (isModuleOrEnumDeclaration(original)) { 956 return append(appendExportsOfDeclaration(statements, original), node); 957 } 958 } 959 960 return node; 961 } 962 963 /** 964 * Appends the exports of an ImportDeclaration to a statement list, returning the 965 * statement list. 966 * 967 * @param statements A statement list to which the down-level export statements are to be 968 * appended. If `statements` is `undefined`, a new array is allocated if statements are 969 * appended. 970 * @param decl The declaration whose exports are to be recorded. 971 */ 972 function appendExportsOfImportDeclaration(statements: Statement[] | undefined, decl: ImportDeclaration) { 973 if (moduleInfo.exportEquals) { 974 return statements; 975 } 976 977 const importClause = decl.importClause; 978 if (!importClause) { 979 return statements; 980 } 981 982 if (importClause.name) { 983 statements = appendExportsOfDeclaration(statements, importClause); 984 } 985 986 const namedBindings = importClause.namedBindings; 987 if (namedBindings) { 988 switch (namedBindings.kind) { 989 case SyntaxKind.NamespaceImport: 990 statements = appendExportsOfDeclaration(statements, namedBindings); 991 break; 992 993 case SyntaxKind.NamedImports: 994 for (const importBinding of namedBindings.elements) { 995 statements = appendExportsOfDeclaration(statements, importBinding); 996 } 997 998 break; 999 } 1000 } 1001 1002 return statements; 1003 } 1004 1005 /** 1006 * Appends the export of an ImportEqualsDeclaration to a statement list, returning the 1007 * statement list. 1008 * 1009 * @param statements A statement list to which the down-level export statements are to be 1010 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1011 * appended. 1012 * @param decl The declaration whose exports are to be recorded. 1013 */ 1014 function appendExportsOfImportEqualsDeclaration(statements: Statement[] | undefined, decl: ImportEqualsDeclaration): Statement[] | undefined { 1015 if (moduleInfo.exportEquals) { 1016 return statements; 1017 } 1018 1019 return appendExportsOfDeclaration(statements, decl); 1020 } 1021 1022 /** 1023 * Appends the exports of a VariableStatement to a statement list, returning the statement 1024 * list. 1025 * 1026 * @param statements A statement list to which the down-level export statements are to be 1027 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1028 * appended. 1029 * @param node The VariableStatement whose exports are to be recorded. 1030 * @param exportSelf A value indicating whether to also export each VariableDeclaration of 1031 * `nodes` declaration list. 1032 */ 1033 function appendExportsOfVariableStatement(statements: Statement[] | undefined, node: VariableStatement, exportSelf: boolean): Statement[] | undefined { 1034 if (moduleInfo.exportEquals) { 1035 return statements; 1036 } 1037 1038 for (const decl of node.declarationList.declarations) { 1039 if (decl.initializer || exportSelf) { 1040 statements = appendExportsOfBindingElement(statements, decl, exportSelf); 1041 } 1042 } 1043 1044 return statements; 1045 } 1046 1047 /** 1048 * Appends the exports of a VariableDeclaration or BindingElement to a statement list, 1049 * returning the statement list. 1050 * 1051 * @param statements A statement list to which the down-level export statements are to be 1052 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1053 * appended. 1054 * @param decl The declaration whose exports are to be recorded. 1055 * @param exportSelf A value indicating whether to also export the declaration itself. 1056 */ 1057 function appendExportsOfBindingElement(statements: Statement[] | undefined, decl: VariableDeclaration | BindingElement, exportSelf: boolean): Statement[] | undefined { 1058 if (moduleInfo.exportEquals) { 1059 return statements; 1060 } 1061 1062 if (isBindingPattern(decl.name)) { 1063 for (const element of decl.name.elements) { 1064 if (!isOmittedExpression(element)) { 1065 statements = appendExportsOfBindingElement(statements, element, exportSelf); 1066 } 1067 } 1068 } 1069 else if (!isGeneratedIdentifier(decl.name)) { 1070 let excludeName: string | undefined; 1071 if (exportSelf) { 1072 statements = appendExportStatement(statements, decl.name, factory.getLocalName(decl)); 1073 excludeName = idText(decl.name); 1074 } 1075 1076 statements = appendExportsOfDeclaration(statements, decl, excludeName); 1077 } 1078 1079 return statements; 1080 } 1081 1082 /** 1083 * Appends the exports of a ClassDeclaration or FunctionDeclaration to a statement list, 1084 * returning the statement list. 1085 * 1086 * @param statements A statement list to which the down-level export statements are to be 1087 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1088 * appended. 1089 * @param decl The declaration whose exports are to be recorded. 1090 */ 1091 function appendExportsOfHoistedDeclaration(statements: Statement[] | undefined, decl: ClassDeclaration | FunctionDeclaration): Statement[] | undefined { 1092 if (moduleInfo.exportEquals) { 1093 return statements; 1094 } 1095 1096 let excludeName: string | undefined; 1097 if (hasSyntacticModifier(decl, ModifierFlags.Export)) { 1098 const exportName = hasSyntacticModifier(decl, ModifierFlags.Default) ? factory.createStringLiteral("default") : decl.name!; 1099 statements = appendExportStatement(statements, exportName, factory.getLocalName(decl)); 1100 excludeName = getTextOfIdentifierOrLiteral(exportName); 1101 } 1102 1103 if (decl.name) { 1104 statements = appendExportsOfDeclaration(statements, decl, excludeName); 1105 } 1106 1107 return statements; 1108 } 1109 1110 /** 1111 * Appends the exports of a declaration to a statement list, returning the statement list. 1112 * 1113 * @param statements A statement list to which the down-level export statements are to be 1114 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1115 * appended. 1116 * @param decl The declaration to export. 1117 * @param excludeName An optional name to exclude from exports. 1118 */ 1119 function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration, excludeName?: string): Statement[] | undefined { 1120 if (moduleInfo.exportEquals) { 1121 return statements; 1122 } 1123 1124 const name = factory.getDeclarationName(decl); 1125 const exportSpecifiers = moduleInfo.exportSpecifiers.get(idText(name)); 1126 if (exportSpecifiers) { 1127 for (const exportSpecifier of exportSpecifiers) { 1128 if (exportSpecifier.name.escapedText !== excludeName) { 1129 statements = appendExportStatement(statements, exportSpecifier.name, name); 1130 } 1131 } 1132 } 1133 return statements; 1134 } 1135 1136 /** 1137 * Appends the down-level representation of an export to a statement list, returning the 1138 * statement list. 1139 * 1140 * @param statements A statement list to which the down-level export statements are to be 1141 * appended. If `statements` is `undefined`, a new array is allocated if statements are 1142 * appended. 1143 * @param exportName The name of the export. 1144 * @param expression The expression to export. 1145 * @param allowComments Whether to allow comments on the export. 1146 */ 1147 function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier | StringLiteral, expression: Expression, allowComments?: boolean): Statement[] | undefined { 1148 statements = append(statements, createExportStatement(exportName, expression, allowComments)); 1149 return statements; 1150 } 1151 1152 /** 1153 * Creates a call to the current file's export function to export a value. 1154 * 1155 * @param name The bound name of the export. 1156 * @param value The exported value. 1157 * @param allowComments An optional value indicating whether to emit comments for the statement. 1158 */ 1159 function createExportStatement(name: Identifier | StringLiteral, value: Expression, allowComments?: boolean) { 1160 const statement = factory.createExpressionStatement(createExportExpression(name, value)); 1161 startOnNewLine(statement); 1162 if (!allowComments) { 1163 setEmitFlags(statement, EmitFlags.NoComments); 1164 } 1165 1166 return statement; 1167 } 1168 1169 /** 1170 * Creates a call to the current file's export function to export a value. 1171 * 1172 * @param name The bound name of the export. 1173 * @param value The exported value. 1174 */ 1175 function createExportExpression(name: Identifier | StringLiteral, value: Expression) { 1176 const exportName = isIdentifier(name) ? factory.createStringLiteralFromNode(name) : name; 1177 setEmitFlags(value, getEmitFlags(value) | EmitFlags.NoComments); 1178 return setCommentRange(factory.createCallExpression(exportFunction, /*typeArguments*/ undefined, [exportName, value]), value); 1179 } 1180 1181 // 1182 // Top-Level or Nested Source Element Visitors 1183 // 1184 1185 /** 1186 * Visit nested elements at the top-level of a module. 1187 * 1188 * @param node The node to visit. 1189 */ 1190 function topLevelNestedVisitor(node: Node): VisitResult<Node> { 1191 switch (node.kind) { 1192 case SyntaxKind.VariableStatement: 1193 return visitVariableStatement(node as VariableStatement); 1194 1195 case SyntaxKind.FunctionDeclaration: 1196 return visitFunctionDeclaration(node as FunctionDeclaration); 1197 1198 case SyntaxKind.ClassDeclaration: 1199 return visitClassDeclaration(node as ClassDeclaration); 1200 1201 case SyntaxKind.ForStatement: 1202 return visitForStatement(node as ForStatement, /*isTopLevel*/ true); 1203 1204 case SyntaxKind.ForInStatement: 1205 return visitForInStatement(node as ForInStatement); 1206 1207 case SyntaxKind.ForOfStatement: 1208 return visitForOfStatement(node as ForOfStatement); 1209 1210 case SyntaxKind.DoStatement: 1211 return visitDoStatement(node as DoStatement); 1212 1213 case SyntaxKind.WhileStatement: 1214 return visitWhileStatement(node as WhileStatement); 1215 1216 case SyntaxKind.LabeledStatement: 1217 return visitLabeledStatement(node as LabeledStatement); 1218 1219 case SyntaxKind.WithStatement: 1220 return visitWithStatement(node as WithStatement); 1221 1222 case SyntaxKind.SwitchStatement: 1223 return visitSwitchStatement(node as SwitchStatement); 1224 1225 case SyntaxKind.CaseBlock: 1226 return visitCaseBlock(node as CaseBlock); 1227 1228 case SyntaxKind.CaseClause: 1229 return visitCaseClause(node as CaseClause); 1230 1231 case SyntaxKind.DefaultClause: 1232 return visitDefaultClause(node as DefaultClause); 1233 1234 case SyntaxKind.TryStatement: 1235 return visitTryStatement(node as TryStatement); 1236 1237 case SyntaxKind.CatchClause: 1238 return visitCatchClause(node as CatchClause); 1239 1240 case SyntaxKind.Block: 1241 return visitBlock(node as Block); 1242 1243 case SyntaxKind.MergeDeclarationMarker: 1244 return visitMergeDeclarationMarker(node as MergeDeclarationMarker); 1245 1246 case SyntaxKind.EndOfDeclarationMarker: 1247 return visitEndOfDeclarationMarker(node as EndOfDeclarationMarker); 1248 1249 default: 1250 return visitor(node); 1251 } 1252 } 1253 1254 /** 1255 * Visits the body of a ForStatement to hoist declarations. 1256 * 1257 * @param node The node to visit. 1258 */ 1259 function visitForStatement(node: ForStatement, isTopLevel: boolean): VisitResult<Statement> { 1260 const savedEnclosingBlockScopedContainer = enclosingBlockScopedContainer; 1261 enclosingBlockScopedContainer = node; 1262 1263 node = factory.updateForStatement( 1264 node, 1265 visitNode(node.initializer, isTopLevel ? visitForInitializer : discardedValueVisitor, isForInitializer), 1266 visitNode(node.condition, visitor, isExpression), 1267 visitNode(node.incrementor, discardedValueVisitor, isExpression), 1268 visitIterationBody(node.statement, isTopLevel ? topLevelNestedVisitor : visitor, context) 1269 ); 1270 1271 enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; 1272 return node; 1273 } 1274 1275 /** 1276 * Visits the body of a ForInStatement to hoist declarations. 1277 * 1278 * @param node The node to visit. 1279 */ 1280 function visitForInStatement(node: ForInStatement): VisitResult<Statement> { 1281 const savedEnclosingBlockScopedContainer = enclosingBlockScopedContainer; 1282 enclosingBlockScopedContainer = node; 1283 1284 node = factory.updateForInStatement( 1285 node, 1286 visitForInitializer(node.initializer), 1287 visitNode(node.expression, visitor, isExpression), 1288 visitIterationBody(node.statement, topLevelNestedVisitor, context) 1289 ); 1290 1291 enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; 1292 return node; 1293 } 1294 1295 /** 1296 * Visits the body of a ForOfStatement to hoist declarations. 1297 * 1298 * @param node The node to visit. 1299 */ 1300 function visitForOfStatement(node: ForOfStatement): VisitResult<Statement> { 1301 const savedEnclosingBlockScopedContainer = enclosingBlockScopedContainer; 1302 enclosingBlockScopedContainer = node; 1303 1304 node = factory.updateForOfStatement( 1305 node, 1306 node.awaitModifier, 1307 visitForInitializer(node.initializer), 1308 visitNode(node.expression, visitor, isExpression), 1309 visitIterationBody(node.statement, topLevelNestedVisitor, context) 1310 ); 1311 1312 enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; 1313 return node; 1314 } 1315 1316 /** 1317 * Determines whether to hoist the initializer of a ForStatement, ForInStatement, or 1318 * ForOfStatement. 1319 * 1320 * @param node The node to test. 1321 */ 1322 function shouldHoistForInitializer(node: ForInitializer): node is VariableDeclarationList { 1323 return isVariableDeclarationList(node) 1324 && shouldHoistVariableDeclarationList(node); 1325 } 1326 1327 /** 1328 * Visits the initializer of a ForStatement, ForInStatement, or ForOfStatement 1329 * 1330 * @param node The node to visit. 1331 */ 1332 function visitForInitializer(node: ForInitializer): ForInitializer { 1333 if (shouldHoistForInitializer(node)) { 1334 let expressions: Expression[] | undefined; 1335 for (const variable of node.declarations) { 1336 expressions = append(expressions, transformInitializedVariable(variable, /*isExportedDeclaration*/ false)); 1337 if (!variable.initializer) { 1338 hoistBindingElement(variable); 1339 } 1340 } 1341 1342 return expressions ? factory.inlineExpressions(expressions) : factory.createOmittedExpression(); 1343 } 1344 else { 1345 return visitNode(node, discardedValueVisitor, isExpression); 1346 } 1347 } 1348 1349 /** 1350 * Visits the body of a DoStatement to hoist declarations. 1351 * 1352 * @param node The node to visit. 1353 */ 1354 function visitDoStatement(node: DoStatement): VisitResult<Statement> { 1355 return factory.updateDoStatement( 1356 node, 1357 visitIterationBody(node.statement, topLevelNestedVisitor, context), 1358 visitNode(node.expression, visitor, isExpression) 1359 ); 1360 } 1361 1362 /** 1363 * Visits the body of a WhileStatement to hoist declarations. 1364 * 1365 * @param node The node to visit. 1366 */ 1367 function visitWhileStatement(node: WhileStatement): VisitResult<Statement> { 1368 return factory.updateWhileStatement( 1369 node, 1370 visitNode(node.expression, visitor, isExpression), 1371 visitIterationBody(node.statement, topLevelNestedVisitor, context) 1372 ); 1373 } 1374 1375 /** 1376 * Visits the body of a LabeledStatement to hoist declarations. 1377 * 1378 * @param node The node to visit. 1379 */ 1380 function visitLabeledStatement(node: LabeledStatement): VisitResult<Statement> { 1381 return factory.updateLabeledStatement( 1382 node, 1383 node.label, 1384 visitNode(node.statement, topLevelNestedVisitor, isStatement, factory.liftToBlock) 1385 ); 1386 } 1387 1388 /** 1389 * Visits the body of a WithStatement to hoist declarations. 1390 * 1391 * @param node The node to visit. 1392 */ 1393 function visitWithStatement(node: WithStatement): VisitResult<Statement> { 1394 return factory.updateWithStatement( 1395 node, 1396 visitNode(node.expression, visitor, isExpression), 1397 visitNode(node.statement, topLevelNestedVisitor, isStatement, factory.liftToBlock) 1398 ); 1399 } 1400 1401 /** 1402 * Visits the body of a SwitchStatement to hoist declarations. 1403 * 1404 * @param node The node to visit. 1405 */ 1406 function visitSwitchStatement(node: SwitchStatement): VisitResult<Statement> { 1407 return factory.updateSwitchStatement( 1408 node, 1409 visitNode(node.expression, visitor, isExpression), 1410 visitNode(node.caseBlock, topLevelNestedVisitor, isCaseBlock) 1411 ); 1412 } 1413 1414 /** 1415 * Visits the body of a CaseBlock to hoist declarations. 1416 * 1417 * @param node The node to visit. 1418 */ 1419 function visitCaseBlock(node: CaseBlock): CaseBlock { 1420 const savedEnclosingBlockScopedContainer = enclosingBlockScopedContainer; 1421 enclosingBlockScopedContainer = node; 1422 1423 node = factory.updateCaseBlock( 1424 node, 1425 visitNodes(node.clauses, topLevelNestedVisitor, isCaseOrDefaultClause) 1426 ); 1427 1428 enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; 1429 return node; 1430 } 1431 1432 /** 1433 * Visits the body of a CaseClause to hoist declarations. 1434 * 1435 * @param node The node to visit. 1436 */ 1437 function visitCaseClause(node: CaseClause): VisitResult<CaseOrDefaultClause> { 1438 return factory.updateCaseClause( 1439 node, 1440 visitNode(node.expression, visitor, isExpression), 1441 visitNodes(node.statements, topLevelNestedVisitor, isStatement) 1442 ); 1443 } 1444 1445 /** 1446 * Visits the body of a DefaultClause to hoist declarations. 1447 * 1448 * @param node The node to visit. 1449 */ 1450 function visitDefaultClause(node: DefaultClause): VisitResult<CaseOrDefaultClause> { 1451 return visitEachChild(node, topLevelNestedVisitor, context); 1452 } 1453 1454 /** 1455 * Visits the body of a TryStatement to hoist declarations. 1456 * 1457 * @param node The node to visit. 1458 */ 1459 function visitTryStatement(node: TryStatement): VisitResult<Statement> { 1460 return visitEachChild(node, topLevelNestedVisitor, context); 1461 } 1462 1463 /** 1464 * Visits the body of a CatchClause to hoist declarations. 1465 * 1466 * @param node The node to visit. 1467 */ 1468 function visitCatchClause(node: CatchClause): CatchClause { 1469 const savedEnclosingBlockScopedContainer = enclosingBlockScopedContainer; 1470 enclosingBlockScopedContainer = node; 1471 1472 node = factory.updateCatchClause( 1473 node, 1474 node.variableDeclaration, 1475 visitNode(node.block, topLevelNestedVisitor, isBlock) 1476 ); 1477 1478 enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; 1479 return node; 1480 } 1481 1482 /** 1483 * Visits the body of a Block to hoist declarations. 1484 * 1485 * @param node The node to visit. 1486 */ 1487 function visitBlock(node: Block): Block { 1488 const savedEnclosingBlockScopedContainer = enclosingBlockScopedContainer; 1489 enclosingBlockScopedContainer = node; 1490 1491 node = visitEachChild(node, topLevelNestedVisitor, context); 1492 1493 enclosingBlockScopedContainer = savedEnclosingBlockScopedContainer; 1494 return node; 1495 } 1496 1497 // 1498 // Destructuring Assignment Visitors 1499 // 1500 1501 /** 1502 * Visit nodes to flatten destructuring assignments to exported symbols. 1503 * 1504 * @param node The node to visit. 1505 */ 1506 function visitorWorker(node: Node, valueIsDiscarded: boolean): VisitResult<Node> { 1507 if (!(node.transformFlags & (TransformFlags.ContainsDestructuringAssignment | TransformFlags.ContainsDynamicImport | TransformFlags.ContainsUpdateExpressionForIdentifier))) { 1508 return node; 1509 } 1510 switch (node.kind) { 1511 case SyntaxKind.ForStatement: 1512 return visitForStatement(node as ForStatement, /*isTopLevel*/ false); 1513 case SyntaxKind.ExpressionStatement: 1514 return visitExpressionStatement(node as ExpressionStatement); 1515 case SyntaxKind.ParenthesizedExpression: 1516 return visitParenthesizedExpression(node as ParenthesizedExpression, valueIsDiscarded); 1517 case SyntaxKind.PartiallyEmittedExpression: 1518 return visitPartiallyEmittedExpression(node as PartiallyEmittedExpression, valueIsDiscarded); 1519 case SyntaxKind.BinaryExpression: 1520 if (isDestructuringAssignment(node)) { 1521 return visitDestructuringAssignment(node, valueIsDiscarded); 1522 } 1523 break; 1524 case SyntaxKind.CallExpression: 1525 if (isImportCall(node)) { 1526 return visitImportCallExpression(node); 1527 } 1528 break; 1529 case SyntaxKind.PrefixUnaryExpression: 1530 case SyntaxKind.PostfixUnaryExpression: 1531 return visitPrefixOrPostfixUnaryExpression(node as PrefixUnaryExpression | PostfixUnaryExpression, valueIsDiscarded); 1532 } 1533 return visitEachChild(node, visitor, context); 1534 } 1535 1536 /** 1537 * Visit nodes to flatten destructuring assignments to exported symbols. 1538 * 1539 * @param node The node to visit. 1540 */ 1541 function visitor(node: Node): VisitResult<Node> { 1542 return visitorWorker(node, /*valueIsDiscarded*/ false); 1543 } 1544 1545 function discardedValueVisitor(node: Node): VisitResult<Node> { 1546 return visitorWorker(node, /*valueIsDiscarded*/ true); 1547 } 1548 1549 function visitExpressionStatement(node: ExpressionStatement) { 1550 return factory.updateExpressionStatement(node, visitNode(node.expression, discardedValueVisitor, isExpression)); 1551 } 1552 1553 function visitParenthesizedExpression(node: ParenthesizedExpression, valueIsDiscarded: boolean) { 1554 return factory.updateParenthesizedExpression(node, visitNode(node.expression, valueIsDiscarded ? discardedValueVisitor : visitor, isExpression)); 1555 } 1556 1557 function visitPartiallyEmittedExpression(node: PartiallyEmittedExpression, valueIsDiscarded: boolean) { 1558 return factory.updatePartiallyEmittedExpression(node, visitNode(node.expression, valueIsDiscarded ? discardedValueVisitor : visitor, isExpression)); 1559 } 1560 1561 function visitImportCallExpression(node: ImportCall): Expression { 1562 // import("./blah") 1563 // emit as 1564 // System.register([], function (_export, _context) { 1565 // return { 1566 // setters: [], 1567 // execute: () => { 1568 // _context.import('./blah'); 1569 // } 1570 // }; 1571 // }); 1572 const externalModuleName = getExternalModuleNameLiteral(factory, node, currentSourceFile, host, resolver, compilerOptions); 1573 const firstArgument = visitNode(firstOrUndefined(node.arguments), visitor); 1574 // 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. 1575 const argument = externalModuleName && (!firstArgument || !isStringLiteral(firstArgument) || firstArgument.text !== externalModuleName.text) ? externalModuleName : firstArgument; 1576 return factory.createCallExpression( 1577 factory.createPropertyAccessExpression( 1578 contextObject, 1579 factory.createIdentifier("import") 1580 ), 1581 /*typeArguments*/ undefined, 1582 argument ? [argument] : [] 1583 ); 1584 } 1585 1586 /** 1587 * Visits a DestructuringAssignment to flatten destructuring to exported symbols. 1588 * 1589 * @param node The node to visit. 1590 */ 1591 function visitDestructuringAssignment(node: DestructuringAssignment, valueIsDiscarded: boolean): VisitResult<Expression> { 1592 if (hasExportedReferenceInDestructuringTarget(node.left)) { 1593 return flattenDestructuringAssignment( 1594 node, 1595 visitor, 1596 context, 1597 FlattenLevel.All, 1598 !valueIsDiscarded 1599 ); 1600 } 1601 1602 return visitEachChild(node, visitor, context); 1603 } 1604 1605 /** 1606 * Determines whether the target of a destructuring assignment refers to an exported symbol. 1607 * 1608 * @param node The destructuring target. 1609 */ 1610 function hasExportedReferenceInDestructuringTarget(node: Expression | ObjectLiteralElementLike): boolean { 1611 if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true)) { 1612 return hasExportedReferenceInDestructuringTarget(node.left); 1613 } 1614 else if (isSpreadElement(node)) { 1615 return hasExportedReferenceInDestructuringTarget(node.expression); 1616 } 1617 else if (isObjectLiteralExpression(node)) { 1618 return some(node.properties, hasExportedReferenceInDestructuringTarget); 1619 } 1620 else if (isArrayLiteralExpression(node)) { 1621 return some(node.elements, hasExportedReferenceInDestructuringTarget); 1622 } 1623 else if (isShorthandPropertyAssignment(node)) { 1624 return hasExportedReferenceInDestructuringTarget(node.name); 1625 } 1626 else if (isPropertyAssignment(node)) { 1627 return hasExportedReferenceInDestructuringTarget(node.initializer); 1628 } 1629 else if (isIdentifier(node)) { 1630 const container = resolver.getReferencedExportContainer(node); 1631 return container !== undefined && container.kind === SyntaxKind.SourceFile; 1632 } 1633 else { 1634 return false; 1635 } 1636 } 1637 1638 function visitPrefixOrPostfixUnaryExpression(node: PrefixUnaryExpression | PostfixUnaryExpression, valueIsDiscarded: boolean) { 1639 // When we see a prefix or postfix increment expression whose operand is an exported 1640 // symbol, we should ensure all exports of that symbol are updated with the correct 1641 // value. 1642 // 1643 // - We do not transform generated identifiers for any reason. 1644 // - We do not transform identifiers tagged with the LocalName flag. 1645 // - We do not transform identifiers that were originally the name of an enum or 1646 // namespace due to how they are transformed in TypeScript. 1647 // - We only transform identifiers that are exported at the top level. 1648 if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) 1649 && isIdentifier(node.operand) 1650 && !isGeneratedIdentifier(node.operand) 1651 && !isLocalName(node.operand) 1652 && !isDeclarationNameOfEnumOrNamespace(node.operand)) { 1653 const exportedNames = getExports(node.operand); 1654 if (exportedNames) { 1655 let temp: Identifier | undefined; 1656 let expression: Expression = visitNode(node.operand, visitor, isExpression); 1657 if (isPrefixUnaryExpression(node)) { 1658 expression = factory.updatePrefixUnaryExpression(node, expression); 1659 } 1660 else { 1661 expression = factory.updatePostfixUnaryExpression(node, expression); 1662 if (!valueIsDiscarded) { 1663 temp = factory.createTempVariable(hoistVariableDeclaration); 1664 expression = factory.createAssignment(temp, expression); 1665 setTextRange(expression, node); 1666 } 1667 expression = factory.createComma(expression, factory.cloneNode(node.operand)); 1668 setTextRange(expression, node); 1669 } 1670 1671 for (const exportName of exportedNames) { 1672 expression = createExportExpression(exportName, preventSubstitution(expression)); 1673 } 1674 1675 if (temp) { 1676 expression = factory.createComma(expression, temp); 1677 setTextRange(expression, node); 1678 } 1679 1680 return expression; 1681 } 1682 } 1683 return visitEachChild(node, visitor, context); 1684 } 1685 1686 // 1687 // Modifier Visitors 1688 // 1689 1690 /** 1691 * Visit nodes to elide module-specific modifiers. 1692 * 1693 * @param node The node to visit. 1694 */ 1695 function modifierVisitor(node: Node): VisitResult<Node> { 1696 switch (node.kind) { 1697 case SyntaxKind.ExportKeyword: 1698 case SyntaxKind.DefaultKeyword: 1699 return undefined; 1700 } 1701 return node; 1702 } 1703 1704 // 1705 // Emit Notification 1706 // 1707 1708 /** 1709 * Hook for node emit notifications. 1710 * 1711 * @param hint A hint as to the intended usage of the node. 1712 * @param node The node to emit. 1713 * @param emitCallback A callback used to emit the node in the printer. 1714 */ 1715 function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void { 1716 if (node.kind === SyntaxKind.SourceFile) { 1717 const id = getOriginalNodeId(node); 1718 currentSourceFile = node as SourceFile; 1719 moduleInfo = moduleInfoMap[id]; 1720 exportFunction = exportFunctionsMap[id]; 1721 noSubstitution = noSubstitutionMap[id]; 1722 contextObject = contextObjectMap[id]; 1723 1724 if (noSubstitution) { 1725 delete noSubstitutionMap[id]; 1726 } 1727 1728 previousOnEmitNode(hint, node, emitCallback); 1729 1730 currentSourceFile = undefined!; 1731 moduleInfo = undefined!; 1732 exportFunction = undefined!; 1733 contextObject = undefined!; 1734 noSubstitution = undefined; 1735 } 1736 else { 1737 previousOnEmitNode(hint, node, emitCallback); 1738 } 1739 } 1740 1741 // 1742 // Substitutions 1743 // 1744 1745 /** 1746 * Hooks node substitutions. 1747 * 1748 * @param hint A hint as to the intended usage of the node. 1749 * @param node The node to substitute. 1750 */ 1751 function onSubstituteNode(hint: EmitHint, node: Node) { 1752 node = previousOnSubstituteNode(hint, node); 1753 if (isSubstitutionPrevented(node)) { 1754 return node; 1755 } 1756 1757 if (hint === EmitHint.Expression) { 1758 return substituteExpression(node as Expression); 1759 } 1760 else if (hint === EmitHint.Unspecified) { 1761 return substituteUnspecified(node); 1762 } 1763 1764 return node; 1765 } 1766 1767 /** 1768 * Substitute the node, if necessary. 1769 * 1770 * @param node The node to substitute. 1771 */ 1772 function substituteUnspecified(node: Node) { 1773 switch (node.kind) { 1774 case SyntaxKind.ShorthandPropertyAssignment: 1775 return substituteShorthandPropertyAssignment(node as ShorthandPropertyAssignment); 1776 } 1777 return node; 1778 } 1779 1780 /** 1781 * Substitution for a ShorthandPropertyAssignment whose name that may contain an imported or exported symbol. 1782 * 1783 * @param node The node to substitute. 1784 */ 1785 function substituteShorthandPropertyAssignment(node: ShorthandPropertyAssignment) { 1786 const name = node.name; 1787 if (!isGeneratedIdentifier(name) && !isLocalName(name)) { 1788 const importDeclaration = resolver.getReferencedImportDeclaration(name); 1789 if (importDeclaration) { 1790 if (isImportClause(importDeclaration)) { 1791 return setTextRange( 1792 factory.createPropertyAssignment( 1793 factory.cloneNode(name), 1794 factory.createPropertyAccessExpression( 1795 factory.getGeneratedNameForNode(importDeclaration.parent), 1796 factory.createIdentifier("default") 1797 ) 1798 ), 1799 /*location*/ node 1800 ); 1801 } 1802 else if (isImportSpecifier(importDeclaration)) { 1803 return setTextRange( 1804 factory.createPropertyAssignment( 1805 factory.cloneNode(name), 1806 factory.createPropertyAccessExpression( 1807 factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), 1808 factory.cloneNode(importDeclaration.propertyName || importDeclaration.name) 1809 ), 1810 ), 1811 /*location*/ node 1812 ); 1813 } 1814 } 1815 } 1816 return node; 1817 } 1818 1819 /** 1820 * Substitute the expression, if necessary. 1821 * 1822 * @param node The node to substitute. 1823 */ 1824 function substituteExpression(node: Expression) { 1825 switch (node.kind) { 1826 case SyntaxKind.Identifier: 1827 return substituteExpressionIdentifier(node as Identifier); 1828 case SyntaxKind.BinaryExpression: 1829 return substituteBinaryExpression(node as BinaryExpression); 1830 case SyntaxKind.MetaProperty: 1831 return substituteMetaProperty(node as MetaProperty); 1832 } 1833 1834 return node; 1835 } 1836 1837 /** 1838 * Substitution for an Identifier expression that may contain an imported or exported symbol. 1839 * 1840 * @param node The node to substitute. 1841 */ 1842 function substituteExpressionIdentifier(node: Identifier): Expression { 1843 if (getEmitFlags(node) & EmitFlags.HelperName) { 1844 const externalHelpersModuleName = getExternalHelpersModuleName(currentSourceFile); 1845 if (externalHelpersModuleName) { 1846 return factory.createPropertyAccessExpression(externalHelpersModuleName, node); 1847 } 1848 1849 return node; 1850 } 1851 1852 // When we see an identifier in an expression position that 1853 // points to an imported symbol, we should substitute a qualified 1854 // reference to the imported symbol if one is needed. 1855 // 1856 // - We do not substitute generated identifiers for any reason. 1857 // - We do not substitute identifiers tagged with the LocalName flag. 1858 if (!isGeneratedIdentifier(node) && !isLocalName(node)) { 1859 const importDeclaration = resolver.getReferencedImportDeclaration(node); 1860 if (importDeclaration) { 1861 if (isImportClause(importDeclaration)) { 1862 return setTextRange( 1863 factory.createPropertyAccessExpression( 1864 factory.getGeneratedNameForNode(importDeclaration.parent), 1865 factory.createIdentifier("default") 1866 ), 1867 /*location*/ node 1868 ); 1869 } 1870 else if (isImportSpecifier(importDeclaration)) { 1871 return setTextRange( 1872 factory.createPropertyAccessExpression( 1873 factory.getGeneratedNameForNode(importDeclaration.parent?.parent?.parent || importDeclaration), 1874 factory.cloneNode(importDeclaration.propertyName || importDeclaration.name) 1875 ), 1876 /*location*/ node 1877 ); 1878 } 1879 } 1880 } 1881 1882 return node; 1883 } 1884 1885 /** 1886 * Substitution for a BinaryExpression that may contain an imported or exported symbol. 1887 * 1888 * @param node The node to substitute. 1889 */ 1890 function substituteBinaryExpression(node: BinaryExpression): Expression { 1891 // When we see an assignment expression whose left-hand side is an exported symbol, 1892 // we should ensure all exports of that symbol are updated with the correct value. 1893 // 1894 // - We do not substitute generated identifiers for any reason. 1895 // - We do not substitute identifiers tagged with the LocalName flag. 1896 // - We do not substitute identifiers that were originally the name of an enum or 1897 // namespace due to how they are transformed in TypeScript. 1898 // - We only substitute identifiers that are exported at the top level. 1899 if (isAssignmentOperator(node.operatorToken.kind) 1900 && isIdentifier(node.left) 1901 && !isGeneratedIdentifier(node.left) 1902 && !isLocalName(node.left) 1903 && !isDeclarationNameOfEnumOrNamespace(node.left)) { 1904 const exportedNames = getExports(node.left); 1905 if (exportedNames) { 1906 // For each additional export of the declaration, apply an export assignment. 1907 let expression: Expression = node; 1908 for (const exportName of exportedNames) { 1909 expression = createExportExpression(exportName, preventSubstitution(expression)); 1910 } 1911 1912 return expression; 1913 } 1914 } 1915 1916 return node; 1917 } 1918 1919 function substituteMetaProperty(node: MetaProperty) { 1920 if (isImportMeta(node)) { 1921 return factory.createPropertyAccessExpression(contextObject, factory.createIdentifier("meta")); 1922 } 1923 return node; 1924 } 1925 1926 /** 1927 * Gets the exports of a name. 1928 * 1929 * @param name The name. 1930 */ 1931 function getExports(name: Identifier) { 1932 let exportedNames: Identifier[] | undefined; 1933 if (!isGeneratedIdentifier(name)) { 1934 const valueDeclaration = resolver.getReferencedImportDeclaration(name) 1935 || resolver.getReferencedValueDeclaration(name); 1936 1937 if (valueDeclaration) { 1938 const exportContainer = resolver.getReferencedExportContainer(name, /*prefixLocals*/ false); 1939 if (exportContainer && exportContainer.kind === SyntaxKind.SourceFile) { 1940 exportedNames = append(exportedNames, factory.getDeclarationName(valueDeclaration)); 1941 } 1942 1943 exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]); 1944 } 1945 } 1946 1947 return exportedNames; 1948 } 1949 1950 /** 1951 * Prevent substitution of a node for this transformer. 1952 * 1953 * @param node The node which should not be substituted. 1954 */ 1955 function preventSubstitution<T extends Node>(node: T): T { 1956 if (noSubstitution === undefined) noSubstitution = []; 1957 noSubstitution[getNodeId(node)] = true; 1958 return node; 1959 } 1960 1961 /** 1962 * Determines whether a node should not be substituted. 1963 * 1964 * @param node The node to test. 1965 */ 1966 function isSubstitutionPrevented(node: Node) { 1967 return noSubstitution && node.id && noSubstitution[node.id]; 1968 } 1969} 1970