@@ -45,6 +45,14 @@ namespace ts {
4545 return transformers ;
4646 }
4747
48+ /**
49+ * Tracks a monotonically increasing transformation id used to associate a node with a specific
50+ * transformation. This ensures transient properties related to transformations can be safely
51+ * stored on source tree nodes that may be reused across multiple transformations (such as
52+ * with compile-on-save).
53+ */
54+ let nextTransformId = 1 ;
55+
4856 /**
4957 * Transforms an array of SourceFiles by passing them through each transformer.
5058 *
@@ -54,12 +62,13 @@ namespace ts {
5462 * @param transforms An array of Transformers.
5563 */
5664 export function transformFiles ( resolver : EmitResolver , host : EmitHost , sourceFiles : SourceFile [ ] , transformers : Transformer [ ] ) {
57- const nodeEmitFlags : Map < NodeEmitFlags > = { } ;
58- const sourceMapRanges : Map < TextRange > = { } ;
59- const commentRanges : Map < TextRange > = { } ;
65+ const transformId = nextTransformId ++ ;
66+ const tokenSourceMapRanges : Map < TextRange > = { } ;
6067 const lexicalEnvironmentVariableDeclarationsStack : VariableDeclaration [ ] [ ] = [ ] ;
6168 const lexicalEnvironmentFunctionDeclarationsStack : FunctionDeclaration [ ] [ ] = [ ] ;
6269 const enabledSyntaxKindFeatures = new Array < SyntaxKindFeatureFlags > ( SyntaxKind . Count ) ;
70+ const sourceTreeNodesWithAnnotations : Node [ ] = [ ] ;
71+
6372 let lastNodeEmitFlagsNode : Node ;
6473 let lastNodeEmitFlags : NodeEmitFlags ;
6574 let lastSourceMapRangeNode : Node ;
@@ -118,7 +127,20 @@ namespace ts {
118127 }
119128
120129 currentSourceFile = sourceFile ;
121- return transformation ( sourceFile ) ;
130+ sourceFile = transformation ( sourceFile ) ;
131+
132+ // Cleanup source tree nodes with annotations
133+ for ( const node of sourceTreeNodesWithAnnotations ) {
134+ if ( node . transformId === transformId ) {
135+ node . transformId = 0 ;
136+ node . emitFlags = 0 ;
137+ node . commentRange = undefined ;
138+ node . sourceMapRange = undefined ;
139+ }
140+ }
141+
142+ sourceTreeNodesWithAnnotations . length = 0 ;
143+ return sourceFile ;
122144 }
123145
124146 /**
@@ -181,6 +203,28 @@ namespace ts {
181203 emit ( node ) ;
182204 }
183205
206+ /**
207+ * Associates a node with the current transformation, initializing
208+ * various transient transformation properties.
209+ *
210+ * @param node The node.
211+ */
212+ function beforeSetAnnotation ( node : Node ) {
213+ if ( node . transformId !== transformId ) {
214+ node . transformId = transformId ;
215+ if ( ( node . flags & NodeFlags . Synthesized ) === 0 ) {
216+ node . emitFlags = 0 ;
217+ node . sourceMapRange = undefined ;
218+ node . commentRange = undefined ;
219+
220+ // To avoid holding onto transformation artifacts, we keep track of any
221+ // source tree node we are annotating. This allows us to clean them up after
222+ // all transformations have completed.
223+ sourceTreeNodesWithAnnotations . push ( node ) ;
224+ }
225+ }
226+ }
227+
184228 /**
185229 * Gets flags that control emit behavior of a node.
186230 *
@@ -196,15 +240,19 @@ namespace ts {
196240 return lastNodeEmitFlags ;
197241 }
198242
199-
243+ // Get the emit flags for a node or from one of its original nodes.
200244 let flags : NodeEmitFlags ;
201- while ( node ) {
202- let flags = node . id ? nodeEmitFlags [ node . id ] : undefined ;
203- if ( flags !== undefined ) {
204- break ;
245+ let current = node ;
246+ while ( current ) {
247+ if ( current . transformId === transformId ) {
248+ const nodeEmitFlags = current . emitFlags ;
249+ if ( nodeEmitFlags ) {
250+ flags = nodeEmitFlags & ~ NodeEmitFlags . HasNodeEmitFlags ;
251+ break ;
252+ }
205253 }
206254
207- node = node . original ;
255+ current = current . original ;
208256 }
209257
210258 // Cache the most recently requested value.
@@ -220,14 +268,17 @@ namespace ts {
220268 * @param emitFlags The NodeEmitFlags for the node.
221269 */
222270 function setNodeEmitFlags < T extends Node > ( node : T , emitFlags : NodeEmitFlags ) {
271+ // Merge existing flags.
223272 if ( emitFlags & NodeEmitFlags . Merge ) {
224273 emitFlags = getNodeEmitFlags ( node ) | ( emitFlags & ~ NodeEmitFlags . Merge ) ;
225274 }
226275
276+ beforeSetAnnotation ( node ) ;
277+
227278 // Cache the most recently requested value.
228279 lastNodeEmitFlagsNode = node ;
229280 lastNodeEmitFlags = emitFlags ;
230- nodeEmitFlags [ getNodeId ( node ) ] = emitFlags ;
281+ node . emitFlags = emitFlags | NodeEmitFlags . HasNodeEmitFlags ;
231282 return node ;
232283 }
233284
@@ -246,12 +297,15 @@ namespace ts {
246297 return lastSourceMapRange || node ;
247298 }
248299
300+ // Get the custom source map range for a node or from one of its original nodes.
249301 let range : TextRange ;
250302 let current = node ;
251303 while ( current ) {
252- range = current . id ? sourceMapRanges [ current . id ] : undefined ;
253- if ( range !== undefined ) {
254- break ;
304+ if ( current . transformId === transformId ) {
305+ range = current . sourceMapRange ;
306+ if ( range !== undefined ) {
307+ break ;
308+ }
255309 }
256310
257311 current = current . original ;
@@ -270,10 +324,12 @@ namespace ts {
270324 * @param range The text range.
271325 */
272326 function setSourceMapRange < T extends Node > ( node : T , range : TextRange ) {
327+ beforeSetAnnotation ( node ) ;
328+
273329 // Cache the most recently requested value.
274330 lastSourceMapRangeNode = node ;
275331 lastSourceMapRange = range ;
276- sourceMapRanges [ getNodeId ( node ) ] = range ;
332+ node . sourceMapRange = range ;
277333 return node ;
278334 }
279335
@@ -290,13 +346,15 @@ namespace ts {
290346 // As a performance optimization, use the cached value of the most recent node.
291347 // This helps for cases where this function is called repeatedly for the same node.
292348 if ( lastTokenSourceMapRangeNode === node && lastTokenSourceMapRangeToken === token ) {
293- return lastSourceMapRange ;
349+ return lastTokenSourceMapRange ;
294350 }
295351
352+ // Get the custom token source map range for a node or from one of its original nodes.
353+ // Custom token ranges are not stored on the node to avoid the GC burden.
296354 let range : TextRange ;
297355 let current = node ;
298356 while ( current ) {
299- range = current . id ? sourceMapRanges [ current . id + "-" + token ] : undefined ;
357+ range = current . id ? tokenSourceMapRanges [ current . id + "-" + token ] : undefined ;
300358 if ( range !== undefined ) {
301359 break ;
302360 }
@@ -323,7 +381,7 @@ namespace ts {
323381 lastTokenSourceMapRangeNode = node ;
324382 lastTokenSourceMapRangeToken = token ;
325383 lastTokenSourceMapRange = range ;
326- sourceMapRanges [ getNodeId ( node ) + "-" + token ] = range ;
384+ tokenSourceMapRanges [ getNodeId ( node ) + "-" + token ] = range ;
327385 return node ;
328386 }
329387
@@ -342,12 +400,15 @@ namespace ts {
342400 return lastCommentMapRange || node ;
343401 }
344402
403+ // Get the custom comment range for a node or from one of its original nodes.
345404 let range : TextRange ;
346405 let current = node ;
347406 while ( current ) {
348- range = current . id ? commentRanges [ current . id ] : undefined ;
349- if ( range !== undefined ) {
350- break ;
407+ if ( current . transformId === transformId ) {
408+ range = current . commentRange ;
409+ if ( range !== undefined ) {
410+ break ;
411+ }
351412 }
352413
353414 current = current . original ;
@@ -363,10 +424,12 @@ namespace ts {
363424 * Sets a custom text range to use when emitting comments.
364425 */
365426 function setCommentRange < T extends Node > ( node : T , range : TextRange ) {
427+ beforeSetAnnotation ( node ) ;
428+
366429 // Cache the most recently requested value.
367430 lastCommentMapRangeNode = node ;
368431 lastCommentMapRange = range ;
369- commentRanges [ getNodeId ( node ) ] = range ;
432+ node . commentRange = range ;
370433 return node ;
371434 }
372435
0 commit comments