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