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