@@ -282,7 +282,6 @@ export class Codegen {
282282 private buildTreeCache : Map < string , Promise < NodeTree > > = new Map ( )
283283 // Collect fire-and-forget addComponentTree promises so we can await them
284284 // before rendering component codes (decouples INSTANCE buildTree from addComponentTree)
285- private pendingComponentTrees : Promise < void > [ ] = [ ]
286285
287286 constructor ( private node : SceneNode ) {
288287 this . node = node
@@ -356,10 +355,12 @@ export class Codegen {
356355 this . tree = tree
357356 }
358357
359- // Await all fire-and-forget addComponentTree calls before rendering
360- if ( this . pendingComponentTrees . length > 0 ) {
361- await Promise . all ( this . pendingComponentTrees )
362- this . pendingComponentTrees = [ ]
358+ // Drain all addComponentTree promises, including nested ones added during execution.
359+ // Uses addComponentTreePromises Map which stably tracks every fired promise.
360+ let _prevSize = 0
361+ while ( this . addComponentTreePromises . size > _prevSize ) {
362+ _prevSize = this . addComponentTreePromises . size
363+ await Promise . all ( this . addComponentTreePromises . values ( ) )
363364 }
364365
365366 // Sync componentTrees to components
@@ -409,13 +410,6 @@ export class Codegen {
409410 globalBuildTreeCache . set ( cacheKey , promise )
410411 }
411412 const result = await promise
412- // When called as the root-level buildTree (node === this.node),
413- // drain any fire-and-forget addComponentTree promises so that
414- // getComponentTrees() is populated before the caller inspects it.
415- if ( node === this . node && this . pendingComponentTrees . length > 0 ) {
416- await Promise . all ( this . pendingComponentTrees )
417- this . pendingComponentTrees = [ ]
418- }
419413 return result
420414 }
421415
@@ -431,10 +425,9 @@ export class Codegen {
431425 node === this . node . defaultVariant ) ||
432426 this . node . type === 'COMPONENT' )
433427 ) {
434- this . pendingComponentTrees . push (
435- this . addComponentTree (
436- node . type === 'COMPONENT_SET' ? node . defaultVariant : node ,
437- ) ,
428+ // Fire-and-forget — errors collected via addComponentTreePromises in run().
429+ this . addComponentTree (
430+ node . type === 'COMPONENT_SET' ? node . defaultVariant : node ,
438431 )
439432 }
440433
@@ -462,7 +455,7 @@ export class Codegen {
462455 // Fire addComponentTree without awaiting — it runs in the background.
463456 // All pending promises are collected and awaited in run() before rendering.
464457 if ( mainComponent ) {
465- this . pendingComponentTrees . push ( this . addComponentTree ( mainComponent ) )
458+ this . addComponentTree ( mainComponent )
466459 }
467460
468461 const componentName = getComponentName ( mainComponent || node )
@@ -570,7 +563,10 @@ export class Codegen {
570563 if ( ! globalAssetNodes . has ( assetKey ) ) {
571564 globalAssetNodes . set ( assetKey , { node, type : assetNode } )
572565 }
573- const props = await getProps ( node )
566+ const baseProps = await getProps ( node )
567+ // Clone to avoid mutating the shared getProps cache — subsequent
568+ // codegen runs (e.g. ResponsiveCodegen) reuse the cached reference.
569+ const props : Record < string , unknown > = { ...baseProps }
574570 props . src = `/${ assetNode === 'svg' ? 'icons' : 'images' } /${ node . name } .${ assetNode } `
575571 if ( assetNode === 'svg' ) {
576572 const maskColor = await checkSameColor ( node )
@@ -679,10 +675,11 @@ export class Codegen {
679675 async getTree ( ) : Promise < NodeTree > {
680676 if ( ! this . tree ) {
681677 this . tree = await this . buildTree ( this . node )
682- // Await any fire-and-forget addComponentTree calls launched during buildTree
683- if ( this . pendingComponentTrees . length > 0 ) {
684- await Promise . all ( this . pendingComponentTrees )
685- this . pendingComponentTrees = [ ]
678+ // Drain all addComponentTree promises (including nested ones)
679+ let _prevSize = 0
680+ while ( this . addComponentTreePromises . size > _prevSize ) {
681+ _prevSize = this . addComponentTreePromises . size
682+ await Promise . all ( this . addComponentTreePromises . values ( ) )
686683 }
687684 }
688685 return this . tree
@@ -702,15 +699,19 @@ export class Codegen {
702699 // when multiple INSTANCE nodes reference the same component
703700 private addComponentTreePromises : Map < string , Promise < void > > = new Map ( )
704701
705- private async addComponentTree ( node : ComponentNode ) : Promise < void > {
702+ private addComponentTree ( node : ComponentNode ) : Promise < void > {
706703 const nodeId = node . id || node . name
707- if ( this . componentTrees . has ( nodeId ) ) return
704+ if ( this . componentTrees . has ( nodeId ) ) return Promise . resolve ( )
708705
709- // If already in-flight, await the same promise
706+ // If already in-flight, return the same promise
710707 const inflight = this . addComponentTreePromises . get ( nodeId )
711708 if ( inflight ) return inflight
712709
710+ // Store the raw promise (may reject) for drain in run().
711+ // Attach a no-op .catch so fire-and-forget callers don't
712+ // trigger unhandled rejection warnings.
713713 const promise = this . doAddComponentTree ( node , nodeId )
714+ promise . catch ( ( ) => { } )
714715 this . addComponentTreePromises . set ( nodeId , promise )
715716 return promise
716717 }
0 commit comments