• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2namespace ts {
3    /**
4     * Indicates whether to emit type metadata in the new format.
5     */
6    const USE_NEW_TYPE_METADATA_FORMAT = false;
7
8    const enum TypeScriptSubstitutionFlags {
9        /** Enables substitutions for namespace exports. */
10        NamespaceExports = 1 << 1,
11        /* Enables substitutions for unqualified enum members */
12        NonQualifiedEnumMembers = 1 << 3
13    }
14
15    const enum ClassFacts {
16        None = 0,
17        HasStaticInitializedProperties = 1 << 0,
18        HasConstructorDecorators = 1 << 1,
19        HasMemberDecorators = 1 << 2,
20        IsExportOfNamespace = 1 << 3,
21        IsNamedExternalExport = 1 << 4,
22        IsDefaultExternalExport = 1 << 5,
23        IsDerivedClass = 1 << 6,
24        UseImmediatelyInvokedFunctionExpression = 1 << 7,
25
26        HasAnyDecorators = HasConstructorDecorators | HasMemberDecorators,
27        NeedsName = HasStaticInitializedProperties | HasMemberDecorators,
28        MayNeedImmediatelyInvokedFunctionExpression = HasAnyDecorators | HasStaticInitializedProperties,
29        IsExported = IsExportOfNamespace | IsDefaultExternalExport | IsNamedExternalExport,
30    }
31
32    export function transformTypeScript(context: TransformationContext) {
33        const {
34            factory,
35            getEmitHelperFactory: emitHelpers,
36            startLexicalEnvironment,
37            resumeLexicalEnvironment,
38            endLexicalEnvironment,
39            hoistVariableDeclaration,
40        } = context;
41
42        const resolver = context.getEmitResolver();
43        const compilerOptions = context.getCompilerOptions();
44        const languageVersion = getEmitScriptTarget(compilerOptions);
45        const moduleKind = getEmitModuleKind(compilerOptions);
46        const typeSerializer = compilerOptions.emitDecoratorMetadata ? createRuntimeTypeSerializer(context) : undefined;
47
48        // Save the previous transformation hooks.
49        const previousOnEmitNode = context.onEmitNode;
50        const previousOnSubstituteNode = context.onSubstituteNode;
51
52        // Set new transformation hooks.
53        context.onEmitNode = onEmitNode;
54        context.onSubstituteNode = onSubstituteNode;
55
56        // Enable substitution for property/element access to emit const enum values.
57        context.enableSubstitution(SyntaxKind.PropertyAccessExpression);
58        context.enableSubstitution(SyntaxKind.ElementAccessExpression);
59
60        // These variables contain state that changes as we descend into the tree.
61        let currentSourceFile: SourceFile;
62        let currentNamespace: ModuleDeclaration;
63        let currentNamespaceContainerName: Identifier;
64        let currentLexicalScope: SourceFile | Block | ModuleBlock | CaseBlock;
65        let currentScopeFirstDeclarationsOfName: UnderscoreEscapedMap<Node> | undefined;
66        let currentClassHasParameterProperties: boolean | undefined;
67
68        /**
69         * Keeps track of whether expression substitution has been enabled for specific edge cases.
70         * They are persisted between each SourceFile transformation and should not be reset.
71         */
72        let enabledSubstitutions: TypeScriptSubstitutionFlags;
73
74        /**
75         * Keeps track of whether we are within any containing namespaces when performing
76         * just-in-time substitution while printing an expression identifier.
77         */
78        let applicableSubstitutions: TypeScriptSubstitutionFlags;
79
80        return transformSourceFileOrBundle;
81
82        function transformSourceFileOrBundle(node: SourceFile | Bundle) {
83            if (node.kind === SyntaxKind.Bundle) {
84                return transformBundle(node);
85            }
86            return transformSourceFile(node);
87        }
88
89        function transformBundle(node: Bundle) {
90            return factory.createBundle(node.sourceFiles.map(transformSourceFile), mapDefined(node.prepends, prepend => {
91                if (prepend.kind === SyntaxKind.InputFiles) {
92                    return createUnparsedSourceFile(prepend, "js");
93                }
94                return prepend;
95            }));
96        }
97
98        /**
99         * Transform TypeScript-specific syntax in a SourceFile.
100         *
101         * @param node A SourceFile node.
102         */
103        function transformSourceFile(node: SourceFile) {
104            if (node.isDeclarationFile) {
105                return node;
106            }
107
108            currentSourceFile = node;
109
110            const visited = saveStateAndInvoke(node, visitSourceFile);
111            addEmitHelpers(visited, context.readEmitHelpers());
112
113            currentSourceFile = undefined!;
114            return visited;
115        }
116
117        /**
118         * Visits a node, saving and restoring state variables on the stack.
119         *
120         * @param node The node to visit.
121         */
122        function saveStateAndInvoke<T>(node: Node, f: (node: Node) => T): T {
123            // Save state
124            const savedCurrentScope = currentLexicalScope;
125            const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName;
126            const savedCurrentClassHasParameterProperties = currentClassHasParameterProperties;
127
128            // Handle state changes before visiting a node.
129            onBeforeVisitNode(node);
130
131            const visited = f(node);
132
133            // Restore state
134            if (currentLexicalScope !== savedCurrentScope) {
135                currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName;
136            }
137
138            currentLexicalScope = savedCurrentScope;
139            currentClassHasParameterProperties = savedCurrentClassHasParameterProperties;
140            return visited;
141        }
142
143        /**
144         * Performs actions that should always occur immediately before visiting a node.
145         *
146         * @param node The node to visit.
147         */
148        function onBeforeVisitNode(node: Node) {
149            switch (node.kind) {
150                case SyntaxKind.SourceFile:
151                case SyntaxKind.CaseBlock:
152                case SyntaxKind.ModuleBlock:
153                case SyntaxKind.Block:
154                    currentLexicalScope = node as SourceFile | CaseBlock | ModuleBlock | Block;
155                    currentScopeFirstDeclarationsOfName = undefined;
156                    break;
157
158                case SyntaxKind.ClassDeclaration:
159                case SyntaxKind.FunctionDeclaration:
160                    if (hasSyntacticModifier(node, ModifierFlags.Ambient)) {
161                        break;
162                    }
163
164                    // Record these declarations provided that they have a name.
165                    if ((node as ClassDeclaration | FunctionDeclaration).name) {
166                        recordEmittedDeclarationInScope(node as ClassDeclaration | FunctionDeclaration);
167                    }
168                    else {
169                        // These nodes should always have names unless they are default-exports;
170                        // however, class declaration parsing allows for undefined names, so syntactically invalid
171                        // programs may also have an undefined name.
172                        Debug.assert(node.kind === SyntaxKind.ClassDeclaration || hasSyntacticModifier(node, ModifierFlags.Default));
173                    }
174
175                    break;
176            }
177        }
178
179        /**
180         * General-purpose node visitor.
181         *
182         * @param node The node to visit.
183         */
184        function visitor(node: Node): VisitResult<Node> {
185            return saveStateAndInvoke(node, visitorWorker);
186        }
187
188        /**
189         * Visits and possibly transforms any node.
190         *
191         * @param node The node to visit.
192         */
193        function visitorWorker(node: Node): VisitResult<Node> {
194            if (node.transformFlags & TransformFlags.ContainsTypeScript) {
195                return visitTypeScript(node);
196            }
197            return node;
198        }
199
200        /**
201         * Specialized visitor that visits the immediate children of a SourceFile.
202         *
203         * @param node The node to visit.
204         */
205        function sourceElementVisitor(node: Node): VisitResult<Node> {
206            return saveStateAndInvoke(node, sourceElementVisitorWorker);
207        }
208
209        /**
210         * Specialized visitor that visits the immediate children of a SourceFile.
211         *
212         * @param node The node to visit.
213         */
214        function sourceElementVisitorWorker(node: Node): VisitResult<Node> {
215            switch (node.kind) {
216                case SyntaxKind.ImportDeclaration:
217                case SyntaxKind.ImportEqualsDeclaration:
218                case SyntaxKind.ExportAssignment:
219                case SyntaxKind.ExportDeclaration:
220                    return visitElidableStatement(node as ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration);
221                default:
222                    return visitorWorker(node);
223            }
224        }
225
226        function visitElidableStatement(node: ImportDeclaration | ImportEqualsDeclaration | ExportAssignment | ExportDeclaration): VisitResult<Node> {
227            const parsed = getParseTreeNode(node);
228            if (parsed !== node) {
229                // If the node has been transformed by a `before` transformer, perform no ellision on it
230                // As the type information we would attempt to lookup to perform ellision is potentially unavailable for the synthesized nodes
231                // We do not reuse `visitorWorker`, as the ellidable statement syntax kinds are technically unrecognized by the switch-case in `visitTypeScript`,
232                // and will trigger debug failures when debug verbosity is turned up
233                if (node.transformFlags & TransformFlags.ContainsTypeScript) {
234                    // This node contains TypeScript, so we should visit its children.
235                    return visitEachChild(node, visitor, context);
236                }
237                // Otherwise, we can just return the node
238                return node;
239            }
240            switch (node.kind) {
241                case SyntaxKind.ImportDeclaration:
242                    return visitImportDeclaration(node);
243                case SyntaxKind.ImportEqualsDeclaration:
244                    return visitImportEqualsDeclaration(node);
245                case SyntaxKind.ExportAssignment:
246                    return visitExportAssignment(node);
247                case SyntaxKind.ExportDeclaration:
248                    return visitExportDeclaration(node);
249                default:
250                    Debug.fail("Unhandled ellided statement");
251            }
252        }
253
254        /**
255         * Specialized visitor that visits the immediate children of a namespace.
256         *
257         * @param node The node to visit.
258         */
259        function namespaceElementVisitor(node: Node): VisitResult<Node> {
260            return saveStateAndInvoke(node, namespaceElementVisitorWorker);
261        }
262
263        /**
264         * Specialized visitor that visits the immediate children of a namespace.
265         *
266         * @param node The node to visit.
267         */
268        function namespaceElementVisitorWorker(node: Node): VisitResult<Node> {
269            if (node.kind === SyntaxKind.ExportDeclaration ||
270                node.kind === SyntaxKind.ImportDeclaration ||
271                node.kind === SyntaxKind.ImportClause ||
272                (node.kind === SyntaxKind.ImportEqualsDeclaration &&
273                 (node as ImportEqualsDeclaration).moduleReference.kind === SyntaxKind.ExternalModuleReference)) {
274                // do not emit ES6 imports and exports since they are illegal inside a namespace
275                return undefined;
276            }
277            else if (node.transformFlags & TransformFlags.ContainsTypeScript || hasSyntacticModifier(node, ModifierFlags.Export)) {
278                return visitTypeScript(node);
279            }
280
281            return node;
282        }
283
284        /**
285         * Gets a specialized visitor that visits the immediate children of a class with TypeScript syntax.
286         *
287         * @param parent The class containing the elements to visit.
288         */
289        function getClassElementVisitor(parent: ClassLikeDeclaration): (node: Node) => VisitResult<Node> {
290            return node => saveStateAndInvoke(node, n => classElementVisitorWorker(n, parent));
291        }
292
293        /**
294         * Specialized visitor that visits the immediate children of a class with TypeScript syntax.
295         *
296         * @param node The node to visit.
297         */
298        function classElementVisitorWorker(node: Node, parent: ClassLikeDeclaration): VisitResult<Node> {
299            switch (node.kind) {
300                case SyntaxKind.Constructor:
301                    return visitConstructor(node as ConstructorDeclaration);
302
303                case SyntaxKind.PropertyDeclaration:
304                    // Property declarations are not TypeScript syntax, but they must be visited
305                    // for the decorator transformation.
306                    return visitPropertyDeclaration(node as PropertyDeclaration, parent);
307
308                case SyntaxKind.GetAccessor:
309                    // Get Accessors can have TypeScript modifiers, decorators, and type annotations.
310                    return visitGetAccessor(node as GetAccessorDeclaration, parent);
311
312                case SyntaxKind.SetAccessor:
313                    // Set Accessors can have TypeScript modifiers and type annotations.
314                    return visitSetAccessor(node as SetAccessorDeclaration, parent);
315
316                case SyntaxKind.MethodDeclaration:
317                    // TypeScript method declarations may have decorators, modifiers
318                    // or type annotations.
319                    return visitMethodDeclaration(node as MethodDeclaration, parent);
320
321                case SyntaxKind.ClassStaticBlockDeclaration:
322                    return visitEachChild(node, visitor, context);
323
324                case SyntaxKind.SemicolonClassElement:
325                    return node;
326
327                case SyntaxKind.IndexSignature:
328                    // Index signatures are elided
329                    return;
330
331                default:
332                    return Debug.failBadSyntaxKind(node);
333            }
334        }
335
336        function getObjectLiteralElementVisitor(parent: ObjectLiteralExpression): (node: Node) => VisitResult<Node> {
337            return node => saveStateAndInvoke(node, n => objectLiteralElementVisitorWorker(n, parent));
338        }
339
340        function objectLiteralElementVisitorWorker(node: Node, parent: ObjectLiteralExpression): VisitResult<Node> {
341            switch (node.kind) {
342                case SyntaxKind.PropertyAssignment:
343                case SyntaxKind.ShorthandPropertyAssignment:
344                case SyntaxKind.SpreadAssignment:
345                    return visitor(node);
346
347                case SyntaxKind.GetAccessor:
348                    // Get Accessors can have TypeScript modifiers, decorators, and type annotations.
349                    return visitGetAccessor(node as GetAccessorDeclaration, parent);
350
351                case SyntaxKind.SetAccessor:
352                    // Set Accessors can have TypeScript modifiers and type annotations.
353                    return visitSetAccessor(node as SetAccessorDeclaration, parent);
354
355                case SyntaxKind.MethodDeclaration:
356                    // TypeScript method declarations may have decorators, modifiers
357                    // or type annotations.
358                    return visitMethodDeclaration(node as MethodDeclaration, parent);
359
360                default:
361                    return Debug.failBadSyntaxKind(node);
362            }
363        }
364
365        function modifierVisitor(node: Node): VisitResult<Node> {
366            if (isDecorator(node)) return undefined;
367            if (modifierToFlag(node.kind) & ModifierFlags.TypeScriptModifier) {
368                return undefined;
369            }
370            else if (currentNamespace && node.kind === SyntaxKind.ExportKeyword) {
371                return undefined;
372            }
373
374            return node;
375        }
376
377        /**
378         * Branching visitor, visits a TypeScript syntax node.
379         *
380         * @param node The node to visit.
381         */
382        function visitTypeScript(node: Node): VisitResult<Node> {
383            if (isStatement(node) && hasSyntacticModifier(node, ModifierFlags.Ambient)) {
384                // TypeScript ambient declarations are elided, but some comments may be preserved.
385                // See the implementation of `getLeadingComments` in comments.ts for more details.
386                return factory.createNotEmittedStatement(node);
387            }
388
389            switch (node.kind) {
390                case SyntaxKind.ExportKeyword:
391                case SyntaxKind.DefaultKeyword:
392                    // ES6 export and default modifiers are elided when inside a namespace.
393                    return currentNamespace ? undefined : node;
394
395                case SyntaxKind.PublicKeyword:
396                case SyntaxKind.PrivateKeyword:
397                case SyntaxKind.ProtectedKeyword:
398                case SyntaxKind.AbstractKeyword:
399                case SyntaxKind.OverrideKeyword:
400                case SyntaxKind.ConstKeyword:
401                case SyntaxKind.DeclareKeyword:
402                case SyntaxKind.ReadonlyKeyword:
403                case SyntaxKind.InKeyword:
404                case SyntaxKind.OutKeyword:
405                // TypeScript accessibility and readonly modifiers are elided
406                // falls through
407                case SyntaxKind.ArrayType:
408                case SyntaxKind.TupleType:
409                case SyntaxKind.OptionalType:
410                case SyntaxKind.RestType:
411                case SyntaxKind.TypeLiteral:
412                case SyntaxKind.TypePredicate:
413                case SyntaxKind.TypeParameter:
414                case SyntaxKind.AnyKeyword:
415                case SyntaxKind.UnknownKeyword:
416                case SyntaxKind.BooleanKeyword:
417                case SyntaxKind.StringKeyword:
418                case SyntaxKind.NumberKeyword:
419                case SyntaxKind.NeverKeyword:
420                case SyntaxKind.VoidKeyword:
421                case SyntaxKind.SymbolKeyword:
422                case SyntaxKind.ConstructorType:
423                case SyntaxKind.FunctionType:
424                case SyntaxKind.TypeQuery:
425                case SyntaxKind.TypeReference:
426                case SyntaxKind.UnionType:
427                case SyntaxKind.IntersectionType:
428                case SyntaxKind.ConditionalType:
429                case SyntaxKind.ParenthesizedType:
430                case SyntaxKind.ThisType:
431                case SyntaxKind.TypeOperator:
432                case SyntaxKind.IndexedAccessType:
433                case SyntaxKind.MappedType:
434                case SyntaxKind.LiteralType:
435                    // TypeScript type nodes are elided.
436                    // falls through
437
438                case SyntaxKind.IndexSignature:
439                    // TypeScript index signatures are elided.
440                    return undefined;
441
442                case SyntaxKind.TypeAliasDeclaration:
443                    // TypeScript type-only declarations are elided.
444                    return factory.createNotEmittedStatement(node);
445
446                case SyntaxKind.NamespaceExportDeclaration:
447                    // TypeScript namespace export declarations are elided.
448                    return undefined;
449
450                case SyntaxKind.InterfaceDeclaration:
451                    // TypeScript interfaces are elided, but some comments may be preserved.
452                    // See the implementation of `getLeadingComments` in comments.ts for more details.
453                    return factory.createNotEmittedStatement(node);
454
455                case SyntaxKind.ClassDeclaration:
456                    // This may be a class declaration with TypeScript syntax extensions.
457                    //
458                    // TypeScript class syntax extensions include:
459                    // - decorators
460                    // - optional `implements` heritage clause
461                    // - parameter property assignments in the constructor
462                    // - index signatures
463                    // - method overload signatures
464                    return visitClassDeclaration(node as ClassDeclaration);
465
466                case SyntaxKind.ClassExpression:
467                    // This may be a class expression with TypeScript syntax extensions.
468                    //
469                    // TypeScript class syntax extensions include:
470                    // - decorators
471                    // - optional `implements` heritage clause
472                    // - parameter property assignments in the constructor
473                    // - index signatures
474                    // - method overload signatures
475                    return visitClassExpression(node as ClassExpression);
476
477                case SyntaxKind.AnnotationDeclaration:
478                    return factory.createNotEmittedStatement(node);
479
480                case SyntaxKind.HeritageClause:
481                    // This may be a heritage clause with TypeScript syntax extensions.
482                    //
483                    // TypeScript heritage clause extensions include:
484                    // - `implements` clause
485                    return visitHeritageClause(node as HeritageClause);
486
487                case SyntaxKind.ExpressionWithTypeArguments:
488                    // TypeScript supports type arguments on an expression in an `extends` heritage clause.
489                    return visitExpressionWithTypeArguments(node as ExpressionWithTypeArguments);
490
491                case SyntaxKind.ObjectLiteralExpression:
492                    return visitObjectLiteralExpression(node as ObjectLiteralExpression);
493
494                case SyntaxKind.Constructor:
495                case SyntaxKind.PropertyDeclaration:
496                case SyntaxKind.AnnotationPropertyDeclaration:
497                case SyntaxKind.MethodDeclaration:
498                case SyntaxKind.GetAccessor:
499                case SyntaxKind.SetAccessor:
500                case SyntaxKind.ClassStaticBlockDeclaration:
501                    return Debug.fail("Class and object literal elements must be visited with their respective visitors");
502
503                case SyntaxKind.FunctionDeclaration:
504                    // Typescript function declarations can have modifiers, decorators, and type annotations.
505                    return visitFunctionDeclaration(node as FunctionDeclaration);
506
507                case SyntaxKind.FunctionExpression:
508                    // TypeScript function expressions can have modifiers and type annotations.
509                    return visitFunctionExpression(node as FunctionExpression);
510
511                case SyntaxKind.ArrowFunction:
512                    // TypeScript arrow functions can have modifiers and type annotations.
513                    return visitArrowFunction(node as ArrowFunction);
514
515                case SyntaxKind.Parameter:
516                    // This may be a parameter declaration with TypeScript syntax extensions.
517                    //
518                    // TypeScript parameter declaration syntax extensions include:
519                    // - decorators
520                    // - accessibility modifiers
521                    // - the question mark (?) token for optional parameters
522                    // - type annotations
523                    // - this parameters
524                    return visitParameter(node as ParameterDeclaration);
525
526                case SyntaxKind.ParenthesizedExpression:
527                    // ParenthesizedExpressions are TypeScript if their expression is a
528                    // TypeAssertion or AsExpression
529                    return visitParenthesizedExpression(node as ParenthesizedExpression);
530
531                case SyntaxKind.TypeAssertionExpression:
532                case SyntaxKind.AsExpression:
533                    // TypeScript type assertions are removed, but their subtrees are preserved.
534                    return visitAssertionExpression(node as AssertionExpression);
535
536                case SyntaxKind.SatisfiesExpression:
537                    return visitSatisfiesExpression(node as SatisfiesExpression);
538
539                case SyntaxKind.CallExpression:
540                    return visitCallExpression(node as CallExpression);
541
542                case SyntaxKind.NewExpression:
543                    return visitNewExpression(node as NewExpression);
544
545                case SyntaxKind.TaggedTemplateExpression:
546                    return visitTaggedTemplateExpression(node as TaggedTemplateExpression);
547
548                case SyntaxKind.NonNullExpression:
549                    // TypeScript non-null expressions are removed, but their subtrees are preserved.
550                    return visitNonNullExpression(node as NonNullExpression);
551
552                case SyntaxKind.EnumDeclaration:
553                    // TypeScript enum declarations do not exist in ES6 and must be rewritten.
554                    return visitEnumDeclaration(node as EnumDeclaration);
555
556                case SyntaxKind.VariableStatement:
557                    // TypeScript namespace exports for variable statements must be transformed.
558                    return visitVariableStatement(node as VariableStatement);
559
560                case SyntaxKind.VariableDeclaration:
561                    return visitVariableDeclaration(node as VariableDeclaration);
562
563                case SyntaxKind.ModuleDeclaration:
564                    // TypeScript namespace declarations must be transformed.
565                    return visitModuleDeclaration(node as ModuleDeclaration);
566
567                case SyntaxKind.ImportEqualsDeclaration:
568                    // TypeScript namespace or external module import.
569                    return visitImportEqualsDeclaration(node as ImportEqualsDeclaration);
570
571                case SyntaxKind.JsxSelfClosingElement:
572                    return visitJsxSelfClosingElement(node as JsxSelfClosingElement);
573
574                case SyntaxKind.JsxOpeningElement:
575                    return visitJsxJsxOpeningElement(node as JsxOpeningElement);
576
577                default:
578                    // node contains some other TypeScript syntax
579                    return visitEachChild(node, visitor, context);
580            }
581        }
582
583        function visitSourceFile(node: SourceFile) {
584            const alwaysStrict = getStrictOptionValue(compilerOptions, "alwaysStrict") &&
585                !(isExternalModule(node) && moduleKind >= ModuleKind.ES2015) &&
586                !isJsonSourceFile(node);
587
588            return factory.updateSourceFile(
589                node,
590                visitLexicalEnvironment(node.statements, sourceElementVisitor, context, /*start*/ 0, alwaysStrict));
591        }
592
593        function visitObjectLiteralExpression(node: ObjectLiteralExpression) {
594            return factory.updateObjectLiteralExpression(
595                node,
596                visitNodes(node.properties, getObjectLiteralElementVisitor(node), isObjectLiteralElement)
597            );
598        }
599
600        function getClassFacts(node: ClassDeclaration, staticProperties: readonly PropertyDeclaration[]) {
601            let facts = ClassFacts.None;
602            if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties;
603            const extendsClauseElement = getEffectiveBaseTypeNode(node);
604            if (extendsClauseElement && skipOuterExpressions(extendsClauseElement.expression).kind !== SyntaxKind.NullKeyword) facts |= ClassFacts.IsDerivedClass;
605            if (classOrConstructorParameterIsDecorated(node)) facts |= ClassFacts.HasConstructorDecorators;
606            if (childIsDecorated(node)) facts |= ClassFacts.HasMemberDecorators;
607            if (isExportOfNamespace(node)) facts |= ClassFacts.IsExportOfNamespace;
608            else if (isDefaultExternalModuleExport(node)) facts |= ClassFacts.IsDefaultExternalExport;
609            else if (isNamedExternalModuleExport(node)) facts |= ClassFacts.IsNamedExternalExport;
610            if (languageVersion <= ScriptTarget.ES5 && (facts & ClassFacts.MayNeedImmediatelyInvokedFunctionExpression)) facts |= ClassFacts.UseImmediatelyInvokedFunctionExpression;
611            return facts;
612        }
613
614        function hasTypeScriptClassSyntax(node: Node) {
615            return !!(node.transformFlags & TransformFlags.ContainsTypeScriptClassSyntax);
616        }
617
618        function isClassLikeDeclarationWithTypeScriptSyntax(node: ClassLikeDeclaration) {
619            return hasDecorators(node)
620                || some(node.typeParameters)
621                || some(node.heritageClauses, hasTypeScriptClassSyntax)
622                || some(node.members, hasTypeScriptClassSyntax);
623        }
624
625        function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> {
626            if (!isClassLikeDeclarationWithTypeScriptSyntax(node) && !(currentNamespace && hasSyntacticModifier(node, ModifierFlags.Export))) {
627                return factory.updateClassDeclaration(
628                    node,
629                    visitNodes(node.modifiers, modifierVisitor, isModifier),
630                    node.name,
631                    /*typeParameters*/ undefined,
632                    visitNodes(node.heritageClauses, visitor, isHeritageClause),
633                    visitNodes(node.members, getClassElementVisitor(node), isClassElement)
634                );
635            }
636
637            const staticProperties = getProperties(node, /*requireInitializer*/ true, /*isStatic*/ true);
638            const facts = getClassFacts(node, staticProperties);
639
640            if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression) {
641                context.startLexicalEnvironment();
642            }
643
644            const name = node.name || (facts & ClassFacts.NeedsName ? factory.getGeneratedNameForNode(node) : undefined);
645            const allDecorators = getAllDecoratorsOfClass(node);
646            const decorators = transformAllDecoratorsOfDeclaration(node, node, allDecorators);
647
648            // we do not emit modifiers on the declaration if we are emitting an IIFE
649            const modifiers = !(facts & ClassFacts.UseImmediatelyInvokedFunctionExpression)
650                ? visitNodes(node.modifiers, modifierVisitor, isModifier)
651                : elideNodes(factory, node.modifiers); // preserve positions, if available
652
653            //  ${modifiers} class ${name} ${heritageClauses} {
654            //      ${members}
655            //  }
656            const classStatement = factory.updateClassDeclaration(
657                node,
658                concatenate<ModifierLike>(decorators, modifiers),
659                name,
660                /*typeParameters*/ undefined,
661                visitNodes(node.heritageClauses, visitor, isHeritageClause),
662                transformClassMembers(node)
663            );
664
665            // To better align with the old emitter, we should not emit a trailing source map
666            // entry if the class has static properties.
667            let emitFlags = getEmitFlags(node);
668            if (facts & ClassFacts.HasStaticInitializedProperties) {
669                emitFlags |= EmitFlags.NoTrailingSourceMap;
670            }
671
672            setEmitFlags(classStatement, emitFlags);
673
674            let statements: Statement[] = [classStatement];
675
676            if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression) {
677                // When we emit a TypeScript class down to ES5, we must wrap it in an IIFE so that the
678                // 'es2015' transformer can properly nest static initializers and decorators. The result
679                // looks something like:
680                //
681                //  var C = function () {
682                //      class C {
683                //      }
684                //      C.static_prop = 1;
685                //      return C;
686                //  }();
687                //
688                const closingBraceLocation = createTokenRange(skipTrivia(currentSourceFile.text, node.members.end), SyntaxKind.CloseBraceToken);
689                const localName = factory.getInternalName(node);
690
691                // The following partially-emitted expression exists purely to align our sourcemap
692                // emit with the original emitter.
693                const outer = factory.createPartiallyEmittedExpression(localName);
694                setTextRangeEnd(outer, closingBraceLocation.end);
695                setEmitFlags(outer, EmitFlags.NoComments);
696
697                const statement = factory.createReturnStatement(outer);
698                setTextRangePos(statement, closingBraceLocation.pos);
699                setEmitFlags(statement, EmitFlags.NoComments | EmitFlags.NoTokenSourceMaps);
700                statements.push(statement);
701
702                insertStatementsAfterStandardPrologue(statements, context.endLexicalEnvironment());
703
704                const iife = factory.createImmediatelyInvokedArrowFunction(statements);
705                setEmitFlags(iife, EmitFlags.TypeScriptClassWrapper);
706
707                const varStatement = factory.createVariableStatement(
708                    /*modifiers*/ undefined,
709                    factory.createVariableDeclarationList([
710                        factory.createVariableDeclaration(
711                            factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ false),
712                            /*exclamationToken*/ undefined,
713                            /*type*/ undefined,
714                            iife
715                        )
716                    ])
717                );
718
719                setOriginalNode(varStatement, node);
720                setCommentRange(varStatement, node);
721                setSourceMapRange(varStatement, moveRangePastDecorators(node));
722                startOnNewLine(varStatement);
723                statements = [varStatement];
724            }
725
726            // If the class is exported as part of a TypeScript namespace, emit the namespace export.
727            // Otherwise, if the class was exported at the top level and was decorated, emit an export
728            // declaration or export default for the class.
729            if (facts & ClassFacts.IsExportOfNamespace) {
730                addExportMemberAssignment(statements, node);
731            }
732            else if (facts & ClassFacts.UseImmediatelyInvokedFunctionExpression || facts & ClassFacts.HasConstructorDecorators) {
733                if (facts & ClassFacts.IsDefaultExternalExport) {
734                    statements.push(factory.createExportDefault(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)));
735                }
736                else if (facts & ClassFacts.IsNamedExternalExport) {
737                    statements.push(factory.createExternalModuleExport(factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)));
738                }
739            }
740
741            if (statements.length > 1) {
742                // Add a DeclarationMarker as a marker for the end of the declaration
743                statements.push(factory.createEndOfDeclarationMarker(node));
744                setEmitFlags(classStatement, getEmitFlags(classStatement) | EmitFlags.HasEndOfDeclarationMarker);
745            }
746
747            return singleOrMany(statements);
748        }
749
750        function visitClassExpression(node: ClassExpression): Expression {
751            const allDecorators = getAllDecoratorsOfClass(node);
752            const decorators = transformAllDecoratorsOfDeclaration(node, node, allDecorators);
753            return factory.updateClassExpression(
754                node,
755                decorators,
756                node.name,
757                /*typeParameters*/ undefined,
758                visitNodes(node.heritageClauses, visitor, isHeritageClause),
759                isClassLikeDeclarationWithTypeScriptSyntax(node) ?
760                    transformClassMembers(node) :
761                    visitNodes(node.members, getClassElementVisitor(node), isClassElement)
762            );
763        }
764
765        /**
766         * Transforms the members of a class.
767         *
768         * @param node The current class.
769         */
770        function transformClassMembers(node: ClassDeclaration | ClassExpression) {
771            const members: ClassElement[] = [];
772            const constructor = getFirstConstructorWithBody(node);
773            const parametersWithPropertyAssignments = constructor &&
774                filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor));
775
776            if (parametersWithPropertyAssignments) {
777                for (const parameter of parametersWithPropertyAssignments) {
778                    if (isIdentifier(parameter.name)) {
779                        members.push(setOriginalNode(factory.createPropertyDeclaration(
780                            /*modifiers*/ undefined,
781                            parameter.name,
782                            /*questionOrExclamationToken*/ undefined,
783                            /*type*/ undefined,
784                            /*initializer*/ undefined), parameter));
785                    }
786                }
787            }
788
789            addRange(members, visitNodes(node.members, getClassElementVisitor(node), isClassElement));
790            return setTextRange(factory.createNodeArray(members), /*location*/ node.members);
791        }
792
793        /**
794         * Transforms all of the decorators for a declaration into an array of expressions.
795         *
796         * @param node The declaration node.
797         * @param allDecorators An object containing all of the decorators for the declaration.
798         */
799        function transformAllDecoratorsOfDeclaration(node: Declaration, container: ClassLikeDeclaration, allDecorators: AllDecorators | undefined) {
800            if (!allDecorators) {
801                return undefined;
802            }
803
804            const decorators = visitArray(allDecorators.decorators, visitor, isDecorator);
805            const parameterDecorators = flatMap(allDecorators.parameters, transformDecoratorsOfParameter);
806            const metadataDecorators = some(decorators) || some(parameterDecorators) ? getTypeMetadata(node, container) : undefined;
807            const result = factory.createNodeArray(concatenate(concatenate(decorators, parameterDecorators), metadataDecorators));
808            const pos = firstOrUndefined(allDecorators.decorators)?.pos ?? -1;
809            const end = lastOrUndefined(allDecorators.decorators)?.end ?? -1;
810            setTextRangePosEnd(result, pos, end);
811            return result;
812        }
813
814        /**
815         * Transforms the decorators of a parameter into decorators of the class/method.
816         *
817         * @param parameterDecorators The decorators for the parameter at the provided offset.
818         * @param parameterOffset The offset of the parameter.
819         */
820        function transformDecoratorsOfParameter(parameterDecorators: Decorator[], parameterOffset: number) {
821            if (parameterDecorators) {
822                const decorators: Decorator[] = [];
823                for (const parameterDecorator of parameterDecorators) {
824                    const expression = visitNode(parameterDecorator.expression, visitor, isExpression);
825                    const helper = emitHelpers().createParamHelper(expression, parameterOffset);
826                    setTextRange(helper, parameterDecorator.expression);
827                    setEmitFlags(helper, EmitFlags.NoComments);
828
829                    const decorator = factory.createDecorator(helper);
830                    setSourceMapRange(decorator, parameterDecorator.expression);
831                    setCommentRange(decorator, parameterDecorator.expression);
832                    setEmitFlags(decorator, EmitFlags.NoComments);
833                    decorators.push(decorator);
834                }
835                return decorators;
836            }
837        }
838
839        /**
840         * Gets optional type metadata for a declaration.
841         *
842         * @param node The declaration node.
843         */
844        function getTypeMetadata(node: Declaration, container: ClassLikeDeclaration) {
845            return USE_NEW_TYPE_METADATA_FORMAT ?
846                getNewTypeMetadata(node, container) :
847                getOldTypeMetadata(node, container);
848        }
849
850        function getOldTypeMetadata(node: Declaration, container: ClassLikeDeclaration) {
851            if (typeSerializer) {
852                let decorators: Decorator[] | undefined;
853                if (shouldAddTypeMetadata(node)) {
854                    const typeMetadata = emitHelpers().createMetadataHelper("design:type", typeSerializer.serializeTypeOfNode({ currentLexicalScope, currentNameScope: container }, node));
855                    decorators = append(decorators, factory.createDecorator(typeMetadata));
856                }
857                if (shouldAddParamTypesMetadata(node)) {
858                    const paramTypesMetadata = emitHelpers().createMetadataHelper("design:paramtypes", typeSerializer.serializeParameterTypesOfNode({ currentLexicalScope, currentNameScope: container }, node, container));
859                    decorators = append(decorators, factory.createDecorator(paramTypesMetadata));
860                }
861                if (shouldAddReturnTypeMetadata(node)) {
862                    const returnTypeMetadata = emitHelpers().createMetadataHelper("design:returntype", typeSerializer.serializeReturnTypeOfNode({ currentLexicalScope, currentNameScope: container }, node));
863                    decorators = append(decorators, factory.createDecorator(returnTypeMetadata));
864                }
865                return decorators;
866            }
867        }
868
869        function getNewTypeMetadata(node: Declaration, container: ClassLikeDeclaration) {
870            if (typeSerializer) {
871                let properties: ObjectLiteralElementLike[] | undefined;
872                if (shouldAddTypeMetadata(node)) {
873                    const typeProperty = factory.createPropertyAssignment("type", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), typeSerializer.serializeTypeOfNode({ currentLexicalScope, currentNameScope: container }, node)));
874                    properties = append(properties, typeProperty);
875                }
876                if (shouldAddParamTypesMetadata(node)) {
877                    const paramTypeProperty = factory.createPropertyAssignment("paramTypes", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), typeSerializer.serializeParameterTypesOfNode({ currentLexicalScope, currentNameScope: container }, node, container)));
878                    properties = append(properties, paramTypeProperty);
879                }
880                if (shouldAddReturnTypeMetadata(node)) {
881                    const returnTypeProperty = factory.createPropertyAssignment("returnType", factory.createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, factory.createToken(SyntaxKind.EqualsGreaterThanToken), typeSerializer.serializeReturnTypeOfNode({ currentLexicalScope, currentNameScope: container }, node)));
882                    properties = append(properties, returnTypeProperty);
883                }
884                if (properties) {
885                    const typeInfoMetadata = emitHelpers().createMetadataHelper("design:typeinfo", factory.createObjectLiteralExpression(properties, /*multiLine*/ true));
886                    return [factory.createDecorator(typeInfoMetadata)];
887                }
888            }
889        }
890
891        /**
892         * Determines whether to emit the "design:type" metadata based on the node's kind.
893         * The caller should have already tested whether the node has decorators and whether the
894         * emitDecoratorMetadata compiler option is set.
895         *
896         * @param node The node to test.
897         */
898        function shouldAddTypeMetadata(node: Declaration): node is MethodDeclaration | AccessorDeclaration | PropertyDeclaration {
899            const kind = node.kind;
900            return kind === SyntaxKind.MethodDeclaration
901                || kind === SyntaxKind.GetAccessor
902                || kind === SyntaxKind.SetAccessor
903                || kind === SyntaxKind.PropertyDeclaration;
904        }
905
906        /**
907         * Determines whether to emit the "design:returntype" metadata based on the node's kind.
908         * The caller should have already tested whether the node has decorators and whether the
909         * emitDecoratorMetadata compiler option is set.
910         *
911         * @param node The node to test.
912         */
913        function shouldAddReturnTypeMetadata(node: Declaration): node is MethodDeclaration {
914            return node.kind === SyntaxKind.MethodDeclaration;
915        }
916
917        /**
918         * Determines whether to emit the "design:paramtypes" metadata based on the node's kind.
919         * The caller should have already tested whether the node has decorators and whether the
920         * emitDecoratorMetadata compiler option is set.
921         *
922         * @param node The node to test.
923         */
924        function shouldAddParamTypesMetadata(node: Declaration): node is ClassLikeDeclaration & { _hasConstructorBrand: never } | MethodDeclaration | AccessorDeclaration {
925            switch (node.kind) {
926                case SyntaxKind.ClassDeclaration:
927                case SyntaxKind.ClassExpression:
928                    return getFirstConstructorWithBody(node as ClassLikeDeclaration) !== undefined;
929                case SyntaxKind.MethodDeclaration:
930                case SyntaxKind.GetAccessor:
931                case SyntaxKind.SetAccessor:
932                    return true;
933            }
934            return false;
935        }
936
937        /**
938         * Gets an expression that represents a property name (for decorated properties or enums).
939         * For a computed property, a name is generated for the node.
940         *
941         * @param member The member whose name should be converted into an expression.
942         */
943        function getExpressionForPropertyName(member: ClassElement | EnumMember, generateNameForComputedPropertyName: boolean): Expression {
944            const name = member.name!;
945            if (isPrivateIdentifier(name)) {
946                return factory.createIdentifier("");
947            }
948            else if (isComputedPropertyName(name)) {
949                return generateNameForComputedPropertyName && !isSimpleInlineableExpression(name.expression)
950                    ? factory.getGeneratedNameForNode(name)
951                    : name.expression;
952            }
953            else if (isIdentifier(name)) {
954                return factory.createStringLiteral(idText(name));
955            }
956            else {
957                return factory.cloneNode(name);
958            }
959        }
960
961        /**
962         * Visits the property name of a class element, for use when emitting property
963         * initializers. For a computed property on a node with decorators, a temporary
964         * value is stored for later use.
965         *
966         * @param member The member whose name should be visited.
967         */
968        function visitPropertyNameOfClassElement(member: ClassElement): PropertyName {
969            const name = member.name!;
970            // Computed property names need to be transformed into a hoisted variable when they are used more than once.
971            // The names are used more than once when:
972            //   - the property is non-static and its initializer is moved to the constructor (when there are parameter property assignments).
973            //   - the property has a decorator.
974            if (isComputedPropertyName(name) && ((!hasStaticModifier(member) && currentClassHasParameterProperties) || hasDecorators(member))) {
975                const expression = visitNode(name.expression, visitor, isExpression);
976                const innerExpression = skipPartiallyEmittedExpressions(expression);
977                if (!isSimpleInlineableExpression(innerExpression)) {
978                    const generatedName = factory.getGeneratedNameForNode(name);
979                    hoistVariableDeclaration(generatedName);
980                    return factory.updateComputedPropertyName(name, factory.createAssignment(generatedName, expression));
981                }
982            }
983            return visitNode(name, visitor, isPropertyName);
984        }
985
986        /**
987         * Transforms a HeritageClause with TypeScript syntax.
988         *
989         * This function will only be called when one of the following conditions are met:
990         * - The node is a non-`extends` heritage clause that should be elided.
991         * - The node is an `extends` heritage clause that should be visited, but only allow a single type.
992         *
993         * @param node The HeritageClause to transform.
994         */
995        function visitHeritageClause(node: HeritageClause): HeritageClause | undefined {
996            if (node.token === SyntaxKind.ImplementsKeyword) {
997                // implements clauses are elided
998                return undefined;
999            }
1000            return visitEachChild(node, visitor, context);
1001        }
1002
1003        /**
1004         * Transforms an ExpressionWithTypeArguments with TypeScript syntax.
1005         *
1006         * This function will only be called when one of the following conditions are met:
1007         * - The node contains type arguments that should be elided.
1008         *
1009         * @param node The ExpressionWithTypeArguments to transform.
1010         */
1011        function visitExpressionWithTypeArguments(node: ExpressionWithTypeArguments): ExpressionWithTypeArguments {
1012            return factory.updateExpressionWithTypeArguments(
1013                node,
1014                visitNode(node.expression, visitor, isLeftHandSideExpression),
1015                /*typeArguments*/ undefined
1016            );
1017        }
1018
1019        /**
1020         * Determines whether to emit a function-like declaration. We should not emit the
1021         * declaration if it does not have a body.
1022         *
1023         * @param node The declaration node.
1024         */
1025        function shouldEmitFunctionLikeDeclaration<T extends FunctionLikeDeclaration>(node: T): node is T & { body: NonNullable<T["body"]> } {
1026            return !nodeIsMissing(node.body);
1027        }
1028
1029        function visitPropertyDeclaration(node: PropertyDeclaration, parent: ClassLikeDeclaration) {
1030            const isAmbient = node.flags & NodeFlags.Ambient || hasSyntacticModifier(node, ModifierFlags.Abstract);
1031            if (isAmbient && !hasDecorators(node)) {
1032                return undefined;
1033            }
1034
1035            const allDecorators = getAllDecoratorsOfClassElement(node, parent);
1036            const decorators = transformAllDecoratorsOfDeclaration(node, parent, allDecorators);
1037
1038            // Preserve a `declare x` property with decorators to be handled by the decorators transform
1039            if (isAmbient) {
1040                return factory.updatePropertyDeclaration(
1041                    node,
1042                    concatenate<ModifierLike>(decorators, factory.createModifiersFromModifierFlags(ModifierFlags.Ambient)),
1043                    visitNode(node.name, visitor, isPropertyName),
1044                    /*questionOrExclamationToken*/ undefined,
1045                    /*type*/ undefined,
1046                    /*initializer*/ undefined
1047                );
1048            }
1049
1050            return factory.updatePropertyDeclaration(
1051                node,
1052                concatenate(decorators, visitNodes(node.modifiers, modifierVisitor, isModifierLike)),
1053                visitPropertyNameOfClassElement(node),
1054                /*questionOrExclamationToken*/ undefined,
1055                /*type*/ undefined,
1056                visitNode(node.initializer, visitor)
1057            );
1058        }
1059
1060        function visitConstructor(node: ConstructorDeclaration) {
1061            if (!shouldEmitFunctionLikeDeclaration(node)) {
1062                return undefined;
1063            }
1064
1065            return factory.updateConstructorDeclaration(
1066                node,
1067                /*modifiers*/ undefined,
1068                visitParameterList(node.parameters, visitor, context),
1069                transformConstructorBody(node.body, node)
1070            );
1071        }
1072
1073        function transformConstructorBody(body: Block, constructor: ConstructorDeclaration) {
1074            const parametersWithPropertyAssignments = constructor &&
1075                filter(constructor.parameters, p => isParameterPropertyDeclaration(p, constructor));
1076            if (!some(parametersWithPropertyAssignments)) {
1077                return visitFunctionBody(body, visitor, context);
1078            }
1079
1080            let statements: Statement[] = [];
1081
1082            resumeLexicalEnvironment();
1083
1084            const prologueStatementCount = factory.copyPrologue(body.statements, statements, /*ensureUseStrict*/ false, visitor);
1085            const superStatementIndex = findSuperStatementIndex(body.statements, prologueStatementCount);
1086
1087            // If there was a super call, visit existing statements up to and including it
1088            if (superStatementIndex >= 0) {
1089                addRange(
1090                    statements,
1091                    visitNodes(body.statements, visitor, isStatement, prologueStatementCount, superStatementIndex + 1 - prologueStatementCount),
1092                );
1093            }
1094
1095            // Transform parameters into property assignments. Transforms this:
1096            //
1097            //  constructor (public x, public y) {
1098            //  }
1099            //
1100            // Into this:
1101            //
1102            //  constructor (x, y) {
1103            //      this.x = x;
1104            //      this.y = y;
1105            //  }
1106            //
1107            const parameterPropertyAssignments = mapDefined(parametersWithPropertyAssignments, transformParameterWithPropertyAssignment);
1108
1109            // If there is a super() call, the parameter properties go immediately after it
1110            if (superStatementIndex >= 0) {
1111                addRange(statements, parameterPropertyAssignments);
1112            }
1113            // Since there was no super() call, parameter properties are the first statements in the constructor after any prologue statements
1114            else {
1115                statements = [
1116                    ...statements.slice(0, prologueStatementCount),
1117                    ...parameterPropertyAssignments,
1118                    ...statements.slice(prologueStatementCount),
1119                ];
1120            }
1121
1122            // Add remaining statements from the body, skipping the super() call if it was found and any (already added) prologue statements
1123            const start = superStatementIndex >= 0 ? superStatementIndex + 1 : prologueStatementCount;
1124            addRange(statements, visitNodes(body.statements, visitor, isStatement, start));
1125
1126            // End the lexical environment.
1127            statements = factory.mergeLexicalEnvironment(statements, endLexicalEnvironment());
1128            const block = factory.createBlock(setTextRange(factory.createNodeArray(statements), body.statements), /*multiLine*/ true);
1129            setTextRange(block, /*location*/ body);
1130            setOriginalNode(block, body);
1131            return block;
1132        }
1133
1134        /**
1135         * Transforms a parameter into a property assignment statement.
1136         *
1137         * @param node The parameter declaration.
1138         */
1139        function transformParameterWithPropertyAssignment(node: ParameterPropertyDeclaration) {
1140            const name = node.name;
1141            if (!isIdentifier(name)) {
1142                return undefined;
1143            }
1144
1145            // TODO(rbuckton): Does this need to be parented?
1146            const propertyName = setParent(setTextRange(factory.cloneNode(name), name), name.parent);
1147            setEmitFlags(propertyName, EmitFlags.NoComments | EmitFlags.NoSourceMap);
1148
1149            // TODO(rbuckton): Does this need to be parented?
1150            const localName = setParent(setTextRange(factory.cloneNode(name), name), name.parent);
1151            setEmitFlags(localName, EmitFlags.NoComments);
1152
1153            return startOnNewLine(
1154                removeAllComments(
1155                    setTextRange(
1156                        setOriginalNode(
1157                            factory.createExpressionStatement(
1158                                factory.createAssignment(
1159                                    setTextRange(
1160                                        factory.createPropertyAccessExpression(
1161                                            factory.createThis(),
1162                                            propertyName
1163                                        ),
1164                                        node.name
1165                                    ),
1166                                    localName
1167                                )
1168                            ),
1169                            node
1170                        ),
1171                        moveRangePos(node, -1)
1172                    )
1173                )
1174            );
1175        }
1176
1177        function visitMethodDeclaration(node: MethodDeclaration, parent: ClassLikeDeclaration | ObjectLiteralExpression) {
1178            if (!(node.transformFlags & TransformFlags.ContainsTypeScript)) {
1179                return node;
1180            }
1181
1182            if (!shouldEmitFunctionLikeDeclaration(node)) {
1183                return undefined;
1184            }
1185
1186            const allDecorators = isClassLike(parent) ? getAllDecoratorsOfClassElement(node, parent) : undefined;
1187            const decorators = isClassLike(parent) ? transformAllDecoratorsOfDeclaration(node, parent, allDecorators) : undefined;
1188            return factory.updateMethodDeclaration(
1189                node,
1190                concatenate(decorators, visitNodes(node.modifiers, modifierVisitor, isModifierLike)),
1191                node.asteriskToken,
1192                visitPropertyNameOfClassElement(node),
1193                /*questionToken*/ undefined,
1194                /*typeParameters*/ undefined,
1195                visitParameterList(node.parameters, visitor, context),
1196                /*type*/ undefined,
1197                visitFunctionBody(node.body, visitor, context)
1198            );
1199        }
1200
1201        /**
1202         * Determines whether to emit an accessor declaration. We should not emit the
1203         * declaration if it does not have a body and is abstract.
1204         *
1205         * @param node The declaration node.
1206         */
1207        function shouldEmitAccessorDeclaration(node: AccessorDeclaration) {
1208            return !(nodeIsMissing(node.body) && hasSyntacticModifier(node, ModifierFlags.Abstract));
1209        }
1210
1211        function visitGetAccessor(node: GetAccessorDeclaration, parent: ClassLikeDeclaration | ObjectLiteralExpression) {
1212            if (!(node.transformFlags & TransformFlags.ContainsTypeScript)) {
1213                return node;
1214            }
1215
1216            if (!shouldEmitAccessorDeclaration(node)) {
1217                return undefined;
1218            }
1219
1220            const decorators = isClassLike(parent) ?
1221                transformAllDecoratorsOfDeclaration(node, parent, getAllDecoratorsOfClassElement(node, parent)) :
1222                undefined;
1223
1224            return factory.updateGetAccessorDeclaration(
1225                node,
1226                concatenate(decorators, visitNodes(node.modifiers, modifierVisitor, isModifierLike)),
1227                visitPropertyNameOfClassElement(node),
1228                visitParameterList(node.parameters, visitor, context),
1229                /*type*/ undefined,
1230                visitFunctionBody(node.body, visitor, context) || factory.createBlock([])
1231            );
1232        }
1233
1234        function visitSetAccessor(node: SetAccessorDeclaration, parent: ClassLikeDeclaration | ObjectLiteralExpression) {
1235            if (!(node.transformFlags & TransformFlags.ContainsTypeScript)) {
1236                return node;
1237            }
1238
1239            if (!shouldEmitAccessorDeclaration(node)) {
1240                return undefined;
1241            }
1242
1243            const decorators = isClassLike(parent) ?
1244                transformAllDecoratorsOfDeclaration(node, parent, getAllDecoratorsOfClassElement(node, parent)) :
1245                undefined;
1246
1247            return factory.updateSetAccessorDeclaration(
1248                node,
1249                concatenate(decorators, visitNodes(node.modifiers, modifierVisitor, isModifierLike)),
1250                visitPropertyNameOfClassElement(node),
1251                visitParameterList(node.parameters, visitor, context),
1252                visitFunctionBody(node.body, visitor, context) || factory.createBlock([])
1253            );
1254        }
1255
1256        function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> {
1257            if (!shouldEmitFunctionLikeDeclaration(node)) {
1258                return factory.createNotEmittedStatement(node);
1259            }
1260            const updated = factory.updateFunctionDeclaration(
1261                node,
1262                visitNodes(node.modifiers, modifierVisitor, isModifier),
1263                node.asteriskToken,
1264                node.name,
1265                /*typeParameters*/ undefined,
1266                visitParameterList(node.parameters, visitor, context),
1267                /*type*/ undefined,
1268                visitFunctionBody(node.body, visitor, context) || factory.createBlock([])
1269            );
1270            if (isExportOfNamespace(node)) {
1271                const statements: Statement[] = [updated];
1272                addExportMemberAssignment(statements, node);
1273                return statements;
1274            }
1275            return updated;
1276        }
1277
1278        function visitFunctionExpression(node: FunctionExpression): Expression {
1279            if (!shouldEmitFunctionLikeDeclaration(node)) {
1280                return factory.createOmittedExpression();
1281            }
1282            const updated = factory.updateFunctionExpression(
1283                node,
1284                visitNodes(node.modifiers, modifierVisitor, isModifier),
1285                node.asteriskToken,
1286                node.name,
1287                /*typeParameters*/ undefined,
1288                visitParameterList(node.parameters, visitor, context),
1289                /*type*/ undefined,
1290                visitFunctionBody(node.body, visitor, context) || factory.createBlock([])
1291            );
1292            return updated;
1293        }
1294
1295        function visitArrowFunction(node: ArrowFunction) {
1296            const updated = factory.updateArrowFunction(
1297                node,
1298                visitNodes(node.modifiers, modifierVisitor, isModifier),
1299                /*typeParameters*/ undefined,
1300                visitParameterList(node.parameters, visitor, context),
1301                /*type*/ undefined,
1302                node.equalsGreaterThanToken,
1303                visitFunctionBody(node.body, visitor, context),
1304            );
1305            return updated;
1306        }
1307
1308        function visitParameter(node: ParameterDeclaration) {
1309            if (parameterIsThisKeyword(node)) {
1310                return undefined;
1311            }
1312
1313            const updated = factory.updateParameterDeclaration(
1314                node,
1315                elideNodes(factory, node.modifiers), // preserve positions, if available
1316                node.dotDotDotToken,
1317                visitNode(node.name, visitor, isBindingName),
1318                /*questionToken*/ undefined,
1319                /*type*/ undefined,
1320                visitNode(node.initializer, visitor, isExpression)
1321            );
1322            if (updated !== node) {
1323                // While we emit the source map for the node after skipping decorators and modifiers,
1324                // we need to emit the comments for the original range.
1325                setCommentRange(updated, node);
1326                setTextRange(updated, moveRangePastModifiers(node));
1327                setSourceMapRange(updated, moveRangePastModifiers(node));
1328                setEmitFlags(updated.name, EmitFlags.NoTrailingSourceMap);
1329            }
1330            return updated;
1331        }
1332
1333        function visitVariableStatement(node: VariableStatement): Statement | undefined {
1334            if (isExportOfNamespace(node)) {
1335                const variables = getInitializedVariables(node.declarationList);
1336                if (variables.length === 0) {
1337                    // elide statement if there are no initialized variables.
1338                    return undefined;
1339                }
1340
1341                return setTextRange(
1342                    factory.createExpressionStatement(
1343                        factory.inlineExpressions(
1344                            map(variables, transformInitializedVariable)
1345                        )
1346                    ),
1347                    node
1348                );
1349            }
1350            else {
1351                return visitEachChild(node, visitor, context);
1352            }
1353        }
1354
1355        function transformInitializedVariable(node: InitializedVariableDeclaration): Expression {
1356            const name = node.name;
1357            if (isBindingPattern(name)) {
1358                return flattenDestructuringAssignment(
1359                    node,
1360                    visitor,
1361                    context,
1362                    FlattenLevel.All,
1363                    /*needsValue*/ false,
1364                    createNamespaceExportExpression
1365                );
1366            }
1367            else {
1368                return setTextRange(
1369                    factory.createAssignment(
1370                        getNamespaceMemberNameWithSourceMapsAndWithoutComments(name),
1371                        visitNode(node.initializer, visitor, isExpression)
1372                    ),
1373                    /*location*/ node
1374                );
1375            }
1376        }
1377
1378        function visitVariableDeclaration(node: VariableDeclaration) {
1379            const updated = factory.updateVariableDeclaration(
1380                node,
1381                visitNode(node.name, visitor, isBindingName),
1382                /*exclamationToken*/ undefined,
1383                /*type*/ undefined,
1384                visitNode(node.initializer, visitor, isExpression));
1385            if (node.type) {
1386                setTypeNode(updated.name, node.type);
1387            }
1388            return updated;
1389        }
1390
1391        function visitParenthesizedExpression(node: ParenthesizedExpression): Expression {
1392            const innerExpression = skipOuterExpressions(node.expression, ~OuterExpressionKinds.Assertions);
1393            if (isAssertionExpression(innerExpression)) {
1394                // Make sure we consider all nested cast expressions, e.g.:
1395                // (<any><number><any>-A).x;
1396                const expression = visitNode(node.expression, visitor, isExpression);
1397
1398                // We have an expression of the form: (<Type>SubExpr). Emitting this as (SubExpr)
1399                // is really not desirable. We would like to emit the subexpression as-is. Omitting
1400                // the parentheses, however, could cause change in the semantics of the generated
1401                // code if the casted expression has a lower precedence than the rest of the
1402                // expression.
1403                //
1404                // To preserve comments, we return a "PartiallyEmittedExpression" here which will
1405                // preserve the position information of the original expression.
1406                //
1407                // Due to the auto-parenthesization rules used by the visitor and factory functions
1408                // we can safely elide the parentheses here, as a new synthetic
1409                // ParenthesizedExpression will be inserted if we remove parentheses too
1410                // aggressively.
1411                //
1412                // If there are leading comments on the expression itself, the emitter will handle ASI
1413                // for return, throw, and yield by re-introducing parenthesis during emit on an as-need
1414                // basis.
1415                return factory.createPartiallyEmittedExpression(expression, node);
1416            }
1417
1418            return visitEachChild(node, visitor, context);
1419        }
1420
1421        function visitAssertionExpression(node: AssertionExpression): Expression {
1422            const expression = visitNode(node.expression, visitor, isExpression);
1423            return factory.createPartiallyEmittedExpression(expression, node);
1424        }
1425
1426        function visitNonNullExpression(node: NonNullExpression): Expression {
1427            const expression = visitNode(node.expression, visitor, isLeftHandSideExpression);
1428            return factory.createPartiallyEmittedExpression(expression, node);
1429        }
1430
1431        function visitSatisfiesExpression(node: SatisfiesExpression): Expression {
1432            const expression = visitNode(node.expression, visitor, isExpression);
1433            return factory.createPartiallyEmittedExpression(expression, node);
1434        }
1435
1436        function visitCallExpression(node: CallExpression) {
1437            return factory.updateCallExpression(
1438                node,
1439                visitNode(node.expression, visitor, isExpression),
1440                /*typeArguments*/ undefined,
1441                visitNodes(node.arguments, visitor, isExpression));
1442        }
1443
1444        function visitNewExpression(node: NewExpression) {
1445            return factory.updateNewExpression(
1446                node,
1447                visitNode(node.expression, visitor, isExpression),
1448                /*typeArguments*/ undefined,
1449                visitNodes(node.arguments, visitor, isExpression));
1450        }
1451
1452        function visitTaggedTemplateExpression(node: TaggedTemplateExpression) {
1453            return factory.updateTaggedTemplateExpression(
1454                node,
1455                visitNode(node.tag, visitor, isExpression),
1456                /*typeArguments*/ undefined,
1457                visitNode(node.template, visitor, isExpression));
1458        }
1459
1460        function visitJsxSelfClosingElement(node: JsxSelfClosingElement) {
1461            return factory.updateJsxSelfClosingElement(
1462                node,
1463                visitNode(node.tagName, visitor, isJsxTagNameExpression),
1464                /*typeArguments*/ undefined,
1465                visitNode(node.attributes, visitor, isJsxAttributes));
1466        }
1467
1468        function visitJsxJsxOpeningElement(node: JsxOpeningElement) {
1469            return factory.updateJsxOpeningElement(
1470                node,
1471                visitNode(node.tagName, visitor, isJsxTagNameExpression),
1472                /*typeArguments*/ undefined,
1473                visitNode(node.attributes, visitor, isJsxAttributes));
1474        }
1475
1476        /**
1477         * Determines whether to emit an enum declaration.
1478         *
1479         * @param node The enum declaration node.
1480         */
1481        function shouldEmitEnumDeclaration(node: EnumDeclaration) {
1482            return !isEnumConst(node)
1483                || shouldPreserveConstEnums(compilerOptions);
1484        }
1485
1486        /**
1487         * Visits an enum declaration.
1488         *
1489         * This function will be called any time a TypeScript enum is encountered.
1490         *
1491         * @param node The enum declaration node.
1492         */
1493        function visitEnumDeclaration(node: EnumDeclaration): VisitResult<Statement> {
1494            if (!shouldEmitEnumDeclaration(node)) {
1495                return factory.createNotEmittedStatement(node);
1496            }
1497
1498            const statements: Statement[] = [];
1499
1500            // We request to be advised when the printer is about to print this node. This allows
1501            // us to set up the correct state for later substitutions.
1502            let emitFlags = EmitFlags.AdviseOnEmitNode;
1503
1504            // If needed, we should emit a variable declaration for the enum. If we emit
1505            // a leading variable declaration, we should not emit leading comments for the
1506            // enum body.
1507            const varAdded = addVarForEnumOrModuleDeclaration(statements, node);
1508            if (varAdded) {
1509                // We should still emit the comments if we are emitting a system module.
1510                if (moduleKind !== ModuleKind.System || currentLexicalScope !== currentSourceFile) {
1511                    emitFlags |= EmitFlags.NoLeadingComments;
1512                }
1513            }
1514
1515            // `parameterName` is the declaration name used inside of the enum.
1516            const parameterName = getNamespaceParameterName(node);
1517
1518            // `containerName` is the expression used inside of the enum for assignments.
1519            const containerName = getNamespaceContainerName(node);
1520
1521            // `exportName` is the expression used within this node's container for any exported references.
1522            const exportName = hasSyntacticModifier(node, ModifierFlags.Export)
1523                ? factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true)
1524                : factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
1525
1526            //  x || (x = {})
1527            //  exports.x || (exports.x = {})
1528            let moduleArg =
1529                factory.createLogicalOr(
1530                    exportName,
1531                    factory.createAssignment(
1532                        exportName,
1533                        factory.createObjectLiteralExpression()
1534                    )
1535                );
1536
1537            if (hasNamespaceQualifiedExportName(node)) {
1538                // `localName` is the expression used within this node's containing scope for any local references.
1539                const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
1540
1541                //  x = (exports.x || (exports.x = {}))
1542                moduleArg = factory.createAssignment(localName, moduleArg);
1543            }
1544
1545            //  (function (x) {
1546            //      x[x["y"] = 0] = "y";
1547            //      ...
1548            //  })(x || (x = {}));
1549            const enumStatement = factory.createExpressionStatement(
1550                factory.createCallExpression(
1551                    factory.createFunctionExpression(
1552                        /*modifiers*/ undefined,
1553                        /*asteriskToken*/ undefined,
1554                        /*name*/ undefined,
1555                        /*typeParameters*/ undefined,
1556                        [factory.createParameterDeclaration(/*modifiers*/ undefined, /*dotDotDotToken*/ undefined, parameterName)],
1557                        /*type*/ undefined,
1558                        transformEnumBody(node, containerName)
1559                    ),
1560                    /*typeArguments*/ undefined,
1561                    [moduleArg]
1562                )
1563            );
1564
1565            setOriginalNode(enumStatement, node);
1566            if (varAdded) {
1567                // If a variable was added, synthetic comments are emitted on it, not on the moduleStatement.
1568                setSyntheticLeadingComments(enumStatement, undefined);
1569                setSyntheticTrailingComments(enumStatement, undefined);
1570            }
1571            setTextRange(enumStatement, node);
1572            addEmitFlags(enumStatement, emitFlags);
1573            statements.push(enumStatement);
1574
1575            // Add a DeclarationMarker for the enum to preserve trailing comments and mark
1576            // the end of the declaration.
1577            statements.push(factory.createEndOfDeclarationMarker(node));
1578            return statements;
1579        }
1580
1581        /**
1582         * Transforms the body of an enum declaration.
1583         *
1584         * @param node The enum declaration node.
1585         */
1586        function transformEnumBody(node: EnumDeclaration, localName: Identifier): Block {
1587            const savedCurrentNamespaceLocalName = currentNamespaceContainerName;
1588            currentNamespaceContainerName = localName;
1589
1590            const statements: Statement[] = [];
1591            startLexicalEnvironment();
1592            const members = map(node.members, transformEnumMember);
1593            insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
1594            addRange(statements, members);
1595
1596            currentNamespaceContainerName = savedCurrentNamespaceLocalName;
1597            return factory.createBlock(
1598                setTextRange(factory.createNodeArray(statements), /*location*/ node.members),
1599                /*multiLine*/ true
1600            );
1601        }
1602
1603        /**
1604         * Transforms an enum member into a statement.
1605         *
1606         * @param member The enum member node.
1607         */
1608        function transformEnumMember(member: EnumMember): Statement {
1609            // enums don't support computed properties
1610            // we pass false as 'generateNameForComputedPropertyName' for a backward compatibility purposes
1611            // old emitter always generate 'expression' part of the name as-is.
1612            const name = getExpressionForPropertyName(member, /*generateNameForComputedPropertyName*/ false);
1613            const valueExpression = transformEnumMemberDeclarationValue(member);
1614            const innerAssignment = factory.createAssignment(
1615                factory.createElementAccessExpression(
1616                    currentNamespaceContainerName,
1617                    name
1618                ),
1619                valueExpression
1620            );
1621            const outerAssignment = valueExpression.kind === SyntaxKind.StringLiteral ?
1622                innerAssignment :
1623                factory.createAssignment(
1624                    factory.createElementAccessExpression(
1625                        currentNamespaceContainerName,
1626                        innerAssignment
1627                    ),
1628                    name
1629                );
1630            return setTextRange(
1631                factory.createExpressionStatement(
1632                    setTextRange(
1633                        outerAssignment,
1634                        member
1635                    )
1636                ),
1637                member
1638            );
1639        }
1640
1641        /**
1642         * Transforms the value of an enum member.
1643         *
1644         * @param member The enum member node.
1645         */
1646        function transformEnumMemberDeclarationValue(member: EnumMember): Expression {
1647            const value = resolver.getConstantValue(member);
1648            if (value !== undefined) {
1649                return typeof value === "string" ? factory.createStringLiteral(value) : factory.createNumericLiteral(value);
1650            }
1651            else {
1652                enableSubstitutionForNonQualifiedEnumMembers();
1653                if (member.initializer) {
1654                    return visitNode(member.initializer, visitor, isExpression);
1655                }
1656                else {
1657                    return factory.createVoidZero();
1658                }
1659            }
1660        }
1661
1662        /**
1663         * Determines whether to elide a module declaration.
1664         *
1665         * @param node The module declaration node.
1666         */
1667        function shouldEmitModuleDeclaration(nodeIn: ModuleDeclaration) {
1668            const node = getParseTreeNode(nodeIn, isModuleDeclaration);
1669            if (!node) {
1670                // If we can't find a parse tree node, assume the node is instantiated.
1671                return true;
1672            }
1673            return isInstantiatedModule(node, shouldPreserveConstEnums(compilerOptions));
1674        }
1675
1676        /**
1677         * Determines whether an exported declaration will have a qualified export name (e.g. `f.x`
1678         * or `exports.x`).
1679         */
1680        function hasNamespaceQualifiedExportName(node: Node) {
1681            return isExportOfNamespace(node)
1682                || (isExternalModuleExport(node)
1683                    && moduleKind !== ModuleKind.ES2015
1684                    && moduleKind !== ModuleKind.ES2020
1685                    && moduleKind !== ModuleKind.ES2022
1686                    && moduleKind !== ModuleKind.ESNext
1687                    && moduleKind !== ModuleKind.System);
1688        }
1689
1690        /**
1691         * Records that a declaration was emitted in the current scope, if it was the first
1692         * declaration for the provided symbol.
1693         */
1694        function recordEmittedDeclarationInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration) {
1695            if (!currentScopeFirstDeclarationsOfName) {
1696                currentScopeFirstDeclarationsOfName = new Map();
1697            }
1698
1699            const name = declaredNameInScope(node);
1700            if (!currentScopeFirstDeclarationsOfName.has(name)) {
1701                currentScopeFirstDeclarationsOfName.set(name, node);
1702            }
1703        }
1704
1705        /**
1706         * Determines whether a declaration is the first declaration with
1707         * the same name emitted in the current scope.
1708         */
1709        function isFirstEmittedDeclarationInScope(node: ModuleDeclaration | EnumDeclaration) {
1710            if (currentScopeFirstDeclarationsOfName) {
1711                const name = declaredNameInScope(node);
1712                return currentScopeFirstDeclarationsOfName.get(name) === node;
1713            }
1714            return true;
1715        }
1716
1717        function declaredNameInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration): __String {
1718            Debug.assertNode(node.name, isIdentifier);
1719            return node.name.escapedText;
1720        }
1721
1722        /**
1723         * Adds a leading VariableStatement for a enum or module declaration.
1724         */
1725        function addVarForEnumOrModuleDeclaration(statements: Statement[], node: ModuleDeclaration | EnumDeclaration) {
1726            // Emit a variable statement for the module. We emit top-level enums as a `var`
1727            // declaration to avoid static errors in global scripts scripts due to redeclaration.
1728            // enums in any other scope are emitted as a `let` declaration.
1729            const statement = factory.createVariableStatement(
1730                visitNodes(node.modifiers, modifierVisitor, isModifier),
1731                factory.createVariableDeclarationList([
1732                    factory.createVariableDeclaration(
1733                        factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true)
1734                    )
1735                ], currentLexicalScope.kind === SyntaxKind.SourceFile ? NodeFlags.None : NodeFlags.Let)
1736            );
1737
1738            setOriginalNode(statement, node);
1739
1740            recordEmittedDeclarationInScope(node);
1741            if (isFirstEmittedDeclarationInScope(node)) {
1742                // Adjust the source map emit to match the old emitter.
1743                if (node.kind === SyntaxKind.EnumDeclaration) {
1744                    setSourceMapRange(statement.declarationList, node);
1745                }
1746                else {
1747                    setSourceMapRange(statement, node);
1748                }
1749
1750                // Trailing comments for module declaration should be emitted after the function closure
1751                // instead of the variable statement:
1752                //
1753                //     /** Module comment*/
1754                //     module m1 {
1755                //         function foo4Export() {
1756                //         }
1757                //     } // trailing comment module
1758                //
1759                // Should emit:
1760                //
1761                //     /** Module comment*/
1762                //     var m1;
1763                //     (function (m1) {
1764                //         function foo4Export() {
1765                //         }
1766                //     })(m1 || (m1 = {})); // trailing comment module
1767                //
1768                setCommentRange(statement, node);
1769                addEmitFlags(statement, EmitFlags.NoTrailingComments | EmitFlags.HasEndOfDeclarationMarker);
1770                statements.push(statement);
1771                return true;
1772            }
1773            else {
1774                // For an EnumDeclaration or ModuleDeclaration that merges with a preceeding
1775                // declaration we do not emit a leading variable declaration. To preserve the
1776                // begin/end semantics of the declararation and to properly handle exports
1777                // we wrap the leading variable declaration in a `MergeDeclarationMarker`.
1778                const mergeMarker = factory.createMergeDeclarationMarker(statement);
1779                setEmitFlags(mergeMarker, EmitFlags.NoComments | EmitFlags.HasEndOfDeclarationMarker);
1780                statements.push(mergeMarker);
1781                return false;
1782            }
1783        }
1784
1785        /**
1786         * Visits a module declaration node.
1787         *
1788         * This function will be called any time a TypeScript namespace (ModuleDeclaration) is encountered.
1789         *
1790         * @param node The module declaration node.
1791         */
1792        function visitModuleDeclaration(node: ModuleDeclaration): VisitResult<Statement> {
1793            if (!shouldEmitModuleDeclaration(node)) {
1794                return factory.createNotEmittedStatement(node);
1795            }
1796
1797            Debug.assertNode(node.name, isIdentifier, "A TypeScript namespace should have an Identifier name.");
1798            enableSubstitutionForNamespaceExports();
1799
1800            const statements: Statement[] = [];
1801
1802            // We request to be advised when the printer is about to print this node. This allows
1803            // us to set up the correct state for later substitutions.
1804            let emitFlags = EmitFlags.AdviseOnEmitNode;
1805
1806            // If needed, we should emit a variable declaration for the module. If we emit
1807            // a leading variable declaration, we should not emit leading comments for the
1808            // module body.
1809            const varAdded = addVarForEnumOrModuleDeclaration(statements, node);
1810            if (varAdded) {
1811                // We should still emit the comments if we are emitting a system module.
1812                if (moduleKind !== ModuleKind.System || currentLexicalScope !== currentSourceFile) {
1813                    emitFlags |= EmitFlags.NoLeadingComments;
1814                }
1815            }
1816
1817            // `parameterName` is the declaration name used inside of the namespace.
1818            const parameterName = getNamespaceParameterName(node);
1819
1820            // `containerName` is the expression used inside of the namespace for exports.
1821            const containerName = getNamespaceContainerName(node);
1822
1823            // `exportName` is the expression used within this node's container for any exported references.
1824            const exportName = hasSyntacticModifier(node, ModifierFlags.Export)
1825                ? factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true)
1826                : factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
1827
1828            //  x || (x = {})
1829            //  exports.x || (exports.x = {})
1830            let moduleArg =
1831                factory.createLogicalOr(
1832                    exportName,
1833                    factory.createAssignment(
1834                        exportName,
1835                        factory.createObjectLiteralExpression()
1836                    )
1837                );
1838
1839            if (hasNamespaceQualifiedExportName(node)) {
1840                // `localName` is the expression used within this node's containing scope for any local references.
1841                const localName = factory.getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
1842
1843                //  x = (exports.x || (exports.x = {}))
1844                moduleArg = factory.createAssignment(localName, moduleArg);
1845            }
1846
1847            //  (function (x_1) {
1848            //      x_1.y = ...;
1849            //  })(x || (x = {}));
1850            const moduleStatement = factory.createExpressionStatement(
1851                factory.createCallExpression(
1852                    factory.createFunctionExpression(
1853                        /*modifiers*/ undefined,
1854                        /*asteriskToken*/ undefined,
1855                        /*name*/ undefined,
1856                        /*typeParameters*/ undefined,
1857                        [factory.createParameterDeclaration(/*modifiers*/ undefined, /*dotDotDotToken*/ undefined, parameterName)],
1858                        /*type*/ undefined,
1859                        transformModuleBody(node, containerName)
1860                    ),
1861                    /*typeArguments*/ undefined,
1862                    [moduleArg]
1863                )
1864            );
1865
1866            setOriginalNode(moduleStatement, node);
1867            if (varAdded) {
1868                // If a variable was added, synthetic comments are emitted on it, not on the moduleStatement.
1869                setSyntheticLeadingComments(moduleStatement, undefined);
1870                setSyntheticTrailingComments(moduleStatement, undefined);
1871            }
1872            setTextRange(moduleStatement, node);
1873            addEmitFlags(moduleStatement, emitFlags);
1874            statements.push(moduleStatement);
1875
1876            // Add a DeclarationMarker for the namespace to preserve trailing comments and mark
1877            // the end of the declaration.
1878            statements.push(factory.createEndOfDeclarationMarker(node));
1879            return statements;
1880        }
1881
1882        /**
1883         * Transforms the body of a module declaration.
1884         *
1885         * @param node The module declaration node.
1886         */
1887        function transformModuleBody(node: ModuleDeclaration, namespaceLocalName: Identifier): Block {
1888            const savedCurrentNamespaceContainerName = currentNamespaceContainerName;
1889            const savedCurrentNamespace = currentNamespace;
1890            const savedCurrentScopeFirstDeclarationsOfName = currentScopeFirstDeclarationsOfName;
1891            currentNamespaceContainerName = namespaceLocalName;
1892            currentNamespace = node;
1893            currentScopeFirstDeclarationsOfName = undefined;
1894
1895            const statements: Statement[] = [];
1896            startLexicalEnvironment();
1897
1898            let statementsLocation: TextRange | undefined;
1899            let blockLocation: TextRange | undefined;
1900            if (node.body) {
1901                if (node.body.kind === SyntaxKind.ModuleBlock) {
1902                    saveStateAndInvoke(node.body, body => addRange(statements, visitNodes((body as ModuleBlock).statements, namespaceElementVisitor, isStatement)));
1903                    statementsLocation = node.body.statements;
1904                    blockLocation = node.body;
1905                }
1906                else {
1907                    const result = visitModuleDeclaration(node.body as ModuleDeclaration);
1908                    if (result) {
1909                        if (isArray(result)) {
1910                            addRange(statements, result);
1911                        }
1912                        else {
1913                            statements.push(result);
1914                        }
1915                    }
1916
1917                    const moduleBlock = getInnerMostModuleDeclarationFromDottedModule(node)!.body as ModuleBlock;
1918                    statementsLocation = moveRangePos(moduleBlock.statements, -1);
1919                }
1920            }
1921
1922            insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
1923            currentNamespaceContainerName = savedCurrentNamespaceContainerName;
1924            currentNamespace = savedCurrentNamespace;
1925            currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName;
1926
1927            const block = factory.createBlock(
1928                setTextRange(
1929                    factory.createNodeArray(statements),
1930                    /*location*/ statementsLocation
1931                ),
1932                /*multiLine*/ true
1933            );
1934            setTextRange(block, blockLocation);
1935
1936            // namespace hello.hi.world {
1937            //      function foo() {}
1938            //
1939            //      // TODO, blah
1940            // }
1941            //
1942            // should be emitted as
1943            //
1944            // var hello;
1945            // (function (hello) {
1946            //     var hi;
1947            //     (function (hi) {
1948            //         var world;
1949            //         (function (world) {
1950            //             function foo() { }
1951            //             // TODO, blah
1952            //         })(world = hi.world || (hi.world = {}));
1953            //     })(hi = hello.hi || (hello.hi = {}));
1954            // })(hello || (hello = {}));
1955            // We only want to emit comment on the namespace which contains block body itself, not the containing namespaces.
1956            if (!node.body || node.body.kind !== SyntaxKind.ModuleBlock) {
1957                setEmitFlags(block, getEmitFlags(block) | EmitFlags.NoComments);
1958            }
1959            return block;
1960        }
1961
1962        function getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration: ModuleDeclaration): ModuleDeclaration | undefined {
1963            if (moduleDeclaration.body!.kind === SyntaxKind.ModuleDeclaration) {
1964                const recursiveInnerModule = getInnerMostModuleDeclarationFromDottedModule(moduleDeclaration.body as ModuleDeclaration);
1965                return recursiveInnerModule || moduleDeclaration.body as ModuleDeclaration;
1966            }
1967        }
1968
1969        /**
1970         * Visits an import declaration, eliding it if it is type-only or if it has an import clause that may be elided.
1971         *
1972         * @param node The import declaration node.
1973         */
1974        function visitImportDeclaration(node: ImportDeclaration): VisitResult<Statement> {
1975            if (!node.importClause) {
1976                // Do not elide a side-effect only import declaration.
1977                //  import "foo";
1978                return node;
1979            }
1980            if (node.importClause.isTypeOnly) {
1981                // Always elide type-only imports
1982                return undefined;
1983            }
1984
1985            // Elide the declaration if the import clause was elided.
1986            const importClause = visitNode(node.importClause, visitImportClause, isImportClause);
1987            return importClause ||
1988                compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve ||
1989                compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error
1990                ? factory.updateImportDeclaration(
1991                    node,
1992                    /*modifiers*/ undefined,
1993                    importClause,
1994                    node.moduleSpecifier,
1995                    node.assertClause)
1996                : undefined;
1997        }
1998
1999        /**
2000         * Visits an import clause, eliding it if its `name` and `namedBindings` may both be elided.
2001         *
2002         * @param node The import clause node.
2003         */
2004        function visitImportClause(node: ImportClause): VisitResult<ImportClause> {
2005            Debug.assert(!node.isTypeOnly);
2006            // Elide the import clause if we elide both its name and its named bindings.
2007            const name = shouldEmitAliasDeclaration(node) ? node.name : undefined;
2008            const namedBindings = visitNode(node.namedBindings, visitNamedImportBindings, isNamedImportBindings);
2009            return (name || namedBindings) ? factory.updateImportClause(node, /*isTypeOnly*/ false, name, namedBindings) : undefined;
2010        }
2011
2012        /**
2013         * Visits named import bindings, eliding them if their targets, their references, and the compilation settings allow.
2014         *
2015         * @param node The named import bindings node.
2016         */
2017        function visitNamedImportBindings(node: NamedImportBindings): VisitResult<NamedImportBindings> {
2018            if (node.kind === SyntaxKind.NamespaceImport) {
2019                // Elide a namespace import if it is not referenced.
2020                return shouldEmitAliasDeclaration(node) ? node : undefined;
2021            }
2022            else {
2023                // Elide named imports if all of its import specifiers are elided and settings allow.
2024                const allowEmpty = compilerOptions.preserveValueImports && (
2025                    compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve ||
2026                    compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error);
2027                const elements = visitNodes(node.elements, visitImportSpecifier, isImportSpecifier);
2028                return allowEmpty || some(elements) ? factory.updateNamedImports(node, elements) : undefined;
2029            }
2030        }
2031
2032        /**
2033         * Visits an import specifier, eliding it if its target, its references, and the compilation settings allow.
2034         *
2035         * @param node The import specifier node.
2036         */
2037        function visitImportSpecifier(node: ImportSpecifier): VisitResult<ImportSpecifier> {
2038            return !node.isTypeOnly && shouldEmitAliasDeclaration(node) ? node : undefined;
2039        }
2040
2041        /**
2042         * Visits an export assignment, eliding it if it does not contain a clause that resolves
2043         * to a value.
2044         *
2045         * @param node The export assignment node.
2046         */
2047        function visitExportAssignment(node: ExportAssignment): VisitResult<Statement> {
2048            // Elide the export assignment if it does not reference a value.
2049            return resolver.isValueAliasDeclaration(node)
2050                ? visitEachChild(node, visitor, context)
2051                : undefined;
2052        }
2053
2054        /**
2055         * Visits an export declaration, eliding it if it does not contain a clause that resolves to a value.
2056         *
2057         * @param node The export declaration node.
2058         */
2059        function visitExportDeclaration(node: ExportDeclaration): VisitResult<Statement> {
2060            if (node.isTypeOnly) {
2061                return undefined;
2062            }
2063
2064            if (!node.exportClause || isNamespaceExport(node.exportClause)) {
2065                // never elide `export <whatever> from <whereever>` declarations -
2066                // they should be kept for sideffects/untyped exports, even when the
2067                // type checker doesn't know about any exports
2068                return node;
2069            }
2070
2071            // Elide the export declaration if all of its named exports are elided.
2072            const allowEmpty = !!node.moduleSpecifier && (
2073                compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve ||
2074                compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error);
2075            const exportClause = visitNode(
2076                node.exportClause,
2077                (bindings: NamedExportBindings) => visitNamedExportBindings(bindings, allowEmpty),
2078                isNamedExportBindings);
2079
2080            return exportClause
2081                ? factory.updateExportDeclaration(
2082                    node,
2083                    /*modifiers*/ undefined,
2084                    node.isTypeOnly,
2085                    exportClause,
2086                    node.moduleSpecifier,
2087                    node.assertClause)
2088                : undefined;
2089        }
2090
2091        /**
2092         * Visits named exports, eliding it if it does not contain an export specifier that
2093         * resolves to a value.
2094         *
2095         * @param node The named exports node.
2096         */
2097        function visitNamedExports(node: NamedExports, allowEmpty: boolean): VisitResult<NamedExports> {
2098            // Elide the named exports if all of its export specifiers were elided.
2099            const elements = visitNodes(node.elements, visitExportSpecifier, isExportSpecifier);
2100            return allowEmpty || some(elements) ? factory.updateNamedExports(node, elements) : undefined;
2101        }
2102
2103        function visitNamespaceExports(node: NamespaceExport): VisitResult<NamespaceExport> {
2104            return factory.updateNamespaceExport(node, visitNode(node.name, visitor, isIdentifier));
2105        }
2106
2107        function visitNamedExportBindings(node: NamedExportBindings, allowEmpty: boolean): VisitResult<NamedExportBindings> {
2108            return isNamespaceExport(node) ? visitNamespaceExports(node) : visitNamedExports(node, allowEmpty);
2109        }
2110
2111        /**
2112         * Visits an export specifier, eliding it if it does not resolve to a value.
2113         *
2114         * @param node The export specifier node.
2115         */
2116        function visitExportSpecifier(node: ExportSpecifier): VisitResult<ExportSpecifier> {
2117            // Elide an export specifier if it does not reference a value.
2118            return !node.isTypeOnly && resolver.isValueAliasDeclaration(node) ? node : undefined;
2119        }
2120
2121        /**
2122         * Determines whether to emit an import equals declaration.
2123         *
2124         * @param node The import equals declaration node.
2125         */
2126        function shouldEmitImportEqualsDeclaration(node: ImportEqualsDeclaration) {
2127            // preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when
2128            // - current file is not external module
2129            // - import declaration is top level and target is value imported by entity name
2130            return shouldEmitAliasDeclaration(node)
2131                || (!isExternalModule(currentSourceFile)
2132                    && resolver.isTopLevelValueImportEqualsWithEntityName(node));
2133        }
2134
2135        /**
2136         * Visits an import equals declaration.
2137         *
2138         * @param node The import equals declaration node.
2139         */
2140        function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): VisitResult<Statement> {
2141            // Always elide type-only imports
2142            if (node.isTypeOnly) {
2143                return undefined;
2144            }
2145
2146            if (isExternalModuleImportEqualsDeclaration(node)) {
2147                const isReferenced = shouldEmitAliasDeclaration(node);
2148                // If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'.
2149                if (!isReferenced && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve) {
2150                    return setOriginalNode(
2151                        setTextRange(
2152                            factory.createImportDeclaration(
2153                                /*modifiers*/ undefined,
2154                                /*importClause*/ undefined,
2155                                node.moduleReference.expression,
2156                                /*assertClause*/ undefined
2157                            ),
2158                            node,
2159                        ),
2160                        node,
2161                    );
2162                }
2163
2164                return isReferenced ? visitEachChild(node, visitor, context) : undefined;
2165            }
2166
2167            if (!shouldEmitImportEqualsDeclaration(node)) {
2168                return undefined;
2169            }
2170
2171            const moduleReference = createExpressionFromEntityName(factory, node.moduleReference as EntityName);
2172            setEmitFlags(moduleReference, EmitFlags.NoComments | EmitFlags.NoNestedComments);
2173
2174            if (isNamedExternalModuleExport(node) || !isExportOfNamespace(node)) {
2175                //  export var ${name} = ${moduleReference};
2176                //  var ${name} = ${moduleReference};
2177                return setOriginalNode(
2178                    setTextRange(
2179                        factory.createVariableStatement(
2180                            visitNodes(node.modifiers, modifierVisitor, isModifier),
2181                            factory.createVariableDeclarationList([
2182                                setOriginalNode(
2183                                    factory.createVariableDeclaration(
2184                                        node.name,
2185                                        /*exclamationToken*/ undefined,
2186                                        /*type*/ undefined,
2187                                        moduleReference
2188                                    ),
2189                                    node
2190                                )
2191                            ])
2192                        ),
2193                        node
2194                    ),
2195                    node
2196                );
2197            }
2198            else {
2199                // exports.${name} = ${moduleReference};
2200                return setOriginalNode(
2201                    createNamespaceExport(
2202                        node.name,
2203                        moduleReference,
2204                        node
2205                    ),
2206                    node
2207                );
2208            }
2209        }
2210
2211        /**
2212         * Gets a value indicating whether the node is exported from a namespace.
2213         *
2214         * @param node The node to test.
2215         */
2216        function isExportOfNamespace(node: Node) {
2217            return currentNamespace !== undefined && hasSyntacticModifier(node, ModifierFlags.Export);
2218        }
2219
2220        /**
2221         * Gets a value indicating whether the node is exported from an external module.
2222         *
2223         * @param node The node to test.
2224         */
2225        function isExternalModuleExport(node: Node) {
2226            return currentNamespace === undefined && hasSyntacticModifier(node, ModifierFlags.Export);
2227        }
2228
2229        /**
2230         * Gets a value indicating whether the node is a named export from an external module.
2231         *
2232         * @param node The node to test.
2233         */
2234        function isNamedExternalModuleExport(node: Node) {
2235            return isExternalModuleExport(node)
2236                && !hasSyntacticModifier(node, ModifierFlags.Default);
2237        }
2238
2239        /**
2240         * Gets a value indicating whether the node is the default export of an external module.
2241         *
2242         * @param node The node to test.
2243         */
2244        function isDefaultExternalModuleExport(node: Node) {
2245            return isExternalModuleExport(node)
2246                && hasSyntacticModifier(node, ModifierFlags.Default);
2247        }
2248
2249        function addExportMemberAssignment(statements: Statement[], node: ClassDeclaration | FunctionDeclaration) {
2250            const expression = factory.createAssignment(
2251                factory.getExternalModuleOrNamespaceExportName(currentNamespaceContainerName, node, /*allowComments*/ false, /*allowSourceMaps*/ true),
2252                factory.getLocalName(node)
2253            );
2254            setSourceMapRange(expression, createRange(node.name ? node.name.pos : node.pos, node.end));
2255
2256            const statement = factory.createExpressionStatement(expression);
2257            setSourceMapRange(statement, createRange(-1, node.end));
2258            statements.push(statement);
2259        }
2260
2261        function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) {
2262            return setTextRange(
2263                factory.createExpressionStatement(
2264                    factory.createAssignment(
2265                        factory.getNamespaceMemberName(currentNamespaceContainerName, exportName, /*allowComments*/ false, /*allowSourceMaps*/ true),
2266                        exportValue
2267                    )
2268                ),
2269                location
2270            );
2271        }
2272
2273        function createNamespaceExportExpression(exportName: Identifier, exportValue: Expression, location?: TextRange) {
2274            return setTextRange(factory.createAssignment(getNamespaceMemberNameWithSourceMapsAndWithoutComments(exportName), exportValue), location);
2275        }
2276
2277        function getNamespaceMemberNameWithSourceMapsAndWithoutComments(name: Identifier) {
2278            return factory.getNamespaceMemberName(currentNamespaceContainerName, name, /*allowComments*/ false, /*allowSourceMaps*/ true);
2279        }
2280
2281        /**
2282         * Gets the declaration name used inside of a namespace or enum.
2283         */
2284        function getNamespaceParameterName(node: ModuleDeclaration | EnumDeclaration) {
2285            const name = factory.getGeneratedNameForNode(node);
2286            setSourceMapRange(name, node.name);
2287            return name;
2288        }
2289
2290        /**
2291         * Gets the expression used to refer to a namespace or enum within the body
2292         * of its declaration.
2293         */
2294        function getNamespaceContainerName(node: ModuleDeclaration | EnumDeclaration) {
2295            return factory.getGeneratedNameForNode(node);
2296        }
2297
2298        function enableSubstitutionForNonQualifiedEnumMembers() {
2299            if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers) === 0) {
2300                enabledSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
2301                context.enableSubstitution(SyntaxKind.Identifier);
2302            }
2303        }
2304
2305        function enableSubstitutionForNamespaceExports() {
2306            if ((enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) === 0) {
2307                enabledSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports;
2308
2309                // We need to enable substitutions for identifiers and shorthand property assignments. This allows us to
2310                // substitute the names of exported members of a namespace.
2311                context.enableSubstitution(SyntaxKind.Identifier);
2312                context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment);
2313
2314                // We need to be notified when entering and exiting namespaces.
2315                context.enableEmitNotification(SyntaxKind.ModuleDeclaration);
2316            }
2317        }
2318
2319        function isTransformedModuleDeclaration(node: Node): boolean {
2320            return getOriginalNode(node).kind === SyntaxKind.ModuleDeclaration;
2321        }
2322
2323        function isTransformedEnumDeclaration(node: Node): boolean {
2324            return getOriginalNode(node).kind === SyntaxKind.EnumDeclaration;
2325        }
2326
2327        /**
2328         * Hook for node emit.
2329         *
2330         * @param hint A hint as to the intended usage of the node.
2331         * @param node The node to emit.
2332         * @param emit A callback used to emit the node in the printer.
2333         */
2334        function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
2335            const savedApplicableSubstitutions = applicableSubstitutions;
2336            const savedCurrentSourceFile = currentSourceFile;
2337
2338            if (isSourceFile(node)) {
2339                currentSourceFile = node;
2340            }
2341
2342            if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && isTransformedModuleDeclaration(node)) {
2343                applicableSubstitutions |= TypeScriptSubstitutionFlags.NamespaceExports;
2344            }
2345
2346            if (enabledSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && isTransformedEnumDeclaration(node)) {
2347                applicableSubstitutions |= TypeScriptSubstitutionFlags.NonQualifiedEnumMembers;
2348            }
2349
2350            previousOnEmitNode(hint, node, emitCallback);
2351
2352            applicableSubstitutions = savedApplicableSubstitutions;
2353            currentSourceFile = savedCurrentSourceFile;
2354        }
2355
2356        /**
2357         * Hooks node substitutions.
2358         *
2359         * @param hint A hint as to the intended usage of the node.
2360         * @param node The node to substitute.
2361         */
2362        function onSubstituteNode(hint: EmitHint, node: Node) {
2363            node = previousOnSubstituteNode(hint, node);
2364            if (hint === EmitHint.Expression) {
2365                return substituteExpression(node as Expression);
2366            }
2367            else if (isShorthandPropertyAssignment(node)) {
2368                return substituteShorthandPropertyAssignment(node);
2369            }
2370
2371            return node;
2372        }
2373
2374        function substituteShorthandPropertyAssignment(node: ShorthandPropertyAssignment): ObjectLiteralElementLike {
2375            if (enabledSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports) {
2376                const name = node.name;
2377                const exportedName = trySubstituteNamespaceExportedName(name);
2378                if (exportedName) {
2379                    // A shorthand property with an assignment initializer is probably part of a
2380                    // destructuring assignment
2381                    if (node.objectAssignmentInitializer) {
2382                        const initializer = factory.createAssignment(exportedName, node.objectAssignmentInitializer);
2383                        return setTextRange(factory.createPropertyAssignment(name, initializer), node);
2384                    }
2385                    return setTextRange(factory.createPropertyAssignment(name, exportedName), node);
2386                }
2387            }
2388            return node;
2389        }
2390
2391        function substituteExpression(node: Expression) {
2392            switch (node.kind) {
2393                case SyntaxKind.Identifier:
2394                    return substituteExpressionIdentifier(node as Identifier);
2395                case SyntaxKind.PropertyAccessExpression:
2396                    return substitutePropertyAccessExpression(node as PropertyAccessExpression);
2397                case SyntaxKind.ElementAccessExpression:
2398                    return substituteElementAccessExpression(node as ElementAccessExpression);
2399            }
2400
2401            return node;
2402        }
2403
2404        function substituteExpressionIdentifier(node: Identifier): Expression {
2405            return trySubstituteNamespaceExportedName(node)
2406                || node;
2407        }
2408
2409        function trySubstituteNamespaceExportedName(node: Identifier): Expression | undefined {
2410            // If this is explicitly a local name, do not substitute.
2411            if (enabledSubstitutions & applicableSubstitutions && !isGeneratedIdentifier(node) && !isLocalName(node)) {
2412                // If we are nested within a namespace declaration, we may need to qualifiy
2413                // an identifier that is exported from a merged namespace.
2414                const container = resolver.getReferencedExportContainer(node, /*prefixLocals*/ false);
2415                if (container && container.kind !== SyntaxKind.SourceFile) {
2416                    const substitute =
2417                        (applicableSubstitutions & TypeScriptSubstitutionFlags.NamespaceExports && container.kind === SyntaxKind.ModuleDeclaration) ||
2418                        (applicableSubstitutions & TypeScriptSubstitutionFlags.NonQualifiedEnumMembers && container.kind === SyntaxKind.EnumDeclaration);
2419                    if (substitute) {
2420                        return setTextRange(
2421                            factory.createPropertyAccessExpression(factory.getGeneratedNameForNode(container), node),
2422                            /*location*/ node
2423                        );
2424                    }
2425                }
2426            }
2427
2428            return undefined;
2429        }
2430
2431        function substitutePropertyAccessExpression(node: PropertyAccessExpression) {
2432            return substituteConstantValue(node);
2433        }
2434
2435        function substituteElementAccessExpression(node: ElementAccessExpression) {
2436            return substituteConstantValue(node);
2437        }
2438
2439        function safeMultiLineComment(value: string): string {
2440            return value.replace(/\*\//g, "*_/");
2441        }
2442
2443        function substituteConstantValue(node: PropertyAccessExpression | ElementAccessExpression): LeftHandSideExpression {
2444            const constantValue = tryGetConstEnumValue(node);
2445            if (constantValue !== undefined) {
2446                // track the constant value on the node for the printer in needsDotDotForPropertyAccess
2447                setConstantValue(node, constantValue);
2448
2449                const substitute = typeof constantValue === "string" ? factory.createStringLiteral(constantValue) : factory.createNumericLiteral(constantValue);
2450                if (!compilerOptions.removeComments) {
2451                    const originalNode = getOriginalNode(node, isAccessExpression);
2452
2453                    addSyntheticTrailingComment(substitute, SyntaxKind.MultiLineCommentTrivia, ` ${safeMultiLineComment(getTextOfNode(originalNode))} `);
2454                }
2455                return substitute;
2456            }
2457
2458            return node;
2459        }
2460
2461        function tryGetConstEnumValue(node: Node): string | number | undefined {
2462            if (compilerOptions.isolatedModules) {
2463                return undefined;
2464            }
2465
2466            return isPropertyAccessExpression(node) || isElementAccessExpression(node) ? resolver.getConstantValue(node) : undefined;
2467        }
2468
2469        function shouldEmitAliasDeclaration(node: Node): boolean {
2470            return isInJSFile(node) ||
2471                (compilerOptions.preserveValueImports
2472                    ? resolver.isValueAliasDeclaration(node)
2473                    : resolver.isReferencedAliasDeclaration(node));
2474        }
2475    }
2476}
2477