Files
hyperframes/packages/core/src/figma/nodeDocument.ts
T
Vance IngallsandClaude Fable 5 ee7a96147a feat(core,cli): figma component import with binding-aware node-to-html mapper (M3) (#1872)
resolveBindings: scan the full tree (boundVariables + style ids, alias
chains, children) and partition exact-ID-only against the binding index
before any CSS is emitted per spec 7.1 - never value matching.

nodeToHtml: absolute geometry at figma bounds inside a fixed-size root,
solid/linear-gradient fills, corner radius, opacity, drop shadow, blur,
text styles; resolved bindings emit var(--slug, literal), unresolved
bake literals with data-figma-unresolved; visible:false respected;
vectors/boolean ops route to a rasterize list.

hyperframes figma component: tree -> bindings -> html, rasterize
fallback via Phase-1 asset export with src backfill, registry-item
packaging, unresolved-binding guidance in output.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 18:19:01 -07:00

30 lines
922 B
TypeScript

import type { FigmaNodeDocument } from "./client";
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
/** Narrow an unknown child entry to a node document, or null. */
function asNodeDocument(value: unknown): FigmaNodeDocument | null {
if (
isRecord(value) &&
typeof value.id === "string" &&
typeof value.name === "string" &&
typeof value.type === "string"
) {
return { ...value, id: value.id, name: value.name, type: value.type };
}
return null;
}
/** Typed children of a node document (unknown-shaped entries skipped). */
export function childDocuments(node: FigmaNodeDocument): FigmaNodeDocument[] {
if (!Array.isArray(node.children)) return [];
const out: FigmaNodeDocument[] = [];
for (const child of node.children) {
const doc = asNodeDocument(child);
if (doc) out.push(doc);
}
return out;
}