• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    addRange, append, Bundle, chainBundle, CompilerOptions, createEmitHelperFactory, CustomTransformer,
3    CustomTransformerFactory, CustomTransformers, Debug, DiagnosticWithLocation, disposeEmitNodes, EmitFlags,
4    EmitHelper, EmitHint, EmitHost, EmitResolver, EmitTransformers, emptyArray, factory, FunctionDeclaration,
5    getEmitFlags, getEmitModuleKind, getEmitScriptTarget, getJSXTransformEnabled, getParseTreeNode, getSourceFileOfNode,
6    Identifier, isBundle, isSourceFile, LexicalEnvironmentFlags, map, memoize, MemoryDotting, ModuleKind, Node, PerformanceDotting,
7    NodeFactory, NodeFlags, noop, notImplemented, returnUndefined, ScriptTarget, setEmitFlags, some, SourceFile,
8    Statement, SyntaxKind, tracing, TransformationContext, TransformationResult, transformClassFields,
9    transformDeclarations, transformECMAScriptModule, Transformer, TransformerFactory, transformES2015, transformES2016,
10    transformES2017, transformES2018, transformES2019, transformES2020, transformES2021, transformES5, transformESNext,
11    transformGenerators, transformJsx, transformLegacyDecorators, transformModule, transformNodeModule,
12    transformSystemModule, transformTypeScript, VariableDeclaration,
13} from "./_namespaces/ts";
14import * as performance from "./_namespaces/ts.performance";
15
16function getModuleTransformer(moduleKind: ModuleKind): TransformerFactory<SourceFile | Bundle> {
17    switch (moduleKind) {
18        case ModuleKind.ESNext:
19        case ModuleKind.ES2022:
20        case ModuleKind.ES2020:
21        case ModuleKind.ES2015:
22            return transformECMAScriptModule;
23        case ModuleKind.System:
24            return transformSystemModule;
25        case ModuleKind.Node16:
26        case ModuleKind.NodeNext:
27            return transformNodeModule;
28        default:
29            return transformModule;
30    }
31}
32
33const enum TransformationState {
34    Uninitialized,
35    Initialized,
36    Completed,
37    Disposed
38}
39
40const enum SyntaxKindFeatureFlags {
41    Substitution = 1 << 0,
42    EmitNotifications = 1 << 1,
43}
44
45/** @internal */
46export const noTransformers: EmitTransformers = { scriptTransformers: emptyArray, declarationTransformers: emptyArray };
47
48/** @internal */
49export function getTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnlyDtsFiles?: boolean): EmitTransformers {
50    return {
51        scriptTransformers: getScriptTransformers(compilerOptions, customTransformers, emitOnlyDtsFiles),
52        declarationTransformers: getDeclarationTransformers(customTransformers),
53    };
54}
55
56function getScriptTransformers(compilerOptions: CompilerOptions, customTransformers?: CustomTransformers, emitOnlyDtsFiles?: boolean) {
57    if (emitOnlyDtsFiles) return emptyArray;
58
59    const languageVersion = getEmitScriptTarget(compilerOptions);
60    const moduleKind = getEmitModuleKind(compilerOptions);
61    const transformers: TransformerFactory<SourceFile | Bundle>[] = [];
62
63    addRange(transformers, customTransformers && map(customTransformers.before, wrapScriptTransformerFactory));
64
65    transformers.push(transformTypeScript);
66    transformers.push(transformLegacyDecorators);
67    transformers.push(transformClassFields);
68
69    if (getJSXTransformEnabled(compilerOptions)) {
70        transformers.push(transformJsx);
71    }
72
73    if (languageVersion < ScriptTarget.ESNext) {
74        transformers.push(transformESNext);
75    }
76
77    if (languageVersion < ScriptTarget.ES2021) {
78        transformers.push(transformES2021);
79    }
80
81    if (languageVersion < ScriptTarget.ES2020) {
82        transformers.push(transformES2020);
83    }
84
85    if (languageVersion < ScriptTarget.ES2019) {
86        transformers.push(transformES2019);
87    }
88
89    if (languageVersion < ScriptTarget.ES2018) {
90        transformers.push(transformES2018);
91    }
92
93    if (languageVersion < ScriptTarget.ES2017) {
94        transformers.push(transformES2017);
95    }
96
97    if (languageVersion < ScriptTarget.ES2016) {
98        transformers.push(transformES2016);
99    }
100
101    if (languageVersion < ScriptTarget.ES2015) {
102        transformers.push(transformES2015);
103        transformers.push(transformGenerators);
104    }
105
106    transformers.push(getModuleTransformer(moduleKind));
107
108    // The ES5 transformer is last so that it can substitute expressions like `exports.default`
109    // for ES3.
110    if (languageVersion < ScriptTarget.ES5) {
111        transformers.push(transformES5);
112    }
113
114    addRange(transformers, customTransformers && map(customTransformers.after, wrapScriptTransformerFactory));
115    return transformers;
116}
117
118function getDeclarationTransformers(customTransformers?: CustomTransformers) {
119    const transformers: TransformerFactory<SourceFile | Bundle>[] = [];
120    transformers.push(transformDeclarations);
121    addRange(transformers, customTransformers && map(customTransformers.afterDeclarations, wrapDeclarationTransformerFactory));
122    return transformers;
123}
124
125/**
126 * Wrap a custom script or declaration transformer object in a `Transformer` callback with fallback support for transforming bundles.
127 */
128function wrapCustomTransformer(transformer: CustomTransformer): Transformer<Bundle | SourceFile> {
129    return node => isBundle(node) ? transformer.transformBundle(node) : transformer.transformSourceFile(node);
130}
131
132/**
133 * Wrap a transformer factory that may return a custom script or declaration transformer object.
134 */
135function wrapCustomTransformerFactory<T extends SourceFile | Bundle>(transformer: TransformerFactory<T> | CustomTransformerFactory, handleDefault: (context: TransformationContext, tx: Transformer<T>) => Transformer<Bundle | SourceFile>): TransformerFactory<Bundle | SourceFile> {
136    return context => {
137        const customTransformer = transformer(context);
138        return typeof customTransformer === "function"
139            ? handleDefault(context, customTransformer)
140            : wrapCustomTransformer(customTransformer);
141    };
142}
143
144function wrapScriptTransformerFactory(transformer: TransformerFactory<SourceFile> | CustomTransformerFactory): TransformerFactory<Bundle | SourceFile> {
145    return wrapCustomTransformerFactory(transformer, chainBundle);
146}
147
148function wrapDeclarationTransformerFactory(transformer: TransformerFactory<Bundle | SourceFile> | CustomTransformerFactory): TransformerFactory<Bundle | SourceFile> {
149    return wrapCustomTransformerFactory(transformer, (_, node) => node);
150}
151
152/** @internal */
153export function noEmitSubstitution(_hint: EmitHint, node: Node) {
154    return node;
155}
156
157/** @internal */
158export function noEmitNotification(hint: EmitHint, node: Node, callback: (hint: EmitHint, node: Node) => void) {
159    callback(hint, node);
160}
161
162/**
163 * Transforms an array of SourceFiles by passing them through each transformer.
164 *
165 * @param resolver The emit resolver provided by the checker.
166 * @param host The emit host object used to interact with the file system.
167 * @param options Compiler options to surface in the `TransformationContext`.
168 * @param nodes An array of nodes to transform.
169 * @param transforms An array of `TransformerFactory` callbacks.
170 * @param allowDtsFiles A value indicating whether to allow the transformation of .d.ts files.
171 *
172 * @internal
173 */
174export function transformNodes<T extends Node>(resolver: EmitResolver | undefined, host: EmitHost | undefined, factory: NodeFactory, options: CompilerOptions, nodes: readonly T[], transformers: readonly TransformerFactory<T>[], allowDtsFiles: boolean): TransformationResult<T> {
175    const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count);
176    let lexicalEnvironmentVariableDeclarations: VariableDeclaration[];
177    let lexicalEnvironmentFunctionDeclarations: FunctionDeclaration[];
178    let lexicalEnvironmentStatements: Statement[];
179    let lexicalEnvironmentFlags = LexicalEnvironmentFlags.None;
180    let lexicalEnvironmentVariableDeclarationsStack: VariableDeclaration[][] = [];
181    let lexicalEnvironmentFunctionDeclarationsStack: FunctionDeclaration[][] = [];
182    let lexicalEnvironmentStatementsStack: Statement[][] = [];
183    let lexicalEnvironmentFlagsStack: LexicalEnvironmentFlags[] = [];
184    let lexicalEnvironmentStackOffset = 0;
185    let lexicalEnvironmentSuspended = false;
186    let blockScopedVariableDeclarationsStack: Identifier[][] = [];
187    let blockScopeStackOffset = 0;
188    let blockScopedVariableDeclarations: Identifier[];
189    let emitHelpers: EmitHelper[] | undefined;
190    let onSubstituteNode: TransformationContext["onSubstituteNode"] = noEmitSubstitution;
191    let onEmitNode: TransformationContext["onEmitNode"] = noEmitNotification;
192    let state = TransformationState.Uninitialized;
193    const diagnostics: DiagnosticWithLocation[] = [];
194
195    // The transformation context is provided to each transformer as part of transformer
196    // initialization.
197    const context: TransformationContext = {
198        factory,
199        getCompilerOptions: () => options,
200        getEmitResolver: () => resolver!, // TODO: GH#18217
201        getEmitHost: () => host!, // TODO: GH#18217
202        getEmitHelperFactory: memoize(() => createEmitHelperFactory(context)),
203        startLexicalEnvironment,
204        suspendLexicalEnvironment,
205        resumeLexicalEnvironment,
206        endLexicalEnvironment,
207        setLexicalEnvironmentFlags,
208        getLexicalEnvironmentFlags,
209        hoistVariableDeclaration,
210        hoistFunctionDeclaration,
211        addInitializationStatement,
212        startBlockScope,
213        endBlockScope,
214        addBlockScopedVariable,
215        requestEmitHelper,
216        readEmitHelpers,
217        enableSubstitution,
218        enableEmitNotification,
219        isSubstitutionEnabled,
220        isEmitNotificationEnabled,
221        isLexicalEnvironmentSuspended,
222        get onSubstituteNode() { return onSubstituteNode; },
223        set onSubstituteNode(value) {
224            Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
225            Debug.assert(value !== undefined, "Value must not be 'undefined'");
226            onSubstituteNode = value;
227        },
228        get onEmitNode() { return onEmitNode; },
229        set onEmitNode(value) {
230            Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
231            Debug.assert(value !== undefined, "Value must not be 'undefined'");
232            onEmitNode = value;
233        },
234        addDiagnostic(diag) {
235            diagnostics.push(diag);
236        }
237    };
238
239    // Ensure the parse tree is clean before applying transformations
240    for (const node of nodes) {
241        disposeEmitNodes(getSourceFileOfNode(getParseTreeNode(node)));
242    }
243
244    const recordInfo = MemoryDotting.recordStage(MemoryDotting.TRANSFORM);
245    performance.mark("beforeTransform");
246
247    // Chain together and initialize each transformer.
248    const transformersWithContext = transformers.map(t => t(context));
249    const transformation = (node: T): T => {
250        for (const transform of transformersWithContext) {
251            node = transform(node);
252        }
253        return node;
254    };
255
256    // prevent modification of transformation hooks.
257    state = TransformationState.Initialized;
258
259    // Transform each node.
260    const transformed: T[] = [];
261    for (const node of nodes) {
262        tracing?.push(tracing.Phase.Emit, "transformNodes", node.kind === SyntaxKind.SourceFile ? { path: (node as any as SourceFile).path } : { kind: node.kind, pos: node.pos, end: node.end });
263        PerformanceDotting.start("transformRoot");
264        transformed.push((allowDtsFiles ? transformation : transformRoot)(node));
265        PerformanceDotting.stop("transformRoot");
266        tracing?.pop();
267    }
268
269    // prevent modification of the lexical environment.
270    state = TransformationState.Completed;
271
272    performance.mark("afterTransform");
273    MemoryDotting.stopRecordStage(recordInfo);
274    performance.measure("transformTime", "beforeTransform", "afterTransform");
275
276    return {
277        transformed,
278        substituteNode,
279        emitNodeWithNotification,
280        isEmitNotificationEnabled,
281        dispose,
282        diagnostics
283    };
284
285    function transformRoot(node: T) {
286        return node && (!isSourceFile(node) || !node.isDeclarationFile) ? transformation(node) : node;
287    }
288
289    /**
290     * Enables expression substitutions in the pretty printer for the provided SyntaxKind.
291     */
292    function enableSubstitution(kind: SyntaxKind) {
293        Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
294        enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.Substitution;
295    }
296
297    /**
298     * Determines whether expression substitutions are enabled for the provided node.
299     */
300    function isSubstitutionEnabled(node: Node) {
301        return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0
302            && (getEmitFlags(node) & EmitFlags.NoSubstitution) === 0;
303    }
304
305    /**
306     * Emits a node with possible substitution.
307     *
308     * @param hint A hint as to the intended usage of the node.
309     * @param node The node to emit.
310     * @param emitCallback The callback used to emit the node or its substitute.
311     */
312    function substituteNode(hint: EmitHint, node: Node) {
313        Debug.assert(state < TransformationState.Disposed, "Cannot substitute a node after the result is disposed.");
314        return node && isSubstitutionEnabled(node) && onSubstituteNode(hint, node) || node;
315    }
316
317    /**
318     * Enables before/after emit notifications in the pretty printer for the provided SyntaxKind.
319     */
320    function enableEmitNotification(kind: SyntaxKind) {
321        Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
322        enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.EmitNotifications;
323    }
324
325    /**
326     * Determines whether before/after emit notifications should be raised in the pretty
327     * printer when it emits a node.
328     */
329    function isEmitNotificationEnabled(node: Node) {
330        return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.EmitNotifications) !== 0
331            || (getEmitFlags(node) & EmitFlags.AdviseOnEmitNode) !== 0;
332    }
333
334    /**
335     * Emits a node with possible emit notification.
336     *
337     * @param hint A hint as to the intended usage of the node.
338     * @param node The node to emit.
339     * @param emitCallback The callback used to emit the node.
340     */
341    function emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
342        Debug.assert(state < TransformationState.Disposed, "Cannot invoke TransformationResult callbacks after the result is disposed.");
343        if (node) {
344            // TODO: Remove check and unconditionally use onEmitNode when API is breakingly changed
345            // (see https://github.com/microsoft/TypeScript/pull/36248/files/5062623f39120171b98870c71344b3242eb03d23#r369766739)
346            if (isEmitNotificationEnabled(node)) {
347                onEmitNode(hint, node, emitCallback);
348            }
349            else {
350                emitCallback(hint, node);
351            }
352        }
353    }
354
355    /**
356     * Records a hoisted variable declaration for the provided name within a lexical environment.
357     */
358    function hoistVariableDeclaration(name: Identifier): void {
359        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
360        Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
361        const decl = setEmitFlags(factory.createVariableDeclaration(name), EmitFlags.NoNestedSourceMaps);
362        if (!lexicalEnvironmentVariableDeclarations) {
363            lexicalEnvironmentVariableDeclarations = [decl];
364        }
365        else {
366            lexicalEnvironmentVariableDeclarations.push(decl);
367        }
368        if (lexicalEnvironmentFlags & LexicalEnvironmentFlags.InParameters) {
369            lexicalEnvironmentFlags |= LexicalEnvironmentFlags.VariablesHoistedInParameters;
370        }
371    }
372
373    /**
374     * Records a hoisted function declaration within a lexical environment.
375     */
376    function hoistFunctionDeclaration(func: FunctionDeclaration): void {
377        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
378        Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
379        setEmitFlags(func, EmitFlags.CustomPrologue);
380        if (!lexicalEnvironmentFunctionDeclarations) {
381            lexicalEnvironmentFunctionDeclarations = [func];
382        }
383        else {
384            lexicalEnvironmentFunctionDeclarations.push(func);
385        }
386    }
387
388    /**
389     * Adds an initialization statement to the top of the lexical environment.
390     */
391    function addInitializationStatement(node: Statement): void {
392        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
393        Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
394        setEmitFlags(node, EmitFlags.CustomPrologue);
395        if (!lexicalEnvironmentStatements) {
396            lexicalEnvironmentStatements = [node];
397        }
398        else {
399            lexicalEnvironmentStatements.push(node);
400        }
401    }
402
403    /**
404     * Starts a new lexical environment. Any existing hoisted variable or function declarations
405     * are pushed onto a stack, and the related storage variables are reset.
406     */
407    function startLexicalEnvironment(): void {
408        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
409        Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
410        Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended.");
411
412        // Save the current lexical environment. Rather than resizing the array we adjust the
413        // stack size variable. This allows us to reuse existing array slots we've
414        // already allocated between transformations to avoid allocation and GC overhead during
415        // transformation.
416        lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations;
417        lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations;
418        lexicalEnvironmentStatementsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentStatements;
419        lexicalEnvironmentFlagsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFlags;
420        lexicalEnvironmentStackOffset++;
421        lexicalEnvironmentVariableDeclarations = undefined!;
422        lexicalEnvironmentFunctionDeclarations = undefined!;
423        lexicalEnvironmentStatements = undefined!;
424        lexicalEnvironmentFlags = LexicalEnvironmentFlags.None;
425    }
426
427    /** Suspends the current lexical environment, usually after visiting a parameter list. */
428    function suspendLexicalEnvironment(): void {
429        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
430        Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
431        Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is already suspended.");
432        lexicalEnvironmentSuspended = true;
433    }
434
435    /** Resumes a suspended lexical environment, usually before visiting a function body. */
436    function resumeLexicalEnvironment(): void {
437        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
438        Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
439        Debug.assert(lexicalEnvironmentSuspended, "Lexical environment is not suspended.");
440        lexicalEnvironmentSuspended = false;
441    }
442
443    /**
444     * Ends a lexical environment. The previous set of hoisted declarations are restored and
445     * any hoisted declarations added in this environment are returned.
446     */
447    function endLexicalEnvironment(): Statement[] | undefined {
448        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
449        Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
450        Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended.");
451
452        let statements: Statement[] | undefined;
453        if (lexicalEnvironmentVariableDeclarations ||
454            lexicalEnvironmentFunctionDeclarations ||
455            lexicalEnvironmentStatements) {
456            if (lexicalEnvironmentFunctionDeclarations) {
457                statements = [...lexicalEnvironmentFunctionDeclarations];
458            }
459
460            if (lexicalEnvironmentVariableDeclarations) {
461                const statement = factory.createVariableStatement(
462                    /*modifiers*/ undefined,
463                    factory.createVariableDeclarationList(lexicalEnvironmentVariableDeclarations)
464                );
465
466                setEmitFlags(statement, EmitFlags.CustomPrologue);
467
468                if (!statements) {
469                    statements = [statement];
470                }
471                else {
472                    statements.push(statement);
473                }
474            }
475
476            if (lexicalEnvironmentStatements) {
477                if (!statements) {
478                    statements = [...lexicalEnvironmentStatements];
479                }
480                else {
481                    statements = [...statements, ...lexicalEnvironmentStatements];
482                }
483            }
484        }
485
486        // Restore the previous lexical environment.
487        lexicalEnvironmentStackOffset--;
488        lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
489        lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
490        lexicalEnvironmentStatements = lexicalEnvironmentStatementsStack[lexicalEnvironmentStackOffset];
491        lexicalEnvironmentFlags = lexicalEnvironmentFlagsStack[lexicalEnvironmentStackOffset];
492        if (lexicalEnvironmentStackOffset === 0) {
493            lexicalEnvironmentVariableDeclarationsStack = [];
494            lexicalEnvironmentFunctionDeclarationsStack = [];
495            lexicalEnvironmentStatementsStack = [];
496            lexicalEnvironmentFlagsStack = [];
497        }
498        return statements;
499    }
500
501    function setLexicalEnvironmentFlags(flags: LexicalEnvironmentFlags, value: boolean): void {
502        lexicalEnvironmentFlags = value ?
503            lexicalEnvironmentFlags | flags :
504            lexicalEnvironmentFlags & ~flags;
505    }
506
507    function getLexicalEnvironmentFlags(): LexicalEnvironmentFlags {
508        return lexicalEnvironmentFlags;
509    }
510
511    /**
512     * Starts a block scope. Any existing block hoisted variables are pushed onto the stack and the related storage variables are reset.
513     */
514    function startBlockScope() {
515        Debug.assert(state > TransformationState.Uninitialized, "Cannot start a block scope during initialization.");
516        Debug.assert(state < TransformationState.Completed, "Cannot start a block scope after transformation has completed.");
517        blockScopedVariableDeclarationsStack[blockScopeStackOffset] = blockScopedVariableDeclarations;
518        blockScopeStackOffset++;
519        blockScopedVariableDeclarations = undefined!;
520    }
521
522    /**
523     * Ends a block scope. The previous set of block hoisted variables are restored. Any hoisted declarations are returned.
524     */
525    function endBlockScope() {
526        Debug.assert(state > TransformationState.Uninitialized, "Cannot end a block scope during initialization.");
527        Debug.assert(state < TransformationState.Completed, "Cannot end a block scope after transformation has completed.");
528        const statements: Statement[] | undefined = some(blockScopedVariableDeclarations) ?
529            [
530                factory.createVariableStatement(
531                    /*modifiers*/ undefined,
532                    factory.createVariableDeclarationList(
533                        blockScopedVariableDeclarations.map(identifier => factory.createVariableDeclaration(identifier)),
534                        NodeFlags.Let
535                    )
536                )
537            ] : undefined;
538        blockScopeStackOffset--;
539        blockScopedVariableDeclarations = blockScopedVariableDeclarationsStack[blockScopeStackOffset];
540        if (blockScopeStackOffset === 0) {
541            blockScopedVariableDeclarationsStack = [];
542        }
543        return statements;
544    }
545
546    function addBlockScopedVariable(name: Identifier): void {
547        Debug.assert(blockScopeStackOffset > 0, "Cannot add a block scoped variable outside of an iteration body.");
548        (blockScopedVariableDeclarations || (blockScopedVariableDeclarations = [])).push(name);
549    }
550
551    function requestEmitHelper(helper: EmitHelper): void {
552        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization.");
553        Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
554        Debug.assert(!helper.scoped, "Cannot request a scoped emit helper.");
555        if (helper.dependencies) {
556            for (const h of helper.dependencies) {
557                requestEmitHelper(h);
558            }
559        }
560        emitHelpers = append(emitHelpers, helper);
561    }
562
563    function readEmitHelpers(): EmitHelper[] | undefined {
564        Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization.");
565        Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
566        const helpers = emitHelpers;
567        emitHelpers = undefined;
568        return helpers;
569    }
570
571    function dispose() {
572        if (state < TransformationState.Disposed) {
573            // Clean up emit nodes on parse tree
574            for (const node of nodes) {
575                disposeEmitNodes(getSourceFileOfNode(getParseTreeNode(node)));
576            }
577
578            // Release references to external entries for GC purposes.
579            lexicalEnvironmentVariableDeclarations = undefined!;
580            lexicalEnvironmentVariableDeclarationsStack = undefined!;
581            lexicalEnvironmentFunctionDeclarations = undefined!;
582            lexicalEnvironmentFunctionDeclarationsStack = undefined!;
583            onSubstituteNode = undefined!;
584            onEmitNode = undefined!;
585            emitHelpers = undefined;
586
587            // Prevent further use of the transformation result.
588            state = TransformationState.Disposed;
589        }
590    }
591
592    /** Determines whether the lexical environment is suspended */
593    function isLexicalEnvironmentSuspended(): boolean {
594        return lexicalEnvironmentSuspended;
595    }
596}
597
598export const nullTransformationContext: TransformationContext = {
599    factory: factory, // eslint-disable-line object-shorthand
600    getCompilerOptions: () => ({}),
601    getEmitResolver: notImplemented,
602    getEmitHost: notImplemented,
603    getEmitHelperFactory: notImplemented,
604    startLexicalEnvironment: noop,
605    resumeLexicalEnvironment: noop,
606    suspendLexicalEnvironment: noop,
607    endLexicalEnvironment: returnUndefined,
608    setLexicalEnvironmentFlags: noop,
609    getLexicalEnvironmentFlags: () => 0,
610    hoistVariableDeclaration: noop,
611    hoistFunctionDeclaration: noop,
612    addInitializationStatement: noop,
613    startBlockScope: noop,
614    endBlockScope: returnUndefined,
615    addBlockScopedVariable: noop,
616    requestEmitHelper: noop,
617    readEmitHelpers: notImplemented,
618    enableSubstitution: noop,
619    enableEmitNotification: noop,
620    isSubstitutionEnabled: notImplemented,
621    isEmitNotificationEnabled: notImplemented,
622    onSubstituteNode: noEmitSubstitution,
623    onEmitNode: noEmitNotification,
624    addDiagnostic: noop,
625};
626