• 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            get onSubstituteNode() { return onSubstituteNode; },
203            set onSubstituteNode(value) {
204                Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
205                Debug.assert(value !== undefined, "Value must not be 'undefined'");
206                onSubstituteNode = value;
207            },
208            get onEmitNode() { return onEmitNode; },
209            set onEmitNode(value) {
210                Debug.assert(state < TransformationState.Initialized, "Cannot modify transformation hooks after initialization has completed.");
211                Debug.assert(value !== undefined, "Value must not be 'undefined'");
212                onEmitNode = value;
213            },
214            addDiagnostic(diag) {
215                diagnostics.push(diag);
216            }
217        };
218
219        // Ensure the parse tree is clean before applying transformations
220        for (const node of nodes) {
221            disposeEmitNodes(getSourceFileOfNode(getParseTreeNode(node)));
222        }
223
224        performance.mark("beforeTransform");
225
226        // Chain together and initialize each transformer.
227        const transformersWithContext = transformers.map(t => t(context));
228        const transformation = (node: T): T => {
229            for (const transform of transformersWithContext) {
230                node = transform(node);
231            }
232            return node;
233        };
234
235        // prevent modification of transformation hooks.
236        state = TransformationState.Initialized;
237
238        // Transform each node.
239        const transformed: T[] = [];
240        for (const node of nodes) {
241            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 });
242            transformed.push((allowDtsFiles ? transformation : transformRoot)(node));
243            tracing?.pop();
244        }
245
246        // prevent modification of the lexical environment.
247        state = TransformationState.Completed;
248
249        performance.mark("afterTransform");
250        performance.measure("transformTime", "beforeTransform", "afterTransform");
251
252        return {
253            transformed,
254            substituteNode,
255            emitNodeWithNotification,
256            isEmitNotificationEnabled,
257            dispose,
258            diagnostics
259        };
260
261        function transformRoot(node: T) {
262            return node && (!isSourceFile(node) || !node.isDeclarationFile) ? transformation(node) : node;
263        }
264
265        /**
266         * Enables expression substitutions in the pretty printer for the provided SyntaxKind.
267         */
268        function enableSubstitution(kind: SyntaxKind) {
269            Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
270            enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.Substitution;
271        }
272
273        /**
274         * Determines whether expression substitutions are enabled for the provided node.
275         */
276        function isSubstitutionEnabled(node: Node) {
277            return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.Substitution) !== 0
278                && (getEmitFlags(node) & EmitFlags.NoSubstitution) === 0;
279        }
280
281        /**
282         * Emits a node with possible substitution.
283         *
284         * @param hint A hint as to the intended usage of the node.
285         * @param node The node to emit.
286         * @param emitCallback The callback used to emit the node or its substitute.
287         */
288        function substituteNode(hint: EmitHint, node: Node) {
289            Debug.assert(state < TransformationState.Disposed, "Cannot substitute a node after the result is disposed.");
290            return node && isSubstitutionEnabled(node) && onSubstituteNode(hint, node) || node;
291        }
292
293        /**
294         * Enables before/after emit notifications in the pretty printer for the provided SyntaxKind.
295         */
296        function enableEmitNotification(kind: SyntaxKind) {
297            Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
298            enabledSyntaxKindFeatures[kind] |= SyntaxKindFeatureFlags.EmitNotifications;
299        }
300
301        /**
302         * Determines whether before/after emit notifications should be raised in the pretty
303         * printer when it emits a node.
304         */
305        function isEmitNotificationEnabled(node: Node) {
306            return (enabledSyntaxKindFeatures[node.kind] & SyntaxKindFeatureFlags.EmitNotifications) !== 0
307                || (getEmitFlags(node) & EmitFlags.AdviseOnEmitNode) !== 0;
308        }
309
310        /**
311         * Emits a node with possible emit notification.
312         *
313         * @param hint A hint as to the intended usage of the node.
314         * @param node The node to emit.
315         * @param emitCallback The callback used to emit the node.
316         */
317        function emitNodeWithNotification(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
318            Debug.assert(state < TransformationState.Disposed, "Cannot invoke TransformationResult callbacks after the result is disposed.");
319            if (node) {
320                // TODO: Remove check and unconditionally use onEmitNode when API is breakingly changed
321                // (see https://github.com/microsoft/TypeScript/pull/36248/files/5062623f39120171b98870c71344b3242eb03d23#r369766739)
322                if (isEmitNotificationEnabled(node)) {
323                    onEmitNode(hint, node, emitCallback);
324                }
325                else {
326                    emitCallback(hint, node);
327                }
328            }
329        }
330
331        /**
332         * Records a hoisted variable declaration for the provided name within a lexical environment.
333         */
334        function hoistVariableDeclaration(name: Identifier): void {
335            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
336            Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
337            const decl = setEmitFlags(factory.createVariableDeclaration(name), EmitFlags.NoNestedSourceMaps);
338            if (!lexicalEnvironmentVariableDeclarations) {
339                lexicalEnvironmentVariableDeclarations = [decl];
340            }
341            else {
342                lexicalEnvironmentVariableDeclarations.push(decl);
343            }
344            if (lexicalEnvironmentFlags & LexicalEnvironmentFlags.InParameters) {
345                lexicalEnvironmentFlags |= LexicalEnvironmentFlags.VariablesHoistedInParameters;
346            }
347        }
348
349        /**
350         * Records a hoisted function declaration within a lexical environment.
351         */
352        function hoistFunctionDeclaration(func: FunctionDeclaration): void {
353            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
354            Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
355            setEmitFlags(func, EmitFlags.CustomPrologue);
356            if (!lexicalEnvironmentFunctionDeclarations) {
357                lexicalEnvironmentFunctionDeclarations = [func];
358            }
359            else {
360                lexicalEnvironmentFunctionDeclarations.push(func);
361            }
362        }
363
364        /**
365         * Adds an initialization statement to the top of the lexical environment.
366         */
367        function addInitializationStatement(node: Statement): void {
368            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
369            Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
370            setEmitFlags(node, EmitFlags.CustomPrologue);
371            if (!lexicalEnvironmentStatements) {
372                lexicalEnvironmentStatements = [node];
373            }
374            else {
375                lexicalEnvironmentStatements.push(node);
376            }
377        }
378
379        /**
380         * Starts a new lexical environment. Any existing hoisted variable or function declarations
381         * are pushed onto a stack, and the related storage variables are reset.
382         */
383        function startLexicalEnvironment(): void {
384            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
385            Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
386            Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended.");
387
388            // Save the current lexical environment. Rather than resizing the array we adjust the
389            // stack size variable. This allows us to reuse existing array slots we've
390            // already allocated between transformations to avoid allocation and GC overhead during
391            // transformation.
392            lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentVariableDeclarations;
393            lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFunctionDeclarations;
394            lexicalEnvironmentStatementsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentStatements;
395            lexicalEnvironmentFlagsStack[lexicalEnvironmentStackOffset] = lexicalEnvironmentFlags;
396            lexicalEnvironmentStackOffset++;
397            lexicalEnvironmentVariableDeclarations = undefined!;
398            lexicalEnvironmentFunctionDeclarations = undefined!;
399            lexicalEnvironmentStatements = undefined!;
400            lexicalEnvironmentFlags = LexicalEnvironmentFlags.None;
401        }
402
403        /** Suspends the current lexical environment, usually after visiting a parameter list. */
404        function suspendLexicalEnvironment(): void {
405            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
406            Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
407            Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is already suspended.");
408            lexicalEnvironmentSuspended = true;
409        }
410
411        /** Resumes a suspended lexical environment, usually before visiting a function body. */
412        function resumeLexicalEnvironment(): void {
413            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
414            Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
415            Debug.assert(lexicalEnvironmentSuspended, "Lexical environment is not suspended.");
416            lexicalEnvironmentSuspended = false;
417        }
418
419        /**
420         * Ends a lexical environment. The previous set of hoisted declarations are restored and
421         * any hoisted declarations added in this environment are returned.
422         */
423        function endLexicalEnvironment(): Statement[] | undefined {
424            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the lexical environment during initialization.");
425            Debug.assert(state < TransformationState.Completed, "Cannot modify the lexical environment after transformation has completed.");
426            Debug.assert(!lexicalEnvironmentSuspended, "Lexical environment is suspended.");
427
428            let statements: Statement[] | undefined;
429            if (lexicalEnvironmentVariableDeclarations ||
430                lexicalEnvironmentFunctionDeclarations ||
431                lexicalEnvironmentStatements) {
432                if (lexicalEnvironmentFunctionDeclarations) {
433                    statements = [...lexicalEnvironmentFunctionDeclarations];
434                }
435
436                if (lexicalEnvironmentVariableDeclarations) {
437                    const statement = factory.createVariableStatement(
438                        /*modifiers*/ undefined,
439                        factory.createVariableDeclarationList(lexicalEnvironmentVariableDeclarations)
440                    );
441
442                    setEmitFlags(statement, EmitFlags.CustomPrologue);
443
444                    if (!statements) {
445                        statements = [statement];
446                    }
447                    else {
448                        statements.push(statement);
449                    }
450                }
451
452                if (lexicalEnvironmentStatements) {
453                    if (!statements) {
454                        statements = [...lexicalEnvironmentStatements];
455                    }
456                    else {
457                        statements = [...statements, ...lexicalEnvironmentStatements];
458                    }
459                }
460            }
461
462            // Restore the previous lexical environment.
463            lexicalEnvironmentStackOffset--;
464            lexicalEnvironmentVariableDeclarations = lexicalEnvironmentVariableDeclarationsStack[lexicalEnvironmentStackOffset];
465            lexicalEnvironmentFunctionDeclarations = lexicalEnvironmentFunctionDeclarationsStack[lexicalEnvironmentStackOffset];
466            lexicalEnvironmentStatements = lexicalEnvironmentStatementsStack[lexicalEnvironmentStackOffset];
467            lexicalEnvironmentFlags = lexicalEnvironmentFlagsStack[lexicalEnvironmentStackOffset];
468            if (lexicalEnvironmentStackOffset === 0) {
469                lexicalEnvironmentVariableDeclarationsStack = [];
470                lexicalEnvironmentFunctionDeclarationsStack = [];
471                lexicalEnvironmentStatementsStack = [];
472                lexicalEnvironmentFlagsStack = [];
473            }
474            return statements;
475        }
476
477        function setLexicalEnvironmentFlags(flags: LexicalEnvironmentFlags, value: boolean): void {
478            lexicalEnvironmentFlags = value ?
479                lexicalEnvironmentFlags | flags :
480                lexicalEnvironmentFlags & ~flags;
481        }
482
483        function getLexicalEnvironmentFlags(): LexicalEnvironmentFlags {
484            return lexicalEnvironmentFlags;
485        }
486
487        /**
488         * Starts a block scope. Any existing block hoisted variables are pushed onto the stack and the related storage variables are reset.
489         */
490        function startBlockScope() {
491            Debug.assert(state > TransformationState.Uninitialized, "Cannot start a block scope during initialization.");
492            Debug.assert(state < TransformationState.Completed, "Cannot start a block scope after transformation has completed.");
493            blockScopedVariableDeclarationsStack[blockScopeStackOffset] = blockScopedVariableDeclarations;
494            blockScopeStackOffset++;
495            blockScopedVariableDeclarations = undefined!;
496        }
497
498        /**
499         * Ends a block scope. The previous set of block hoisted variables are restored. Any hoisted declarations are returned.
500         */
501        function endBlockScope() {
502            Debug.assert(state > TransformationState.Uninitialized, "Cannot end a block scope during initialization.");
503            Debug.assert(state < TransformationState.Completed, "Cannot end a block scope after transformation has completed.");
504            const statements: Statement[] | undefined = some(blockScopedVariableDeclarations) ?
505                [
506                    factory.createVariableStatement(
507                        /*modifiers*/ undefined,
508                        factory.createVariableDeclarationList(
509                            blockScopedVariableDeclarations.map(identifier => factory.createVariableDeclaration(identifier)),
510                            NodeFlags.Let
511                        )
512                    )
513                ] : undefined;
514            blockScopeStackOffset--;
515            blockScopedVariableDeclarations = blockScopedVariableDeclarationsStack[blockScopeStackOffset];
516            if (blockScopeStackOffset === 0) {
517                blockScopedVariableDeclarationsStack = [];
518            }
519            return statements;
520        }
521
522        function addBlockScopedVariable(name: Identifier): void {
523            Debug.assert(blockScopeStackOffset > 0, "Cannot add a block scoped variable outside of an iteration body.");
524            (blockScopedVariableDeclarations || (blockScopedVariableDeclarations = [])).push(name);
525        }
526
527        function requestEmitHelper(helper: EmitHelper): void {
528            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization.");
529            Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
530            Debug.assert(!helper.scoped, "Cannot request a scoped emit helper.");
531            if (helper.dependencies) {
532                for (const h of helper.dependencies) {
533                    requestEmitHelper(h);
534                }
535            }
536            emitHelpers = append(emitHelpers, helper);
537        }
538
539        function readEmitHelpers(): EmitHelper[] | undefined {
540            Debug.assert(state > TransformationState.Uninitialized, "Cannot modify the transformation context during initialization.");
541            Debug.assert(state < TransformationState.Completed, "Cannot modify the transformation context after transformation has completed.");
542            const helpers = emitHelpers;
543            emitHelpers = undefined;
544            return helpers;
545        }
546
547        function dispose() {
548            if (state < TransformationState.Disposed) {
549                // Clean up emit nodes on parse tree
550                for (const node of nodes) {
551                    disposeEmitNodes(getSourceFileOfNode(getParseTreeNode(node)));
552                }
553
554                // Release references to external entries for GC purposes.
555                lexicalEnvironmentVariableDeclarations = undefined!;
556                lexicalEnvironmentVariableDeclarationsStack = undefined!;
557                lexicalEnvironmentFunctionDeclarations = undefined!;
558                lexicalEnvironmentFunctionDeclarationsStack = undefined!;
559                onSubstituteNode = undefined!;
560                onEmitNode = undefined!;
561                emitHelpers = undefined;
562
563                // Prevent further use of the transformation result.
564                state = TransformationState.Disposed;
565            }
566        }
567    }
568
569    export const nullTransformationContext: TransformationContext = {
570        factory: factory, // eslint-disable-line object-shorthand
571        getCompilerOptions: () => ({}),
572        getEmitResolver: notImplemented,
573        getEmitHost: notImplemented,
574        getEmitHelperFactory: notImplemented,
575        startLexicalEnvironment: noop,
576        resumeLexicalEnvironment: noop,
577        suspendLexicalEnvironment: noop,
578        endLexicalEnvironment: returnUndefined,
579        setLexicalEnvironmentFlags: noop,
580        getLexicalEnvironmentFlags: () => 0,
581        hoistVariableDeclaration: noop,
582        hoistFunctionDeclaration: noop,
583        addInitializationStatement: noop,
584        startBlockScope: noop,
585        endBlockScope: returnUndefined,
586        addBlockScopedVariable: noop,
587        requestEmitHelper: noop,
588        readEmitHelpers: notImplemented,
589        enableSubstitution: noop,
590        enableEmitNotification: noop,
591        isSubstitutionEnabled: notImplemented,
592        isEmitNotificationEnabled: notImplemented,
593        onSubstituteNode: noEmitSubstitution,
594        onEmitNode: noEmitNotification,
595        addDiagnostic: noop,
596    };
597}
598