mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
fix(cli): figma component import survives unrenderable nodes (#2022)
Live testing against a real community file (Ratings) found a nested instance node figma refuses to render as svg — which aborted the entire component import. The rasterize loop now retries the node as png, and only if both formats fail warns and skips THAT node (placeholder keeps its data-figma-rasterize marker, no src) instead of failing the import. On the file that surfaced this, the png retry recovers the node — 31/31 placeholders get assets. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
337d0b51bc
commit
4c8064d4d3
@@ -7,6 +7,7 @@
|
||||
import { defineCommand } from "citty";
|
||||
import {
|
||||
createFigmaClient,
|
||||
FigmaClientError,
|
||||
nodeToHtml,
|
||||
parseFigmaRef,
|
||||
readBindings,
|
||||
@@ -41,6 +42,8 @@ export interface ComponentImportResult {
|
||||
htmlPath: string;
|
||||
unresolved: BindingSite[];
|
||||
rasterized: RasterizeRequest[];
|
||||
/** node ids figma refused to render as svg AND png — placeholders shipped without src */
|
||||
failedRasterize: string[];
|
||||
}
|
||||
|
||||
export async function runComponentImport(
|
||||
@@ -68,12 +71,33 @@ export async function runComponentImport(
|
||||
// replaceAll covers the same node appearing twice in the tree.
|
||||
let html = mapped.html;
|
||||
const frozenAssets: string[] = [];
|
||||
const failedRasterize: string[] = [];
|
||||
for (const req of mapped.rasterize) {
|
||||
const asset = await runAssetImport(
|
||||
`${ref.fileKey}:${req.nodeId}`,
|
||||
{ format: "svg", description: req.name },
|
||||
{ projectDir: deps.projectDir, client: deps.client, download: deps.download },
|
||||
);
|
||||
// figma sometimes refuses to render a node (nested instances commonly
|
||||
// fail as svg) — retry once as png, then skip THIS node and keep the
|
||||
// import: one unrenderable node must not abort the whole component. The
|
||||
// placeholder keeps its data-figma-rasterize marker (no src) so the gap
|
||||
// is visible and hand-fixable.
|
||||
let asset = null;
|
||||
for (const format of ["svg", "png"] as const) {
|
||||
try {
|
||||
asset = await runAssetImport(
|
||||
`${ref.fileKey}:${req.nodeId}`,
|
||||
{ format, description: req.name },
|
||||
{ projectDir: deps.projectDir, client: deps.client, download: deps.download },
|
||||
);
|
||||
break;
|
||||
} catch (err) {
|
||||
if (!(err instanceof FigmaClientError) || err.code !== "RENDER_FAILED") throw err;
|
||||
}
|
||||
}
|
||||
if (asset === null) {
|
||||
failedRasterize.push(req.nodeId);
|
||||
console.warn(
|
||||
`could not render node ${req.nodeId} ("${req.name}") as svg or png — leaving its placeholder without src`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
frozenAssets.push(asset.record.path);
|
||||
// src is a URL — always forward slashes, even when relative() yields
|
||||
// windows separators.
|
||||
@@ -120,6 +144,7 @@ export async function runComponentImport(
|
||||
htmlPath: relative(deps.projectDir, htmlFile),
|
||||
unresolved: bindings.unresolved,
|
||||
rasterized: mapped.rasterize,
|
||||
failedRasterize,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,8 +164,14 @@ export default defineCommand({
|
||||
download: downloadRender,
|
||||
});
|
||||
console.log(`imported component "${result.name}" → ${result.htmlPath}`);
|
||||
if (result.rasterized.length > 0)
|
||||
console.log(`rasterized ${result.rasterized.length} node(s) via asset export`);
|
||||
if (result.rasterized.length > 0) {
|
||||
const ok = result.rasterized.length - result.failedRasterize.length;
|
||||
console.log(
|
||||
result.failedRasterize.length > 0
|
||||
? `rasterized ${ok}/${result.rasterized.length} node(s) via asset export — ${result.failedRasterize.length} skipped (unrenderable; placeholders have no src)`
|
||||
: `rasterized ${result.rasterized.length} node(s) via asset export`,
|
||||
);
|
||||
}
|
||||
if (result.unresolved.length > 0) {
|
||||
console.log(
|
||||
`${result.unresolved.length} binding(s) reference tokens not yet imported — colors baked as literals (flagged data-figma-unresolved). Run \`hyperframes figma tokens\` on the source/library file, then re-import to link them.`,
|
||||
@@ -151,6 +182,7 @@ export default defineCommand({
|
||||
phase: "component",
|
||||
unresolvedBindings: result.unresolved.length,
|
||||
rasterizedNodes: result.rasterized.length,
|
||||
rasterizeFailures: result.failedRasterize.length,
|
||||
durationMs: Date.now() - t0,
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user