figma-json-tree 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -0
- package/dist/cli/index.js +1138 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/figma-html.js +95 -0
- package/dist/figma-html.js.map +1 -0
- package/dist/figma-json-fetch.js +53 -0
- package/dist/figma-json-fetch.js.map +1 -0
- package/dist/figma-tree.js +808 -0
- package/dist/figma-tree.js.map +1 -0
- package/dist/types/cli/args.d.ts +27 -0
- package/dist/types/cli/args.d.ts.map +1 -0
- package/dist/types/cli/commands/download.d.ts +4 -0
- package/dist/types/cli/commands/download.d.ts.map +1 -0
- package/dist/types/cli/commands/export.d.ts +3 -0
- package/dist/types/cli/commands/export.d.ts.map +1 -0
- package/dist/types/cli/commands/query.d.ts +3 -0
- package/dist/types/cli/commands/query.d.ts.map +1 -0
- package/dist/types/cli/env.d.ts +2 -0
- package/dist/types/cli/env.d.ts.map +1 -0
- package/dist/types/cli/html/input.d.ts +5 -0
- package/dist/types/cli/html/input.d.ts.map +1 -0
- package/dist/types/cli/index.d.ts +3 -0
- package/dist/types/cli/index.d.ts.map +1 -0
- package/dist/types/cli/output.d.ts +4 -0
- package/dist/types/cli/output.d.ts.map +1 -0
- package/dist/types/cli/run.d.ts +8 -0
- package/dist/types/cli/run.d.ts.map +1 -0
- package/dist/types/figma-html/attributes.d.ts +4 -0
- package/dist/types/figma-html/attributes.d.ts.map +1 -0
- package/dist/types/figma-html/classes.d.ts +3 -0
- package/dist/types/figma-html/classes.d.ts.map +1 -0
- package/dist/types/figma-html/escape.d.ts +2 -0
- package/dist/types/figma-html/escape.d.ts.map +1 -0
- package/dist/types/figma-html/index.d.ts +3 -0
- package/dist/types/figma-html/index.d.ts.map +1 -0
- package/dist/types/figma-html/renderer.d.ts +5 -0
- package/dist/types/figma-html/renderer.d.ts.map +1 -0
- package/dist/types/figma-html/types.d.ts +4 -0
- package/dist/types/figma-html/types.d.ts.map +1 -0
- package/dist/types/figma-html/validate.d.ts +3 -0
- package/dist/types/figma-html/validate.d.ts.map +1 -0
- package/dist/types/figma-json-fetch/client.d.ts +8 -0
- package/dist/types/figma-json-fetch/client.d.ts.map +1 -0
- package/dist/types/figma-json-fetch/endpoints.d.ts +3 -0
- package/dist/types/figma-json-fetch/endpoints.d.ts.map +1 -0
- package/dist/types/figma-json-fetch/errors.d.ts +7 -0
- package/dist/types/figma-json-fetch/errors.d.ts.map +1 -0
- package/dist/types/figma-json-fetch/index.d.ts +4 -0
- package/dist/types/figma-json-fetch/index.d.ts.map +1 -0
- package/dist/types/figma-json-fetch/request.d.ts +3 -0
- package/dist/types/figma-json-fetch/request.d.ts.map +1 -0
- package/dist/types/figma-json-fetch/types.d.ts +9 -0
- package/dist/types/figma-json-fetch/types.d.ts.map +1 -0
- package/dist/types/figma-tree/index.d.ts +10 -0
- package/dist/types/figma-tree/index.d.ts.map +1 -0
- package/dist/types/figma-tree/input/normalize.d.ts +11 -0
- package/dist/types/figma-tree/input/normalize.d.ts.map +1 -0
- package/dist/types/figma-tree/ir/converters/diagnostics.d.ts +3 -0
- package/dist/types/figma-tree/ir/converters/diagnostics.d.ts.map +1 -0
- package/dist/types/figma-tree/ir/converters/layout.d.ts +4 -0
- package/dist/types/figma-tree/ir/converters/layout.d.ts.map +1 -0
- package/dist/types/figma-tree/ir/converters/style.d.ts +6 -0
- package/dist/types/figma-tree/ir/converters/style.d.ts.map +1 -0
- package/dist/types/figma-tree/ir/converters/text.d.ts +4 -0
- package/dist/types/figma-tree/ir/converters/text.d.ts.map +1 -0
- package/dist/types/figma-tree/ir/model/document.d.ts +12 -0
- package/dist/types/figma-tree/ir/model/document.d.ts.map +1 -0
- package/dist/types/figma-tree/ir/model/types.d.ts +61 -0
- package/dist/types/figma-tree/ir/model/types.d.ts.map +1 -0
- package/dist/types/figma-tree/ir/pipeline.d.ts +5 -0
- package/dist/types/figma-tree/ir/pipeline.d.ts.map +1 -0
- package/dist/types/figma-tree/ir/plugins/run.d.ts +11 -0
- package/dist/types/figma-tree/ir/plugins/run.d.ts.map +1 -0
- package/dist/types/figma-tree/model/types.d.ts +30 -0
- package/dist/types/figma-tree/model/types.d.ts.map +1 -0
- package/dist/types/figma-tree/model/value.d.ts +4 -0
- package/dist/types/figma-tree/model/value.d.ts.map +1 -0
- package/dist/types/figma-tree/selector/match.d.ts +4 -0
- package/dist/types/figma-tree/selector/match.d.ts.map +1 -0
- package/dist/types/figma-tree/selector/parser.d.ts +3 -0
- package/dist/types/figma-tree/selector/parser.d.ts.map +1 -0
- package/dist/types/figma-tree/selector/types.d.ts +24 -0
- package/dist/types/figma-tree/selector/types.d.ts.map +1 -0
- package/dist/types/figma-tree/tailwind/convert.d.ts +3 -0
- package/dist/types/figma-tree/tailwind/convert.d.ts.map +1 -0
- package/dist/types/figma-tree/tailwind/style.d.ts +6 -0
- package/dist/types/figma-tree/tailwind/style.d.ts.map +1 -0
- package/dist/types/figma-tree/tree/figma-tree.d.ts +39 -0
- package/dist/types/figma-tree/tree/figma-tree.d.ts.map +1 -0
- package/dist/types/figma-tree/tree/records.d.ts +5 -0
- package/dist/types/figma-tree/tree/records.d.ts.map +1 -0
- package/docs/api.md +89 -0
- package/docs/architecture.md +70 -0
- package/docs/html.md +90 -0
- package/docs/ir.md +84 -0
- package/docs/verification.md +33 -0
- package/package.json +57 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/figma-json-fetch/endpoints.ts","../../src/figma-json-fetch/errors.ts","../../src/figma-json-fetch/request.ts","../../src/figma-json-fetch/client.ts","../../src/cli/args.ts","../../src/cli/output.ts","../../src/cli/commands/download.ts","../../src/figma-html/classes.ts","../../src/figma-html/escape.ts","../../src/figma-html/attributes.ts","../../src/figma-html/validate.ts","../../src/figma-html/renderer.ts","../../src/figma-tree/model/value.ts","../../src/figma-tree/input/normalize.ts","../../src/figma-tree/tailwind/style.ts","../../src/figma-tree/tailwind/convert.ts","../../src/figma-tree/ir/model/document.ts","../../src/figma-tree/ir/plugins/run.ts","../../src/figma-tree/selector/types.ts","../../src/figma-tree/tree/records.ts","../../src/figma-tree/ir/converters/style.ts","../../src/figma-tree/ir/converters/diagnostics.ts","../../src/figma-tree/ir/converters/layout.ts","../../src/figma-tree/ir/converters/text.ts","../../src/figma-tree/ir/pipeline.ts","../../src/figma-tree/selector/match.ts","../../src/figma-tree/selector/parser.ts","../../src/figma-tree/tree/figma-tree.ts","../../src/cli/html/input.ts","../../src/cli/commands/export.ts","../../src/cli/commands/query.ts","../../src/cli/env.ts","../../src/cli/run.ts","../../src/cli/index.ts"],"sourcesContent":["const API_URL = 'https://api.figma.com/v1'\n\nexport function fileEndpoint(fileKey: string): URL {\n if (!/^[a-zA-Z0-9_-]+$/.test(fileKey)) throw new TypeError('Expected a non-empty Figma file key')\n return new URL(`${API_URL}/files/${encodeURIComponent(fileKey)}`)\n}\n\nexport function nodesEndpoint(fileKey: string, nodeIds: readonly string[]): URL {\n if (!nodeIds.length || nodeIds.some(id => !/^I?\\d+:\\d+(?:;I?\\d+:\\d+)*$/.test(id)))\n throw new TypeError('Expected API node IDs such as 49:7390 or I49:7403;38:5243')\n const url = fileEndpoint(fileKey)\n url.pathname += '/nodes'\n url.searchParams.set('ids', [...new Set(nodeIds)].join(','))\n return url\n}\n","export class FigmaHTTPError extends Error {\n override name = 'FigmaHTTPError'\n constructor(\n readonly status: number,\n readonly retryAfter: string | null,\n ) {\n super(\n `Figma API request failed (HTTP ${status})${retryAfter ? `; Retry-After: ${retryAfter}` : ''}`,\n )\n }\n}\n","import { FigmaHTTPError } from './errors.js'\nimport type { RequestOptions } from './types.js'\n\nexport async function requestJSON<T>(\n fetcher: typeof globalThis.fetch,\n url: URL,\n token: string,\n options: RequestOptions,\n): Promise<T> {\n const response = await fetcher(url, {\n headers: { 'X-Figma-Token': token, Accept: 'application/json' },\n signal: options.signal,\n redirect: 'error',\n })\n if (!response.ok) throw new FigmaHTTPError(response.status, response.headers.get('retry-after'))\n return (await response.json()) as T\n}\n","import { fileEndpoint, nodesEndpoint } from './endpoints.js'\nimport { requestJSON } from './request.js'\nimport type {\n FigmaClientOptions,\n GetFileNodesResponse,\n GetFileResponse,\n RequestOptions,\n} from './types.js'\n\nexport class FigmaClient {\n readonly #token: string\n readonly #fetch: typeof globalThis.fetch\n constructor(options: FigmaClientOptions) {\n if (typeof options.token !== 'string' || !options.token.trim())\n throw new TypeError('A Figma token is required')\n this.#token = options.token\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis)\n }\n getFile(fileKey: string, options: RequestOptions = {}): Promise<GetFileResponse> {\n return requestJSON(this.#fetch, fileEndpoint(fileKey), this.#token, options)\n }\n getNodes(\n fileKey: string,\n nodeIds: readonly string[],\n options: RequestOptions = {},\n ): Promise<GetFileNodesResponse> {\n return requestJSON(this.#fetch, nodesEndpoint(fileKey, nodeIds), this.#token, options)\n }\n}\n","import { parseArgs as parseNodeArgs } from 'node:util'\nimport type { Selector } from '../figma-tree/index.js'\nimport type { HTMLStyles } from './html/input.js'\n\nexport interface DownloadArgs {\n command: 'download'\n fileKey: string\n nodeIds?: string[]\n out?: string\n force: boolean\n}\n\nexport interface QueryArgs {\n command: 'query' | 'queryAll'\n input: string\n selector: Selector\n out?: string\n force: boolean\n}\n\nexport interface ExportArgs {\n command: 'export'\n input: string\n selector?: string\n styles?: HTMLStyles\n out?: string\n force: boolean\n}\n\nexport const HELP = `Usage:\n figma-json-tree download --file FILE_KEY [--nodes 49:7390,49:7391] [--out file.json] [--force]\n figma-json-tree query --input file.json --selector 'FRAME[name=\"Hover Card\"]' [--out node.json] [--force]\n figma-json-tree queryAll --input file.json --selector '[name=\"Hover Card\"]' [--out nodes.json] [--force]\n figma-json-tree queryAll --input file.json --name-regex '^ToBe' [--regex-flags i]\n figma-json-tree export --input file.json --selector 'FRAME[name=\"Hover Card\"]' --format html [--styles inline|tailwind] [--out page.html] [--force]\n figma-json-tree export --input design.ir.json --format html [--styles tailwind]\n\nUse either --selector or --name-regex. --regex-flags applies only to --name-regex.\nquery returns the first original subtree or null; queryAll returns an array (possibly empty).\nInputs: parsed file/node API responses or a single subtree saved as JSON.\nAuthentication: FIGMA_TOKEN is required only for download. Queries and exports read local files.\nexport accepts original Figma JSON, standard IR or Tailwind IR and exports one root.\nFor export, --format defaults to html. Styles default to inline (Tailwind for Tailwind IR).\nexport returns body contents only: no doctype, html, head, body, style or script tags.\nTailwind exports contain classes and residual inline styles; the host project builds the CSS.\nWithout --out, JSON or exported HTML is written to stdout. Existing files require --force.\n`\n\nconst commonOptions = {\n out: { type: 'string' },\n force: { type: 'boolean', default: false },\n help: { type: 'boolean', short: 'h' },\n} as const\n\nfunction validateOutput(out?: string): void {\n if (out !== undefined && !out.trim()) throw new Error('--out must be a non-empty path')\n}\n\nfunction parseDownload(args: string[]): DownloadArgs | 'help' {\n const { values } = parseNodeArgs({\n args,\n strict: true,\n options: {\n ...commonOptions,\n file: { type: 'string' },\n nodes: { type: 'string' },\n },\n })\n if (values.help) return 'help'\n if (!values.file) throw new Error('--file FILE_KEY is required')\n if (values.nodes !== undefined && !values.nodes.trim())\n throw new Error('--nodes must contain at least one node ID')\n validateOutput(values.out)\n return {\n command: 'download',\n fileKey: values.file,\n nodeIds: values.nodes?.split(',').map(id => id.trim()),\n out: values.out,\n force: values.force,\n }\n}\n\nfunction querySelector(selector?: string, pattern?: string, flags?: string): Selector {\n if ((selector !== undefined) === (pattern !== undefined))\n throw new Error('Provide exactly one of --selector or --name-regex')\n if (selector !== undefined) {\n if (flags !== undefined) throw new Error('--regex-flags requires --name-regex')\n if (!selector.trim()) throw new Error('--selector must be non-empty')\n return selector\n }\n return { name: new RegExp(pattern!, flags) }\n}\n\nfunction parseQuery(command: QueryArgs['command'], args: string[]): QueryArgs | 'help' {\n const { values } = parseNodeArgs({\n args,\n strict: true,\n options: {\n ...commonOptions,\n input: { type: 'string' },\n selector: { type: 'string' },\n 'name-regex': { type: 'string' },\n 'regex-flags': { type: 'string' },\n },\n })\n if (values.help) return 'help'\n if (!values.input?.trim()) throw new Error('--input must specify a local JSON file')\n validateOutput(values.out)\n return {\n command,\n input: values.input,\n selector: querySelector(values.selector, values['name-regex'], values['regex-flags']),\n out: values.out,\n force: values.force,\n }\n}\n\nfunction parseExport(args: string[]): ExportArgs | 'help' {\n const { values } = parseNodeArgs({\n args,\n strict: true,\n options: {\n ...commonOptions,\n input: { type: 'string' },\n selector: { type: 'string' },\n format: { type: 'string', default: 'html' },\n styles: { type: 'string' },\n },\n })\n if (values.help) return 'help'\n if (!values.input?.trim()) throw new Error('--input must specify a local JSON file')\n if (values.format !== 'html') throw new Error('--format currently supports only html')\n if (values.styles !== undefined && values.styles !== 'inline' && values.styles !== 'tailwind')\n throw new Error('--styles must be inline or tailwind')\n if (values.selector !== undefined && !values.selector.trim())\n throw new Error('--selector must be non-empty')\n validateOutput(values.out)\n return {\n command: 'export',\n input: values.input,\n selector: values.selector,\n styles: values.styles,\n out: values.out,\n force: values.force,\n }\n}\n\nexport function parseArgs(args: string[]): DownloadArgs | QueryArgs | ExportArgs | 'help' {\n const [command, ...options] = args\n if (args.length === 1 && (command === '--help' || command === '-h')) return 'help'\n if (command === 'download') return parseDownload(options)\n if (command === 'query' || command === 'queryAll') return parseQuery(command, options)\n if (command === 'export') return parseExport(options)\n throw new Error('Expected download, query, queryAll or export; use --help')\n}\n","import { writeFile } from 'node:fs/promises'\n\nexport async function writeJSON(\n value: unknown,\n path: string | undefined,\n force: boolean,\n stdout: (text: string) => void,\n): Promise<void> {\n await writeText(`${JSON.stringify(value, null, 2)}\\n`, path, force, stdout)\n}\n\nexport async function writeText(\n text: string,\n path: string | undefined,\n force: boolean,\n stdout: (text: string) => void,\n): Promise<void> {\n if (!path) stdout(text)\n else await writeFile(path, text, { flag: force ? 'w' : 'wx' })\n}\n\nexport function safeErrorMessage(error: unknown, token?: string): string {\n const message = error instanceof Error ? error.message : 'Unknown error'\n return token ? message.split(token).join('[redacted]') : message\n}\n","import type { FigmaClient } from '../../figma-json-fetch/index.js'\nimport type { DownloadArgs } from '../args.js'\nimport { writeJSON } from '../output.js'\n\nexport async function download(\n args: DownloadArgs,\n client: FigmaClient,\n stdout: (text: string) => void,\n): Promise<void> {\n const response = args.nodeIds\n ? await client.getNodes(args.fileKey, args.nodeIds)\n : await client.getFile(args.fileKey)\n await writeJSON(response, args.out, args.force, stdout)\n}\n","/** Round numeric arbitrary values only; preserve selectors, URLs and custom names. */\nexport function formatTailwindClasses(className: string): string {\n return className.replace(\n /\\[((?:[a-z-]+:)?)([+-]?(?:\\d+\\.\\d+|\\.\\d+))([a-z%]*)\\]/gi,\n (_match, hint: string, value: string, unit: string) =>\n `[${hint}${Number(Number(value).toFixed(2))}${unit}]`,\n )\n}\n","const entities: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n}\n\nexport function escapeHTML(value: string): string {\n return value.replace(/[&<>\"']/g, character => entities[character]!)\n}\n","import { formatTailwindClasses } from './classes.js'\nimport { escapeHTML } from './escape.js'\nimport type { HTMLNode } from './types.js'\n\nfunction cssProperty(name: string): string {\n if (name.startsWith('--')) return name\n const kebab = name.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)\n return kebab.startsWith('ms-') ? `-${kebab}` : kebab\n}\n\nexport function serializeStyle(style: HTMLNode['style']): string {\n return Object.entries(style)\n .map(([property, value]) => `${cssProperty(property)}:${value}`)\n .join(';')\n}\n\nexport function renderAttributes(node: HTMLNode, tailwind: boolean): string {\n const attributes: [string, string][] = [\n ['data-figma-id', node.source.id],\n ['data-figma-name', node.source.name],\n ['data-figma-type', node.source.type],\n ]\n if (tailwind && 'className' in node && node.className)\n attributes.push(['class', formatTailwindClasses(node.className)])\n const style = serializeStyle(node.visible ? node.style : { ...node.style, display: 'none' })\n if (style) attributes.push(['style', style])\n const serialized = attributes.map(([name, value]) => ` ${name}=\"${escapeHTML(value)}\"`).join('')\n return node.visible ? serialized : `${serialized} hidden`\n}\n","import type { HTMLInput, HTMLNode } from './types.js'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction validateNode(value: unknown, tailwind: boolean): asserts value is HTMLNode {\n if (\n !isRecord(value) ||\n !isRecord(value.source) ||\n typeof value.source.id !== 'string' ||\n typeof value.source.name !== 'string' ||\n typeof value.source.type !== 'string' ||\n typeof value.visible !== 'boolean' ||\n !isRecord(value.style) ||\n !Array.isArray(value.children)\n ) {\n throw new TypeError('Expected an IR node with source, visible, style and children')\n }\n if (\n value.text !== undefined &&\n (!isRecord(value.text) || typeof value.text.characters !== 'string')\n ) {\n throw new TypeError(`Invalid text in IR node ${value.source.id}`)\n }\n if (tailwind && typeof value.className !== 'string') {\n throw new TypeError(`Missing className in Tailwind node ${value.source.id}`)\n }\n for (const [property, cssValue] of Object.entries(value.style)) {\n if (!/^(?:--[\\w-]+|[a-zA-Z][a-zA-Z0-9-]*)$/.test(property)) {\n throw new TypeError(`Invalid CSS property in IR node ${value.source.id}`)\n }\n if (\n typeof cssValue !== 'string' &&\n (typeof cssValue !== 'number' || !Number.isFinite(cssValue))\n ) {\n throw new TypeError(`Invalid CSS value in IR node ${value.source.id}`)\n }\n }\n}\n\nexport function assertHTMLInput(input: unknown): asserts input is HTMLInput {\n if (\n !isRecord(input) ||\n input.schemaVersion !== 1 ||\n !Array.isArray(input.diagnostics) ||\n (input.target !== undefined && input.target !== 'tailwind-v4')\n ) {\n throw new TypeError('Expected a standard IR document or Tailwind v4 IR (schemaVersion 1)')\n }\n const tailwind = input.target === 'tailwind-v4'\n const pending: unknown[] = [input.root]\n const seen = new Set<unknown>()\n while (pending.length) {\n const current = pending.pop()\n if (seen.has(current)) throw new TypeError('IR children must be an acyclic tree')\n seen.add(current)\n validateNode(current, tailwind)\n for (const child of current.children) pending.push(child)\n }\n}\n","import { renderAttributes } from './attributes.js'\nimport { formatTailwindClasses } from './classes.js'\nimport { escapeHTML } from './escape.js'\nimport type { HTMLInput, HTMLNode } from './types.js'\nimport { assertHTMLInput } from './validate.js'\n\n/** One neutral div per IR node; no inferred interactions or semantic controls. */\nexport function renderHTML(input: HTMLInput): string {\n assertHTMLInput(input)\n const tailwind = 'target' in input && input.target === 'tailwind-v4'\n const output: string[] = []\n const pending: (HTMLNode | string)[] = [input.root]\n while (pending.length) {\n const current = pending.pop()!\n if (typeof current === 'string') {\n output.push(current)\n continue\n }\n output.push(`<div${renderAttributes(current, tailwind)}>`)\n if (current.text) output.push(escapeHTML(current.text.characters))\n pending.push('</div>')\n for (let index = current.children.length - 1; index >= 0; index--)\n pending.push(current.children[index]!)\n }\n return output.join('')\n}\n\nexport function collectTailwindClasses(input: HTMLInput): string[] {\n assertHTMLInput(input)\n if (!('target' in input) || input.target !== 'tailwind-v4') return []\n const classes = new Set<string>()\n const pending: HTMLNode[] = [input.root]\n while (pending.length) {\n const node = pending.pop()!\n if ('className' in node) {\n for (const name of formatTailwindClasses(node.className).split(/\\s+/).filter(Boolean))\n classes.add(name)\n }\n for (const child of node.children) pending.push(child)\n }\n return [...classes].sort()\n}\n","export function isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\nexport function deepFreeze<T>(value: T): T {\n const pending: unknown[] = [value]\n const seen = new WeakSet<object>()\n while (pending.length) {\n const current = pending.pop()\n if (current === null || typeof current !== 'object' || seen.has(current)) continue\n seen.add(current)\n for (const child of Object.values(current)) pending.push(child)\n Object.freeze(current)\n }\n return value\n}\n\nexport function numberValue(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n","import type { ComponentMetadata, Diagnostic, FigmaNodeData } from '../model/types.js'\nimport { deepFreeze, isObject } from '../model/value.js'\n\nexport class FigmaInputError extends Error {\n override name = 'FigmaInputError'\n}\n\nexport interface NormalizedInput {\n roots: FigmaNodeData[]\n components: Map<string, ComponentMetadata>\n diagnostics: Diagnostic[]\n}\n\nfunction addComponents(target: Map<string, ComponentMetadata>, value: unknown): void {\n if (!isObject(value)) return\n for (const [id, metadata] of Object.entries(value)) {\n if (isObject(metadata)) target.set(id, metadata)\n }\n}\n\nfunction validateNodes(roots: unknown[]): asserts roots is FigmaNodeData[] {\n const pending = [...roots]\n const seen = new WeakSet<object>()\n while (pending.length) {\n const node = pending.pop()\n if (\n !isObject(node) ||\n typeof node.id !== 'string' ||\n typeof node.name !== 'string' ||\n typeof node.type !== 'string'\n ) {\n throw new FigmaInputError('Each node must contain string id, name and type fields')\n }\n if (seen.has(node))\n throw new FigmaInputError('Input must be a JSON tree without cyclic or shared node objects')\n seen.add(node)\n if (node.children === undefined) continue\n if (!Array.isArray(node.children))\n throw new FigmaInputError(`Invalid children for node ${node.id}`)\n for (const child of node.children) pending.push(child)\n }\n}\n\nexport function normalizeInput(input: unknown): NormalizedInput {\n if (!isObject(input)) throw new FigmaInputError('Expected a parsed Figma response or node object')\n const components = new Map<string, ComponentMetadata>()\n const diagnostics: Diagnostic[] = []\n const roots: unknown[] = []\n addComponents(components, input.components)\n if (typeof input.type === 'string') roots.push(input)\n else if (isObject(input.document)) roots.push(input.document)\n else if (isObject(input.nodes)) {\n for (const [id, entry] of Object.entries(input.nodes)) {\n if (entry === null) {\n diagnostics.push({\n code: 'missing-node',\n nodeId: id,\n message: 'Figma returned null for this node',\n })\n } else if (isObject(entry) && isObject(entry.document)) {\n roots.push(entry.document)\n addComponents(components, entry.components)\n } else throw new FigmaInputError(`Invalid node response entry: ${id}`)\n }\n } else throw new FigmaInputError('Expected document, nodes, or a single node')\n validateNodes(roots)\n try {\n const copied = structuredClone({ roots, components, diagnostics })\n deepFreeze(copied.roots)\n for (const metadata of copied.components.values()) deepFreeze(metadata)\n return copied\n } catch (error) {\n throw new FigmaInputError(\n `Input must be structured-cloneable JSON: ${error instanceof Error ? error.message : 'invalid value'}`,\n )\n }\n}\n","import type { CSSStyle } from '../ir/model/types.js'\n\nconst keywords: Record<string, Record<string, string>> = {\n display: { flex: 'flex', none: 'hidden' },\n position: { relative: 'relative', absolute: 'absolute' },\n boxSizing: { 'border-box': 'box-border' },\n flexDirection: { row: 'flex-row', column: 'flex-col' },\n flexWrap: { wrap: 'flex-wrap' },\n justifyContent: {\n 'flex-start': 'justify-start',\n 'flex-end': 'justify-end',\n center: 'justify-center',\n 'space-between': 'justify-between',\n },\n alignItems: {\n 'flex-start': 'items-start',\n 'flex-end': 'items-end',\n center: 'items-center',\n baseline: 'items-baseline',\n },\n alignContent: { 'space-between': 'content-between' },\n alignSelf: { stretch: 'self-stretch' },\n overflow: { hidden: 'overflow-hidden' },\n borderStyle: { solid: 'border-solid' },\n textAlign: {\n left: 'text-left',\n right: 'text-right',\n center: 'text-center',\n justify: 'text-justify',\n },\n fontStyle: { italic: 'italic' },\n textDecorationLine: { underline: 'underline', 'line-through': 'line-through' },\n textTransform: { uppercase: 'uppercase', lowercase: 'lowercase', capitalize: 'capitalize' },\n whiteSpace: { 'pre-wrap': 'whitespace-pre-wrap' },\n flexGrow: { '1': 'grow' },\n flexShrink: { '0': 'shrink-0' },\n width: { '100%': 'w-full', 'fit-content': 'w-fit' },\n height: { '100%': 'h-full', 'fit-content': 'h-fit' },\n}\n\nconst prefixes: Record<string, string> = {\n width: 'w',\n height: 'h',\n minWidth: 'min-w',\n maxWidth: 'max-w',\n minHeight: 'min-h',\n maxHeight: 'max-h',\n left: 'left',\n top: 'top',\n gap: 'gap',\n rowGap: 'gap-y',\n columnGap: 'gap-x',\n paddingTop: 'pt',\n paddingRight: 'pr',\n paddingBottom: 'pb',\n paddingLeft: 'pl',\n borderRadius: 'rounded',\n borderTopLeftRadius: 'rounded-tl',\n borderTopRightRadius: 'rounded-tr',\n borderBottomLeftRadius: 'rounded-bl',\n borderBottomRightRadius: 'rounded-br',\n borderWidth: 'border',\n borderTopWidth: 'border-t',\n borderRightWidth: 'border-r',\n borderBottomWidth: 'border-b',\n borderLeftWidth: 'border-l',\n backgroundColor: 'bg',\n color: 'text',\n borderColor: 'border',\n fontSize: 'text',\n fontWeight: 'font',\n lineHeight: 'leading',\n letterSpacing: 'tracking',\n opacity: 'opacity',\n flexBasis: 'basis',\n}\n\nfunction arbitraryClass(property: string, value: string | number): string | undefined {\n const prefix = prefixes[property]\n const literal = String(value)\n // Unrepresentable/custom CSS remains inline instead of emitting a broken class.\n if (!prefix || /[\\s[\\]_'\"\\\\;]/.test(literal)) return undefined\n const hint = property === 'fontSize' ? 'length:' : property === 'fontWeight' ? 'number:' : ''\n return `${prefix}-[${hint}${literal}]`\n}\n\nexport function convertCSS(style: CSSStyle): { className: string; style: CSSStyle } {\n const classes: string[] = []\n const residual: CSSStyle = {}\n for (const [property, value] of Object.entries(style)) {\n const className = keywords[property]?.[String(value)] ?? arbitraryClass(property, value)\n if (className) classes.push(className)\n else residual[property] = value\n }\n return { className: [...new Set(classes)].join(' '), style: residual }\n}\n","import type { IRData, IRNode, TailwindIR, TailwindNode } from '../ir/model/types.js'\nimport { convertCSS } from './style.js'\n\nexport function convertToTailwind(data: IRData): TailwindIR {\n const input = structuredClone(data)\n const records: IRNode[] = []\n const stack = [input.root]\n while (stack.length) {\n const node = stack.pop()!\n records.push(node)\n for (const child of node.children) stack.push(child)\n }\n const converted = new Map<IRNode, TailwindNode>()\n for (const node of records.reverse()) {\n converted.set(node, {\n ...node,\n ...convertCSS(node.style),\n children: node.children.map(child => converted.get(child)!),\n })\n }\n return {\n schemaVersion: 1,\n target: 'tailwind-v4',\n root: converted.get(input.root)!,\n diagnostics: input.diagnostics,\n }\n}\n","import type { DeepReadonly } from '../../model/types.js'\nimport { deepFreeze } from '../../model/value.js'\nimport { convertToTailwind } from '../../tailwind/convert.js'\nimport type { IRData, TailwindIR } from './types.js'\n\nexport class IRDocument {\n readonly #data: IRData\n\n constructor(data: IRData) {\n this.#data = deepFreeze(structuredClone(data))\n }\n\n get schemaVersion(): 1 {\n return 1\n }\n get root(): DeepReadonly<IRData['root']> {\n return this.#data.root as unknown as DeepReadonly<IRData['root']>\n }\n get diagnostics(): DeepReadonly<IRData['diagnostics']> {\n return this.#data.diagnostics\n }\n toJSON(): IRData {\n return structuredClone(this.#data)\n }\n toTailwind(): TailwindIR {\n return convertToTailwind(this.#data)\n }\n}\n","import type { DeepReadonly, NodeRecord } from '../../model/types.js'\nimport { deepFreeze, isObject } from '../../model/value.js'\nimport type { IRNode, IRPlugin, IRPluginContext } from '../model/types.js'\n\nexport class IRPluginError extends Error {\n override name = 'IRPluginError'\n constructor(\n readonly pluginName: string,\n readonly nodeId: string,\n cause: unknown,\n ) {\n super(`IR plugin \"${pluginName}\" failed for node ${nodeId}`, { cause })\n }\n}\n\nexport function validateIRNode(node: unknown): asserts node is IRNode {\n if (\n !isObject(node) ||\n !isObject(node.source) ||\n typeof node.source.id !== 'string' ||\n typeof node.source.name !== 'string' ||\n typeof node.source.type !== 'string' ||\n !['container', 'text', 'shape', 'instance', 'unknown'].includes(String(node.kind)) ||\n typeof node.visible !== 'boolean' ||\n !Array.isArray(node.children) ||\n !isObject(node.layout) ||\n !['none', 'horizontal', 'vertical'].includes(String(node.layout.mode)) ||\n typeof node.layout.wrap !== 'boolean' ||\n !isObject(node.size) ||\n !['FIXED', 'HUG', 'FILL'].includes(String(node.size.widthMode)) ||\n !['FIXED', 'HUG', 'FILL'].includes(String(node.size.heightMode)) ||\n !isObject(node.style) ||\n !isObject(node.extensions)\n )\n throw new TypeError('Plugin must return a standard IRNode')\n for (const value of Object.values(node.style)) {\n if (typeof value !== 'string' && (typeof value !== 'number' || !Number.isFinite(value)))\n throw new TypeError('Invalid IR style value')\n }\n for (const axis of ['width', 'height']) {\n const value = node.size[axis]\n if (value !== undefined && (typeof value !== 'number' || !Number.isFinite(value)))\n throw new TypeError('Invalid IR size')\n }\n if (node.text !== undefined && (!isObject(node.text) || typeof node.text.characters !== 'string'))\n throw new TypeError('Invalid IR text')\n}\n\nfunction validateJSON(value: unknown, ancestors = new Set<object>()): void {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') return\n if (typeof value === 'number' && Number.isFinite(value)) return\n if (\n typeof value !== 'object' ||\n (!Array.isArray(value) &&\n Object.getPrototypeOf(value) !== Object.prototype &&\n Object.getPrototypeOf(value) !== null)\n )\n throw new TypeError('Extensions must contain JSON values')\n if (ancestors.has(value)) throw new TypeError('Cyclic extension value')\n ancestors.add(value)\n for (const child of Object.values(value)) validateJSON(child, ancestors)\n ancestors.delete(value)\n}\n\nfunction validatePluginTree(node: unknown): asserts node is IRNode {\n const pending: unknown[] = [node]\n const seen = new Set<unknown>()\n while (pending.length) {\n const current = pending.pop()\n if (seen.has(current)) throw new TypeError('IR children must form a tree')\n seen.add(current)\n validateIRNode(current)\n validateJSON(current.extensions)\n for (const child of current.children) pending.push(child)\n }\n}\n\nexport function runPlugins(\n initial: IRNode,\n record: NodeRecord,\n plugins: readonly IRPlugin[],\n): IRNode {\n const context: IRPluginContext = Object.freeze({\n source: record.source,\n parent: record.parent?.source,\n component: record.component,\n })\n return plugins.reduce((current, plugin) => {\n try {\n const result: unknown = plugin.transform(\n deepFreeze(current) as unknown as DeepReadonly<IRNode>,\n context,\n )\n validatePluginTree(result)\n return structuredClone(result)\n } catch (cause) {\n throw new IRPluginError(plugin.name, record.source.id, cause)\n }\n }, initial)\n}\n","export const attributes = ['id', 'name', 'type', 'visible', 'componentName'] as const\nexport type Attribute = (typeof attributes)[number]\nexport interface QueryCondition {\n id?: string | RegExp\n name?: string | RegExp\n type?: string | RegExp\n visible?: boolean\n componentName?: string | RegExp\n}\nexport type Selector = string | QueryCondition\nexport interface AttributeTest {\n key: Attribute\n operator: 'exists' | '=' | '*=' | '^=' | '$=' | 'regex'\n value?: string | boolean | RegExp\n}\nexport interface SelectorPart {\n type?: string\n tests: AttributeTest[]\n relation?: 'descendant' | 'child'\n}\nexport class SelectorSyntaxError extends Error {\n override name = 'SelectorSyntaxError'\n}\n","import type { NormalizedInput } from '../input/normalize.js'\nimport type { DeepReadonly, FigmaNodeData, NodeRecord } from '../model/types.js'\n\nexport function* walk(roots: readonly NodeRecord[]): Generator<NodeRecord> {\n const stack = [...roots].reverse()\n while (stack.length) {\n const node = stack.pop()!\n yield node\n for (let i = node.children.length - 1; i >= 0; i--) stack.push(node.children[i]!)\n }\n}\n\nfunction localComponentNames(roots: readonly FigmaNodeData[]): Map<string, string> {\n const names = new Map<string, string>()\n const stack: DeepReadonly<FigmaNodeData>[] = [...roots]\n while (stack.length) {\n const node = stack.pop()!\n if (node.type === 'COMPONENT') names.set(node.id, node.name)\n for (const child of node.children ?? []) stack.push(child)\n }\n return names\n}\n\nexport function buildRecords(input: NormalizedInput): NodeRecord[] {\n const names = localComponentNames(input.roots)\n const roots: NodeRecord[] = []\n const stack = input.roots\n .map(source => ({\n source: source as DeepReadonly<FigmaNodeData>,\n parent: undefined as NodeRecord | undefined,\n }))\n .reverse()\n while (stack.length) {\n const { source, parent } = stack.pop()!\n const componentId = typeof source.componentId === 'string' ? source.componentId : undefined\n const component = componentId ? input.components.get(componentId) : undefined\n const componentName = component?.name ?? (componentId ? names.get(componentId) : undefined)\n const record: NodeRecord = { source, parent, children: [], componentName, component }\n if (parent) parent.children.push(record)\n else roots.push(record)\n const children = source.children ?? []\n for (let i = children.length - 1; i >= 0; i--)\n stack.push({ source: children[i]!, parent: record })\n }\n return roots\n}\n","import type { DeepReadonly, FigmaNodeData } from '../../model/types.js'\nimport { isObject, numberValue } from '../../model/value.js'\nimport type { CSSStyle } from '../model/types.js'\n\nfunction channel(value: unknown): number {\n return Math.round(Math.max(0, Math.min(1, numberValue(value) ?? 0)) * 255)\n}\n\nexport function solidColor(paint: unknown): string | undefined {\n if (!isObject(paint) || paint.type !== 'SOLID' || !isObject(paint.color)) return undefined\n const { color } = paint\n const alpha = Math.max(\n 0,\n Math.min(1, (numberValue(color.a) ?? 1) * (numberValue(paint.opacity) ?? 1)),\n )\n return `rgba(${channel(color.r)},${channel(color.g)},${channel(color.b)},${Number(alpha.toFixed(6))})`\n}\n\nexport function visiblePaints(value: unknown): Record<string, unknown>[] {\n return Array.isArray(value)\n ? value.filter(\n (paint): paint is Record<string, unknown> => isObject(paint) && paint.visible !== false,\n )\n : []\n}\n\nexport function convertStyle(node: DeepReadonly<FigmaNodeData>): CSSStyle {\n const style: CSSStyle = {}\n const fills = visiblePaints(node.fills)\n const fill = fills.length === 1 ? solidColor(fills[0]) : undefined\n if (fill) style[node.type === 'TEXT' ? 'color' : 'backgroundColor'] = fill\n const strokes = visiblePaints(node.strokes)\n const stroke = strokes.length === 1 ? solidColor(strokes[0]) : undefined\n if (stroke) {\n style.borderColor = stroke\n style.borderStyle = 'solid'\n const weight = numberValue(node.strokeWeight)\n if (weight !== undefined) style.borderWidth = `${weight}px`\n if (isObject(node.individualStrokeWeights)) {\n for (const [side, property] of Object.entries({\n top: 'borderTopWidth',\n right: 'borderRightWidth',\n bottom: 'borderBottomWidth',\n left: 'borderLeftWidth',\n })) {\n const value = numberValue(node.individualStrokeWeights[side])\n if (value !== undefined) style[property] = `${value}px`\n }\n }\n }\n const radius = numberValue(node.cornerRadius)\n if (radius !== undefined) style.borderRadius = `${radius}px`\n if (Array.isArray(node.rectangleCornerRadii)) {\n const corners = [\n 'borderTopLeftRadius',\n 'borderTopRightRadius',\n 'borderBottomRightRadius',\n 'borderBottomLeftRadius',\n ]\n node.rectangleCornerRadii.forEach((value, index) => {\n if (corners[index] && numberValue(value) !== undefined) style[corners[index]!] = `${value}px`\n })\n }\n if (node.type === 'ELLIPSE') style.borderRadius = '50%'\n const opacity = numberValue(node.opacity)\n if (opacity !== undefined) style.opacity = opacity\n if (node.visible === false) style.display = 'none'\n return style\n}\n","import type { Diagnostic, NodeRecord } from '../../model/types.js'\nimport { isObject } from '../../model/value.js'\nimport { visiblePaints } from './style.js'\n\nexport function unsupportedProperties(record: NodeRecord, unknownKind: boolean): Diagnostic[] {\n const node = record.source\n const properties: string[] = []\n if (unknownKind) properties.push('type')\n for (const key of ['fills', 'strokes']) {\n const paints = visiblePaints(node[key])\n if (paints.length > 1 || paints.some(paint => paint.type !== 'SOLID')) properties.push(key)\n }\n if (\n Array.isArray(node.effects) &&\n node.effects.some(effect => isObject(effect) && effect.visible !== false)\n )\n properties.push('effects')\n if (isObject(node.styleOverrideTable) && Object.keys(node.styleOverrideTable).length)\n properties.push('styleOverrideTable')\n if (\n node.type === 'VECTOR' ||\n node.type === 'BOOLEAN_OPERATION' ||\n node.type === 'STAR' ||\n node.type === 'REGULAR_POLYGON' ||\n node.type === 'LINE'\n )\n properties.push('vectorGeometry')\n if (node.layoutMode === 'GRID') properties.push('layoutMode')\n if (typeof node.rotation === 'number' && node.rotation !== 0) properties.push('rotation')\n if (typeof node.itemSpacing === 'number' && node.itemSpacing < 0) properties.push('itemSpacing')\n if (visiblePaints(node.strokes).length && node.strokeAlign && node.strokeAlign !== 'INSIDE')\n properties.push('strokeAlign')\n if (node.isMask === true) properties.push('isMask')\n if (typeof node.cornerSmoothing === 'number' && node.cornerSmoothing > 0)\n properties.push('cornerSmoothing')\n return properties.map(property => ({\n code: 'unsupported-property',\n nodeId: node.id,\n property,\n message: `${property} is not fully represented in the basic IR`,\n }))\n}\n","import type { NodeRecord } from '../../model/types.js'\nimport { isObject, numberValue } from '../../model/value.js'\nimport type { CSSStyle, IRNode, SizeMode } from '../model/types.js'\n\nfunction sizingMode(value: unknown, fallback: SizeMode): SizeMode {\n return value === 'FIXED' || value === 'HUG' || value === 'FILL' ? value : fallback\n}\n\nfunction legacyMode(record: NodeRecord, axis: 'width' | 'height'): SizeMode {\n const horizontal = record.source.layoutMode === 'HORIZONTAL'\n const primary = (axis === 'width') === horizontal\n const own = primary ? record.source.primaryAxisSizingMode : record.source.counterAxisSizingMode\n const parentHorizontal = record.parent?.source.layoutMode === 'HORIZONTAL'\n const parentPrimary = (axis === 'width') === parentHorizontal\n if (\n record.parent?.source.layoutMode === 'HORIZONTAL' ||\n record.parent?.source.layoutMode === 'VERTICAL'\n ) {\n if (parentPrimary && record.source.layoutGrow === 1) return 'FILL'\n if (!parentPrimary && record.source.layoutAlign === 'STRETCH') return 'FILL'\n }\n return own === 'AUTO' ? 'HUG' : 'FIXED'\n}\n\nfunction dimensions(record: NodeRecord): IRNode['size'] {\n const box = isObject(record.source.absoluteBoundingBox) ? record.source.absoluteBoundingBox : {}\n const size = isObject(record.source.size) ? record.source.size : {}\n return {\n width: numberValue(size.x) ?? numberValue(box.width),\n height: numberValue(size.y) ?? numberValue(box.height),\n widthMode: sizingMode(record.source.layoutSizingHorizontal, legacyMode(record, 'width')),\n heightMode: sizingMode(record.source.layoutSizingVertical, legacyMode(record, 'height')),\n }\n}\n\nfunction applyDimension(\n style: CSSStyle,\n axis: 'width' | 'height',\n value: number | undefined,\n mode: SizeMode,\n record: NodeRecord,\n isRoot: boolean,\n): void {\n if (mode === 'FIXED') {\n if (value !== undefined) style[axis] = `${value}px`\n } else if (mode === 'HUG') style[axis] = 'fit-content'\n else {\n const parentAxis = record.parent?.source.layoutMode === 'HORIZONTAL' ? 'width' : 'height'\n const parentIsFlex = ['HORIZONTAL', 'VERTICAL'].includes(\n String(record.parent?.source.layoutMode),\n )\n if (!isRoot && parentIsFlex && parentAxis === axis) {\n style.flexGrow = 1\n style.flexBasis = '0px'\n style[axis === 'width' ? 'minWidth' : 'minHeight'] = '0px'\n } else if (!isRoot && parentIsFlex) style.alignSelf = 'stretch'\n else style[axis] = '100%'\n }\n}\n\nfunction autoLayoutStyle(record: NodeRecord): CSSStyle {\n const node = record.source\n if (node.layoutMode !== 'HORIZONTAL' && node.layoutMode !== 'VERTICAL') return {}\n const style: CSSStyle = {\n display: 'flex',\n flexDirection: node.layoutMode === 'HORIZONTAL' ? 'row' : 'column',\n }\n const primary: Record<string, string> = {\n MIN: 'flex-start',\n MAX: 'flex-end',\n CENTER: 'center',\n SPACE_BETWEEN: 'space-between',\n }\n const counter: Record<string, string> = {\n MIN: 'flex-start',\n MAX: 'flex-end',\n CENTER: 'center',\n BASELINE: 'baseline',\n }\n if (typeof node.primaryAxisAlignItems === 'string')\n style.justifyContent = primary[node.primaryAxisAlignItems] ?? 'flex-start'\n if (typeof node.counterAxisAlignItems === 'string')\n style.alignItems = counter[node.counterAxisAlignItems] ?? 'flex-start'\n else style.alignItems = 'flex-start'\n const gap = numberValue(node.itemSpacing)\n if (gap !== undefined && gap >= 0) style.gap = `${gap}px`\n for (const key of ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'] as const) {\n const value = numberValue(node[key])\n if (value !== undefined) style[key] = `${value}px`\n }\n if (node.layoutWrap === 'WRAP') {\n style.flexWrap = 'wrap'\n const counterGap = numberValue(node.counterAxisSpacing)\n if (counterGap !== undefined && counterGap >= 0)\n style[node.layoutMode === 'HORIZONTAL' ? 'rowGap' : 'columnGap'] = `${counterGap}px`\n if (node.counterAxisAlignContent === 'SPACE_BETWEEN') style.alignContent = 'space-between'\n }\n return style\n}\n\nfunction positioning(record: NodeRecord, isRoot: boolean): CSSStyle {\n if (isRoot) return { position: 'relative' }\n const parent = record.parent?.source\n const absolute =\n record.source.layoutPositioning === 'ABSOLUTE' ||\n (parent && parent.layoutMode !== 'HORIZONTAL' && parent.layoutMode !== 'VERTICAL')\n if (!absolute) return { position: 'relative', flexShrink: 0 }\n const box = isObject(record.source.absoluteBoundingBox) ? record.source.absoluteBoundingBox : {}\n const parentBox = isObject(parent?.absoluteBoundingBox) ? parent.absoluteBoundingBox : {}\n const x = numberValue(box.x)\n const y = numberValue(box.y)\n return {\n position: 'absolute',\n ...(x !== undefined ? { left: `${x - (numberValue(parentBox.x) ?? 0)}px` } : {}),\n ...(y !== undefined ? { top: `${y - (numberValue(parentBox.y) ?? 0)}px` } : {}),\n }\n}\n\nexport function convertLayout(\n record: NodeRecord,\n isRoot: boolean,\n): Pick<IRNode, 'size' | 'layout' | 'style'> {\n const size = dimensions(record)\n const style: CSSStyle = {\n boxSizing: 'border-box',\n ...positioning(record, isRoot),\n ...autoLayoutStyle(record),\n }\n applyDimension(style, 'width', size.width, size.widthMode, record, isRoot)\n applyDimension(style, 'height', size.height, size.heightMode, record, isRoot)\n for (const key of ['minWidth', 'maxWidth', 'minHeight', 'maxHeight'] as const) {\n const value = numberValue(record.source[key])\n if (value !== undefined) style[key] = `${value}px`\n }\n if (record.source.clipsContent === true) style.overflow = 'hidden'\n return {\n size,\n layout: {\n mode:\n record.source.layoutMode === 'HORIZONTAL'\n ? 'horizontal'\n : record.source.layoutMode === 'VERTICAL'\n ? 'vertical'\n : 'none',\n wrap: record.source.layoutWrap === 'WRAP',\n },\n style,\n }\n}\n","import type { DeepReadonly, FigmaNodeData } from '../../model/types.js'\nimport { isObject, numberValue } from '../../model/value.js'\nimport type { CSSStyle } from '../model/types.js'\n\nexport function convertText(node: DeepReadonly<FigmaNodeData>): CSSStyle {\n if (node.type !== 'TEXT') return {}\n const input = isObject(node.style) ? node.style : {}\n const style: CSSStyle = { whiteSpace: 'pre-wrap' }\n if (typeof input.fontFamily === 'string') style.fontFamily = input.fontFamily\n const size = numberValue(input.fontSize)\n const weight = numberValue(input.fontWeight)\n if (size !== undefined) style.fontSize = `${size}px`\n if (weight !== undefined) style.fontWeight = weight\n if (input.italic === true) style.fontStyle = 'italic'\n if (typeof input.textAlignHorizontal === 'string') {\n const align: Record<string, string> = {\n LEFT: 'left',\n RIGHT: 'right',\n CENTER: 'center',\n JUSTIFIED: 'justify',\n }\n if (align[input.textAlignHorizontal]) style.textAlign = align[input.textAlignHorizontal]!\n }\n const lineHeight = numberValue(input.lineHeightPx)\n if (\n input.lineHeightUnit === 'FONT_SIZE_%' &&\n numberValue(input.lineHeightPercentFontSize) !== undefined\n )\n style.lineHeight = `${input.lineHeightPercentFontSize}%`\n else if (lineHeight !== undefined) style.lineHeight = `${lineHeight}px`\n const letterSpacing = numberValue(input.letterSpacing)\n if (letterSpacing !== undefined) style.letterSpacing = `${letterSpacing}px`\n if (input.textDecoration === 'UNDERLINE') style.textDecorationLine = 'underline'\n if (input.textDecoration === 'STRIKETHROUGH') style.textDecorationLine = 'line-through'\n const textCase: Record<string, string> = {\n UPPER: 'uppercase',\n LOWER: 'lowercase',\n TITLE: 'capitalize',\n }\n if (typeof input.textCase === 'string' && textCase[input.textCase])\n style.textTransform = textCase[input.textCase]!\n return style\n}\n","import type { Diagnostic, NodeRecord } from '../model/types.js'\nimport { walk } from '../tree/records.js'\nimport { unsupportedProperties } from './converters/diagnostics.js'\nimport { convertLayout } from './converters/layout.js'\nimport { convertStyle } from './converters/style.js'\nimport { convertText } from './converters/text.js'\nimport { IRDocument } from './model/document.js'\nimport type { IRNode, IRPlugin } from './model/types.js'\nimport { runPlugins } from './plugins/run.js'\n\nfunction nodeKind(type: string): IRNode['kind'] {\n if (type === 'TEXT') return 'text'\n if (type === 'INSTANCE') return 'instance'\n if (\n ['DOCUMENT', 'CANVAS', 'FRAME', 'GROUP', 'COMPONENT', 'COMPONENT_SET', 'SECTION'].includes(type)\n )\n return 'container'\n if (\n [\n 'RECTANGLE',\n 'ELLIPSE',\n 'LINE',\n 'VECTOR',\n 'BOOLEAN_OPERATION',\n 'STAR',\n 'REGULAR_POLYGON',\n ].includes(type)\n )\n return 'shape'\n return 'unknown'\n}\n\nfunction convertNode(record: NodeRecord, children: IRNode[], isRoot: boolean): IRNode {\n const source = record.source\n const layout = convertLayout(record, isRoot)\n return {\n source: {\n id: source.id,\n name: source.name,\n type: source.type,\n ...(record.componentName ? { componentName: record.componentName } : {}),\n },\n kind: nodeKind(source.type),\n visible: source.visible !== false,\n ...layout,\n style: { ...layout.style, ...convertText(source), ...convertStyle(source) },\n ...(source.type === 'TEXT'\n ? { text: { characters: typeof source.characters === 'string' ? source.characters : '' } }\n : {}),\n children,\n extensions: {},\n }\n}\n\nexport function convertToIR(root: NodeRecord, plugins: readonly IRPlugin[]): IRDocument {\n const records = [...walk([root])]\n const converted = new Map<NodeRecord, IRNode>()\n const diagnostics: Diagnostic[] = []\n for (const record of [...records].reverse()) {\n const initial = convertNode(\n record,\n record.children.map(child => converted.get(child)!),\n record === root,\n )\n converted.set(record, runPlugins(initial, record, plugins))\n }\n for (const record of records)\n diagnostics.push(...unsupportedProperties(record, nodeKind(record.source.type) === 'unknown'))\n return new IRDocument({ schemaVersion: 1, root: converted.get(root)!, diagnostics })\n}\n","import type { NodeRecord } from '../model/types.js'\nimport type { AttributeTest, SelectorPart } from './types.js'\n\nfunction readAttribute(node: NodeRecord, key: AttributeTest['key']): unknown {\n if (key === 'componentName') return node.componentName\n if (key === 'visible') return node.source.visible ?? true\n return node.source[key]\n}\n\nfunction matchesAttribute(node: NodeRecord, test: AttributeTest): boolean {\n const actual = readAttribute(node, test.key)\n if (test.operator === 'exists') return actual !== undefined\n if (test.operator === '=') return actual === test.value\n if (typeof actual !== 'string') return false\n if (test.operator === 'regex') {\n const regex = test.value as RegExp\n return new RegExp(regex.source, regex.flags).test(actual)\n }\n const expected = test.value as string\n if (test.operator === '*=') return actual.includes(expected)\n if (test.operator === '^=') return actual.startsWith(expected)\n return actual.endsWith(expected)\n}\n\nfunction matchesPart(node: NodeRecord, part: SelectorPart): boolean {\n return (\n (!part.type || node.source.type === part.type) &&\n part.tests.every(test => matchesAttribute(node, test))\n )\n}\n\nexport function matchesSelector(\n node: NodeRecord,\n parts: readonly SelectorPart[],\n boundary?: NodeRecord,\n): boolean {\n const pending: { node: NodeRecord; index: number }[] = [{ node, index: parts.length - 1 }]\n while (pending.length) {\n const current = pending.pop()!\n const part = parts[current.index]!\n if (current.node === boundary || !matchesPart(current.node, part)) continue\n if (current.index === 0) return true\n let parent = current.node.parent\n while (parent && parent !== boundary) {\n pending.push({ node: parent, index: current.index - 1 })\n if (part.relation === 'child') break\n parent = parent.parent\n }\n }\n return false\n}\n","import type { Attribute, AttributeTest, Selector, SelectorPart } from './types.js'\nimport { attributes, SelectorSyntaxError } from './types.js'\n\n/** Scan compounds without splitting quoted attribute values. */\nfunction splitSelector(selector: string): { text: string; relation?: 'descendant' | 'child' }[] {\n const parts: { text: string; relation?: 'descendant' | 'child' }[] = []\n let cursor = 0\n let relation: 'descendant' | 'child' | undefined\n while (cursor < selector.length) {\n while (/\\s/.test(selector[cursor] ?? '') && cursor < selector.length) cursor++\n const start = cursor\n let bracket = false\n let quote = ''\n while (cursor < selector.length) {\n const char = selector[cursor]!\n if (quote) {\n if (char === '\\\\') cursor++\n else if (char === quote) quote = ''\n } else if (char === '\"' || char === \"'\") quote = char\n else if (char === '[') {\n if (bracket) throw new SelectorSyntaxError('Nested brackets are not supported')\n bracket = true\n } else if (char === ']') {\n if (!bracket) throw new SelectorSyntaxError('Unexpected closing bracket')\n bracket = false\n } else if (!bracket && (char === '>' || /\\s/.test(char))) break\n cursor++\n }\n if (quote || bracket) throw new SelectorSyntaxError('Unclosed attribute or quoted value')\n if (cursor === start) throw new SelectorSyntaxError('Expected a selector before combinator')\n parts.push({ text: selector.slice(start, cursor), relation })\n while (cursor < selector.length && /\\s/.test(selector[cursor]!)) cursor++\n if (cursor === selector.length) break\n relation = selector[cursor] === '>' ? 'child' : 'descendant'\n if (relation === 'child') {\n cursor++\n while (cursor < selector.length && /\\s/.test(selector[cursor]!)) cursor++\n if (cursor === selector.length) throw new SelectorSyntaxError('Expected selector after >')\n }\n }\n return parts\n}\n\nfunction assertAttribute(key: string): asserts key is Attribute {\n if (!(attributes as readonly string[]).includes(key))\n throw new SelectorSyntaxError(`Unsupported attribute: ${key}`)\n}\n\nfunction parseAttribute(body: string): AttributeTest {\n const match = /^\\s*([A-Za-z][A-Za-z0-9]*)\\s*(?:(\\*=|\\^=|\\$=|=)\\s*(.*?)\\s*)?$/.exec(body)\n if (!match) throw new SelectorSyntaxError(`Invalid attribute: [${body}]`)\n const key = match[1]!\n assertAttribute(key)\n const operator = match[2] as AttributeTest['operator'] | undefined\n if (!operator) return { key, operator: 'exists' }\n const raw = match[3]!\n if (key === 'visible') {\n if (operator !== '=' || !/^(true|false)$/.test(raw))\n throw new SelectorSyntaxError('visible requires an unquoted boolean equality')\n return { key, operator, value: raw === 'true' }\n }\n const quoted = /^([\"'])([\\s\\S]*)\\1$/.exec(raw)\n if (!quoted) throw new SelectorSyntaxError('String attribute values must be quoted')\n const inner = quoted[2]!\n let value = ''\n for (let i = 0; i < inner.length; i++) {\n if (inner[i] === '\\\\') {\n if (i + 1 === inner.length) throw new SelectorSyntaxError('Incomplete escape')\n value += inner[++i]\n } else {\n if (inner[i] === quoted[1]) throw new SelectorSyntaxError('Unescaped quote in attribute')\n value += inner[i]\n }\n }\n return { key, operator, value }\n}\n\nfunction parseCompound(text: string): Omit<SelectorPart, 'relation'> {\n const typeMatch = /^(\\*|[A-Z][A-Z0-9_]*)/.exec(text)\n const type = typeMatch?.[1]\n let cursor = type?.length ?? 0\n const tests: AttributeTest[] = []\n while (cursor < text.length) {\n if (text[cursor] !== '[')\n throw new SelectorSyntaxError(`Unsupported selector near: ${text.slice(cursor)}`)\n const start = ++cursor\n let quote = ''\n while (cursor < text.length) {\n const char = text[cursor]!\n if (quote) {\n if (char === '\\\\') cursor++\n else if (char === quote) quote = ''\n } else if (char === '\"' || char === \"'\") quote = char\n else if (char === ']') break\n cursor++\n }\n tests.push(parseAttribute(text.slice(start, cursor)))\n cursor++\n }\n if (!type && !tests.length) throw new SelectorSyntaxError('Empty selector')\n return { type: type === '*' ? undefined : type, tests }\n}\n\nexport function parseSelector(selector: Selector): SelectorPart[] {\n if (typeof selector === 'string') {\n if (!selector.trim()) throw new SelectorSyntaxError('Empty selector')\n return splitSelector(selector.trim()).map(({ text, relation }) => ({\n ...parseCompound(text),\n relation,\n }))\n }\n if (!selector || typeof selector !== 'object' || Array.isArray(selector))\n throw new SelectorSyntaxError('Expected a string or condition object')\n const tests = Object.entries(selector).map(([key, value]): AttributeTest => {\n assertAttribute(key)\n if (\n key === 'visible'\n ? typeof value !== 'boolean'\n : typeof value !== 'string' && !(value instanceof RegExp)\n ) {\n throw new SelectorSyntaxError(`Invalid condition for ${key}`)\n }\n return { key, operator: value instanceof RegExp ? 'regex' : '=', value }\n })\n return [{ tests }]\n}\n","import { normalizeInput } from '../input/normalize.js'\nimport type { IRDocument } from '../ir/model/document.js'\nimport type { IROptions, IRPlugin } from '../ir/model/types.js'\nimport { convertToIR } from '../ir/pipeline.js'\nimport type { DeepReadonly, Diagnostic, FigmaNodeData, NodeRecord } from '../model/types.js'\nimport { deepFreeze } from '../model/value.js'\nimport { matchesSelector } from '../selector/match.js'\nimport { parseSelector } from '../selector/parser.js'\nimport type { Selector } from '../selector/types.js'\nimport { buildRecords, walk } from './records.js'\n\nexport interface FigmaTreeOptions {\n irPlugins?: readonly IRPlugin[]\n}\n\nclass TreeContext {\n readonly #wrappers = new WeakMap<NodeRecord, FigmaNode>()\n constructor(readonly plugins: readonly IRPlugin[]) {}\n wrap(record: NodeRecord): FigmaNode {\n let wrapper = this.#wrappers.get(record)\n if (!wrapper) {\n wrapper = new FigmaNode(record, this)\n this.#wrappers.set(record, wrapper)\n }\n return wrapper\n }\n *search(\n roots: readonly NodeRecord[],\n selector: Selector,\n boundary?: NodeRecord,\n ): Generator<FigmaNode> {\n const parts = parseSelector(selector)\n const seen = new Set<string>()\n for (const record of walk(roots)) {\n if (!seen.has(record.source.id) && matchesSelector(record, parts, boundary)) {\n seen.add(record.source.id)\n yield this.wrap(record)\n }\n }\n }\n}\n\nexport class FigmaNode {\n readonly #record: NodeRecord\n readonly #context: TreeContext\n /** @internal Obtain nodes through FigmaTree queries. */\n constructor(record: NodeRecord, context: TreeContext) {\n this.#record = record\n this.#context = context\n }\n get id(): string {\n return this.#record.source.id\n }\n get name(): string {\n return this.#record.source.name\n }\n get type(): string {\n return this.#record.source.type\n }\n get componentName(): string | undefined {\n return this.#record.componentName\n }\n get children(): readonly FigmaNode[] {\n return Object.freeze(this.#record.children.map(child => this.#context.wrap(child)))\n }\n query(selector: Selector): FigmaNode | undefined {\n return this.#context.search(this.#record.children, selector, this.#record).next().value\n }\n queryAll(selector: Selector): FigmaNode[] {\n return [...this.#context.search(this.#record.children, selector, this.#record)]\n }\n toJSON(): FigmaNodeData {\n return structuredClone(this.#record.source) as FigmaNodeData\n }\n toIR(options: IROptions = {}): IRDocument {\n return convertToIR(this.#record, [...this.#context.plugins, ...(options.plugins ?? [])])\n }\n}\n\nexport class FigmaTree {\n readonly #records: NodeRecord[]\n readonly #context: TreeContext\n readonly diagnostics: DeepReadonly<Diagnostic[]>\n private constructor(input: unknown, options: FigmaTreeOptions) {\n const normalized = normalizeInput(input)\n this.#records = buildRecords(normalized)\n this.#context = new TreeContext(Object.freeze([...(options.irPlugins ?? [])]))\n this.diagnostics = deepFreeze(normalized.diagnostics)\n }\n static fromJson(input: unknown, options: FigmaTreeOptions = {}): FigmaTree {\n return new FigmaTree(input, options)\n }\n get roots(): readonly FigmaNode[] {\n return Object.freeze(this.#records.map(record => this.#context.wrap(record)))\n }\n query(selector: Selector): FigmaNode | undefined {\n return this.#context.search(this.#records, selector).next().value\n }\n queryAll(selector: Selector): FigmaNode[] {\n return [...this.#context.search(this.#records, selector)]\n }\n}\n","import type { HTMLInput } from '../../figma-html/index.js'\nimport { assertHTMLInput } from '../../figma-html/validate.js'\nimport type { IRData, Selector } from '../../figma-tree/index.js'\nimport { FigmaTree, IRDocument } from '../../figma-tree/index.js'\n\nexport type HTMLStyles = 'inline' | 'tailwind'\n\nfunction isIRInput(value: unknown): boolean {\n return value !== null && typeof value === 'object' && 'root' in value\n}\n\nfunction sourceIR(input: unknown, selector?: Selector): HTMLInput {\n if (isIRInput(input)) {\n if (selector !== undefined)\n throw new Error('--selector is only supported for original Figma JSON')\n assertHTMLInput(input)\n return input\n }\n const tree = FigmaTree.fromJson(input)\n if (selector === undefined && tree.roots.length !== 1) {\n throw new Error('Specify --selector when the Figma input does not have exactly one root')\n }\n const selected = selector === undefined ? tree.roots[0] : tree.query(selector)\n if (!selected) throw new Error('No Figma node matched the export selector')\n return selected.toIR()\n}\n\nexport function prepareHTMLInput(\n input: unknown,\n selector?: Selector,\n styles?: HTMLStyles,\n): HTMLInput {\n const ir = sourceIR(input, selector)\n if ('target' in ir && ir.target === 'tailwind-v4') {\n if (styles === 'inline')\n throw new Error(\n 'Tailwind IR has no original inline styles; use standard IR for --styles inline',\n )\n return ir\n }\n if (styles === 'tailwind') {\n // Construct an owned standard IR document before deriving its Tailwind representation.\n const data = { schemaVersion: ir.schemaVersion, root: ir.root, diagnostics: ir.diagnostics }\n return new IRDocument(structuredClone(data) as unknown as IRData).toTailwind()\n }\n return ir\n}\n","import { readFile } from 'node:fs/promises'\nimport { renderHTML } from '../../figma-html/index.js'\nimport type { ExportArgs } from '../args.js'\nimport { prepareHTMLInput } from '../html/input.js'\nimport { writeText } from '../output.js'\n\nexport async function exportHTML(\n args: ExportArgs,\n stdout: (text: string) => void,\n stderr: (text: string) => void,\n): Promise<void> {\n const input: unknown = JSON.parse(await readFile(args.input, 'utf8'))\n const ir = prepareHTMLInput(input, args.selector, args.styles)\n const html = renderHTML(ir)\n await writeText(html, args.out, args.force, stdout)\n if (ir.diagnostics.length)\n stderr(\n `HTML exported with ${ir.diagnostics.length} IR diagnostics; unsupported design features may be omitted.\\n`,\n )\n}\n","import { readFile } from 'node:fs/promises'\nimport { FigmaTree } from '../../figma-tree/index.js'\nimport type { QueryArgs } from '../args.js'\nimport { writeJSON } from '../output.js'\n\nexport async function query(args: QueryArgs, stdout: (text: string) => void): Promise<void> {\n const tree = FigmaTree.fromJson(JSON.parse(await readFile(args.input, 'utf8')))\n const result =\n args.command === 'query'\n ? (tree.query(args.selector)?.toJSON() ?? null)\n : tree.queryAll(args.selector).map(node => node.toJSON())\n await writeJSON(result, args.out, args.force, stdout)\n}\n","export function readToken(env: NodeJS.ProcessEnv): string {\n const token = env.FIGMA_TOKEN\n if (!token?.trim()) throw new Error('FIGMA_TOKEN environment variable is required')\n return token\n}\n","import { FigmaClient } from '../figma-json-fetch/index.js'\nimport { HELP, parseArgs } from './args.js'\nimport { download } from './commands/download.js'\nimport { exportHTML } from './commands/export.js'\nimport { query } from './commands/query.js'\nimport { readToken } from './env.js'\nimport { safeErrorMessage } from './output.js'\n\nexport interface CLIEnvironment {\n env: NodeJS.ProcessEnv\n stdout: (text: string) => void\n stderr: (text: string) => void\n fetch?: typeof globalThis.fetch\n}\n\nexport async function runCLI(args: string[], environment: CLIEnvironment): Promise<number> {\n try {\n const parsed = parseArgs(args)\n if (parsed === 'help') {\n environment.stdout(HELP)\n return 0\n }\n if (parsed.command === 'download') {\n const client = new FigmaClient({\n token: readToken(environment.env),\n fetch: environment.fetch,\n })\n await download(parsed, client, environment.stdout)\n } else if (parsed.command === 'export') {\n await exportHTML(parsed, environment.stdout, environment.stderr)\n } else await query(parsed, environment.stdout)\n return 0\n } catch (error) {\n environment.stderr(`${safeErrorMessage(error, environment.env.FIGMA_TOKEN)}\\n`)\n return 1\n }\n}\n","#!/usr/bin/env node\nimport { runCLI } from './run.js'\n\nprocess.exitCode = await runCLI(process.argv.slice(2), {\n env: process.env,\n stdout: text => {\n process.stdout.write(text)\n },\n stderr: text => {\n process.stderr.write(text)\n },\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
//#region src/figma-html/classes.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
return e.replace(/\[((?:[a-z-]+:)?)([+-]?(?:\d+\.\d+|\.\d+))([a-z%]*)\]/gi, (e, t, n, r) => `[${t}${Number(Number(n).toFixed(2))}${r}]`);
|
|
4
|
+
}
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/figma-html/escape.ts
|
|
7
|
+
var t = {
|
|
8
|
+
"&": "&",
|
|
9
|
+
"<": "<",
|
|
10
|
+
">": ">",
|
|
11
|
+
"\"": """,
|
|
12
|
+
"'": "'"
|
|
13
|
+
};
|
|
14
|
+
function n(e) {
|
|
15
|
+
return e.replace(/[&<>"']/g, (e) => t[e]);
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/figma-html/attributes.ts
|
|
19
|
+
function r(e) {
|
|
20
|
+
if (e.startsWith("--")) return e;
|
|
21
|
+
let t = e.replace(/[A-Z]/g, (e) => `-${e.toLowerCase()}`);
|
|
22
|
+
return t.startsWith("ms-") ? `-${t}` : t;
|
|
23
|
+
}
|
|
24
|
+
function i(e) {
|
|
25
|
+
return Object.entries(e).map(([e, t]) => `${r(e)}:${t}`).join(";");
|
|
26
|
+
}
|
|
27
|
+
function a(t, r) {
|
|
28
|
+
let a = [
|
|
29
|
+
["data-figma-id", t.source.id],
|
|
30
|
+
["data-figma-name", t.source.name],
|
|
31
|
+
["data-figma-type", t.source.type]
|
|
32
|
+
];
|
|
33
|
+
r && "className" in t && t.className && a.push(["class", e(t.className)]);
|
|
34
|
+
let o = i(t.visible ? t.style : {
|
|
35
|
+
...t.style,
|
|
36
|
+
display: "none"
|
|
37
|
+
});
|
|
38
|
+
o && a.push(["style", o]);
|
|
39
|
+
let s = a.map(([e, t]) => ` ${e}="${n(t)}"`).join("");
|
|
40
|
+
return t.visible ? s : `${s} hidden`;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/figma-html/validate.ts
|
|
44
|
+
function o(e) {
|
|
45
|
+
return typeof e == "object" && !!e && !Array.isArray(e);
|
|
46
|
+
}
|
|
47
|
+
function s(e, t) {
|
|
48
|
+
if (!o(e) || !o(e.source) || typeof e.source.id != "string" || typeof e.source.name != "string" || typeof e.source.type != "string" || typeof e.visible != "boolean" || !o(e.style) || !Array.isArray(e.children)) throw TypeError("Expected an IR node with source, visible, style and children");
|
|
49
|
+
if (e.text !== void 0 && (!o(e.text) || typeof e.text.characters != "string")) throw TypeError(`Invalid text in IR node ${e.source.id}`);
|
|
50
|
+
if (t && typeof e.className != "string") throw TypeError(`Missing className in Tailwind node ${e.source.id}`);
|
|
51
|
+
for (let [t, n] of Object.entries(e.style)) {
|
|
52
|
+
if (!/^(?:--[\w-]+|[a-zA-Z][a-zA-Z0-9-]*)$/.test(t)) throw TypeError(`Invalid CSS property in IR node ${e.source.id}`);
|
|
53
|
+
if (typeof n != "string" && (typeof n != "number" || !Number.isFinite(n))) throw TypeError(`Invalid CSS value in IR node ${e.source.id}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function c(e) {
|
|
57
|
+
if (!o(e) || e.schemaVersion !== 1 || !Array.isArray(e.diagnostics) || e.target !== void 0 && e.target !== "tailwind-v4") throw TypeError("Expected a standard IR document or Tailwind v4 IR (schemaVersion 1)");
|
|
58
|
+
let t = e.target === "tailwind-v4", n = [e.root], r = /* @__PURE__ */ new Set();
|
|
59
|
+
for (; n.length;) {
|
|
60
|
+
let e = n.pop();
|
|
61
|
+
if (r.has(e)) throw TypeError("IR children must be an acyclic tree");
|
|
62
|
+
r.add(e), s(e, t);
|
|
63
|
+
for (let t of e.children) n.push(t);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/figma-html/renderer.ts
|
|
68
|
+
function l(e) {
|
|
69
|
+
c(e);
|
|
70
|
+
let t = "target" in e && e.target === "tailwind-v4", r = [], i = [e.root];
|
|
71
|
+
for (; i.length;) {
|
|
72
|
+
let e = i.pop();
|
|
73
|
+
if (typeof e == "string") {
|
|
74
|
+
r.push(e);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
r.push(`<div${a(e, t)}>`), e.text && r.push(n(e.text.characters)), i.push("</div>");
|
|
78
|
+
for (let t = e.children.length - 1; t >= 0; t--) i.push(e.children[t]);
|
|
79
|
+
}
|
|
80
|
+
return r.join("");
|
|
81
|
+
}
|
|
82
|
+
function u(t) {
|
|
83
|
+
if (c(t), !("target" in t) || t.target !== "tailwind-v4") return [];
|
|
84
|
+
let n = /* @__PURE__ */ new Set(), r = [t.root];
|
|
85
|
+
for (; r.length;) {
|
|
86
|
+
let t = r.pop();
|
|
87
|
+
if ("className" in t) for (let r of e(t.className).split(/\s+/).filter(Boolean)) n.add(r);
|
|
88
|
+
for (let e of t.children) r.push(e);
|
|
89
|
+
}
|
|
90
|
+
return [...n].sort();
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { u as collectTailwindClasses, l as renderHTML };
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=figma-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"figma-html.js","names":[],"sources":["../src/figma-html/classes.ts","../src/figma-html/escape.ts","../src/figma-html/attributes.ts","../src/figma-html/validate.ts","../src/figma-html/renderer.ts"],"sourcesContent":["/** Round numeric arbitrary values only; preserve selectors, URLs and custom names. */\nexport function formatTailwindClasses(className: string): string {\n return className.replace(\n /\\[((?:[a-z-]+:)?)([+-]?(?:\\d+\\.\\d+|\\.\\d+))([a-z%]*)\\]/gi,\n (_match, hint: string, value: string, unit: string) =>\n `[${hint}${Number(Number(value).toFixed(2))}${unit}]`,\n )\n}\n","const entities: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n}\n\nexport function escapeHTML(value: string): string {\n return value.replace(/[&<>\"']/g, character => entities[character]!)\n}\n","import { formatTailwindClasses } from './classes.js'\nimport { escapeHTML } from './escape.js'\nimport type { HTMLNode } from './types.js'\n\nfunction cssProperty(name: string): string {\n if (name.startsWith('--')) return name\n const kebab = name.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)\n return kebab.startsWith('ms-') ? `-${kebab}` : kebab\n}\n\nexport function serializeStyle(style: HTMLNode['style']): string {\n return Object.entries(style)\n .map(([property, value]) => `${cssProperty(property)}:${value}`)\n .join(';')\n}\n\nexport function renderAttributes(node: HTMLNode, tailwind: boolean): string {\n const attributes: [string, string][] = [\n ['data-figma-id', node.source.id],\n ['data-figma-name', node.source.name],\n ['data-figma-type', node.source.type],\n ]\n if (tailwind && 'className' in node && node.className)\n attributes.push(['class', formatTailwindClasses(node.className)])\n const style = serializeStyle(node.visible ? node.style : { ...node.style, display: 'none' })\n if (style) attributes.push(['style', style])\n const serialized = attributes.map(([name, value]) => ` ${name}=\"${escapeHTML(value)}\"`).join('')\n return node.visible ? serialized : `${serialized} hidden`\n}\n","import type { HTMLInput, HTMLNode } from './types.js'\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction validateNode(value: unknown, tailwind: boolean): asserts value is HTMLNode {\n if (\n !isRecord(value) ||\n !isRecord(value.source) ||\n typeof value.source.id !== 'string' ||\n typeof value.source.name !== 'string' ||\n typeof value.source.type !== 'string' ||\n typeof value.visible !== 'boolean' ||\n !isRecord(value.style) ||\n !Array.isArray(value.children)\n ) {\n throw new TypeError('Expected an IR node with source, visible, style and children')\n }\n if (\n value.text !== undefined &&\n (!isRecord(value.text) || typeof value.text.characters !== 'string')\n ) {\n throw new TypeError(`Invalid text in IR node ${value.source.id}`)\n }\n if (tailwind && typeof value.className !== 'string') {\n throw new TypeError(`Missing className in Tailwind node ${value.source.id}`)\n }\n for (const [property, cssValue] of Object.entries(value.style)) {\n if (!/^(?:--[\\w-]+|[a-zA-Z][a-zA-Z0-9-]*)$/.test(property)) {\n throw new TypeError(`Invalid CSS property in IR node ${value.source.id}`)\n }\n if (\n typeof cssValue !== 'string' &&\n (typeof cssValue !== 'number' || !Number.isFinite(cssValue))\n ) {\n throw new TypeError(`Invalid CSS value in IR node ${value.source.id}`)\n }\n }\n}\n\nexport function assertHTMLInput(input: unknown): asserts input is HTMLInput {\n if (\n !isRecord(input) ||\n input.schemaVersion !== 1 ||\n !Array.isArray(input.diagnostics) ||\n (input.target !== undefined && input.target !== 'tailwind-v4')\n ) {\n throw new TypeError('Expected a standard IR document or Tailwind v4 IR (schemaVersion 1)')\n }\n const tailwind = input.target === 'tailwind-v4'\n const pending: unknown[] = [input.root]\n const seen = new Set<unknown>()\n while (pending.length) {\n const current = pending.pop()\n if (seen.has(current)) throw new TypeError('IR children must be an acyclic tree')\n seen.add(current)\n validateNode(current, tailwind)\n for (const child of current.children) pending.push(child)\n }\n}\n","import { renderAttributes } from './attributes.js'\nimport { formatTailwindClasses } from './classes.js'\nimport { escapeHTML } from './escape.js'\nimport type { HTMLInput, HTMLNode } from './types.js'\nimport { assertHTMLInput } from './validate.js'\n\n/** One neutral div per IR node; no inferred interactions or semantic controls. */\nexport function renderHTML(input: HTMLInput): string {\n assertHTMLInput(input)\n const tailwind = 'target' in input && input.target === 'tailwind-v4'\n const output: string[] = []\n const pending: (HTMLNode | string)[] = [input.root]\n while (pending.length) {\n const current = pending.pop()!\n if (typeof current === 'string') {\n output.push(current)\n continue\n }\n output.push(`<div${renderAttributes(current, tailwind)}>`)\n if (current.text) output.push(escapeHTML(current.text.characters))\n pending.push('</div>')\n for (let index = current.children.length - 1; index >= 0; index--)\n pending.push(current.children[index]!)\n }\n return output.join('')\n}\n\nexport function collectTailwindClasses(input: HTMLInput): string[] {\n assertHTMLInput(input)\n if (!('target' in input) || input.target !== 'tailwind-v4') return []\n const classes = new Set<string>()\n const pending: HTMLNode[] = [input.root]\n while (pending.length) {\n const node = pending.pop()!\n if ('className' in node) {\n for (const name of formatTailwindClasses(node.className).split(/\\s+/).filter(Boolean))\n classes.add(name)\n }\n for (const child of node.children) pending.push(child)\n }\n return [...classes].sort()\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
function e(e) {
|
|
2
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(e)) throw TypeError("Expected a non-empty Figma file key");
|
|
3
|
+
return new URL(`https://api.figma.com/v1/files/${encodeURIComponent(e)}`);
|
|
4
|
+
}
|
|
5
|
+
function t(t, n) {
|
|
6
|
+
if (!n.length || n.some((e) => !/^I?\d+:\d+(?:;I?\d+:\d+)*$/.test(e))) throw TypeError("Expected API node IDs such as 49:7390 or I49:7403;38:5243");
|
|
7
|
+
let r = e(t);
|
|
8
|
+
return r.pathname += "/nodes", r.searchParams.set("ids", [...new Set(n)].join(",")), r;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/figma-json-fetch/errors.ts
|
|
12
|
+
var n = class extends Error {
|
|
13
|
+
status;
|
|
14
|
+
retryAfter;
|
|
15
|
+
name = "FigmaHTTPError";
|
|
16
|
+
constructor(e, t) {
|
|
17
|
+
super(`Figma API request failed (HTTP ${e})${t ? `; Retry-After: ${t}` : ""}`), this.status = e, this.retryAfter = t;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/figma-json-fetch/request.ts
|
|
22
|
+
async function r(e, t, r, i) {
|
|
23
|
+
let a = await e(t, {
|
|
24
|
+
headers: {
|
|
25
|
+
"X-Figma-Token": r,
|
|
26
|
+
Accept: "application/json"
|
|
27
|
+
},
|
|
28
|
+
signal: i.signal,
|
|
29
|
+
redirect: "error"
|
|
30
|
+
});
|
|
31
|
+
if (!a.ok) throw new n(a.status, a.headers.get("retry-after"));
|
|
32
|
+
return await a.json();
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/figma-json-fetch/client.ts
|
|
36
|
+
var i = class {
|
|
37
|
+
#e;
|
|
38
|
+
#t;
|
|
39
|
+
constructor(e) {
|
|
40
|
+
if (typeof e.token != "string" || !e.token.trim()) throw TypeError("A Figma token is required");
|
|
41
|
+
this.#e = e.token, this.#t = e.fetch ?? globalThis.fetch.bind(globalThis);
|
|
42
|
+
}
|
|
43
|
+
getFile(t, n = {}) {
|
|
44
|
+
return r(this.#t, e(t), this.#e, n);
|
|
45
|
+
}
|
|
46
|
+
getNodes(e, n, i = {}) {
|
|
47
|
+
return r(this.#t, t(e, n), this.#e, i);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
export { i as FigmaClient, n as FigmaHTTPError };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=figma-json-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"figma-json-fetch.js","names":[],"sources":["../src/figma-json-fetch/endpoints.ts","../src/figma-json-fetch/errors.ts","../src/figma-json-fetch/request.ts","../src/figma-json-fetch/client.ts"],"sourcesContent":["const API_URL = 'https://api.figma.com/v1'\n\nexport function fileEndpoint(fileKey: string): URL {\n if (!/^[a-zA-Z0-9_-]+$/.test(fileKey)) throw new TypeError('Expected a non-empty Figma file key')\n return new URL(`${API_URL}/files/${encodeURIComponent(fileKey)}`)\n}\n\nexport function nodesEndpoint(fileKey: string, nodeIds: readonly string[]): URL {\n if (!nodeIds.length || nodeIds.some(id => !/^I?\\d+:\\d+(?:;I?\\d+:\\d+)*$/.test(id)))\n throw new TypeError('Expected API node IDs such as 49:7390 or I49:7403;38:5243')\n const url = fileEndpoint(fileKey)\n url.pathname += '/nodes'\n url.searchParams.set('ids', [...new Set(nodeIds)].join(','))\n return url\n}\n","export class FigmaHTTPError extends Error {\n override name = 'FigmaHTTPError'\n constructor(\n readonly status: number,\n readonly retryAfter: string | null,\n ) {\n super(\n `Figma API request failed (HTTP ${status})${retryAfter ? `; Retry-After: ${retryAfter}` : ''}`,\n )\n }\n}\n","import { FigmaHTTPError } from './errors.js'\nimport type { RequestOptions } from './types.js'\n\nexport async function requestJSON<T>(\n fetcher: typeof globalThis.fetch,\n url: URL,\n token: string,\n options: RequestOptions,\n): Promise<T> {\n const response = await fetcher(url, {\n headers: { 'X-Figma-Token': token, Accept: 'application/json' },\n signal: options.signal,\n redirect: 'error',\n })\n if (!response.ok) throw new FigmaHTTPError(response.status, response.headers.get('retry-after'))\n return (await response.json()) as T\n}\n","import { fileEndpoint, nodesEndpoint } from './endpoints.js'\nimport { requestJSON } from './request.js'\nimport type {\n FigmaClientOptions,\n GetFileNodesResponse,\n GetFileResponse,\n RequestOptions,\n} from './types.js'\n\nexport class FigmaClient {\n readonly #token: string\n readonly #fetch: typeof globalThis.fetch\n constructor(options: FigmaClientOptions) {\n if (typeof options.token !== 'string' || !options.token.trim())\n throw new TypeError('A Figma token is required')\n this.#token = options.token\n this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis)\n }\n getFile(fileKey: string, options: RequestOptions = {}): Promise<GetFileResponse> {\n return requestJSON(this.#fetch, fileEndpoint(fileKey), this.#token, options)\n }\n getNodes(\n fileKey: string,\n nodeIds: readonly string[],\n options: RequestOptions = {},\n ): Promise<GetFileNodesResponse> {\n return requestJSON(this.#fetch, nodesEndpoint(fileKey, nodeIds), this.#token, options)\n }\n}\n"],"mappings":"AAEA,SAAgB,EAAa,GAAsB;CACjD,IAAI,CAAC,mBAAmB,KAAK,CAAO,GAAG,MAAU,UAAU,qCAAqC;CAChG,OAAO,IAAI,IAAI,kCAAoB,mBAAmB,CAAO,GAAG;AAClE;AAEA,SAAgB,EAAc,GAAiB,GAAiC;CAC9E,IAAI,CAAC,EAAQ,UAAU,EAAQ,MAAK,MAAM,CAAC,6BAA6B,KAAK,CAAE,CAAC,GAC9E,MAAU,UAAU,2DAA2D;CACjF,IAAM,IAAM,EAAa,CAAO;CAGhC,OAFA,EAAI,YAAY,UAChB,EAAI,aAAa,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GACpD;AACT;;;ACdA,IAAa,IAAb,cAAoC,MAAM;CAG7B;CACA;CAHX,OAAgB;CAChB,YACE,GACA,GACA;EADS,AAET,MACE,kCAAkC,EAAO,GAAG,IAAa,kBAAkB,MAAe,IAC5F,GALS,KAAA,SAAA,GACA,KAAA,aAAA;CAKX;AACF;;;ACPA,eAAsB,EACpB,GACA,GACA,GACA,GACY;CACZ,IAAM,IAAW,MAAM,EAAQ,GAAK;EAClC,SAAS;GAAE,iBAAiB;GAAO,QAAQ;EAAmB;EAC9D,QAAQ,EAAQ;EAChB,UAAU;CACZ,CAAC;CACD,IAAI,CAAC,EAAS,IAAI,MAAM,IAAI,EAAe,EAAS,QAAQ,EAAS,QAAQ,IAAI,aAAa,CAAC;CAC/F,OAAQ,MAAM,EAAS,KAAK;AAC9B;;;ACPA,IAAa,IAAb,MAAyB;CACvB;CACA;CACA,YAAY,GAA6B;EACvC,IAAI,OAAO,EAAQ,SAAU,YAAY,CAAC,EAAQ,MAAM,KAAK,GAC3D,MAAU,UAAU,2BAA2B;EAEjD,AADA,KAAK,KAAS,EAAQ,OACtB,KAAK,KAAS,EAAQ,SAAS,WAAW,MAAM,KAAK,UAAU;CACjE;CACA,QAAQ,GAAiB,IAA0B,CAAC,GAA6B;EAC/E,OAAO,EAAY,KAAK,IAAQ,EAAa,CAAO,GAAG,KAAK,IAAQ,CAAO;CAC7E;CACA,SACE,GACA,GACA,IAA0B,CAAC,GACI;EAC/B,OAAO,EAAY,KAAK,IAAQ,EAAc,GAAS,CAAO,GAAG,KAAK,IAAQ,CAAO;CACvF;AACF"}
|