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.
Files changed (97) hide show
  1. package/README.md +172 -0
  2. package/dist/cli/index.js +1138 -0
  3. package/dist/cli/index.js.map +1 -0
  4. package/dist/figma-html.js +95 -0
  5. package/dist/figma-html.js.map +1 -0
  6. package/dist/figma-json-fetch.js +53 -0
  7. package/dist/figma-json-fetch.js.map +1 -0
  8. package/dist/figma-tree.js +808 -0
  9. package/dist/figma-tree.js.map +1 -0
  10. package/dist/types/cli/args.d.ts +27 -0
  11. package/dist/types/cli/args.d.ts.map +1 -0
  12. package/dist/types/cli/commands/download.d.ts +4 -0
  13. package/dist/types/cli/commands/download.d.ts.map +1 -0
  14. package/dist/types/cli/commands/export.d.ts +3 -0
  15. package/dist/types/cli/commands/export.d.ts.map +1 -0
  16. package/dist/types/cli/commands/query.d.ts +3 -0
  17. package/dist/types/cli/commands/query.d.ts.map +1 -0
  18. package/dist/types/cli/env.d.ts +2 -0
  19. package/dist/types/cli/env.d.ts.map +1 -0
  20. package/dist/types/cli/html/input.d.ts +5 -0
  21. package/dist/types/cli/html/input.d.ts.map +1 -0
  22. package/dist/types/cli/index.d.ts +3 -0
  23. package/dist/types/cli/index.d.ts.map +1 -0
  24. package/dist/types/cli/output.d.ts +4 -0
  25. package/dist/types/cli/output.d.ts.map +1 -0
  26. package/dist/types/cli/run.d.ts +8 -0
  27. package/dist/types/cli/run.d.ts.map +1 -0
  28. package/dist/types/figma-html/attributes.d.ts +4 -0
  29. package/dist/types/figma-html/attributes.d.ts.map +1 -0
  30. package/dist/types/figma-html/classes.d.ts +3 -0
  31. package/dist/types/figma-html/classes.d.ts.map +1 -0
  32. package/dist/types/figma-html/escape.d.ts +2 -0
  33. package/dist/types/figma-html/escape.d.ts.map +1 -0
  34. package/dist/types/figma-html/index.d.ts +3 -0
  35. package/dist/types/figma-html/index.d.ts.map +1 -0
  36. package/dist/types/figma-html/renderer.d.ts +5 -0
  37. package/dist/types/figma-html/renderer.d.ts.map +1 -0
  38. package/dist/types/figma-html/types.d.ts +4 -0
  39. package/dist/types/figma-html/types.d.ts.map +1 -0
  40. package/dist/types/figma-html/validate.d.ts +3 -0
  41. package/dist/types/figma-html/validate.d.ts.map +1 -0
  42. package/dist/types/figma-json-fetch/client.d.ts +8 -0
  43. package/dist/types/figma-json-fetch/client.d.ts.map +1 -0
  44. package/dist/types/figma-json-fetch/endpoints.d.ts +3 -0
  45. package/dist/types/figma-json-fetch/endpoints.d.ts.map +1 -0
  46. package/dist/types/figma-json-fetch/errors.d.ts +7 -0
  47. package/dist/types/figma-json-fetch/errors.d.ts.map +1 -0
  48. package/dist/types/figma-json-fetch/index.d.ts +4 -0
  49. package/dist/types/figma-json-fetch/index.d.ts.map +1 -0
  50. package/dist/types/figma-json-fetch/request.d.ts +3 -0
  51. package/dist/types/figma-json-fetch/request.d.ts.map +1 -0
  52. package/dist/types/figma-json-fetch/types.d.ts +9 -0
  53. package/dist/types/figma-json-fetch/types.d.ts.map +1 -0
  54. package/dist/types/figma-tree/index.d.ts +10 -0
  55. package/dist/types/figma-tree/index.d.ts.map +1 -0
  56. package/dist/types/figma-tree/input/normalize.d.ts +11 -0
  57. package/dist/types/figma-tree/input/normalize.d.ts.map +1 -0
  58. package/dist/types/figma-tree/ir/converters/diagnostics.d.ts +3 -0
  59. package/dist/types/figma-tree/ir/converters/diagnostics.d.ts.map +1 -0
  60. package/dist/types/figma-tree/ir/converters/layout.d.ts +4 -0
  61. package/dist/types/figma-tree/ir/converters/layout.d.ts.map +1 -0
  62. package/dist/types/figma-tree/ir/converters/style.d.ts +6 -0
  63. package/dist/types/figma-tree/ir/converters/style.d.ts.map +1 -0
  64. package/dist/types/figma-tree/ir/converters/text.d.ts +4 -0
  65. package/dist/types/figma-tree/ir/converters/text.d.ts.map +1 -0
  66. package/dist/types/figma-tree/ir/model/document.d.ts +12 -0
  67. package/dist/types/figma-tree/ir/model/document.d.ts.map +1 -0
  68. package/dist/types/figma-tree/ir/model/types.d.ts +61 -0
  69. package/dist/types/figma-tree/ir/model/types.d.ts.map +1 -0
  70. package/dist/types/figma-tree/ir/pipeline.d.ts +5 -0
  71. package/dist/types/figma-tree/ir/pipeline.d.ts.map +1 -0
  72. package/dist/types/figma-tree/ir/plugins/run.d.ts +11 -0
  73. package/dist/types/figma-tree/ir/plugins/run.d.ts.map +1 -0
  74. package/dist/types/figma-tree/model/types.d.ts +30 -0
  75. package/dist/types/figma-tree/model/types.d.ts.map +1 -0
  76. package/dist/types/figma-tree/model/value.d.ts +4 -0
  77. package/dist/types/figma-tree/model/value.d.ts.map +1 -0
  78. package/dist/types/figma-tree/selector/match.d.ts +4 -0
  79. package/dist/types/figma-tree/selector/match.d.ts.map +1 -0
  80. package/dist/types/figma-tree/selector/parser.d.ts +3 -0
  81. package/dist/types/figma-tree/selector/parser.d.ts.map +1 -0
  82. package/dist/types/figma-tree/selector/types.d.ts +24 -0
  83. package/dist/types/figma-tree/selector/types.d.ts.map +1 -0
  84. package/dist/types/figma-tree/tailwind/convert.d.ts +3 -0
  85. package/dist/types/figma-tree/tailwind/convert.d.ts.map +1 -0
  86. package/dist/types/figma-tree/tailwind/style.d.ts +6 -0
  87. package/dist/types/figma-tree/tailwind/style.d.ts.map +1 -0
  88. package/dist/types/figma-tree/tree/figma-tree.d.ts +39 -0
  89. package/dist/types/figma-tree/tree/figma-tree.d.ts.map +1 -0
  90. package/dist/types/figma-tree/tree/records.d.ts +5 -0
  91. package/dist/types/figma-tree/tree/records.d.ts.map +1 -0
  92. package/docs/api.md +89 -0
  93. package/docs/architecture.md +70 -0
  94. package/docs/html.md +90 -0
  95. package/docs/ir.md +84 -0
  96. package/docs/verification.md +33 -0
  97. package/package.json +57 -0
