@styleframe/cli 4.0.0 → 4.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 (67) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/build-CBdsifMN.js +47 -0
  3. package/dist/build-D7cinF0l.cjs +50 -0
  4. package/dist/build-dtcg-BpbjBRCf.js +429 -0
  5. package/dist/build-dtcg-vuGHy-Sl.cjs +434 -0
  6. package/dist/chunk-D6vf50IK.cjs +28 -0
  7. package/dist/commands/build.d.ts +22 -0
  8. package/dist/commands/build.d.ts.map +1 -0
  9. package/dist/commands/dtcg/build-dtcg.d.ts +36 -0
  10. package/dist/commands/dtcg/build-dtcg.d.ts.map +1 -0
  11. package/dist/commands/dtcg/build-dtcg.test.d.ts +2 -0
  12. package/dist/commands/dtcg/build-dtcg.test.d.ts.map +1 -0
  13. package/dist/commands/dtcg/evaluate.d.ts +30 -0
  14. package/dist/commands/dtcg/evaluate.d.ts.map +1 -0
  15. package/dist/commands/dtcg/evaluate.test.d.ts +2 -0
  16. package/dist/commands/dtcg/evaluate.test.d.ts.map +1 -0
  17. package/dist/commands/dtcg/export.d.ts +24 -0
  18. package/dist/commands/dtcg/export.d.ts.map +1 -0
  19. package/dist/commands/dtcg/import.d.ts +38 -0
  20. package/dist/commands/dtcg/import.d.ts.map +1 -0
  21. package/dist/commands/dtcg/index.d.ts +3 -0
  22. package/dist/commands/dtcg/index.d.ts.map +1 -0
  23. package/dist/commands/figma/export.d.ts +24 -0
  24. package/dist/commands/figma/export.d.ts.map +1 -0
  25. package/dist/commands/figma/import.d.ts +38 -0
  26. package/dist/commands/figma/import.d.ts.map +1 -0
  27. package/dist/commands/figma/index.d.ts +3 -0
  28. package/dist/commands/figma/index.d.ts.map +1 -0
  29. package/dist/commands/init/nuxt.d.ts +2 -0
  30. package/dist/commands/init/nuxt.d.ts.map +1 -0
  31. package/dist/commands/init/vite.d.ts +2 -0
  32. package/dist/commands/init/vite.d.ts.map +1 -0
  33. package/dist/commands/init.d.ts +26 -0
  34. package/dist/commands/init.d.ts.map +1 -0
  35. package/dist/constants.d.ts +8 -0
  36. package/dist/constants.d.ts.map +1 -0
  37. package/dist/dtcg-D1-iITOr.js +14 -0
  38. package/dist/dtcg-D84AfyzO.cjs +13 -0
  39. package/dist/export-CBdPGGEq.js +66 -0
  40. package/dist/export-DmPAU9Wh.cjs +69 -0
  41. package/dist/export-ONk9eKoZ.cjs +86 -0
  42. package/dist/export-suUS16eO.js +83 -0
  43. package/dist/figma-BvXoqRPU.cjs +13 -0
  44. package/dist/figma-D2RJh56T.js +14 -0
  45. package/dist/import-BQrcHNjK.cjs +126 -0
  46. package/dist/import-Bll_uBvJ.js +123 -0
  47. package/dist/import-MqLYxb8d.js +114 -0
  48. package/dist/import-ibQc_GXm.cjs +117 -0
  49. package/dist/index.cjs +16 -16
  50. package/dist/index.d.ts +3 -4
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +16 -17
  53. package/dist/init-CAO0mA_w.js +262 -0
  54. package/dist/init-CaJoUVv2.cjs +265 -0
  55. package/dist/utils.d.ts +7 -0
  56. package/dist/utils.d.ts.map +1 -0
  57. package/package.json +13 -17
  58. package/dist/build-BIWOTFZD.cjs +0 -49
  59. package/dist/build-CANA04j1.js +0 -49
  60. package/dist/export-BMneJTdq.cjs +0 -517
  61. package/dist/export-Cx6awh55.js +0 -517
  62. package/dist/import-BLbc2zWU.cjs +0 -90
  63. package/dist/import-a5DtGEAY.js +0 -90
  64. package/dist/index-BTHfb82h.js +0 -14
  65. package/dist/index-jMzviwjD.cjs +0 -14
  66. package/dist/init-CKeTXHp5.js +0 -234
  67. package/dist/init-CO7VnQKe.cjs +0 -234
