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}