@@ -0,0 +1,1138 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs as e } from "node:util";
3
+ import { readFile as t, writeFile as n } from "node:fs/promises";
4
+ //#region src/figma-json-fetch/endpoints.ts
5
+ var r = "https://api.figma.com/v1";
6
+ function i(e) {
7
+ if (!/^[a-zA-Z0-9_-]+$/.test(e)) throw TypeError("Expected a non-empty Figma file key");
8
+ return new URL(`${r}/files/${encodeURIComponent(e)}`);
9
+ }
10
+ function a(e, t) {
11
+ if (!t.length || t.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");
12
+ let n = i(e);
13
+ return n.pathname += "/nodes", n.searchParams.set("ids", [...new Set(t)].join(",")), n;
14
+ }
15
+ //#endregion
16
+ //#region src/figma-json-fetch/errors.ts
17
+ var o = class extends Error {
18
+ status;
19
+ retryAfter;
20
+ name = "FigmaHTTPError";
21
+ constructor(e, t) {
22
+ super(`Figma API request failed (HTTP ${e})${t ? `; Retry-After: ${t}` : ""}`), this.status = e, this.retryAfter = t;
23
+ }
24
+ };
25
+ //#endregion
26
+ //#region src/figma-json-fetch/request.ts
27
+ async function s(e, t, n, r) {
28
+ let i = await e(t, {
29
+ headers: {
30
+ "X-Figma-Token": n,
31
+ Accept: "application/json"
32
+ },
33
+ signal: r.signal,
34
+ redirect: "error"
35
+ });
36
+ if (!i.ok) throw new o(i.status, i.headers.get("retry-after"));
37
+ return await i.json();
38
+ }
39
+ //#endregion
40
+ //#region src/figma-json-fetch/client.ts
41
+ var c = class {
42
+ #e;
43
+ #t;
44
+ constructor(e) {
45
+ if (typeof e.token != "string" || !e.token.trim()) throw TypeError("A Figma token is required");
46
+ this.#e = e.token, this.#t = e.fetch ?? globalThis.fetch.bind(globalThis);
47
+ }
48
+ getFile(e, t = {}) {
49
+ return s(this.#t, i(e), this.#e, t);
50
+ }
51
+ getNodes(e, t, n = {}) {
52
+ return s(this.#t, a(e, t), this.#e, n);
53
+ }
54
+ }, l = "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", u = {
55
+ out: { type: "string" },
56
+ force: {
57
+ type: "boolean",
58
+ default: !1
59
+ },
60
+ help: {
61
+ type: "boolean",
62
+ short: "h"
63
+ }
64
+ };
65
+ function d(e) {
66
+ if (e !== void 0 && !e.trim()) throw Error("--out must be a non-empty path");
67
+ }
68
+ function ee(t) {
69
+ let { values: n } = e({
70
+ args: t,
71
+ strict: !0,
72
+ options: {
73
+ ...u,
74
+ file: { type: "string" },
75
+ nodes: { type: "string" }
76
+ }
77
+ });
78
+ if (n.help) return "help";
79
+ if (!n.file) throw Error("--file FILE_KEY is required");
80
+ if (n.nodes !== void 0 && !n.nodes.trim()) throw Error("--nodes must contain at least one node ID");
81
+ return d(n.out), {
82
+ command: "download",
83
+ fileKey: n.file,
84
+ nodeIds: n.nodes?.split(",").map((e) => e.trim()),
85
+ out: n.out,
86
+ force: n.force
87
+ };
88
+ }
89
+ function te(e, t, n) {
90
+ if (e !== void 0 == (t !== void 0)) throw Error("Provide exactly one of --selector or --name-regex");
91
+ if (e !== void 0) {
92
+ if (n !== void 0) throw Error("--regex-flags requires --name-regex");
93
+ if (!e.trim()) throw Error("--selector must be non-empty");
94
+ return e;
95
+ }
96
+ return { name: new RegExp(t, n) };
97
+ }
98
+ function f(t, n) {
99
+ let { values: r } = e({
100
+ args: n,
101
+ strict: !0,
102
+ options: {
103
+ ...u,
104
+ input: { type: "string" },
105
+ selector: { type: "string" },
106
+ "name-regex": { type: "string" },
107
+ "regex-flags": { type: "string" }
108
+ }
109
+ });
110
+ if (r.help) return "help";
111
+ if (!r.input?.trim()) throw Error("--input must specify a local JSON file");
112
+ return d(r.out), {
113
+ command: t,
114
+ input: r.input,
115
+ selector: te(r.selector, r["name-regex"], r["regex-flags"]),
116
+ out: r.out,
117
+ force: r.force
118
+ };
119
+ }
120
+ function ne(t) {
121
+ let { values: n } = e({
122
+ args: t,
123
+ strict: !0,
124
+ options: {
125
+ ...u,
126
+ input: { type: "string" },
127
+ selector: { type: "string" },
128
+ format: {
129
+ type: "string",
130
+ default: "html"
131
+ },
132
+ styles: { type: "string" }
133
+ }
134
+ });
135
+ if (n.help) return "help";
136
+ if (!n.input?.trim()) throw Error("--input must specify a local JSON file");
137
+ if (n.format !== "html") throw Error("--format currently supports only html");
138
+ if (n.styles !== void 0 && n.styles !== "inline" && n.styles !== "tailwind") throw Error("--styles must be inline or tailwind");
139
+ if (n.selector !== void 0 && !n.selector.trim()) throw Error("--selector must be non-empty");
140
+ return d(n.out), {
141
+ command: "export",
142
+ input: n.input,
143
+ selector: n.selector,
144
+ styles: n.styles,
145
+ out: n.out,
146
+ force: n.force
147
+ };
148
+ }
149
+ function re(e) {
150
+ let [t, ...n] = e;
151
+ if (e.length === 1 && (t === "--help" || t === "-h")) return "help";
152
+ if (t === "download") return ee(n);
153
+ if (t === "query" || t === "queryAll") return f(t, n);
154
+ if (t === "export") return ne(n);
155
+ throw Error("Expected download, query, queryAll or export; use --help");
156
+ }
157
+ //#endregion
158
+ //#region src/cli/output.ts
159
+ async function p(e, t, n, r) {
160
+ await m(`${JSON.stringify(e, null, 2)}\n`, t, n, r);
161
+ }
162
+ async function m(e, t, r, i) {
163
+ t ? await n(t, e, { flag: r ? "w" : "wx" }) : i(e);
164
+ }
165
+ function ie(e, t) {
166
+ let n = e instanceof Error ? e.message : "Unknown error";
167
+ return t ? n.split(t).join("[redacted]") : n;
168
+ }
169
+ //#endregion
170
+ //#region src/cli/commands/download.ts
171
+ async function ae(e, t, n) {
172
+ await p(e.nodeIds ? await t.getNodes(e.fileKey, e.nodeIds) : await t.getFile(e.fileKey), e.out, e.force, n);
173
+ }
174
+ //#endregion
175
+ //#region src/figma-html/classes.ts
176
+ function oe(e) {
177
+ return e.replace(/\[((?:[a-z-]+:)?)([+-]?(?:\d+\.\d+|\.\d+))([a-z%]*)\]/gi, (e, t, n, r) => `[${t}${Number(Number(n).toFixed(2))}${r}]`);
178
+ }
179
+ //#endregion
180
+ //#region src/figma-html/escape.ts
181
+ var h = {
182
+ "&": "&",
183
+ "<": "&lt;",
184
+ ">": "&gt;",
185
+ "\"": "&quot;",
186
+ "'": "&#39;"
187
+ };
188
+ function g(e) {
189
+ return e.replace(/[&<>"']/g, (e) => h[e]);
190
+ }
191
+ //#endregion
192
+ //#region src/figma-html/attributes.ts
193
+ function _(e) {
194
+ if (e.startsWith("--")) return e;
195
+ let t = e.replace(/[A-Z]/g, (e) => `-${e.toLowerCase()}`);
196
+ return t.startsWith("ms-") ? `-${t}` : t;
197
+ }
198
+ function v(e) {
199
+ return Object.entries(e).map(([e, t]) => `${_(e)}:${t}`).join(";");
200
+ }
201
+ function y(e, t) {
202
+ let n = [
203
+ ["data-figma-id", e.source.id],
204
+ ["data-figma-name", e.source.name],
205
+ ["data-figma-type", e.source.type]
206
+ ];
207
+ t && "className" in e && e.className && n.push(["class", oe(e.className)]);
208
+ let r = v(e.visible ? e.style : {
209
+ ...e.style,
210
+ display: "none"
211
+ });
212
+ r && n.push(["style", r]);
213
+ let i = n.map(([e, t]) => ` ${e}="${g(t)}"`).join("");
214
+ return e.visible ? i : `${i} hidden`;
215
+ }
216
+ //#endregion
217
+ //#region src/figma-html/validate.ts
218
+ function b(e) {
219
+ return typeof e == "object" && !!e && !Array.isArray(e);
220
+ }
221
+ function se(e, t) {
222
+ if (!b(e) || !b(e.source) || typeof e.source.id != "string" || typeof e.source.name != "string" || typeof e.source.type != "string" || typeof e.visible != "boolean" || !b(e.style) || !Array.isArray(e.children)) throw TypeError("Expected an IR node with source, visible, style and children");
223
+ if (e.text !== void 0 && (!b(e.text) || typeof e.text.characters != "string")) throw TypeError(`Invalid text in IR node ${e.source.id}`);
224
+ if (t && typeof e.className != "string") throw TypeError(`Missing className in Tailwind node ${e.source.id}`);
225
+ for (let [t, n] of Object.entries(e.style)) {
226
+ if (!/^(?:--[\w-]+|[a-zA-Z][a-zA-Z0-9-]*)$/.test(t)) throw TypeError(`Invalid CSS property in IR node ${e.source.id}`);
227
+ if (typeof n != "string" && (typeof n != "number" || !Number.isFinite(n))) throw TypeError(`Invalid CSS value in IR node ${e.source.id}`);
228
+ }
229
+ }
230
+ function x(e) {
231
+ if (!b(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)");
232
+ let t = e.target === "tailwind-v4", n = [e.root], r = /* @__PURE__ */ new Set();
233
+ for (; n.length;) {
234
+ let e = n.pop();
235
+ if (r.has(e)) throw TypeError("IR children must be an acyclic tree");
236
+ r.add(e), se(e, t);
237
+ for (let t of e.children) n.push(t);
238
+ }
239
+ }
240
+ //#endregion
241
+ //#region src/figma-html/renderer.ts
242
+ function ce(e) {
243
+ x(e);
244
+ let t = "target" in e && e.target === "tailwind-v4", n = [], r = [e.root];
245
+ for (; r.length;) {
246
+ let e = r.pop();
247
+ if (typeof e == "string") {
248
+ n.push(e);
249
+ continue;
250
+ }
251
+ n.push(`<div${y(e, t)}>`), e.text && n.push(g(e.text.characters)), r.push("</div>");
252
+ for (let t = e.children.length - 1; t >= 0; t--) r.push(e.children[t]);
253
+ }
254
+ return n.join("");
255
+ }
256
+ //#endregion
257
+ //#region src/figma-tree/model/value.ts
258
+ function S(e) {
259
+ return typeof e == "object" && !!e && !Array.isArray(e);
260
+ }
261
+ function C(e) {
262
+ let t = [e], n = /* @__PURE__ */ new WeakSet();
263
+ for (; t.length;) {
264
+ let e = t.pop();
265
+ if (typeof e == "object" && e && !n.has(e)) {
266
+ n.add(e);
267
+ for (let n of Object.values(e)) t.push(n);
268
+ Object.freeze(e);
269
+ }
270
+ }
271
+ return e;
272
+ }
273
+ function w(e) {
274
+ return typeof e == "number" && Number.isFinite(e) ? e : void 0;
275
+ }
276
+ //#endregion
277
+ //#region src/figma-tree/input/normalize.ts
278
+ var T = class extends Error {
279
+ name = "FigmaInputError";
280
+ };
281
+ function E(e, t) {
282
+ if (S(t)) for (let [n, r] of Object.entries(t)) S(r) && e.set(n, r);
283
+ }
284
+ function le(e) {
285
+ let t = [...e], n = /* @__PURE__ */ new WeakSet();
286
+ for (; t.length;) {
287
+ let e = t.pop();
288
+ if (!S(e) || typeof e.id != "string" || typeof e.name != "string" || typeof e.type != "string") throw new T("Each node must contain string id, name and type fields");
289
+ if (n.has(e)) throw new T("Input must be a JSON tree without cyclic or shared node objects");
290
+ if (n.add(e), e.children !== void 0) {
291
+ if (!Array.isArray(e.children)) throw new T(`Invalid children for node ${e.id}`);
292
+ for (let n of e.children) t.push(n);
293
+ }
294
+ }
295
+ }
296
+ function D(e) {
297
+ if (!S(e)) throw new T("Expected a parsed Figma response or node object");
298
+ let t = /* @__PURE__ */ new Map(), n = [], r = [];
299
+ if (E(t, e.components), typeof e.type == "string") r.push(e);
300
+ else if (S(e.document)) r.push(e.document);
301
+ else if (S(e.nodes)) for (let [i, a] of Object.entries(e.nodes)) if (a === null) n.push({
302
+ code: "missing-node",
303
+ nodeId: i,
304
+ message: "Figma returned null for this node"
305
+ });
306
+ else if (S(a) && S(a.document)) r.push(a.document), E(t, a.components);
307
+ else throw new T(`Invalid node response entry: ${i}`);
308
+ else throw new T("Expected document, nodes, or a single node");
309
+ le(r);
310
+ try {
311
+ let e = structuredClone({
312
+ roots: r,
313
+ components: t,
314
+ diagnostics: n
315
+ });
316
+ C(e.roots);
317
+ for (let t of e.components.values()) C(t);
318
+ return e;
319
+ } catch (e) {
320
+ throw new T(`Input must be structured-cloneable JSON: ${e instanceof Error ? e.message : "invalid value"}`);
321
+ }
322
+ }
323
+ //#endregion
324
+ //#region src/figma-tree/tailwind/style.ts
325
+ var O = {
326
+ display: {
327
+ flex: "flex",
328
+ none: "hidden"
329
+ },
330
+ position: {
331
+ relative: "relative",
332
+ absolute: "absolute"
333
+ },
334
+ boxSizing: { "border-box": "box-border" },
335
+ flexDirection: {
336
+ row: "flex-row",
337
+ column: "flex-col"
338
+ },
339
+ flexWrap: { wrap: "flex-wrap" },
340
+ justifyContent: {
341
+ "flex-start": "justify-start",
342
+ "flex-end": "justify-end",
343
+ center: "justify-center",
344
+ "space-between": "justify-between"
345
+ },
346
+ alignItems: {
347
+ "flex-start": "items-start",
348
+ "flex-end": "items-end",
349
+ center: "items-center",
350
+ baseline: "items-baseline"
351
+ },
352
+ alignContent: { "space-between": "content-between" },
353
+ alignSelf: { stretch: "self-stretch" },
354
+ overflow: { hidden: "overflow-hidden" },
355
+ borderStyle: { solid: "border-solid" },
356
+ textAlign: {
357
+ left: "text-left",
358
+ right: "text-right",
359
+ center: "text-center",
360
+ justify: "text-justify"
361
+ },
362
+ fontStyle: { italic: "italic" },
363
+ textDecorationLine: {
364
+ underline: "underline",
365
+ "line-through": "line-through"
366
+ },
367
+ textTransform: {
368
+ uppercase: "uppercase",
369
+ lowercase: "lowercase",
370
+ capitalize: "capitalize"
371
+ },
372
+ whiteSpace: { "pre-wrap": "whitespace-pre-wrap" },
373
+ flexGrow: { 1: "grow" },
374
+ flexShrink: { 0: "shrink-0" },
375
+ width: {
376
+ "100%": "w-full",
377
+ "fit-content": "w-fit"
378
+ },
379
+ height: {
380
+ "100%": "h-full",
381
+ "fit-content": "h-fit"
382
+ }
383
+ }, k = {
384
+ width: "w",
385
+ height: "h",
386
+ minWidth: "min-w",
387
+ maxWidth: "max-w",
388
+ minHeight: "min-h",
389
+ maxHeight: "max-h",
390
+ left: "left",
391
+ top: "top",
392
+ gap: "gap",
393
+ rowGap: "gap-y",
394
+ columnGap: "gap-x",
395
+ paddingTop: "pt",
396
+ paddingRight: "pr",
397
+ paddingBottom: "pb",
398
+ paddingLeft: "pl",
399
+ borderRadius: "rounded",
400
+ borderTopLeftRadius: "rounded-tl",
401
+ borderTopRightRadius: "rounded-tr",
402
+ borderBottomLeftRadius: "rounded-bl",
403
+ borderBottomRightRadius: "rounded-br",
404
+ borderWidth: "border",
405
+ borderTopWidth: "border-t",
406
+ borderRightWidth: "border-r",
407
+ borderBottomWidth: "border-b",
408
+ borderLeftWidth: "border-l",
409
+ backgroundColor: "bg",
410
+ color: "text",
411
+ borderColor: "border",
412
+ fontSize: "text",
413
+ fontWeight: "font",
414
+ lineHeight: "leading",
415
+ letterSpacing: "tracking",
416
+ opacity: "opacity",
417
+ flexBasis: "basis"
418
+ };
419
+ function A(e, t) {
420
+ let n = k[e], r = String(t);
421
+ if (n && !/[\s[\]_'"\\;]/.test(r)) return `${n}-[${e === "fontSize" ? "length:" : e === "fontWeight" ? "number:" : ""}${r}]`;
422
+ }
423
+ function j(e) {
424
+ let t = [], n = {};
425
+ for (let [r, i] of Object.entries(e)) {
426
+ let e = O[r]?.[String(i)] ?? A(r, i);
427
+ e ? t.push(e) : n[r] = i;
428
+ }
429
+ return {
430
+ className: [...new Set(t)].join(" "),
431
+ style: n
432
+ };
433
+ }
434
+ //#endregion
435
+ //#region src/figma-tree/tailwind/convert.ts
436
+ function M(e) {
437
+ let t = structuredClone(e), n = [], r = [t.root];
438
+ for (; r.length;) {
439
+ let e = r.pop();
440
+ n.push(e);
441
+ for (let t of e.children) r.push(t);
442
+ }
443
+ let i = /* @__PURE__ */ new Map();
444
+ for (let e of n.reverse()) i.set(e, {
445
+ ...e,
446
+ ...j(e.style),
447
+ children: e.children.map((e) => i.get(e))
448
+ });
449
+ return {
450
+ schemaVersion: 1,
451
+ target: "tailwind-v4",
452
+ root: i.get(t.root),
453
+ diagnostics: t.diagnostics
454
+ };
455
+ }
456
+ //#endregion
457
+ //#region src/figma-tree/ir/model/document.ts
458
+ var N = class {
459
+ #e;
460
+ constructor(e) {
461
+ this.#e = C(structuredClone(e));
462
+ }
463
+ get schemaVersion() {
464
+ return 1;
465
+ }
466
+ get root() {
467
+ return this.#e.root;
468
+ }
469
+ get diagnostics() {
470
+ return this.#e.diagnostics;
471
+ }
472
+ toJSON() {
473
+ return structuredClone(this.#e);
474
+ }
475
+ toTailwind() {
476
+ return M(this.#e);
477
+ }
478
+ }, P = class extends Error {
479
+ pluginName;
480
+ nodeId;
481
+ name = "IRPluginError";
482
+ constructor(e, t, n) {
483
+ super(`IR plugin "${e}" failed for node ${t}`, { cause: n }), this.pluginName = e, this.nodeId = t;
484
+ }
485
+ };
486
+ function F(e) {
487
+ if (!S(e) || !S(e.source) || typeof e.source.id != "string" || typeof e.source.name != "string" || typeof e.source.type != "string" || ![
488
+ "container",
489
+ "text",
490
+ "shape",
491
+ "instance",
492
+ "unknown"
493
+ ].includes(String(e.kind)) || typeof e.visible != "boolean" || !Array.isArray(e.children) || !S(e.layout) || ![
494
+ "none",
495
+ "horizontal",
496
+ "vertical"
497
+ ].includes(String(e.layout.mode)) || typeof e.layout.wrap != "boolean" || !S(e.size) || ![
498
+ "FIXED",
499
+ "HUG",
500
+ "FILL"
501
+ ].includes(String(e.size.widthMode)) || ![
502
+ "FIXED",
503
+ "HUG",
504
+ "FILL"
505
+ ].includes(String(e.size.heightMode)) || !S(e.style) || !S(e.extensions)) throw TypeError("Plugin must return a standard IRNode");
506
+ for (let t of Object.values(e.style)) if (typeof t != "string" && (typeof t != "number" || !Number.isFinite(t))) throw TypeError("Invalid IR style value");
507
+ for (let t of ["width", "height"]) {
508
+ let n = e.size[t];
509
+ if (n !== void 0 && (typeof n != "number" || !Number.isFinite(n))) throw TypeError("Invalid IR size");
510
+ }
511
+ if (e.text !== void 0 && (!S(e.text) || typeof e.text.characters != "string")) throw TypeError("Invalid IR text");
512
+ }
513
+ function I(e, t = /* @__PURE__ */ new Set()) {
514
+ if (e !== null && typeof e != "string" && typeof e != "boolean" && !(typeof e == "number" && Number.isFinite(e))) {
515
+ if (typeof e != "object" || !Array.isArray(e) && Object.getPrototypeOf(e) !== Object.prototype && Object.getPrototypeOf(e) !== null) throw TypeError("Extensions must contain JSON values");
516
+ if (t.has(e)) throw TypeError("Cyclic extension value");
517
+ t.add(e);
518
+ for (let n of Object.values(e)) I(n, t);
519
+ t.delete(e);
520
+ }
521
+ }
522
+ function L(e) {
523
+ let t = [e], n = /* @__PURE__ */ new Set();
524
+ for (; t.length;) {
525
+ let e = t.pop();
526
+ if (n.has(e)) throw TypeError("IR children must form a tree");
527
+ n.add(e), F(e), I(e.extensions);
528
+ for (let n of e.children) t.push(n);
529
+ }
530
+ }
531
+ function R(e, t, n) {
532
+ let r = Object.freeze({
533
+ source: t.source,
534
+ parent: t.parent?.source,
535
+ component: t.component
536
+ });
537
+ return n.reduce((e, n) => {
538
+ try {
539
+ let t = n.transform(C(e), r);
540
+ return L(t), structuredClone(t);
541
+ } catch (e) {
542
+ throw new P(n.name, t.source.id, e);
543
+ }
544
+ }, e);
545
+ }
546
+ //#endregion
547
+ //#region src/figma-tree/selector/types.ts
548
+ var z = [
549
+ "id",
550
+ "name",
551
+ "type",
552
+ "visible",
553
+ "componentName"
554
+ ], B = class extends Error {
555
+ name = "SelectorSyntaxError";
556
+ };
557
+ //#endregion
558
+ //#region src/figma-tree/tree/records.ts
559
+ function* V(e) {
560
+ let t = [...e].reverse();
561
+ for (; t.length;) {
562
+ let e = t.pop();
563
+ yield e;
564
+ for (let n = e.children.length - 1; n >= 0; n--) t.push(e.children[n]);
565
+ }
566
+ }
567
+ function H(e) {
568
+ let t = /* @__PURE__ */ new Map(), n = [...e];
569
+ for (; n.length;) {
570
+ let e = n.pop();
571
+ e.type === "COMPONENT" && t.set(e.id, e.name);
572
+ for (let t of e.children ?? []) n.push(t);
573
+ }
574
+ return t;
575
+ }
576
+ function U(e) {
577
+ let t = H(e.roots), n = [], r = e.roots.map((e) => ({
578
+ source: e,
579
+ parent: void 0
580
+ })).reverse();
581
+ for (; r.length;) {
582
+ let { source: i, parent: a } = r.pop(), o = typeof i.componentId == "string" ? i.componentId : void 0, s = o ? e.components.get(o) : void 0, c = {
583
+ source: i,
584
+ parent: a,
585
+ children: [],
586
+ componentName: s?.name ?? (o ? t.get(o) : void 0),
587
+ component: s
588
+ };
589
+ a ? a.children.push(c) : n.push(c);
590
+ let l = i.children ?? [];
591
+ for (let e = l.length - 1; e >= 0; e--) r.push({
592
+ source: l[e],
593
+ parent: c
594
+ });
595
+ }
596
+ return n;
597
+ }
598
+ //#endregion
599
+ //#region src/figma-tree/ir/converters/style.ts
600
+ function W(e) {
601
+ return Math.round(Math.max(0, Math.min(1, w(e) ?? 0)) * 255);
602
+ }
603
+ function G(e) {
604
+ if (!S(e) || e.type !== "SOLID" || !S(e.color)) return;
605
+ let { color: t } = e, n = Math.max(0, Math.min(1, (w(t.a) ?? 1) * (w(e.opacity) ?? 1)));
606
+ return `rgba(${W(t.r)},${W(t.g)},${W(t.b)},${Number(n.toFixed(6))})`;
607
+ }
608
+ function K(e) {
609
+ return Array.isArray(e) ? e.filter((e) => S(e) && e.visible !== !1) : [];
610
+ }
611
+ function ue(e) {
612
+ let t = {}, n = K(e.fills), r = n.length === 1 ? G(n[0]) : void 0;
613
+ r && (t[e.type === "TEXT" ? "color" : "backgroundColor"] = r);
614
+ let i = K(e.strokes), a = i.length === 1 ? G(i[0]) : void 0;
615
+ if (a) {
616
+ t.borderColor = a, t.borderStyle = "solid";
617
+ let n = w(e.strokeWeight);
618
+ if (n !== void 0 && (t.borderWidth = `${n}px`), S(e.individualStrokeWeights)) for (let [n, r] of Object.entries({
619
+ top: "borderTopWidth",
620
+ right: "borderRightWidth",
621
+ bottom: "borderBottomWidth",
622
+ left: "borderLeftWidth"
623
+ })) {
624
+ let i = w(e.individualStrokeWeights[n]);
625
+ i !== void 0 && (t[r] = `${i}px`);
626
+ }
627
+ }
628
+ let o = w(e.cornerRadius);
629
+ if (o !== void 0 && (t.borderRadius = `${o}px`), Array.isArray(e.rectangleCornerRadii)) {
630
+ let n = [
631
+ "borderTopLeftRadius",
632
+ "borderTopRightRadius",
633
+ "borderBottomRightRadius",
634
+ "borderBottomLeftRadius"
635
+ ];
636
+ e.rectangleCornerRadii.forEach((e, r) => {
637
+ n[r] && w(e) !== void 0 && (t[n[r]] = `${e}px`);
638
+ });
639
+ }
640
+ e.type === "ELLIPSE" && (t.borderRadius = "50%");
641
+ let s = w(e.opacity);
642
+ return s !== void 0 && (t.opacity = s), e.visible === !1 && (t.display = "none"), t;
643
+ }
644
+ //#endregion
645
+ //#region src/figma-tree/ir/converters/diagnostics.ts
646
+ function de(e, t) {
647
+ let n = e.source, r = [];
648
+ t && r.push("type");
649
+ for (let e of ["fills", "strokes"]) {
650
+ let t = K(n[e]);
651
+ (t.length > 1 || t.some((e) => e.type !== "SOLID")) && r.push(e);
652
+ }
653
+ return Array.isArray(n.effects) && n.effects.some((e) => S(e) && e.visible !== !1) && r.push("effects"), S(n.styleOverrideTable) && Object.keys(n.styleOverrideTable).length && r.push("styleOverrideTable"), (n.type === "VECTOR" || n.type === "BOOLEAN_OPERATION" || n.type === "STAR" || n.type === "REGULAR_POLYGON" || n.type === "LINE") && r.push("vectorGeometry"), n.layoutMode === "GRID" && r.push("layoutMode"), typeof n.rotation == "number" && n.rotation !== 0 && r.push("rotation"), typeof n.itemSpacing == "number" && n.itemSpacing < 0 && r.push("itemSpacing"), K(n.strokes).length && n.strokeAlign && n.strokeAlign !== "INSIDE" && r.push("strokeAlign"), n.isMask === !0 && r.push("isMask"), typeof n.cornerSmoothing == "number" && n.cornerSmoothing > 0 && r.push("cornerSmoothing"), r.map((e) => ({
654
+ code: "unsupported-property",
655
+ nodeId: n.id,
656
+ property: e,
657
+ message: `${e} is not fully represented in the basic IR`
658
+ }));
659
+ }
660
+ //#endregion
661
+ //#region src/figma-tree/ir/converters/layout.ts
662
+ function q(e, t) {
663
+ return e === "FIXED" || e === "HUG" || e === "FILL" ? e : t;
664
+ }
665
+ function J(e, t) {
666
+ let n = e.source.layoutMode === "HORIZONTAL", r = t === "width" === n ? e.source.primaryAxisSizingMode : e.source.counterAxisSizingMode, i = e.parent?.source.layoutMode === "HORIZONTAL", a = t === "width" === i;
667
+ return (e.parent?.source.layoutMode === "HORIZONTAL" || e.parent?.source.layoutMode === "VERTICAL") && (a && e.source.layoutGrow === 1 || !a && e.source.layoutAlign === "STRETCH") ? "FILL" : r === "AUTO" ? "HUG" : "FIXED";
668
+ }
669
+ function fe(e) {
670
+ let t = S(e.source.absoluteBoundingBox) ? e.source.absoluteBoundingBox : {}, n = S(e.source.size) ? e.source.size : {};
671
+ return {
672
+ width: w(n.x) ?? w(t.width),
673
+ height: w(n.y) ?? w(t.height),
674
+ widthMode: q(e.source.layoutSizingHorizontal, J(e, "width")),
675
+ heightMode: q(e.source.layoutSizingVertical, J(e, "height"))
676
+ };
677
+ }
678
+ function Y(e, t, n, r, i, a) {
679
+ if (r === "FIXED") n !== void 0 && (e[t] = `${n}px`);
680
+ else if (r === "HUG") e[t] = "fit-content";
681
+ else {
682
+ let n = i.parent?.source.layoutMode === "HORIZONTAL" ? "width" : "height", r = ["HORIZONTAL", "VERTICAL"].includes(String(i.parent?.source.layoutMode));
683
+ !a && r && n === t ? (e.flexGrow = 1, e.flexBasis = "0px", e[t === "width" ? "minWidth" : "minHeight"] = "0px") : !a && r ? e.alignSelf = "stretch" : e[t] = "100%";
684
+ }
685
+ }
686
+ function pe(e) {
687
+ let t = e.source;
688
+ if (t.layoutMode !== "HORIZONTAL" && t.layoutMode !== "VERTICAL") return {};
689
+ let n = {
690
+ display: "flex",
691
+ flexDirection: t.layoutMode === "HORIZONTAL" ? "row" : "column"
692
+ };
693
+ typeof t.primaryAxisAlignItems == "string" && (n.justifyContent = {
694
+ MIN: "flex-start",
695
+ MAX: "flex-end",
696
+ CENTER: "center",
697
+ SPACE_BETWEEN: "space-between"
698
+ }[t.primaryAxisAlignItems] ?? "flex-start"), n.alignItems = typeof t.counterAxisAlignItems == "string" ? {
699
+ MIN: "flex-start",
700
+ MAX: "flex-end",
701
+ CENTER: "center",
702
+ BASELINE: "baseline"
703
+ }[t.counterAxisAlignItems] ?? "flex-start" : "flex-start";
704
+ let r = w(t.itemSpacing);
705
+ r !== void 0 && r >= 0 && (n.gap = `${r}px`);
706
+ for (let e of [
707
+ "paddingTop",
708
+ "paddingRight",
709
+ "paddingBottom",
710
+ "paddingLeft"
711
+ ]) {
712
+ let r = w(t[e]);
713
+ r !== void 0 && (n[e] = `${r}px`);
714
+ }
715
+ if (t.layoutWrap === "WRAP") {
716
+ n.flexWrap = "wrap";
717
+ let e = w(t.counterAxisSpacing);
718
+ e !== void 0 && e >= 0 && (n[t.layoutMode === "HORIZONTAL" ? "rowGap" : "columnGap"] = `${e}px`), t.counterAxisAlignContent === "SPACE_BETWEEN" && (n.alignContent = "space-between");
719
+ }
720
+ return n;
721
+ }
722
+ function me(e, t) {
723
+ if (t) return { position: "relative" };
724
+ let n = e.parent?.source;
725
+ if (!(e.source.layoutPositioning === "ABSOLUTE" || n && n.layoutMode !== "HORIZONTAL" && n.layoutMode !== "VERTICAL")) return {
726
+ position: "relative",
727
+ flexShrink: 0
728
+ };
729
+ let r = S(e.source.absoluteBoundingBox) ? e.source.absoluteBoundingBox : {}, i = S(n?.absoluteBoundingBox) ? n.absoluteBoundingBox : {}, a = w(r.x), o = w(r.y);
730
+ return {
731
+ position: "absolute",
732
+ ...a === void 0 ? {} : { left: `${a - (w(i.x) ?? 0)}px` },
733
+ ...o === void 0 ? {} : { top: `${o - (w(i.y) ?? 0)}px` }
734
+ };
735
+ }
736
+ function he(e, t) {
737
+ let n = fe(e), r = {
738
+ boxSizing: "border-box",
739
+ ...me(e, t),
740
+ ...pe(e)
741
+ };
742
+ Y(r, "width", n.width, n.widthMode, e, t), Y(r, "height", n.height, n.heightMode, e, t);
743
+ for (let t of [
744
+ "minWidth",
745
+ "maxWidth",
746
+ "minHeight",
747
+ "maxHeight"
748
+ ]) {
749
+ let n = w(e.source[t]);
750
+ n !== void 0 && (r[t] = `${n}px`);
751
+ }
752
+ return e.source.clipsContent === !0 && (r.overflow = "hidden"), {
753
+ size: n,
754
+ layout: {
755
+ mode: e.source.layoutMode === "HORIZONTAL" ? "horizontal" : e.source.layoutMode === "VERTICAL" ? "vertical" : "none",
756
+ wrap: e.source.layoutWrap === "WRAP"
757
+ },
758
+ style: r
759
+ };
760
+ }
761
+ //#endregion
762
+ //#region src/figma-tree/ir/converters/text.ts
763
+ function ge(e) {
764
+ if (e.type !== "TEXT") return {};
765
+ let t = S(e.style) ? e.style : {}, n = { whiteSpace: "pre-wrap" };
766
+ typeof t.fontFamily == "string" && (n.fontFamily = t.fontFamily);
767
+ let r = w(t.fontSize), i = w(t.fontWeight);
768
+ if (r !== void 0 && (n.fontSize = `${r}px`), i !== void 0 && (n.fontWeight = i), t.italic === !0 && (n.fontStyle = "italic"), typeof t.textAlignHorizontal == "string") {
769
+ let e = {
770
+ LEFT: "left",
771
+ RIGHT: "right",
772
+ CENTER: "center",
773
+ JUSTIFIED: "justify"
774
+ };
775
+ e[t.textAlignHorizontal] && (n.textAlign = e[t.textAlignHorizontal]);
776
+ }
777
+ let a = w(t.lineHeightPx);
778
+ t.lineHeightUnit === "FONT_SIZE_%" && w(t.lineHeightPercentFontSize) !== void 0 ? n.lineHeight = `${t.lineHeightPercentFontSize}%` : a !== void 0 && (n.lineHeight = `${a}px`);
779
+ let o = w(t.letterSpacing);
780
+ o !== void 0 && (n.letterSpacing = `${o}px`), t.textDecoration === "UNDERLINE" && (n.textDecorationLine = "underline"), t.textDecoration === "STRIKETHROUGH" && (n.textDecorationLine = "line-through");
781
+ let s = {
782
+ UPPER: "uppercase",
783
+ LOWER: "lowercase",
784
+ TITLE: "capitalize"
785
+ };
786
+ return typeof t.textCase == "string" && s[t.textCase] && (n.textTransform = s[t.textCase]), n;
787
+ }
788
+ //#endregion
789
+ //#region src/figma-tree/ir/pipeline.ts
790
+ function X(e) {
791
+ return e === "TEXT" ? "text" : e === "INSTANCE" ? "instance" : [
792
+ "DOCUMENT",
793
+ "CANVAS",
794
+ "FRAME",
795
+ "GROUP",
796
+ "COMPONENT",
797
+ "COMPONENT_SET",
798
+ "SECTION"
799
+ ].includes(e) ? "container" : [
800
+ "RECTANGLE",
801
+ "ELLIPSE",
802
+ "LINE",
803
+ "VECTOR",
804
+ "BOOLEAN_OPERATION",
805
+ "STAR",
806
+ "REGULAR_POLYGON"
807
+ ].includes(e) ? "shape" : "unknown";
808
+ }
809
+ function _e(e, t, n) {
810
+ let r = e.source, i = he(e, n);
811
+ return {
812
+ source: {
813
+ id: r.id,
814
+ name: r.name,
815
+ type: r.type,
816
+ ...e.componentName ? { componentName: e.componentName } : {}
817
+ },
818
+ kind: X(r.type),
819
+ visible: r.visible !== !1,
820
+ ...i,
821
+ style: {
822
+ ...i.style,
823
+ ...ge(r),
824
+ ...ue(r)
825
+ },
826
+ ...r.type === "TEXT" ? { text: { characters: typeof r.characters == "string" ? r.characters : "" } } : {},
827
+ children: t,
828
+ extensions: {}
829
+ };
830
+ }
831
+ function ve(e, t) {
832
+ let n = [...V([e])], r = /* @__PURE__ */ new Map(), i = [];
833
+ for (let i of [...n].reverse()) {
834
+ let n = _e(i, i.children.map((e) => r.get(e)), i === e);
835
+ r.set(i, R(n, i, t));
836
+ }
837
+ for (let e of n) i.push(...de(e, X(e.source.type) === "unknown"));
838
+ return new N({
839
+ schemaVersion: 1,
840
+ root: r.get(e),
841
+ diagnostics: i
842
+ });
843
+ }
844
+ //#endregion
845
+ //#region src/figma-tree/selector/match.ts
846
+ function ye(e, t) {
847
+ return t === "componentName" ? e.componentName : t === "visible" ? e.source.visible ?? !0 : e.source[t];
848
+ }
849
+ function be(e, t) {
850
+ let n = ye(e, t.key);
851
+ if (t.operator === "exists") return n !== void 0;
852
+ if (t.operator === "=") return n === t.value;
853
+ if (typeof n != "string") return !1;
854
+ if (t.operator === "regex") {
855
+ let e = t.value;
856
+ return new RegExp(e.source, e.flags).test(n);
857
+ }
858
+ let r = t.value;
859
+ return t.operator === "*=" ? n.includes(r) : t.operator === "^=" ? n.startsWith(r) : n.endsWith(r);
860
+ }
861
+ function xe(e, t) {
862
+ return (!t.type || e.source.type === t.type) && t.tests.every((t) => be(e, t));
863
+ }
864
+ function Se(e, t, n) {
865
+ let r = [{
866
+ node: e,
867
+ index: t.length - 1
868
+ }];
869
+ for (; r.length;) {
870
+ let e = r.pop(), i = t[e.index];
871
+ if (e.node === n || !xe(e.node, i)) continue;
872
+ if (e.index === 0) return !0;
873
+ let a = e.node.parent;
874
+ for (; a && a !== n && (r.push({
875
+ node: a,
876
+ index: e.index - 1
877
+ }), i.relation !== "child");) a = a.parent;
878
+ }
879
+ return !1;
880
+ }
881
+ //#endregion
882
+ //#region src/figma-tree/selector/parser.ts
883
+ function Ce(e) {
884
+ let t = [], n = 0, r;
885
+ for (; n < e.length;) {
886
+ for (; /\s/.test(e[n] ?? "") && n < e.length;) n++;
887
+ let i = n, a = !1, o = "";
888
+ for (; n < e.length;) {
889
+ let t = e[n];
890
+ if (o) t === "\\" ? n++ : t === o && (o = "");
891
+ else if (t === "\"" || t === "'") o = t;
892
+ else if (t === "[") {
893
+ if (a) throw new B("Nested brackets are not supported");
894
+ a = !0;
895
+ } else if (t === "]") {
896
+ if (!a) throw new B("Unexpected closing bracket");
897
+ a = !1;
898
+ } else if (!a && (t === ">" || /\s/.test(t))) break;
899
+ n++;
900
+ }
901
+ if (o || a) throw new B("Unclosed attribute or quoted value");
902
+ if (n === i) throw new B("Expected a selector before combinator");
903
+ for (t.push({
904
+ text: e.slice(i, n),
905
+ relation: r
906
+ }); n < e.length && /\s/.test(e[n]);) n++;
907
+ if (n === e.length) break;
908
+ if (r = e[n] === ">" ? "child" : "descendant", r === "child") {
909
+ for (n++; n < e.length && /\s/.test(e[n]);) n++;
910
+ if (n === e.length) throw new B("Expected selector after >");
911
+ }
912
+ }
913
+ return t;
914
+ }
915
+ function Z(e) {
916
+ if (!z.includes(e)) throw new B(`Unsupported attribute: ${e}`);
917
+ }
918
+ function we(e) {
919
+ let t = /^\s*([A-Za-z][A-Za-z0-9]*)\s*(?:(\*=|\^=|\$=|=)\s*(.*?)\s*)?$/.exec(e);
920
+ if (!t) throw new B(`Invalid attribute: [${e}]`);
921
+ let n = t[1];
922
+ Z(n);
923
+ let r = t[2];
924
+ if (!r) return {
925
+ key: n,
926
+ operator: "exists"
927
+ };
928
+ let i = t[3];
929
+ if (n === "visible") {
930
+ if (r !== "=" || !/^(true|false)$/.test(i)) throw new B("visible requires an unquoted boolean equality");
931
+ return {
932
+ key: n,
933
+ operator: r,
934
+ value: i === "true"
935
+ };
936
+ }
937
+ let a = /^(["'])([\s\S]*)\1$/.exec(i);
938
+ if (!a) throw new B("String attribute values must be quoted");
939
+ let o = a[2], s = "";
940
+ for (let e = 0; e < o.length; e++) if (o[e] === "\\") {
941
+ if (e + 1 === o.length) throw new B("Incomplete escape");
942
+ s += o[++e];
943
+ } else {
944
+ if (o[e] === a[1]) throw new B("Unescaped quote in attribute");
945
+ s += o[e];
946
+ }
947
+ return {
948
+ key: n,
949
+ operator: r,
950
+ value: s
951
+ };
952
+ }
953
+ function Te(e) {
954
+ let t = /^(\*|[A-Z][A-Z0-9_]*)/.exec(e)?.[1], n = t?.length ?? 0, r = [];
955
+ for (; n < e.length;) {
956
+ if (e[n] !== "[") throw new B(`Unsupported selector near: ${e.slice(n)}`);
957
+ let t = ++n, i = "";
958
+ for (; n < e.length;) {
959
+ let t = e[n];
960
+ if (i) t === "\\" ? n++ : t === i && (i = "");
961
+ else if (t === "\"" || t === "'") i = t;
962
+ else if (t === "]") break;
963
+ n++;
964
+ }
965
+ r.push(we(e.slice(t, n))), n++;
966
+ }
967
+ if (!t && !r.length) throw new B("Empty selector");
968
+ return {
969
+ type: t === "*" ? void 0 : t,
970
+ tests: r
971
+ };
972
+ }
973
+ function Ee(e) {
974
+ if (typeof e == "string") {
975
+ if (!e.trim()) throw new B("Empty selector");
976
+ return Ce(e.trim()).map(({ text: e, relation: t }) => ({
977
+ ...Te(e),
978
+ relation: t
979
+ }));
980
+ }
981
+ if (!e || typeof e != "object" || Array.isArray(e)) throw new B("Expected a string or condition object");
982
+ return [{ tests: Object.entries(e).map(([e, t]) => {
983
+ if (Z(e), e === "visible" ? typeof t != "boolean" : typeof t != "string" && !(t instanceof RegExp)) throw new B(`Invalid condition for ${e}`);
984
+ return {
985
+ key: e,
986
+ operator: t instanceof RegExp ? "regex" : "=",
987
+ value: t
988
+ };
989
+ }) }];
990
+ }
991
+ //#endregion
992
+ //#region src/figma-tree/tree/figma-tree.ts
993
+ var De = class {
994
+ plugins;
995
+ #e = /* @__PURE__ */ new WeakMap();
996
+ constructor(e) {
997
+ this.plugins = e;
998
+ }
999
+ wrap(e) {
1000
+ let t = this.#e.get(e);
1001
+ return t || (t = new Oe(e, this), this.#e.set(e, t)), t;
1002
+ }
1003
+ *search(e, t, n) {
1004
+ let r = Ee(t), i = /* @__PURE__ */ new Set();
1005
+ for (let t of V(e)) !i.has(t.source.id) && Se(t, r, n) && (i.add(t.source.id), yield this.wrap(t));
1006
+ }
1007
+ }, Oe = class {
1008
+ #e;
1009
+ #t;
1010
+ constructor(e, t) {
1011
+ this.#e = e, this.#t = t;
1012
+ }
1013
+ get id() {
1014
+ return this.#e.source.id;
1015
+ }
1016
+ get name() {
1017
+ return this.#e.source.name;
1018
+ }
1019
+ get type() {
1020
+ return this.#e.source.type;
1021
+ }
1022
+ get componentName() {
1023
+ return this.#e.componentName;
1024
+ }
1025
+ get children() {
1026
+ return Object.freeze(this.#e.children.map((e) => this.#t.wrap(e)));
1027
+ }
1028
+ query(e) {
1029
+ return this.#t.search(this.#e.children, e, this.#e).next().value;
1030
+ }
1031
+ queryAll(e) {
1032
+ return [...this.#t.search(this.#e.children, e, this.#e)];
1033
+ }
1034
+ toJSON() {
1035
+ return structuredClone(this.#e.source);
1036
+ }
1037
+ toIR(e = {}) {
1038
+ return ve(this.#e, [...this.#t.plugins, ...e.plugins ?? []]);
1039
+ }
1040
+ }, Q = class e {
1041
+ #e;
1042
+ #t;
1043
+ diagnostics;
1044
+ constructor(e, t) {
1045
+ let n = D(e);
1046
+ this.#e = U(n), this.#t = new De(Object.freeze([...t.irPlugins ?? []])), this.diagnostics = C(n.diagnostics);
1047
+ }
1048
+ static fromJson(t, n = {}) {
1049
+ return new e(t, n);
1050
+ }
1051
+ get roots() {
1052
+ return Object.freeze(this.#e.map((e) => this.#t.wrap(e)));
1053
+ }
1054
+ query(e) {
1055
+ return this.#t.search(this.#e, e).next().value;
1056
+ }
1057
+ queryAll(e) {
1058
+ return [...this.#t.search(this.#e, e)];
1059
+ }
1060
+ };
1061
+ //#endregion
1062
+ //#region src/cli/html/input.ts
1063
+ function ke(e) {
1064
+ return typeof e == "object" && !!e && "root" in e;
1065
+ }
1066
+ function Ae(e, t) {
1067
+ if (ke(e)) {
1068
+ if (t !== void 0) throw Error("--selector is only supported for original Figma JSON");
1069
+ return x(e), e;
1070
+ }
1071
+ let n = Q.fromJson(e);
1072
+ if (t === void 0 && n.roots.length !== 1) throw Error("Specify --selector when the Figma input does not have exactly one root");
1073
+ let r = t === void 0 ? n.roots[0] : n.query(t);
1074
+ if (!r) throw Error("No Figma node matched the export selector");
1075
+ return r.toIR();
1076
+ }
1077
+ function $(e, t, n) {
1078
+ let r = Ae(e, t);
1079
+ if ("target" in r && r.target === "tailwind-v4") {
1080
+ if (n === "inline") throw Error("Tailwind IR has no original inline styles; use standard IR for --styles inline");
1081
+ return r;
1082
+ }
1083
+ if (n === "tailwind") {
1084
+ let e = {
1085
+ schemaVersion: r.schemaVersion,
1086
+ root: r.root,
1087
+ diagnostics: r.diagnostics
1088
+ };
1089
+ return new N(structuredClone(e)).toTailwind();
1090
+ }
1091
+ return r;
1092
+ }
1093
+ //#endregion
1094
+ //#region src/cli/commands/export.ts
1095
+ async function je(e, n, r) {
1096
+ let i = $(JSON.parse(await t(e.input, "utf8")), e.selector, e.styles);
1097
+ await m(ce(i), e.out, e.force, n), i.diagnostics.length && r(`HTML exported with ${i.diagnostics.length} IR diagnostics; unsupported design features may be omitted.\n`);
1098
+ }
1099
+ //#endregion
1100
+ //#region src/cli/commands/query.ts
1101
+ async function Me(e, n) {
1102
+ let r = Q.fromJson(JSON.parse(await t(e.input, "utf8")));
1103
+ await p(e.command === "query" ? r.query(e.selector)?.toJSON() ?? null : r.queryAll(e.selector).map((e) => e.toJSON()), e.out, e.force, n);
1104
+ }
1105
+ //#endregion
1106
+ //#region src/cli/env.ts
1107
+ function Ne(e) {
1108
+ let t = e.FIGMA_TOKEN;
1109
+ if (!t?.trim()) throw Error("FIGMA_TOKEN environment variable is required");
1110
+ return t;
1111
+ }
1112
+ //#endregion
1113
+ //#region src/cli/run.ts
1114
+ async function Pe(e, t) {
1115
+ try {
1116
+ let n = re(e);
1117
+ return n === "help" ? (t.stdout(l), 0) : (n.command === "download" ? await ae(n, new c({
1118
+ token: Ne(t.env),
1119
+ fetch: t.fetch
1120
+ }), t.stdout) : n.command === "export" ? await je(n, t.stdout, t.stderr) : await Me(n, t.stdout), 0);
1121
+ } catch (e) {
1122
+ return t.stderr(`${ie(e, t.env.FIGMA_TOKEN)}\n`), 1;
1123
+ }
1124
+ }
1125
+ //#endregion
1126
+ //#region src/cli/index.ts
1127
+ process.exitCode = await Pe(process.argv.slice(2), {
1128
+ env: process.env,
1129
+ stdout: (e) => {
1130
+ process.stdout.write(e);
1131
+ },
1132
+ stderr: (e) => {
1133
+ process.stderr.write(e);
1134
+ }
1135
+ });
1136
+ //#endregion
1137
+
1138
+ //# sourceMappingURL=index.js.map