Skip to content

Commit a30a4b9

Browse files
committed
Fix component issue
1 parent 420f3b7 commit a30a4b9

File tree

2 files changed

+69
-36
lines changed

2 files changed

+69
-36
lines changed

src/code-impl.ts

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -418,10 +418,25 @@ export function registerCodegen(ctx: typeof figma) {
418418
}
419419
}
420420

421-
const allComponentsCodes = [
422-
...componentsResponsiveCodes,
423-
...responsiveComponentsCodes,
424-
]
421+
// Merge component codes: responsive/variant versions override simple ones.
422+
const responsiveOverrides = new Map<
423+
string,
424+
readonly [string, string]
425+
>()
426+
for (const entry of componentsResponsiveCodes)
427+
responsiveOverrides.set(entry[0], entry)
428+
for (const entry of responsiveComponentsCodes)
429+
responsiveOverrides.set(entry[0], entry)
430+
431+
const mergedComponentsCodes: ReadonlyArray<
432+
readonly [string, string]
433+
> =
434+
componentsCodes.length > 0 && responsiveOverrides.size > 0
435+
? componentsCodes.map(
436+
([name, code]) =>
437+
responsiveOverrides.get(name) ?? ([name, code] as const),
438+
)
439+
: componentsCodes
425440

426441
// For INSTANCE nodes, include the referenced component definition(s)
427442
// alongside Usage so developers see both how to use AND the implementation.
@@ -430,14 +445,16 @@ export function registerCodegen(ctx: typeof figma) {
430445
language: 'TYPESCRIPT' | 'BASH'
431446
code: string
432447
}[] = []
433-
if (node.type === 'INSTANCE' && componentsCodes.length > 0) {
434-
const importStatement = generateImportStatements(componentsCodes)
435-
const combinedCode = componentsCodes
448+
if (node.type === 'INSTANCE' && mergedComponentsCodes.length > 0) {
449+
const importStatement = generateImportStatements(
450+
mergedComponentsCodes,
451+
)
452+
const combinedCode = mergedComponentsCodes
436453
.map(([, code]) => code)
437454
.join('\n\n')
438455
const label =
439-
componentsCodes.length === 1
440-
? componentsCodes[0][0]
456+
mergedComponentsCodes.length === 1
457+
? mergedComponentsCodes[0][0]
441458
: `${node.name} - Components`
442459
componentDefinitionResults.push(
443460
{
@@ -448,16 +465,31 @@ export function registerCodegen(ctx: typeof figma) {
448465
{
449466
title: `${label} - CLI (Bash)`,
450467
language: 'BASH',
451-
code: generateBashCLI(componentsCodes),
468+
code: generateBashCLI(mergedComponentsCodes),
452469
},
453470
{
454471
title: `${label} - CLI (PowerShell)`,
455472
language: 'BASH',
456-
code: generatePowerShellCLI(componentsCodes),
473+
code: generatePowerShellCLI(mergedComponentsCodes),
457474
},
458475
)
459476
}
460477

478+
// Collect remaining responsive codes NOT already merged into component definitions.
479+
// Only filter for INSTANCE nodes — other node types don't produce componentDefinitionResults.
480+
const mergedNames =
481+
node.type === 'INSTANCE'
482+
? new Set(mergedComponentsCodes.map(([name]) => name))
483+
: new Set<string>()
484+
const allComponentsCodes = [
485+
...componentsResponsiveCodes.filter(
486+
([name]) => !mergedNames.has(name),
487+
),
488+
...responsiveComponentsCodes.filter(
489+
([name]) => !mergedNames.has(name),
490+
),
491+
]
492+
461493
// For COMPONENT nodes, show both the single-variant code AND Usage.
462494
// For COMPONENT_SET and INSTANCE, show only Usage.
463495
// For all other types, show the main code.

src/codegen/Codegen.ts

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)