• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts {
2    /**
3     * Associates a node with the current transformation, initializing
4     * various transient transformation properties.
5     * @internal
6     */
7    export function getOrCreateEmitNode(node: Node): EmitNode {
8        if (!node.emitNode) {
9            if (isParseTreeNode(node)) {
10                // To avoid holding onto transformation artifacts, we keep track of any
11                // parse tree node we are annotating. This allows us to clean them up after
12                // all transformations have completed.
13                if (node.kind === SyntaxKind.SourceFile) {
14                    return node.emitNode = { annotatedNodes: [node] } as EmitNode;
15                }
16
17                const sourceFile = getSourceFileOfNode(getParseTreeNode(getSourceFileOfNode(node))) ?? Debug.fail("Could not determine parsed source file.");
18                getOrCreateEmitNode(sourceFile).annotatedNodes!.push(node);
19            }
20
21            node.emitNode = {} as EmitNode;
22        }
23
24        return node.emitNode;
25    }
26
27    /**
28     * Clears any `EmitNode` entries from parse-tree nodes.
29     * @param sourceFile A source file.
30     */
31    export function disposeEmitNodes(sourceFile: SourceFile | undefined) {
32        // During transformation we may need to annotate a parse tree node with transient
33        // transformation properties. As parse tree nodes live longer than transformation
34        // nodes, we need to make sure we reclaim any memory allocated for custom ranges
35        // from these nodes to ensure we do not hold onto entire subtrees just for position
36        // information. We also need to reset these nodes to a pre-transformation state
37        // for incremental parsing scenarios so that we do not impact later emit.
38        const annotatedNodes = getSourceFileOfNode(getParseTreeNode(sourceFile))?.emitNode?.annotatedNodes;
39        if (annotatedNodes) {
40            for (const node of annotatedNodes) {
41                node.emitNode = undefined;
42            }
43        }
44    }
45
46    /**
47     * Sets `EmitFlags.NoComments` on a node and removes any leading and trailing synthetic comments.
48     * @internal
49     */
50    export function removeAllComments<T extends Node>(node: T): T {
51        const emitNode = getOrCreateEmitNode(node);
52        emitNode.flags |= EmitFlags.NoComments;
53        emitNode.leadingComments = undefined;
54        emitNode.trailingComments = undefined;
55        return node;
56    }
57
58    /**
59     * Sets flags that control emit behavior of a node.
60     */
61    export function setEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
62        getOrCreateEmitNode(node).flags = emitFlags;
63        return node;
64    }
65
66    /**
67     * Sets flags that control emit behavior of a node.
68     */
69    /* @internal */
70    export function addEmitFlags<T extends Node>(node: T, emitFlags: EmitFlags) {
71        const emitNode = getOrCreateEmitNode(node);
72        emitNode.flags = emitNode.flags | emitFlags;
73        return node;
74    }
75
76    /**
77     * Gets a custom text range to use when emitting source maps.
78     */
79    export function getSourceMapRange(node: Node): SourceMapRange {
80        return node.emitNode?.sourceMapRange ?? node;
81    }
82
83    /**
84     * Sets a custom text range to use when emitting source maps.
85     */
86    export function setSourceMapRange<T extends Node>(node: T, range: SourceMapRange | undefined) {
87        getOrCreateEmitNode(node).sourceMapRange = range;
88        return node;
89    }
90
91    /**
92     * Gets the TextRange to use for source maps for a token of a node.
93     */
94    export function getTokenSourceMapRange(node: Node, token: SyntaxKind): SourceMapRange | undefined {
95        return node.emitNode?.tokenSourceMapRanges?.[token];
96    }
97
98    /**
99     * Sets the TextRange to use for source maps for a token of a node.
100     */
101    export function setTokenSourceMapRange<T extends Node>(node: T, token: SyntaxKind, range: SourceMapRange | undefined) {
102        const emitNode = getOrCreateEmitNode(node);
103        const tokenSourceMapRanges = emitNode.tokenSourceMapRanges ?? (emitNode.tokenSourceMapRanges = []);
104        tokenSourceMapRanges[token] = range;
105        return node;
106    }
107
108    /**
109     * Gets a custom text range to use when emitting comments.
110     */
111    /*@internal*/
112    export function getStartsOnNewLine(node: Node) {
113        return node.emitNode?.startsOnNewLine;
114    }
115
116    /**
117     * Sets a custom text range to use when emitting comments.
118     */
119    /*@internal*/
120    export function setStartsOnNewLine<T extends Node>(node: T, newLine: boolean) {
121        getOrCreateEmitNode(node).startsOnNewLine = newLine;
122        return node;
123    }
124
125    /**
126     * Gets a custom text range to use when emitting comments.
127     */
128    export function getCommentRange(node: Node) {
129        return node.emitNode?.commentRange ?? node;
130    }
131
132    /**
133     * Sets a custom text range to use when emitting comments.
134     */
135    export function setCommentRange<T extends Node>(node: T, range: TextRange) {
136        getOrCreateEmitNode(node).commentRange = range;
137        return node;
138    }
139
140    export function getSyntheticLeadingComments(node: Node): SynthesizedComment[] | undefined {
141        return node.emitNode?.leadingComments;
142    }
143
144    export function setSyntheticLeadingComments<T extends Node>(node: T, comments: SynthesizedComment[] | undefined) {
145        getOrCreateEmitNode(node).leadingComments = comments;
146        return node;
147    }
148
149    export function addSyntheticLeadingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
150        return setSyntheticLeadingComments(node, append<SynthesizedComment>(getSyntheticLeadingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
151    }
152
153    export function getSyntheticTrailingComments(node: Node): SynthesizedComment[] | undefined {
154        return node.emitNode?.trailingComments;
155    }
156
157    export function setSyntheticTrailingComments<T extends Node>(node: T, comments: SynthesizedComment[] | undefined) {
158        getOrCreateEmitNode(node).trailingComments = comments;
159        return node;
160    }
161
162    export function addSyntheticTrailingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
163        return setSyntheticTrailingComments(node, append<SynthesizedComment>(getSyntheticTrailingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
164    }
165
166    export function moveSyntheticComments<T extends Node>(node: T, original: Node): T {
167        setSyntheticLeadingComments(node, getSyntheticLeadingComments(original));
168        setSyntheticTrailingComments(node, getSyntheticTrailingComments(original));
169        const emit = getOrCreateEmitNode(original);
170        emit.leadingComments = undefined;
171        emit.trailingComments = undefined;
172        return node;
173    }
174
175    /**
176     * Gets the constant value to emit for an expression representing an enum.
177     */
178    export function getConstantValue(node: AccessExpression): string | number | undefined {
179        return node.emitNode?.constantValue;
180    }
181
182    /**
183     * Sets the constant value to emit for an expression.
184     */
185    export function setConstantValue(node: AccessExpression, value: string | number): AccessExpression {
186        const emitNode = getOrCreateEmitNode(node);
187        emitNode.constantValue = value;
188        return node;
189    }
190
191    /**
192     * Adds an EmitHelper to a node.
193     */
194    export function addEmitHelper<T extends Node>(node: T, helper: EmitHelper): T {
195        const emitNode = getOrCreateEmitNode(node);
196        emitNode.helpers = append(emitNode.helpers, helper);
197        return node;
198    }
199
200    /**
201     * Add EmitHelpers to a node.
202     */
203    export function addEmitHelpers<T extends Node>(node: T, helpers: EmitHelper[] | undefined): T {
204        if (some(helpers)) {
205            const emitNode = getOrCreateEmitNode(node);
206            for (const helper of helpers) {
207                emitNode.helpers = appendIfUnique(emitNode.helpers, helper);
208            }
209        }
210        return node;
211    }
212
213    /**
214     * Removes an EmitHelper from a node.
215     */
216    export function removeEmitHelper(node: Node, helper: EmitHelper): boolean {
217        const helpers = node.emitNode?.helpers;
218        if (helpers) {
219            return orderedRemoveItem(helpers, helper);
220        }
221        return false;
222    }
223
224    /**
225     * Gets the EmitHelpers of a node.
226     */
227    export function getEmitHelpers(node: Node): EmitHelper[] | undefined {
228        return node.emitNode?.helpers;
229    }
230
231    /**
232     * Moves matching emit helpers from a source node to a target node.
233     */
234    export function moveEmitHelpers(source: Node, target: Node, predicate: (helper: EmitHelper) => boolean) {
235        const sourceEmitNode = source.emitNode;
236        const sourceEmitHelpers = sourceEmitNode && sourceEmitNode.helpers;
237        if (!some(sourceEmitHelpers)) return;
238
239        const targetEmitNode = getOrCreateEmitNode(target);
240        let helpersRemoved = 0;
241        for (let i = 0; i < sourceEmitHelpers.length; i++) {
242            const helper = sourceEmitHelpers[i];
243            if (predicate(helper)) {
244                helpersRemoved++;
245                targetEmitNode.helpers = appendIfUnique(targetEmitNode.helpers, helper);
246            }
247            else if (helpersRemoved > 0) {
248                sourceEmitHelpers[i - helpersRemoved] = helper;
249            }
250        }
251
252        if (helpersRemoved > 0) {
253            sourceEmitHelpers.length -= helpersRemoved;
254        }
255    }
256
257    /* @internal */
258    export function ignoreSourceNewlines<T extends Node>(node: T): T {
259        getOrCreateEmitNode(node).flags |= EmitFlags.IgnoreSourceNewlines;
260        return node;
261    }
262}