@@ -1,517 +0,0 @@
1
- import { writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { loadConfiguration } from "@styleframe/loader";
4
- import { defineCommand } from "citty";
5
- import consola from "consola";
6
- import { formatAlias, classifyValue } from "@styleframe/dtcg";
7
- function isReference(value) {
8
- return typeof value === "object" && value !== null && "type" in value && value.type === "reference";
9
- }
10
- function isCss(value) {
11
- return typeof value === "object" && value !== null && "type" in value && value.type === "css";
12
- }
13
- function isPrimitive(value) {
14
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
15
- }
16
- function resolveReferenceTarget(ref, context) {
17
- const visited = context.visited ?? /* @__PURE__ */ new Set();
18
- if (visited.has(ref.name)) {
19
- return {
20
- resolved: null,
21
- reason: `Reference cycle: ${[...visited, ref.name].join(" → ")}`
22
- };
23
- }
24
- const target = context.variables.get(ref.name);
25
- if (!target) {
26
- if (ref.fallback !== void 0 && ref.fallback !== null) {
27
- return evaluateValue(ref.fallback, context);
28
- }
29
- return { resolved: null, reason: `Unknown reference target: ${ref.name}` };
30
- }
31
- const nextContext = {
32
- variables: context.variables,
33
- visited: /* @__PURE__ */ new Set([...visited, ref.name]),
34
- maxViewport: context.maxViewport
35
- };
36
- return evaluateValue(target.value, nextContext);
37
- }
38
- function evaluateCss(css, context) {
39
- const parts = [];
40
- let unevaluable = false;
41
- let unevaluableReason;
42
- for (const part of css.value) {
43
- if (typeof part === "string") {
44
- parts.push({ kind: "literal", text: part });
45
- continue;
46
- }
47
- const evaluated = evaluateValue(part, context);
48
- if (evaluated.resolved === null) {
49
- unevaluable = true;
50
- unevaluableReason = evaluated.reason;
51
- parts.push({ kind: "value", text: "" });
52
- continue;
53
- }
54
- const text = String(evaluated.resolved);
55
- const numeric = typeof evaluated.resolved === "number" ? evaluated.resolved : void 0;
56
- parts.push({ kind: "value", text, numeric });
57
- }
58
- const folded = parts.map((p) => p.text).join("");
59
- if (unevaluable) {
60
- return {
61
- resolved: null,
62
- cssExpression: folded,
63
- reason: unevaluableReason ?? "Computed expression includes an unresolvable reference"
64
- };
65
- }
66
- const isPureArithmetic = parts.every((p) => {
67
- if (p.kind === "value") return p.numeric !== void 0;
68
- return /^[\d.\s+\-*/()]*$/.test(p.text);
69
- });
70
- if (isPureArithmetic && parts.some((p) => p.kind === "value")) {
71
- try {
72
- const result = safeArithmetic(folded.trim());
73
- if (typeof result === "number" && Number.isFinite(result)) {
74
- return { resolved: result };
75
- }
76
- } catch (err) {
77
- return {
78
- resolved: null,
79
- cssExpression: folded,
80
- reason: `Arithmetic evaluation failed: ${err.message}`
81
- };
82
- }
83
- }
84
- const subResult = substituteFluidUnits(folded, context.maxViewport ?? 1440);
85
- if (subResult && /^[\d.\s+\-*/()]+$/.test(subResult.rebased)) {
86
- try {
87
- const result = safeArithmetic(subResult.rebased.trim());
88
- if (typeof result === "number" && Number.isFinite(result)) {
89
- return {
90
- resolved: result,
91
- ...subResult.substituted ? { normalisationSource: "fluid-max" } : {}
92
- };
93
- }
94
- } catch {
95
- }
96
- }
97
- return { resolved: folded };
98
- }
99
- function substituteFluidUnits(expression, maxViewport) {
100
- let substituted = false;
101
- let rebased = expression.replace(/\bcalc\b/g, "");
102
- if (rebased.includes("100vw")) {
103
- rebased = rebased.replace(/100vw/g, String(maxViewport));
104
- substituted = true;
105
- }
106
- if (/(-?\d*\.?\d+)\s*rem/.test(rebased)) {
107
- rebased = rebased.replace(/(-?\d*\.?\d+)\s*rem/g, "($1 * 16)");
108
- substituted = true;
109
- }
110
- if (/(-?\d*\.?\d+)\s*px/.test(rebased)) {
111
- rebased = rebased.replace(/(-?\d*\.?\d+)\s*px/g, "$1");
112
- substituted = true;
113
- }
114
- if (/[a-zA-Z%]/.test(rebased)) return null;
115
- return { rebased, substituted };
116
- }
117
- function safeArithmetic(expression) {
118
- if (!/^[\d.\s+\-*/()]+$/.test(expression)) {
119
- throw new Error(`Disallowed characters in expression: "${expression}"`);
120
- }
121
- const fn = new Function(`return (${expression});`);
122
- return fn();
123
- }
124
- function evaluateValue(value, context) {
125
- if (value === null || value === void 0) {
126
- return { resolved: null, reason: "Value is null/undefined" };
127
- }
128
- if (isPrimitive(value)) {
129
- return { resolved: value };
130
- }
131
- if (isReference(value)) {
132
- const targetResolution = resolveReferenceTarget(value, context);
133
- const isTopLevel = !context.visited || context.visited.size === 0;
134
- if (isTopLevel && targetResolution.resolved !== null) {
135
- return { ...targetResolution, aliasTarget: value.name };
136
- }
137
- return targetResolution;
138
- }
139
- if (isCss(value)) {
140
- return evaluateCss(value, context);
141
- }
142
- if (Array.isArray(value)) {
143
- return {
144
- resolved: null,
145
- reason: "Heterogeneous array — DTCG composite encoding not yet implemented"
146
- };
147
- }
148
- return {
149
- resolved: null,
150
- reason: `Unsupported value shape: ${typeof value}`
151
- };
152
- }
153
- const TOKENS_SCHEMA_URL = "https://design-tokens.org/schemas/2025.10/tokens.json";
154
- const RESOLVER_SCHEMA_URL = "https://www.designtokens.org/schemas/2025.10/resolver.json";
155
- function buildVariableMap(root) {
156
- const map = /* @__PURE__ */ new Map();
157
- for (const v of root.variables) map.set(v.name, v);
158
- for (const theme of root.themes) {
159
- for (const v of theme.variables) {
160
- if (!map.has(v.name)) map.set(v.name, v);
161
- }
162
- }
163
- return map;
164
- }
165
- function processVariable(variable, context) {
166
- const evaluation = evaluateValue(variable.value, context);
167
- const classification = classifyValueForVariable(variable.name, evaluation);
168
- return { name: variable.name, evaluation, classification };
169
- }
170
- function classifyValueForVariable(name, evaluation) {
171
- if (evaluation.resolved === null) return void 0;
172
- return classifyValue(evaluation.resolved, { path: name });
173
- }
174
- function setNestedToken(doc, dotPath, token) {
175
- const segments = dotPath.split(".");
176
- let cursor = doc;
177
- for (let i = 0; i < segments.length - 1; i++) {
178
- const segment = segments[i];
179
- const next = cursor[segment];
180
- if (typeof next !== "object" || next === null || Array.isArray(next)) {
181
- cursor[segment] = {};
182
- } else if ("$value" in next) {
183
- cursor[segment] = { $root: next };
184
- }
185
- cursor = cursor[segment];
186
- }
187
- const leaf = segments[segments.length - 1];
188
- const existing = cursor[leaf];
189
- if (typeof existing === "object" && existing !== null && !Array.isArray(existing) && !("$value" in existing)) {
190
- existing.$root = token;
191
- } else {
192
- cursor[leaf] = token;
193
- }
194
- }
195
- function setNestedOverride(context, dotPath, value, type) {
196
- const segments = dotPath.split(".");
197
- let cursor = context;
198
- for (let i = 0; i < segments.length - 1; i++) {
199
- const segment = segments[i];
200
- const next = cursor[segment];
201
- if (typeof next !== "object" || next === null || Array.isArray(next) || "$value" in next) {
202
- cursor[segment] = {};
203
- }
204
- cursor = cursor[segment];
205
- }
206
- const token = { $value: value };
207
- if (type !== void 0) token.$type = type;
208
- cursor[segments[segments.length - 1]] = token;
209
- }
210
- function makeAliasToken(target, type, description) {
211
- const token = { $value: formatAlias(target) };
212
- if (type !== void 0) token.$type = type;
213
- return token;
214
- }
215
- function makeValueToken(classification, description, fluidBound) {
216
- const token = {
217
- $value: classification.value,
218
- $type: classification.type
219
- };
220
- if (fluidBound) {
221
- token.$extensions = {
222
- "dev.styleframe": { fluidBound }
223
- };
224
- }
225
- return token;
226
- }
227
- function makeExpressionToken(expression, description) {
228
- const token = {
229
- $value: expression,
230
- $extensions: {
231
- "dev.styleframe": { expression }
232
- }
233
- };
234
- return token;
235
- }
236
- function valuesEqual(a, b) {
237
- if (a === b) return true;
238
- if (typeof a !== typeof b) return false;
239
- if (typeof a === "object") return JSON.stringify(a) === JSON.stringify(b);
240
- return false;
241
- }
242
- function capitalizeContextName(name) {
243
- if (name.length === 0) return name;
244
- return name.charAt(0).toUpperCase() + name.slice(1);
245
- }
246
- function buildDTCG(root, options = {}) {
247
- const includeSchema = options.includeSchema ?? true;
248
- const schemaUrl = options.schemaUrl ?? TOKENS_SCHEMA_URL;
249
- const modifierName = options.modifierName ?? "theme";
250
- const collectionName = options.collectionName ?? "Design Tokens";
251
- const defaultModeName = options.defaultModeName ?? "Default";
252
- const tokensSourceRef = options.tokensSourceRef ?? "tokens.json";
253
- const variableMap = buildVariableMap(root);
254
- const fluidMaxVariable = variableMap.get("fluid.max-width");
255
- const fluidMaxResolved = fluidMaxVariable ? evaluateValue(fluidMaxVariable.value, { variables: variableMap }).resolved : null;
256
- const maxViewport = typeof fluidMaxResolved === "number" ? fluidMaxResolved : 1440;
257
- const context = { variables: variableMap, maxViewport };
258
- const processed = /* @__PURE__ */ new Map();
259
- const typeMap = /* @__PURE__ */ new Map();
260
- for (const variable of root.variables) {
261
- const p = processVariable(variable, context);
262
- processed.set(variable.name, p);
263
- if (p.classification) typeMap.set(variable.name, p.classification.type);
264
- }
265
- const resolveTypeFromAliasChain = (name, seen) => {
266
- if (seen.has(name)) return void 0;
267
- const direct = typeMap.get(name);
268
- if (direct) return direct;
269
- const p = processed.get(name);
270
- if (p?.evaluation.aliasTarget) {
271
- return resolveTypeFromAliasChain(
272
- p.evaluation.aliasTarget,
273
- /* @__PURE__ */ new Set([...seen, name])
274
- );
275
- }
276
- return void 0;
277
- };
278
- const tokens = {};
279
- if (includeSchema) tokens.$schema = schemaUrl;
280
- tokens.$extensions = {
281
- "dev.styleframe": { collection: collectionName }
282
- };
283
- const diagnostics = [];
284
- let emittedCount = 0;
285
- let fluidNormalisedCount = 0;
286
- for (const variable of root.variables) {
287
- const p = processed.get(variable.name);
288
- if (!p) continue;
289
- const { evaluation, classification } = p;
290
- if (evaluation.aliasTarget) {
291
- const targetType = resolveTypeFromAliasChain(
292
- evaluation.aliasTarget,
293
- /* @__PURE__ */ new Set()
294
- );
295
- setNestedToken(
296
- tokens,
297
- variable.name,
298
- makeAliasToken(evaluation.aliasTarget, targetType)
299
- );
300
- emittedCount++;
301
- continue;
302
- }
303
- if (classification) {
304
- const fluidBound = evaluation.normalisationSource === "fluid-max" ? "max" : void 0;
305
- const promoted = fluidBound && classification.type === "number" && typeof classification.value === "number" ? {
306
- type: "dimension",
307
- value: { value: classification.value, unit: "px" }
308
- } : classification;
309
- setNestedToken(
310
- tokens,
311
- variable.name,
312
- makeValueToken(promoted, void 0, fluidBound)
313
- );
314
- if (fluidBound) fluidNormalisedCount++;
315
- emittedCount++;
316
- continue;
317
- }
318
- if (evaluation.cssExpression) {
319
- setNestedToken(
320
- tokens,
321
- variable.name,
322
- makeExpressionToken(evaluation.cssExpression)
323
- );
324
- diagnostics.push({
325
- name: variable.name,
326
- level: "warn",
327
- reason: evaluation.reason ?? "Computed expression — preserved as dev.styleframe.expression extension"
328
- });
329
- emittedCount++;
330
- continue;
331
- }
332
- if (evaluation.resolved !== null && typeof evaluation.resolved !== "boolean") {
333
- setNestedToken(tokens, variable.name, {
334
- $value: String(evaluation.resolved),
335
- $extensions: { "dev.styleframe": { unknownType: true } }
336
- });
337
- diagnostics.push({
338
- name: variable.name,
339
- level: "warn",
340
- reason: "Could not infer DTCG $type — emitted as untyped string"
341
- });
342
- emittedCount++;
343
- continue;
344
- }
345
- diagnostics.push({
346
- name: variable.name,
347
- level: "warn",
348
- reason: evaluation.reason ?? "Unrepresentable value — skipped"
349
- });
350
- }
351
- const themedThemes = root.themes.filter(
352
- (t) => t.variables.length > 0
353
- );
354
- let resolver;
355
- if (themedThemes.length > 0) {
356
- const contexts = {};
357
- for (const theme of themedThemes) {
358
- const contextDoc = {};
359
- for (const variable of theme.variables) {
360
- const themeProcessed = processVariable(variable, context);
361
- const defaultProcessed = processed.get(variable.name);
362
- let themeValue;
363
- let themeType;
364
- if (themeProcessed.evaluation.aliasTarget) {
365
- themeValue = formatAlias(themeProcessed.evaluation.aliasTarget);
366
- themeType = resolveTypeFromAliasChain(
367
- themeProcessed.evaluation.aliasTarget,
368
- /* @__PURE__ */ new Set()
369
- );
370
- } else if (themeProcessed.classification) {
371
- themeValue = themeProcessed.classification.value;
372
- themeType = themeProcessed.classification.type;
373
- }
374
- if (themeValue === void 0) continue;
375
- let defaultValue;
376
- if (defaultProcessed?.evaluation.aliasTarget) {
377
- defaultValue = formatAlias(defaultProcessed.evaluation.aliasTarget);
378
- } else if (defaultProcessed?.classification) {
379
- defaultValue = defaultProcessed.classification.value;
380
- }
381
- if (defaultValue !== void 0 && valuesEqual(themeValue, defaultValue)) {
382
- continue;
383
- }
384
- setNestedOverride(
385
- contextDoc,
386
- variable.name,
387
- themeValue,
388
- themeType
389
- );
390
- }
391
- contexts[capitalizeContextName(theme.name)] = contextDoc;
392
- }
393
- for (const key of Object.keys(contexts)) {
394
- if (Object.keys(contexts[key]).length === 0)
395
- delete contexts[key];
396
- }
397
- const themeContextNames = Object.keys(contexts);
398
- if (themeContextNames.length > 0) {
399
- const allContexts = {
400
- [defaultModeName]: []
401
- };
402
- for (const name of themeContextNames) {
403
- allContexts[name] = [contexts[name]];
404
- }
405
- resolver = {
406
- $schema: RESOLVER_SCHEMA_URL,
407
- version: "2025.10",
408
- modifiers: {
409
- [modifierName]: {
410
- contexts: allContexts,
411
- default: defaultModeName
412
- }
413
- },
414
- resolutionOrder: [
415
- { type: "set", sources: [{ $ref: tokensSourceRef }] },
416
- { $ref: `#/modifiers/${modifierName}` }
417
- ]
418
- };
419
- }
420
- }
421
- return {
422
- tokens,
423
- resolver,
424
- diagnostics,
425
- emittedCount,
426
- fluidNormalisedCount,
427
- maxViewport
428
- };
429
- }
430
- const _export = defineCommand({
431
- meta: {
432
- name: "export",
433
- description: "Export Styleframe variables to spec-conformant DTCG JSON"
434
- },
435
- args: {
436
- config: {
437
- type: "string",
438
- description: "Path to the Styleframe config file",
439
- default: "styleframe.config.ts",
440
- alias: ["c"],
441
- valueHint: "path"
442
- },
443
- output: {
444
- type: "string",
445
- description: "Output JSON file path",
446
- default: "tokens.json",
447
- alias: ["o"],
448
- valueHint: "path"
449
- },
450
- collection: {
451
- type: "string",
452
- description: "Collection name embedded in the export",
453
- default: "Design Tokens",
454
- alias: ["n", "name"]
455
- }
456
- },
457
- async run({ args }) {
458
- const configPath = path.resolve(args.config);
459
- const outputPath = path.resolve(args.output);
460
- consola.info(
461
- `Loading configuration from "${path.relative(process.cwd(), configPath)}"...`
462
- );
463
- const instance = await loadConfiguration({ entry: configPath });
464
- const root = instance.root;
465
- consola.info("Building DTCG document...");
466
- const {
467
- tokens,
468
- resolver,
469
- diagnostics,
470
- emittedCount,
471
- fluidNormalisedCount,
472
- maxViewport
473
- } = buildDTCG(root, {
474
- collectionName: args.collection,
475
- tokensSourceRef: path.basename(outputPath)
476
- });
477
- await writeFile(outputPath, `${JSON.stringify(tokens, null, 2)}
478
- `);
479
- consola.info(
480
- `Wrote ${emittedCount} tokens to "${path.relative(process.cwd(), outputPath)}"`
481
- );
482
- if (fluidNormalisedCount > 0) {
483
- consola.info(
484
- `Normalised ${fluidNormalisedCount} fluid token(s) using max viewport (${maxViewport}px).`
485
- );
486
- }
487
- if (resolver) {
488
- const resolverPath = outputPath.replace(/\.json$/, ".resolver.json");
489
- await writeFile(resolverPath, `${JSON.stringify(resolver, null, 2)}
490
- `);
491
- consola.info(
492
- `Wrote resolver to "${path.relative(process.cwd(), resolverPath)}"`
493
- );
494
- }
495
- const warnings = diagnostics.filter((d) => d.level === "warn");
496
- if (warnings.length > 0) {
497
- consola.warn(
498
- `${warnings.length} variable(s) needed special handling — see Styleframe ↔ DTCG round-trip notes`
499
- );
500
- for (const w of warnings.slice(0, 10)) {
501
- consola.info(` - ${w.name}: ${w.reason}`);
502
- }
503
- if (warnings.length > 10) {
504
- consola.info(` ... and ${warnings.length - 10} more`);
505
- }
506
- consola.info(
507
- 'Tip: untyped tokens are usually CSS keywords (e.g. "normal", "thin", "italic") with no DTCG equivalent — they round-trip to Figma as STRING variables.'
508
- );
509
- }
510
- consola.success(
511
- `Exported ${emittedCount} tokens in DTCG format to "${path.relative(process.cwd(), outputPath)}"`
512
- );
513
- }
514
- });
515
- export {
516
- _export as default
517
- };
@@ -1,90 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const path = require("node:path");
4
- const promises = require("node:fs/promises");
5
- const citty = require("citty");
6
- const consola = require("consola");
7
- const figma = require("@styleframe/figma");
8
- const _import = citty.defineCommand({
9
- meta: {
10
- name: "import",
11
- description: "Generate Styleframe code from DTCG format JSON"
12
- },
13
- args: {
14
- input: {
15
- type: "string",
16
- description: "Input DTCG JSON file path",
17
- required: true,
18
- alias: ["i"],
19
- valueHint: "path"
20
- },
21
- output: {
22
- type: "string",
23
- description: "Output Styleframe TypeScript file path",
24
- default: "tokens.styleframe.ts",
25
- alias: ["o"],
26
- valueHint: "path"
27
- },
28
- composables: {
29
- type: "boolean",
30
- description: "Use @styleframe/theme composables (useColor, useSpacing, etc.)",
31
- default: true
32
- },
33
- rem: {
34
- type: "boolean",
35
- description: "Use rem units for dimensions instead of px",
36
- default: false
37
- },
38
- baseFontSize: {
39
- type: "string",
40
- description: "Base font size for rem conversion (in pixels)",
41
- default: "16"
42
- },
43
- instanceName: {
44
- type: "string",
45
- description: "Name for the Styleframe instance variable",
46
- default: "s"
47
- }
48
- },
49
- async run({ args }) {
50
- const inputPath = path.resolve(args.input);
51
- const outputPath = path.resolve(args.output);
52
- const baseFontSize = Number.parseInt(args.baseFontSize, 10) || 16;
53
- consola.info(
54
- `Reading JSON from "${path.relative(process.cwd(), inputPath)}"...`
55
- );
56
- let data;
57
- try {
58
- const content = await promises.readFile(inputPath, "utf-8");
59
- const rawData = JSON.parse(content);
60
- data = figma.fromDTCG(rawData);
61
- } catch (error) {
62
- consola.error(
63
- `Failed to read or parse input file: ${error instanceof Error ? error.message : error}`
64
- );
65
- process.exit(1);
66
- }
67
- consola.info(
68
- `Generating Styleframe code for ${data.variables.length} variables...`
69
- );
70
- const result = figma.generateStyleframeCode(data, {
71
- useComposables: args.composables,
72
- useRem: args.rem,
73
- baseFontSize,
74
- instanceName: args.instanceName
75
- });
76
- consola.info(`Writing to "${path.relative(process.cwd(), outputPath)}"...`);
77
- await promises.writeFile(outputPath, result.code);
78
- consola.success(`Generated Styleframe code with:`);
79
- consola.info(` - ${result.variables.length} variables`);
80
- consola.info(` - ${result.themes.length} theme(s)`);
81
- if (args.composables && result.imports.length > 1) {
82
- consola.info(
83
- ` - Using composables: ${result.imports.slice(1).join(", ")}`
84
- );
85
- }
86
- consola.info(`
87
- Output: ${path.relative(process.cwd(), outputPath)}`);
88
- }
89
- });
90
- exports.default = _import;
@@ -1,90 +0,0 @@
1
- import path from "node:path";
2
- import { readFile, writeFile } from "node:fs/promises";
3
- import { defineCommand } from "citty";
4
- import consola from "consola";
5
- import { fromDTCG, generateStyleframeCode } from "@styleframe/figma";
6
- const _import = defineCommand({
7
- meta: {
8
- name: "import",
9
- description: "Generate Styleframe code from DTCG format JSON"
10
- },
11
- args: {
12
- input: {
13
- type: "string",
14
- description: "Input DTCG JSON file path",
15
- required: true,
16
- alias: ["i"],
17
- valueHint: "path"
18
- },
19
- output: {
20
- type: "string",
21
- description: "Output Styleframe TypeScript file path",
22
- default: "tokens.styleframe.ts",
23
- alias: ["o"],
24
- valueHint: "path"
25
- },
26
- composables: {
27
- type: "boolean",
28
- description: "Use @styleframe/theme composables (useColor, useSpacing, etc.)",
29
- default: true
30
- },
31
- rem: {
32
- type: "boolean",
33
- description: "Use rem units for dimensions instead of px",
34
- default: false
35
- },
36
- baseFontSize: {
37
- type: "string",
38
- description: "Base font size for rem conversion (in pixels)",
39
- default: "16"
40
- },
41
- instanceName: {
42
- type: "string",
43
- description: "Name for the Styleframe instance variable",
44
- default: "s"
45
- }
46
- },
47
- async run({ args }) {
48
- const inputPath = path.resolve(args.input);
49
- const outputPath = path.resolve(args.output);
50
- const baseFontSize = Number.parseInt(args.baseFontSize, 10) || 16;
51
- consola.info(
52
- `Reading JSON from "${path.relative(process.cwd(), inputPath)}"...`
53
- );
54
- let data;
55
- try {
56
- const content = await readFile(inputPath, "utf-8");
57
- const rawData = JSON.parse(content);
58
- data = fromDTCG(rawData);
59
- } catch (error) {
60
- consola.error(
61
- `Failed to read or parse input file: ${error instanceof Error ? error.message : error}`
62
- );
63
- process.exit(1);
64
- }
65
- consola.info(
66
- `Generating Styleframe code for ${data.variables.length} variables...`
67
- );
68
- const result = generateStyleframeCode(data, {
69
- useComposables: args.composables,
70
- useRem: args.rem,
71
- baseFontSize,
72
- instanceName: args.instanceName
73
- });
74
- consola.info(`Writing to "${path.relative(process.cwd(), outputPath)}"...`);
75
- await writeFile(outputPath, result.code);
76
- consola.success(`Generated Styleframe code with:`);
77
- consola.info(` - ${result.variables.length} variables`);
78
- consola.info(` - ${result.themes.length} theme(s)`);
79
- if (args.composables && result.imports.length > 1) {
80
- consola.info(
81
- ` - Using composables: ${result.imports.slice(1).join(", ")}`
82
- );
83
- }
84
- consola.info(`
85
- Output: ${path.relative(process.cwd(), outputPath)}`);
86
- }
87
- });
88
- export {
89
- _import as default
90
- };