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