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