• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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