@ryndesign/cli 0.1.1

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/dist/bin.cjs ADDED
@@ -0,0 +1,1332 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
+ // If the importer is in node compatibility mode or this is not an ESM
26
+ // file that has been converted to a CommonJS file using a Babel-
27
+ // compatible transform (i.e. "__esModule" has not been set), then set
28
+ // "default" to the CommonJS "module.exports" for node compatibility.
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
+ mod
31
+ ));
32
+
33
+ // src/commands/init.ts
34
+ var init_exports = {};
35
+ __export(init_exports, {
36
+ default: () => init_default
37
+ });
38
+ function generateConfig(platforms, darkMode) {
39
+ const imports = [];
40
+ const generators = [];
41
+ for (const platform of platforms) {
42
+ const pkgName = `@ryndesign/generator-${platform}`;
43
+ const varName = platform.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
44
+ imports.push(`import ${varName} from '${pkgName}';`);
45
+ if (["react", "vue", "svelte"].includes(platform)) {
46
+ generators.push(` ${varName}({
47
+ outDir: 'generated/${platform}',
48
+ cssStrategy: 'css-variables',
49
+ typescript: true,${darkMode ? "\n darkMode: 'media+class'," : ""}
50
+ })`);
51
+ } else {
52
+ generators.push(` ${varName}({
53
+ outDir: 'generated/${platform}',
54
+ })`);
55
+ }
56
+ }
57
+ return `import { defineConfig } from '@ryndesign/cli';
58
+ ${imports.join("\n")}
59
+
60
+ export default defineConfig({
61
+ tokens: ['tokens/**/*.tokens.json'],
62
+ components: ['components/**/*.component.json'],
63
+ outDir: 'generated',
64
+ ${darkMode ? `
65
+ themes: {
66
+ default: 'light',
67
+ dark: { file: 'tokens/dark.tokens.json' },
68
+ },
69
+ ` : ""}
70
+ generators: [
71
+ ${generators.join(",\n")}
72
+ ],
73
+
74
+ preview: { port: 4400, open: true },
75
+ });
76
+ `;
77
+ }
78
+ function getDefaultTokens() {
79
+ return {
80
+ color: {
81
+ $type: "color",
82
+ primary: { $value: "#3B82F6" },
83
+ white: { $value: "#FFFFFF" },
84
+ black: { $value: "#000000" },
85
+ gray: {
86
+ 50: { $value: "#F9FAFB" },
87
+ 900: { $value: "#111827" }
88
+ }
89
+ },
90
+ spacing: {
91
+ $type: "dimension",
92
+ sm: { $value: "8px" },
93
+ md: { $value: "16px" },
94
+ lg: { $value: "24px" }
95
+ }
96
+ };
97
+ }
98
+ function getSemanticTokens() {
99
+ return {
100
+ color: {
101
+ $type: "color",
102
+ background: {
103
+ primary: { $value: "{color.white}" },
104
+ secondary: { $value: "{color.gray.50}" }
105
+ },
106
+ text: {
107
+ primary: { $value: "{color.gray.900}" }
108
+ }
109
+ }
110
+ };
111
+ }
112
+ function getDarkTokens() {
113
+ return {
114
+ $description: "Dark theme overrides",
115
+ $extensions: { "com.ryndesign.theme": { name: "dark", extends: "default" } },
116
+ color: {
117
+ $type: "color",
118
+ background: {
119
+ primary: { $value: "{color.gray.900}" },
120
+ secondary: { $value: "{color.gray.800}" }
121
+ },
122
+ text: {
123
+ primary: { $value: "{color.gray.50}" }
124
+ }
125
+ }
126
+ };
127
+ }
128
+ var import_citty, p, import_picocolors, import_promises, import_node_path, import_meta, init_default;
129
+ var init_init = __esm({
130
+ "src/commands/init.ts"() {
131
+ "use strict";
132
+ import_citty = require("citty");
133
+ p = __toESM(require("@clack/prompts"), 1);
134
+ import_picocolors = __toESM(require("picocolors"), 1);
135
+ import_promises = __toESM(require("fs/promises"), 1);
136
+ import_node_path = __toESM(require("path"), 1);
137
+ import_meta = {};
138
+ init_default = (0, import_citty.defineCommand)({
139
+ meta: {
140
+ name: "init",
141
+ description: "Initialize a new RynDesign project"
142
+ },
143
+ args: {
144
+ template: {
145
+ type: "string",
146
+ description: "Template to use (minimal or full)",
147
+ default: "minimal"
148
+ },
149
+ platforms: {
150
+ type: "string",
151
+ description: "Comma-separated list of target platforms"
152
+ }
153
+ },
154
+ async run({ args }) {
155
+ p.intro(import_picocolors.default.bgCyan(import_picocolors.default.black(" RynDesign Init ")));
156
+ const template = args.template || await p.select({
157
+ message: "Choose a template:",
158
+ options: [
159
+ { value: "minimal", label: "Minimal", hint: "Basic color, spacing, typography tokens" },
160
+ { value: "full", label: "Full", hint: "Complete token set with shadows, borders, gradients" }
161
+ ]
162
+ });
163
+ if (p.isCancel(template)) {
164
+ p.cancel("Operation cancelled.");
165
+ process.exit(0);
166
+ }
167
+ const platformsInput = args.platforms || await p.multiselect({
168
+ message: "Select target platforms:",
169
+ initialValues: ["react", "swiftui"],
170
+ options: [
171
+ { value: "react", label: "React", hint: "recommended" },
172
+ { value: "swiftui", label: "SwiftUI", hint: "recommended" },
173
+ { value: "vue", label: "Vue" },
174
+ { value: "svelte", label: "Svelte" },
175
+ { value: "rails", label: "Rails" },
176
+ { value: "uikit", label: "UIKit" },
177
+ { value: "compose", label: "Jetpack Compose" },
178
+ { value: "android-view", label: "Android View" }
179
+ ],
180
+ required: true
181
+ });
182
+ if (p.isCancel(platformsInput)) {
183
+ p.cancel("Operation cancelled.");
184
+ process.exit(0);
185
+ }
186
+ const platforms = Array.isArray(platformsInput) ? platformsInput : platformsInput.split(",").map((s2) => s2.trim());
187
+ const darkMode = await p.confirm({
188
+ message: "Enable dark mode support?",
189
+ initialValue: true
190
+ });
191
+ if (p.isCancel(darkMode)) {
192
+ p.cancel("Operation cancelled.");
193
+ process.exit(0);
194
+ }
195
+ const s = p.spinner();
196
+ s.start("Creating project files...");
197
+ const cwd = process.cwd();
198
+ await import_promises.default.mkdir(import_node_path.default.join(cwd, "tokens"), { recursive: true });
199
+ await import_promises.default.mkdir(import_node_path.default.join(cwd, "components"), { recursive: true });
200
+ await import_promises.default.mkdir(import_node_path.default.join(cwd, "generated"), { recursive: true });
201
+ const templateDir = import_node_path.default.resolve(
202
+ typeof __dirname !== "undefined" ? import_node_path.default.resolve(__dirname, "../../templates") : import_node_path.default.resolve(new URL(".", import_meta.url).pathname, "../../templates")
203
+ );
204
+ const templateFile = template === "full" ? "full.tokens.json" : "minimal.tokens.json";
205
+ try {
206
+ const templateContent = await import_promises.default.readFile(
207
+ import_node_path.default.join(templateDir, templateFile),
208
+ "utf-8"
209
+ );
210
+ await import_promises.default.writeFile(
211
+ import_node_path.default.join(cwd, "tokens", "base.tokens.json"),
212
+ templateContent
213
+ );
214
+ } catch {
215
+ await import_promises.default.writeFile(
216
+ import_node_path.default.join(cwd, "tokens", "base.tokens.json"),
217
+ JSON.stringify(getDefaultTokens(), null, 2)
218
+ );
219
+ }
220
+ await import_promises.default.writeFile(
221
+ import_node_path.default.join(cwd, "tokens", "semantic.tokens.json"),
222
+ JSON.stringify(getSemanticTokens(), null, 2)
223
+ );
224
+ if (darkMode) {
225
+ try {
226
+ const darkContent = await import_promises.default.readFile(
227
+ import_node_path.default.join(templateDir, "dark.tokens.json"),
228
+ "utf-8"
229
+ );
230
+ await import_promises.default.writeFile(
231
+ import_node_path.default.join(cwd, "tokens", "dark.tokens.json"),
232
+ darkContent
233
+ );
234
+ } catch {
235
+ await import_promises.default.writeFile(
236
+ import_node_path.default.join(cwd, "tokens", "dark.tokens.json"),
237
+ JSON.stringify(getDarkTokens(), null, 2)
238
+ );
239
+ }
240
+ }
241
+ const configContent = generateConfig(platforms, darkMode);
242
+ await import_promises.default.writeFile(import_node_path.default.join(cwd, "ryndesign.config.ts"), configContent);
243
+ try {
244
+ const buttonComp = await import_promises.default.readFile(
245
+ import_node_path.default.join(templateDir, "../components/button.component.json"),
246
+ "utf-8"
247
+ );
248
+ await import_promises.default.writeFile(
249
+ import_node_path.default.join(cwd, "components", "button.component.json"),
250
+ buttonComp
251
+ );
252
+ } catch {
253
+ }
254
+ const gitignorePath = import_node_path.default.join(cwd, ".gitignore");
255
+ try {
256
+ let gitignore = "";
257
+ try {
258
+ gitignore = await import_promises.default.readFile(gitignorePath, "utf-8");
259
+ } catch {
260
+ }
261
+ if (!gitignore.includes("generated/")) {
262
+ gitignore += (gitignore.endsWith("\n") || gitignore === "" ? "" : "\n") + "generated/\n";
263
+ await import_promises.default.writeFile(gitignorePath, gitignore);
264
+ }
265
+ } catch {
266
+ }
267
+ try {
268
+ const pkgJsonPath = import_node_path.default.join(cwd, "package.json");
269
+ const pkgContent = await import_promises.default.readFile(pkgJsonPath, "utf-8");
270
+ const pkg = JSON.parse(pkgContent);
271
+ if (!pkg.scripts) pkg.scripts = {};
272
+ if (!pkg.scripts["generate"]) pkg.scripts["generate"] = "ryndesign generate";
273
+ if (!pkg.scripts["preview"]) pkg.scripts["preview"] = "ryndesign preview";
274
+ await import_promises.default.writeFile(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n");
275
+ } catch {
276
+ }
277
+ s.stop("Project files created!");
278
+ p.note(
279
+ [
280
+ `${import_picocolors.default.green("tokens/")} - Your design tokens`,
281
+ `${import_picocolors.default.green("components/")} - Component definitions`,
282
+ `${import_picocolors.default.green("generated/")} - Generated output (gitignored)`,
283
+ `${import_picocolors.default.green("ryndesign.config.ts")} - Configuration`
284
+ ].join("\n"),
285
+ "Project structure"
286
+ );
287
+ p.outro(import_picocolors.default.green("Run `ryndesign generate` to generate your design system!"));
288
+ }
289
+ });
290
+ }
291
+ });
292
+
293
+ // src/config.ts
294
+ async function loadConfig(configPath) {
295
+ const cwd = process.cwd();
296
+ if (configPath) {
297
+ return loadConfigFile(import_node_path2.default.resolve(cwd, configPath));
298
+ }
299
+ for (const name of CONFIG_NAMES) {
300
+ const absolutePath = import_node_path2.default.resolve(cwd, name);
301
+ try {
302
+ await import_promises2.default.access(absolutePath);
303
+ return loadConfigFile(absolutePath);
304
+ } catch {
305
+ continue;
306
+ }
307
+ }
308
+ return null;
309
+ }
310
+ async function loadConfigFile(absolutePath) {
311
+ try {
312
+ await import_promises2.default.access(absolutePath);
313
+ } catch {
314
+ return null;
315
+ }
316
+ if (absolutePath.endsWith(".json")) {
317
+ try {
318
+ const content = await import_promises2.default.readFile(absolutePath, "utf-8");
319
+ const config = JSON.parse(content);
320
+ return validateConfig(config, absolutePath);
321
+ } catch (err) {
322
+ throw new Error(`Failed to parse config ${absolutePath}: ${err.message}`);
323
+ }
324
+ }
325
+ try {
326
+ const { createJiti } = await import("jiti");
327
+ const loader = createJiti(absolutePath, {
328
+ interopDefault: true
329
+ });
330
+ const config = await loader.import(absolutePath);
331
+ const resolved = config.default ?? config;
332
+ return validateConfig(resolved, absolutePath);
333
+ } catch (err) {
334
+ try {
335
+ const mod = await import(absolutePath);
336
+ const resolved = mod.default ?? mod;
337
+ return validateConfig(resolved, absolutePath);
338
+ } catch {
339
+ throw new Error(`Failed to load config ${absolutePath}: ${err.message}`);
340
+ }
341
+ }
342
+ }
343
+ function validateConfig(config, filePath) {
344
+ if (!config.tokens || !Array.isArray(config.tokens) || config.tokens.length === 0) {
345
+ throw new Error(`Invalid config in ${filePath}: "tokens" must be a non-empty array of glob patterns`);
346
+ }
347
+ if (config.components && !Array.isArray(config.components)) {
348
+ throw new Error(`Invalid config in ${filePath}: "components" must be an array of glob patterns`);
349
+ }
350
+ if (config.generators && !Array.isArray(config.generators)) {
351
+ throw new Error(`Invalid config in ${filePath}: "generators" must be an array`);
352
+ }
353
+ return config;
354
+ }
355
+ var import_node_path2, import_promises2, import_plugin_api, CONFIG_NAMES;
356
+ var init_config = __esm({
357
+ "src/config.ts"() {
358
+ "use strict";
359
+ import_node_path2 = __toESM(require("path"), 1);
360
+ import_promises2 = __toESM(require("fs/promises"), 1);
361
+ import_plugin_api = require("@ryndesign/plugin-api");
362
+ CONFIG_NAMES = [
363
+ "ryndesign.config.ts",
364
+ "ryndesign.config.js",
365
+ "ryndesign.config.mjs",
366
+ "ryndesign.config.json"
367
+ ];
368
+ }
369
+ });
370
+
371
+ // src/commands/generate.ts
372
+ var generate_exports = {};
373
+ __export(generate_exports, {
374
+ default: () => generate_default
375
+ });
376
+ var import_citty2, import_picocolors2, import_node_path3, import_promises3, import_core, generate_default;
377
+ var init_generate = __esm({
378
+ "src/commands/generate.ts"() {
379
+ "use strict";
380
+ import_citty2 = require("citty");
381
+ import_picocolors2 = __toESM(require("picocolors"), 1);
382
+ import_node_path3 = __toESM(require("path"), 1);
383
+ import_promises3 = __toESM(require("fs/promises"), 1);
384
+ import_core = require("@ryndesign/core");
385
+ init_config();
386
+ generate_default = (0, import_citty2.defineCommand)({
387
+ meta: {
388
+ name: "generate",
389
+ description: "Generate design system files for target platforms"
390
+ },
391
+ args: {
392
+ platforms: {
393
+ type: "string",
394
+ description: "Comma-separated list of platforms to generate"
395
+ },
396
+ watch: {
397
+ type: "boolean",
398
+ description: "Watch for changes and regenerate",
399
+ default: false
400
+ },
401
+ clean: {
402
+ type: "boolean",
403
+ description: "Clean output directory before generating",
404
+ default: false
405
+ },
406
+ "dry-run": {
407
+ type: "boolean",
408
+ description: "Show what would be generated without writing files",
409
+ default: false
410
+ },
411
+ config: {
412
+ type: "string",
413
+ description: "Path to config file",
414
+ default: "ryndesign.config.ts"
415
+ }
416
+ },
417
+ async run({ args }) {
418
+ const dryRun = args["dry-run"];
419
+ console.log(import_picocolors2.default.cyan(`\u26A1 RynDesign Generate${dryRun ? " (dry run)" : ""}
420
+ `));
421
+ const config = await loadConfig(args.config);
422
+ if (!config) {
423
+ console.error(import_picocolors2.default.red("Config file not found. Run `ryndesign init` first."));
424
+ process.exit(1);
425
+ }
426
+ const cwd = process.cwd();
427
+ if (args.clean && config.outDir) {
428
+ const outDir = import_node_path3.default.resolve(cwd, config.outDir);
429
+ await import_promises3.default.rm(outDir, { recursive: true, force: true });
430
+ await import_promises3.default.mkdir(outDir, { recursive: true });
431
+ console.log(import_picocolors2.default.yellow(`Cleaned ${config.outDir}/`));
432
+ }
433
+ console.log(import_picocolors2.default.gray("Building token set..."));
434
+ const tokenSet = await (0, import_core.buildTokenSet)({
435
+ tokens: config.tokens,
436
+ basePath: cwd,
437
+ themes: config.themes,
438
+ name: "design-system"
439
+ });
440
+ console.log(import_picocolors2.default.green(`\u2713 Resolved ${tokenSet.tokens.length} tokens`));
441
+ if (Object.keys(tokenSet.themes.themes).length > 0) {
442
+ console.log(import_picocolors2.default.green(`\u2713 Resolved themes: ${Object.keys(tokenSet.themes.themes).join(", ")}`));
443
+ }
444
+ let generators = config.generators ?? [];
445
+ if (args.platforms) {
446
+ const targetPlatforms = args.platforms.split(",").map((s) => s.trim());
447
+ generators = generators.filter((g) => targetPlatforms.includes(g.name));
448
+ }
449
+ if (generators.length === 0) {
450
+ console.log(import_picocolors2.default.yellow("No generators configured. Add generators to your ryndesign.config.ts"));
451
+ return;
452
+ }
453
+ const components = config.components ? await (0, import_core.loadComponents)(config.components, cwd) : [];
454
+ if (components.length > 0) {
455
+ console.log(import_picocolors2.default.green(`\u2713 Loaded ${components.length} component(s)`));
456
+ }
457
+ const resolvedComponents = components.map((compDef) => (0, import_core.resolveComponent)(compDef, tokenSet));
458
+ const allFiles = [];
459
+ const summary = [];
460
+ for (const generator of generators) {
461
+ console.log(import_picocolors2.default.gray(`
462
+ Generating ${generator.displayName}...`));
463
+ let genFiles = 0;
464
+ let genBytes = 0;
465
+ const ctx = {
466
+ tokenSet,
467
+ config: { outDir: config.outDir ?? "generated", ...generator },
468
+ outputDir: import_node_path3.default.resolve(cwd, config.outDir ?? "generated"),
469
+ helpers: (0, import_core.createGeneratorHelpers)(),
470
+ components: resolvedComponents
471
+ };
472
+ try {
473
+ const tokenFiles = await generator.generateTokens(ctx);
474
+ allFiles.push(...tokenFiles);
475
+ for (const file of tokenFiles) {
476
+ genFiles++;
477
+ genBytes += Buffer.byteLength(file.content, "utf-8");
478
+ if (!dryRun) {
479
+ const filePath = import_node_path3.default.resolve(ctx.outputDir, file.path);
480
+ await import_promises3.default.mkdir(import_node_path3.default.dirname(filePath), { recursive: true });
481
+ await import_promises3.default.writeFile(filePath, file.content, "utf-8");
482
+ }
483
+ }
484
+ for (const resolvedComp of resolvedComponents) {
485
+ const compFiles = await generator.generateComponent(resolvedComp, ctx);
486
+ allFiles.push(...compFiles);
487
+ for (const file of compFiles) {
488
+ genFiles++;
489
+ genBytes += Buffer.byteLength(file.content, "utf-8");
490
+ if (!dryRun) {
491
+ const filePath = import_node_path3.default.resolve(ctx.outputDir, file.path);
492
+ await import_promises3.default.mkdir(import_node_path3.default.dirname(filePath), { recursive: true });
493
+ await import_promises3.default.writeFile(filePath, file.content, "utf-8");
494
+ }
495
+ }
496
+ }
497
+ console.log(import_picocolors2.default.green(` \u2713 ${genFiles} files generated for ${generator.displayName}`));
498
+ summary.push({ name: generator.displayName, files: genFiles, bytes: genBytes });
499
+ } catch (err) {
500
+ console.error(import_picocolors2.default.red(` \u2717 ${generator.displayName} failed: ${err.message}`));
501
+ }
502
+ }
503
+ if (summary.length > 0) {
504
+ console.log(import_picocolors2.default.gray("\n Platform Files Size"));
505
+ console.log(import_picocolors2.default.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
506
+ for (const s of summary) {
507
+ const sizeStr = s.bytes < 1024 ? `${s.bytes} B` : `${(s.bytes / 1024).toFixed(1)} KB`;
508
+ console.log(` ${s.name.padEnd(18)} ${String(s.files).padStart(5)} ${sizeStr}`);
509
+ }
510
+ }
511
+ if (config.hooks?.["generate:complete"]) {
512
+ await config.hooks["generate:complete"](allFiles);
513
+ }
514
+ console.log(import_picocolors2.default.green(`
515
+ \u2713 Generation complete! ${allFiles.length} files ${dryRun ? "would be written" : "written"}.`));
516
+ if (args.watch) {
517
+ console.log(import_picocolors2.default.cyan("\nWatching for changes..."));
518
+ const { watch } = await import("chokidar");
519
+ const watchPatterns = [...config.tokens, ...config.components ?? []];
520
+ const watcher = watch(watchPatterns, {
521
+ cwd,
522
+ ignoreInitial: true
523
+ });
524
+ watcher.on("change", async (changedPath) => {
525
+ console.log(import_picocolors2.default.gray(`
526
+ File changed: ${changedPath}`));
527
+ try {
528
+ const newTokenSet = await (0, import_core.buildTokenSet)({
529
+ tokens: config.tokens,
530
+ basePath: cwd,
531
+ themes: config.themes
532
+ });
533
+ const newComponents = config.components ? await (0, import_core.loadComponents)(config.components, cwd) : [];
534
+ const newResolvedComponents = newComponents.map((c) => (0, import_core.resolveComponent)(c, newTokenSet));
535
+ for (const generator of generators) {
536
+ try {
537
+ const ctx = {
538
+ tokenSet: newTokenSet,
539
+ config: { outDir: config.outDir ?? "generated", ...generator },
540
+ outputDir: import_node_path3.default.resolve(cwd, config.outDir ?? "generated"),
541
+ helpers: (0, import_core.createGeneratorHelpers)(),
542
+ components: newResolvedComponents
543
+ };
544
+ const tokenFiles = await generator.generateTokens(ctx);
545
+ for (const file of tokenFiles) {
546
+ const filePath = import_node_path3.default.resolve(ctx.outputDir, file.path);
547
+ await import_promises3.default.mkdir(import_node_path3.default.dirname(filePath), { recursive: true });
548
+ await import_promises3.default.writeFile(filePath, file.content, "utf-8");
549
+ }
550
+ for (const resolvedComp of newResolvedComponents) {
551
+ const compFiles = await generator.generateComponent(resolvedComp, ctx);
552
+ for (const file of compFiles) {
553
+ const filePath = import_node_path3.default.resolve(ctx.outputDir, file.path);
554
+ await import_promises3.default.mkdir(import_node_path3.default.dirname(filePath), { recursive: true });
555
+ await import_promises3.default.writeFile(filePath, file.content, "utf-8");
556
+ }
557
+ }
558
+ } catch (err) {
559
+ console.error(import_picocolors2.default.red(` \u2717 ${generator.displayName}: ${err.message}`));
560
+ }
561
+ }
562
+ console.log(import_picocolors2.default.green("\u2713 Rebuilt and regenerated"));
563
+ } catch (err) {
564
+ console.error(import_picocolors2.default.red(`Error: ${err.message}`));
565
+ }
566
+ });
567
+ }
568
+ }
569
+ });
570
+ }
571
+ });
572
+
573
+ // src/commands/validate.ts
574
+ var validate_exports = {};
575
+ __export(validate_exports, {
576
+ default: () => validate_default
577
+ });
578
+ var import_citty3, import_picocolors3, import_core2, validate_default;
579
+ var init_validate = __esm({
580
+ "src/commands/validate.ts"() {
581
+ "use strict";
582
+ import_citty3 = require("citty");
583
+ import_picocolors3 = __toESM(require("picocolors"), 1);
584
+ import_core2 = require("@ryndesign/core");
585
+ validate_default = (0, import_citty3.defineCommand)({
586
+ meta: {
587
+ name: "validate",
588
+ description: "Validate design token files"
589
+ },
590
+ args: {
591
+ path: {
592
+ type: "positional",
593
+ description: "Path to token file(s)",
594
+ required: false
595
+ },
596
+ strict: {
597
+ type: "boolean",
598
+ description: "Treat warnings as errors",
599
+ default: false
600
+ }
601
+ },
602
+ async run({ args }) {
603
+ console.log(import_picocolors3.default.cyan(" RynDesign Validate\n"));
604
+ const patterns = args.path ? [args.path] : ["tokens/**/*.tokens.json"];
605
+ try {
606
+ const tree = await (0, import_core2.readAndMergeTokenFiles)(patterns);
607
+ (0, import_core2.validateTree)(tree);
608
+ const tokenSet = await (0, import_core2.buildTokenSet)({
609
+ tokens: patterns,
610
+ basePath: process.cwd()
611
+ });
612
+ const issues = (0, import_core2.postValidate)(tokenSet.tokens, tokenSet.themes);
613
+ const errors = issues.filter((i) => i.severity === "error");
614
+ const warnings = issues.filter((i) => i.severity === "warning");
615
+ console.log(import_picocolors3.default.green(` ${tokenSet.tokens.length} tokens resolved`));
616
+ console.log(import_picocolors3.default.green(` ${tokenSet.groups.length} groups`));
617
+ const themeCount = Object.keys(tokenSet.themes.themes).length;
618
+ if (themeCount > 0) {
619
+ console.log(import_picocolors3.default.green(` ${themeCount} theme(s): ${Object.keys(tokenSet.themes.themes).join(", ")}`));
620
+ }
621
+ if (errors.length > 0) {
622
+ console.error(import_picocolors3.default.red(`
623
+ ${errors.length} error(s):
624
+ `));
625
+ for (const issue of errors) {
626
+ console.error(` ${import_picocolors3.default.red("x")} ${issue.path}: ${issue.message}`);
627
+ }
628
+ }
629
+ if (warnings.length > 0) {
630
+ console.log(import_picocolors3.default.yellow(`
631
+ ${warnings.length} warning(s):
632
+ `));
633
+ for (const issue of warnings) {
634
+ console.log(` ${import_picocolors3.default.yellow("!")} ${issue.path}: ${issue.message}`);
635
+ }
636
+ }
637
+ if (errors.length === 0 && warnings.length === 0) {
638
+ console.log(import_picocolors3.default.green("\n All tokens are valid!"));
639
+ } else if (errors.length === 0) {
640
+ console.log(import_picocolors3.default.green("\n Tokens are valid (with warnings)"));
641
+ }
642
+ if (args.strict && (errors.length > 0 || warnings.length > 0)) {
643
+ process.exit(1);
644
+ }
645
+ if (errors.length > 0) {
646
+ process.exit(1);
647
+ }
648
+ } catch (err) {
649
+ if (err instanceof import_core2.TokenValidationError) {
650
+ console.error(import_picocolors3.default.red(` Validation failed with ${err.errors.length} issue(s):
651
+ `));
652
+ for (const issue of err.errors) {
653
+ const icon = issue.severity === "error" ? import_picocolors3.default.red("x") : import_picocolors3.default.yellow("!");
654
+ console.error(` ${icon} ${issue.path.join(".")}: ${issue.message}`);
655
+ }
656
+ process.exit(1);
657
+ }
658
+ throw err;
659
+ }
660
+ }
661
+ });
662
+ }
663
+ });
664
+
665
+ // src/commands/preview.ts
666
+ var preview_exports = {};
667
+ __export(preview_exports, {
668
+ default: () => preview_default
669
+ });
670
+ var import_citty4, import_picocolors4, preview_default;
671
+ var init_preview = __esm({
672
+ "src/commands/preview.ts"() {
673
+ "use strict";
674
+ import_citty4 = require("citty");
675
+ import_picocolors4 = __toESM(require("picocolors"), 1);
676
+ preview_default = (0, import_citty4.defineCommand)({
677
+ meta: {
678
+ name: "preview",
679
+ description: "Start the design system preview server"
680
+ },
681
+ args: {
682
+ port: {
683
+ type: "string",
684
+ description: "Port to run the preview server on",
685
+ default: "4400"
686
+ },
687
+ open: {
688
+ type: "boolean",
689
+ description: "Open browser automatically",
690
+ default: true
691
+ }
692
+ },
693
+ async run({ args }) {
694
+ console.log(import_picocolors4.default.cyan("\u{1F5A5} RynDesign Preview\n"));
695
+ try {
696
+ const { startPreviewServer } = await import("@ryndesign/preview");
697
+ await startPreviewServer({
698
+ port: parseInt(args.port, 10),
699
+ open: args.open
700
+ });
701
+ } catch {
702
+ console.log(import_picocolors4.default.yellow("Preview package not found. Install @ryndesign/preview"));
703
+ console.log(import_picocolors4.default.gray(" pnpm add @ryndesign/preview"));
704
+ }
705
+ }
706
+ });
707
+ }
708
+ });
709
+
710
+ // src/commands/add.ts
711
+ var add_exports = {};
712
+ __export(add_exports, {
713
+ default: () => add_default
714
+ });
715
+ var import_citty5, p2, import_picocolors5, import_promises4, import_node_path4, import_meta2, AVAILABLE_COMPONENTS, COMPONENT_TOKENS, add_default;
716
+ var init_add = __esm({
717
+ "src/commands/add.ts"() {
718
+ "use strict";
719
+ import_citty5 = require("citty");
720
+ p2 = __toESM(require("@clack/prompts"), 1);
721
+ import_picocolors5 = __toESM(require("picocolors"), 1);
722
+ import_promises4 = __toESM(require("fs/promises"), 1);
723
+ import_node_path4 = __toESM(require("path"), 1);
724
+ import_meta2 = {};
725
+ AVAILABLE_COMPONENTS = ["button", "input", "card", "checkbox", "toggle", "badge", "avatar"];
726
+ COMPONENT_TOKENS = {
727
+ button: {
728
+ "component.button": {
729
+ $type: "color",
730
+ primary: {
731
+ background: { $value: "{color.primary}" },
732
+ text: { $value: "{color.white}" },
733
+ hover: { background: { $value: "{color.primary}" } },
734
+ pressed: { background: { $value: "{color.primary}" } }
735
+ },
736
+ secondary: {
737
+ background: { $value: "{color.secondary}" },
738
+ text: { $value: "{color.white}" }
739
+ },
740
+ borderRadius: { $type: "dimension", $value: "8px" },
741
+ sm: { paddingX: { $type: "dimension", $value: "12px" }, paddingY: { $type: "dimension", $value: "6px" }, fontSize: { $type: "dimension", $value: "14px" } },
742
+ md: { paddingX: { $type: "dimension", $value: "16px" }, paddingY: { $type: "dimension", $value: "10px" }, fontSize: { $type: "dimension", $value: "16px" } },
743
+ lg: { paddingX: { $type: "dimension", $value: "24px" }, paddingY: { $type: "dimension", $value: "14px" }, fontSize: { $type: "dimension", $value: "18px" } },
744
+ disabled: { background: { $value: "{color.gray.300}" } }
745
+ }
746
+ }
747
+ };
748
+ add_default = (0, import_citty5.defineCommand)({
749
+ meta: {
750
+ name: "add",
751
+ description: "Add a component definition"
752
+ },
753
+ args: {
754
+ name: {
755
+ type: "positional",
756
+ description: "Component name to add",
757
+ required: false
758
+ },
759
+ "with-tokens": {
760
+ type: "boolean",
761
+ description: "Also scaffold component tokens",
762
+ default: false
763
+ }
764
+ },
765
+ async run({ args }) {
766
+ const name = args.name || await p2.select({
767
+ message: "Choose a component to add:",
768
+ options: AVAILABLE_COMPONENTS.map((c) => ({
769
+ value: c,
770
+ label: c.charAt(0).toUpperCase() + c.slice(1)
771
+ }))
772
+ });
773
+ if (p2.isCancel(name)) {
774
+ p2.cancel("Operation cancelled.");
775
+ process.exit(0);
776
+ }
777
+ const cwd = process.cwd();
778
+ const componentsDir = import_node_path4.default.join(cwd, "components");
779
+ await import_promises4.default.mkdir(componentsDir, { recursive: true });
780
+ const destPath = import_node_path4.default.join(componentsDir, `${name}.component.json`);
781
+ try {
782
+ await import_promises4.default.access(destPath);
783
+ console.log(import_picocolors5.default.yellow(`Component ${name} already exists at ${destPath}`));
784
+ return;
785
+ } catch {
786
+ }
787
+ let copied = false;
788
+ const searchPaths = [
789
+ import_node_path4.default.resolve(__dirname, `../../components/${name}.component.json`),
790
+ // ESM fallback
791
+ typeof __dirname !== "undefined" ? import_node_path4.default.resolve(__dirname, `../../components/${name}.component.json`) : import_node_path4.default.resolve(new URL(".", import_meta2.url).pathname, `../../components/${name}.component.json`)
792
+ ];
793
+ for (const srcPath of searchPaths) {
794
+ try {
795
+ const content = await import_promises4.default.readFile(srcPath, "utf-8");
796
+ await import_promises4.default.writeFile(destPath, content);
797
+ console.log(import_picocolors5.default.green(` Added ${name} component to ${destPath}`));
798
+ copied = true;
799
+ break;
800
+ } catch {
801
+ continue;
802
+ }
803
+ }
804
+ if (!copied) {
805
+ console.error(import_picocolors5.default.red(`Component "${name}" not found in available components.`));
806
+ console.log(import_picocolors5.default.gray(`Available: ${AVAILABLE_COMPONENTS.join(", ")}`));
807
+ return;
808
+ }
809
+ const withTokens = args["with-tokens"];
810
+ if (withTokens && COMPONENT_TOKENS[name]) {
811
+ const tokensDir = import_node_path4.default.join(cwd, "tokens");
812
+ await import_promises4.default.mkdir(tokensDir, { recursive: true });
813
+ const tokenFile = import_node_path4.default.join(tokensDir, `${name}.tokens.json`);
814
+ try {
815
+ await import_promises4.default.access(tokenFile);
816
+ console.log(import_picocolors5.default.yellow(` Token file already exists: ${tokenFile}`));
817
+ } catch {
818
+ await import_promises4.default.writeFile(tokenFile, JSON.stringify(COMPONENT_TOKENS[name], null, 2));
819
+ console.log(import_picocolors5.default.green(` Created component tokens: ${tokenFile}`));
820
+ }
821
+ }
822
+ }
823
+ });
824
+ }
825
+ });
826
+
827
+ // src/commands/figma-pull.ts
828
+ var figma_pull_exports = {};
829
+ __export(figma_pull_exports, {
830
+ default: () => figma_pull_default
831
+ });
832
+ async function readJsonSafe(absolutePath) {
833
+ try {
834
+ const content = await import_promises5.default.readFile(absolutePath, "utf-8");
835
+ return JSON.parse(content);
836
+ } catch {
837
+ return null;
838
+ }
839
+ }
840
+ var import_citty6, import_picocolors6, import_promises5, import_node_path5, figma_pull_default;
841
+ var init_figma_pull = __esm({
842
+ "src/commands/figma-pull.ts"() {
843
+ "use strict";
844
+ import_citty6 = require("citty");
845
+ import_picocolors6 = __toESM(require("picocolors"), 1);
846
+ import_promises5 = __toESM(require("fs/promises"), 1);
847
+ import_node_path5 = __toESM(require("path"), 1);
848
+ init_config();
849
+ figma_pull_default = (0, import_citty6.defineCommand)({
850
+ meta: {
851
+ name: "pull",
852
+ description: "Pull design tokens from Figma"
853
+ },
854
+ args: {
855
+ "file-key": {
856
+ type: "string",
857
+ description: "Figma file key"
858
+ },
859
+ token: {
860
+ type: "string",
861
+ description: "Figma personal access token"
862
+ },
863
+ config: {
864
+ type: "string",
865
+ description: "Path to config file",
866
+ default: "ryndesign.config.ts"
867
+ },
868
+ merge: {
869
+ type: "boolean",
870
+ description: "Merge with existing local tokens instead of overwriting",
871
+ default: false
872
+ },
873
+ strategy: {
874
+ type: "string",
875
+ description: "Merge strategy: prefer-remote, prefer-local, remote-only-new",
876
+ default: "prefer-remote"
877
+ }
878
+ },
879
+ async run({ args }) {
880
+ console.log(import_picocolors6.default.cyan("\u{1F4E5} Figma Pull\n"));
881
+ const config = await loadConfig(args.config);
882
+ const fileKey = args["file-key"] || config?.figma?.fileKey;
883
+ const pat = args.token || config?.figma?.personalAccessToken || process.env.FIGMA_TOKEN;
884
+ if (!fileKey) {
885
+ console.error(import_picocolors6.default.red("Missing Figma file key. Use --file-key or set in config."));
886
+ process.exit(1);
887
+ }
888
+ if (!pat) {
889
+ console.error(import_picocolors6.default.red("Missing Figma token. Use --token, set FIGMA_TOKEN env var, or configure in ryndesign.config.ts"));
890
+ process.exit(1);
891
+ }
892
+ const shouldMerge = args.merge;
893
+ const strategy = args.strategy;
894
+ const validStrategies = ["prefer-remote", "prefer-local", "remote-only-new"];
895
+ if (shouldMerge && !validStrategies.includes(strategy)) {
896
+ console.error(import_picocolors6.default.red(`Invalid merge strategy "${strategy}". Use: ${validStrategies.join(", ")}`));
897
+ process.exit(1);
898
+ }
899
+ try {
900
+ const { fetchFigmaVariables, mapFigmaToTokens, resolveFigmaModes, mergeTokens } = await import("@ryndesign/figma");
901
+ console.log(import_picocolors6.default.gray("Fetching variables from Figma..."));
902
+ const variables = await fetchFigmaVariables({
903
+ fileKey,
904
+ personalAccessToken: pat
905
+ });
906
+ console.log(import_picocolors6.default.green(`\u2713 Fetched ${variables.length} variables`));
907
+ const cwd = process.cwd();
908
+ const modeMapping = config?.figma?.modeMapping;
909
+ if (modeMapping) {
910
+ const modeFiles = resolveFigmaModes(variables, modeMapping);
911
+ for (const [filePath, tree] of Object.entries(modeFiles)) {
912
+ const absolutePath = import_node_path5.default.resolve(cwd, filePath);
913
+ await import_promises5.default.mkdir(import_node_path5.default.dirname(absolutePath), { recursive: true });
914
+ if (shouldMerge) {
915
+ const existing = await readJsonSafe(absolutePath);
916
+ if (existing) {
917
+ const modeTokens = variables.filter((t) => {
918
+ const modeName = Object.keys(modeMapping).find((m) => modeMapping[m] === filePath);
919
+ return modeName && t.modes[modeName] !== void 0;
920
+ });
921
+ const result = mergeTokens(existing, modeTokens, { strategy });
922
+ await import_promises5.default.writeFile(absolutePath, JSON.stringify(result.merged, null, 2), "utf-8");
923
+ console.log(import_picocolors6.default.green(` \u2713 Merged ${filePath}`) + import_picocolors6.default.gray(` (+${result.stats.added} new, ${result.stats.updated} updated, ${result.stats.kept} kept)`));
924
+ continue;
925
+ }
926
+ }
927
+ await import_promises5.default.writeFile(absolutePath, JSON.stringify(tree, null, 2), "utf-8");
928
+ console.log(import_picocolors6.default.green(` \u2713 Written ${filePath}`));
929
+ }
930
+ } else {
931
+ const outPath = import_node_path5.default.resolve(cwd, "tokens/figma.tokens.json");
932
+ await import_promises5.default.mkdir(import_node_path5.default.dirname(outPath), { recursive: true });
933
+ if (shouldMerge) {
934
+ const existing = await readJsonSafe(outPath);
935
+ if (existing) {
936
+ const result = mergeTokens(existing, variables, { strategy });
937
+ await import_promises5.default.writeFile(outPath, JSON.stringify(result.merged, null, 2), "utf-8");
938
+ console.log(import_picocolors6.default.green(` \u2713 Merged tokens/figma.tokens.json`) + import_picocolors6.default.gray(` (+${result.stats.added} new, ${result.stats.updated} updated, ${result.stats.kept} kept)`));
939
+ } else {
940
+ const tree = mapFigmaToTokens(variables);
941
+ await import_promises5.default.writeFile(outPath, JSON.stringify(tree, null, 2), "utf-8");
942
+ console.log(import_picocolors6.default.green(` \u2713 Written tokens/figma.tokens.json`));
943
+ }
944
+ } else {
945
+ const tree = mapFigmaToTokens(variables);
946
+ await import_promises5.default.writeFile(outPath, JSON.stringify(tree, null, 2), "utf-8");
947
+ console.log(import_picocolors6.default.green(` \u2713 Written tokens/figma.tokens.json`));
948
+ }
949
+ }
950
+ console.log(import_picocolors6.default.green("\n\u2713 Figma pull complete!"));
951
+ } catch (err) {
952
+ console.error(import_picocolors6.default.red(`Error: ${err.message}`));
953
+ process.exit(1);
954
+ }
955
+ }
956
+ });
957
+ }
958
+ });
959
+
960
+ // src/commands/figma-push.ts
961
+ var figma_push_exports = {};
962
+ __export(figma_push_exports, {
963
+ default: () => figma_push_default
964
+ });
965
+ var import_citty7, import_picocolors7, import_core3, figma_push_default;
966
+ var init_figma_push = __esm({
967
+ "src/commands/figma-push.ts"() {
968
+ "use strict";
969
+ import_citty7 = require("citty");
970
+ import_picocolors7 = __toESM(require("picocolors"), 1);
971
+ import_core3 = require("@ryndesign/core");
972
+ init_config();
973
+ figma_push_default = (0, import_citty7.defineCommand)({
974
+ meta: {
975
+ name: "push",
976
+ description: "Push design tokens to Figma"
977
+ },
978
+ args: {
979
+ "file-key": {
980
+ type: "string",
981
+ description: "Figma file key"
982
+ },
983
+ token: {
984
+ type: "string",
985
+ description: "Figma personal access token"
986
+ },
987
+ config: {
988
+ type: "string",
989
+ description: "Path to config file",
990
+ default: "ryndesign.config.ts"
991
+ }
992
+ },
993
+ async run({ args }) {
994
+ console.log(import_picocolors7.default.cyan("\u{1F4E4} Figma Push\n"));
995
+ const config = await loadConfig(args.config);
996
+ const fileKey = args["file-key"] || config?.figma?.fileKey;
997
+ const pat = args.token || config?.figma?.personalAccessToken || process.env.FIGMA_TOKEN;
998
+ if (!fileKey) {
999
+ console.error(import_picocolors7.default.red("Missing Figma file key. Use --file-key or set in config."));
1000
+ process.exit(1);
1001
+ }
1002
+ if (!pat) {
1003
+ console.error(import_picocolors7.default.red("Missing Figma token. Use --token, set FIGMA_TOKEN env var, or configure in ryndesign.config.ts"));
1004
+ process.exit(1);
1005
+ }
1006
+ try {
1007
+ const { pushVariablesToFigma } = await import("@ryndesign/figma");
1008
+ const cwd = process.cwd();
1009
+ console.log(import_picocolors7.default.gray("Building token set..."));
1010
+ const tokenSet = await (0, import_core3.buildTokenSet)({
1011
+ tokens: config?.tokens ?? ["tokens/**/*.tokens.json"],
1012
+ basePath: cwd,
1013
+ themes: config?.themes
1014
+ });
1015
+ console.log(import_picocolors7.default.green(`\u2713 Resolved ${tokenSet.tokens.length} tokens`));
1016
+ console.log(import_picocolors7.default.gray("Pushing to Figma..."));
1017
+ const darkTokens = tokenSet.themes.themes["dark"]?.tokens;
1018
+ await pushVariablesToFigma({
1019
+ fileKey,
1020
+ personalAccessToken: pat,
1021
+ tokens: tokenSet.tokens,
1022
+ darkTokens
1023
+ });
1024
+ console.log(import_picocolors7.default.green("\n\u2713 Figma push complete!"));
1025
+ } catch (err) {
1026
+ console.error(import_picocolors7.default.red(`Error: ${err.message}`));
1027
+ process.exit(1);
1028
+ }
1029
+ }
1030
+ });
1031
+ }
1032
+ });
1033
+
1034
+ // src/commands/figma-diff.ts
1035
+ var figma_diff_exports = {};
1036
+ __export(figma_diff_exports, {
1037
+ default: () => figma_diff_default
1038
+ });
1039
+ var import_citty8, import_picocolors8, import_core4, figma_diff_default;
1040
+ var init_figma_diff = __esm({
1041
+ "src/commands/figma-diff.ts"() {
1042
+ "use strict";
1043
+ import_citty8 = require("citty");
1044
+ import_picocolors8 = __toESM(require("picocolors"), 1);
1045
+ import_core4 = require("@ryndesign/core");
1046
+ init_config();
1047
+ figma_diff_default = (0, import_citty8.defineCommand)({
1048
+ meta: {
1049
+ name: "diff",
1050
+ description: "Compare local tokens with Figma"
1051
+ },
1052
+ args: {
1053
+ "file-key": {
1054
+ type: "string",
1055
+ description: "Figma file key"
1056
+ },
1057
+ token: {
1058
+ type: "string",
1059
+ description: "Figma personal access token"
1060
+ },
1061
+ config: {
1062
+ type: "string",
1063
+ description: "Path to config file",
1064
+ default: "ryndesign.config.ts"
1065
+ }
1066
+ },
1067
+ async run({ args }) {
1068
+ console.log(import_picocolors8.default.cyan("\u{1F50D} Figma Diff\n"));
1069
+ const config = await loadConfig(args.config);
1070
+ const fileKey = args["file-key"] || config?.figma?.fileKey;
1071
+ const pat = args.token || config?.figma?.personalAccessToken || process.env.FIGMA_TOKEN;
1072
+ if (!fileKey) {
1073
+ console.error(import_picocolors8.default.red("Missing Figma file key. Use --file-key or set in config."));
1074
+ process.exit(1);
1075
+ }
1076
+ if (!pat) {
1077
+ console.error(import_picocolors8.default.red("Missing Figma token. Use --token, set FIGMA_TOKEN env var, or configure in ryndesign.config.ts"));
1078
+ process.exit(1);
1079
+ }
1080
+ try {
1081
+ const { fetchFigmaVariables, diffFigmaTokens } = await import("@ryndesign/figma");
1082
+ const cwd = process.cwd();
1083
+ console.log(import_picocolors8.default.gray("Building local token set..."));
1084
+ const tokenSet = await (0, import_core4.buildTokenSet)({
1085
+ tokens: config?.tokens ?? ["tokens/**/*.tokens.json"],
1086
+ basePath: cwd,
1087
+ themes: config?.themes
1088
+ });
1089
+ console.log(import_picocolors8.default.gray("Fetching Figma variables..."));
1090
+ const remoteTokens = await fetchFigmaVariables({
1091
+ fileKey,
1092
+ personalAccessToken: pat
1093
+ });
1094
+ const result = diffFigmaTokens(tokenSet.tokens, remoteTokens);
1095
+ if (result.added.length > 0) {
1096
+ console.log(import_picocolors8.default.green(`
1097
+ + Added (local only): ${result.added.length}`));
1098
+ for (const entry of result.added) {
1099
+ console.log(import_picocolors8.default.green(` + ${entry.path}: ${JSON.stringify(entry.local)}`));
1100
+ }
1101
+ }
1102
+ if (result.removed.length > 0) {
1103
+ console.log(import_picocolors8.default.red(`
1104
+ - Removed (Figma only): ${result.removed.length}`));
1105
+ for (const entry of result.removed) {
1106
+ console.log(import_picocolors8.default.red(` - ${entry.path}: ${JSON.stringify(entry.remote)}`));
1107
+ }
1108
+ }
1109
+ if (result.modified.length > 0) {
1110
+ console.log(import_picocolors8.default.yellow(`
1111
+ ~ Modified: ${result.modified.length}`));
1112
+ for (const entry of result.modified) {
1113
+ console.log(import_picocolors8.default.yellow(` ~ ${entry.path}: ${JSON.stringify(entry.local)} \u2190 ${JSON.stringify(entry.remote)}`));
1114
+ }
1115
+ }
1116
+ console.log(import_picocolors8.default.gray(`
1117
+ Unchanged: ${result.unchanged}`));
1118
+ const total = result.added.length + result.removed.length + result.modified.length;
1119
+ if (total === 0) {
1120
+ console.log(import_picocolors8.default.green("\n\u2713 Local and Figma tokens are in sync!"));
1121
+ } else {
1122
+ console.log(import_picocolors8.default.yellow(`
1123
+ ${total} difference(s) found.`));
1124
+ }
1125
+ } catch (err) {
1126
+ console.error(import_picocolors8.default.red(`Error: ${err.message}`));
1127
+ process.exit(1);
1128
+ }
1129
+ }
1130
+ });
1131
+ }
1132
+ });
1133
+
1134
+ // src/commands/figma.ts
1135
+ var figma_exports = {};
1136
+ __export(figma_exports, {
1137
+ default: () => figma_default
1138
+ });
1139
+ var import_citty9, import_picocolors9, import_node_path6, import_promises6, figma_default;
1140
+ var init_figma = __esm({
1141
+ "src/commands/figma.ts"() {
1142
+ "use strict";
1143
+ import_citty9 = require("citty");
1144
+ import_picocolors9 = require("picocolors");
1145
+ import_node_path6 = require("path");
1146
+ import_promises6 = require("fs/promises");
1147
+ init_config();
1148
+ figma_default = (0, import_citty9.defineCommand)({
1149
+ meta: {
1150
+ name: "figma",
1151
+ description: "Figma integration commands"
1152
+ },
1153
+ subCommands: {
1154
+ pull: () => Promise.resolve().then(() => (init_figma_pull(), figma_pull_exports)).then((m) => m.default),
1155
+ push: () => Promise.resolve().then(() => (init_figma_push(), figma_push_exports)).then((m) => m.default),
1156
+ diff: () => Promise.resolve().then(() => (init_figma_diff(), figma_diff_exports)).then((m) => m.default)
1157
+ }
1158
+ });
1159
+ }
1160
+ });
1161
+
1162
+ // src/commands/eject.ts
1163
+ var eject_exports = {};
1164
+ __export(eject_exports, {
1165
+ default: () => eject_default
1166
+ });
1167
+ async function copyDir(src, dest) {
1168
+ await import_promises7.default.mkdir(dest, { recursive: true });
1169
+ const entries = await import_promises7.default.readdir(src, { withFileTypes: true });
1170
+ for (const entry of entries) {
1171
+ const srcPath = import_node_path7.default.join(src, entry.name);
1172
+ const destPath = import_node_path7.default.join(dest, entry.name);
1173
+ if (entry.isDirectory()) {
1174
+ await copyDir(srcPath, destPath);
1175
+ } else {
1176
+ await import_promises7.default.copyFile(srcPath, destPath);
1177
+ }
1178
+ }
1179
+ }
1180
+ async function createSkeletonGenerator(dir, name) {
1181
+ const pascalName = name.charAt(0).toUpperCase() + name.slice(1).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
1182
+ const indexContent = `import type { GeneratorPlugin, GeneratorContext, GeneratedFile, ResolvedComponent } from '@ryndesign/plugin-api';
1183
+
1184
+ export default function ${name.replace(/-/g, "")}Generator(options = {}): GeneratorPlugin {
1185
+ return {
1186
+ name: '${name}',
1187
+ displayName: '${pascalName}',
1188
+ platform: 'web',
1189
+ outputExtensions: ['.ts', '.css'],
1190
+
1191
+ async generateTokens(ctx: GeneratorContext): Promise<GeneratedFile[]> {
1192
+ // TODO: Implement token generation
1193
+ return [];
1194
+ },
1195
+
1196
+ async generateComponent(comp: ResolvedComponent, ctx: GeneratorContext): Promise<GeneratedFile[]> {
1197
+ // TODO: Implement component generation
1198
+ return [];
1199
+ },
1200
+ };
1201
+ }
1202
+ `;
1203
+ await import_promises7.default.writeFile(import_node_path7.default.join(dir, "index.ts"), indexContent, "utf-8");
1204
+ }
1205
+ var import_citty10, p3, import_picocolors10, import_promises7, import_node_path7, GENERATORS, eject_default;
1206
+ var init_eject = __esm({
1207
+ "src/commands/eject.ts"() {
1208
+ "use strict";
1209
+ import_citty10 = require("citty");
1210
+ p3 = __toESM(require("@clack/prompts"), 1);
1211
+ import_picocolors10 = __toESM(require("picocolors"), 1);
1212
+ import_promises7 = __toESM(require("fs/promises"), 1);
1213
+ import_node_path7 = __toESM(require("path"), 1);
1214
+ GENERATORS = ["react", "vue", "svelte", "rails", "swiftui", "uikit", "compose", "android-view"];
1215
+ eject_default = (0, import_citty10.defineCommand)({
1216
+ meta: {
1217
+ name: "eject",
1218
+ description: "Eject a generator for customization"
1219
+ },
1220
+ args: {
1221
+ name: {
1222
+ type: "positional",
1223
+ description: "Generator name to eject",
1224
+ required: false
1225
+ }
1226
+ },
1227
+ async run({ args }) {
1228
+ p3.intro(import_picocolors10.default.bgYellow(import_picocolors10.default.black(" RynDesign Eject ")));
1229
+ const name = args.name || await p3.select({
1230
+ message: "Select a generator to eject:",
1231
+ options: GENERATORS.map((g) => ({ value: g, label: `@ryndesign/generator-${g}` }))
1232
+ });
1233
+ if (p3.isCancel(name)) {
1234
+ p3.cancel("Operation cancelled.");
1235
+ process.exit(0);
1236
+ }
1237
+ if (!GENERATORS.includes(name)) {
1238
+ console.error(import_picocolors10.default.red(`Unknown generator: ${name}`));
1239
+ console.log(import_picocolors10.default.gray(`Available: ${GENERATORS.join(", ")}`));
1240
+ process.exit(1);
1241
+ }
1242
+ const cwd = process.cwd();
1243
+ const destDir = import_node_path7.default.join(cwd, "generators", name);
1244
+ try {
1245
+ await import_promises7.default.access(destDir);
1246
+ console.error(import_picocolors10.default.yellow(`Generator "${name}" already ejected at generators/${name}/`));
1247
+ process.exit(0);
1248
+ } catch {
1249
+ }
1250
+ const confirm3 = await p3.confirm({
1251
+ message: `Eject @ryndesign/generator-${name} to generators/${name}/?`,
1252
+ initialValue: true
1253
+ });
1254
+ if (p3.isCancel(confirm3) || !confirm3) {
1255
+ p3.cancel("Operation cancelled.");
1256
+ process.exit(0);
1257
+ }
1258
+ const s = p3.spinner();
1259
+ s.start(`Ejecting generator-${name}...`);
1260
+ try {
1261
+ const pkgPath = require.resolve(`@ryndesign/generator-${name}/package.json`, {
1262
+ paths: [cwd]
1263
+ });
1264
+ const pkgDir = import_node_path7.default.dirname(pkgPath);
1265
+ const srcDir = import_node_path7.default.join(pkgDir, "src");
1266
+ let sourceDir;
1267
+ try {
1268
+ await import_promises7.default.access(srcDir);
1269
+ sourceDir = srcDir;
1270
+ } catch {
1271
+ sourceDir = import_node_path7.default.join(pkgDir, "dist");
1272
+ }
1273
+ await import_promises7.default.mkdir(destDir, { recursive: true });
1274
+ await copyDir(sourceDir, destDir);
1275
+ s.stop(`Generator ejected!`);
1276
+ p3.note(
1277
+ [
1278
+ `Files copied to: ${import_picocolors10.default.green(`generators/${name}/`)}`,
1279
+ "",
1280
+ "Update your ryndesign.config.ts:",
1281
+ import_picocolors10.default.gray(` import customGenerator from './generators/${name}/index.js';`),
1282
+ "",
1283
+ "Replace the original generator with your custom one."
1284
+ ].join("\n"),
1285
+ "Next steps"
1286
+ );
1287
+ } catch (err) {
1288
+ s.stop("Eject failed");
1289
+ console.error(import_picocolors10.default.red(`Error: ${err.message}`));
1290
+ s.start("Creating skeleton generator...");
1291
+ await createSkeletonGenerator(destDir, name);
1292
+ s.stop("Skeleton generator created!");
1293
+ p3.note(
1294
+ [
1295
+ `Skeleton created at: ${import_picocolors10.default.green(`generators/${name}/`)}`,
1296
+ "",
1297
+ "Customize the generator files and update your config."
1298
+ ].join("\n"),
1299
+ "Next steps"
1300
+ );
1301
+ }
1302
+ p3.outro(import_picocolors10.default.green("Done!"));
1303
+ }
1304
+ });
1305
+ }
1306
+ });
1307
+
1308
+ // src/bin.ts
1309
+ var import_citty12 = require("citty");
1310
+
1311
+ // src/commands/main.ts
1312
+ var import_citty11 = require("citty");
1313
+ var main = (0, import_citty11.defineCommand)({
1314
+ meta: {
1315
+ name: "ryndesign",
1316
+ version: "0.1.0",
1317
+ description: "Multi-platform design system generator"
1318
+ },
1319
+ subCommands: {
1320
+ init: () => Promise.resolve().then(() => (init_init(), init_exports)).then((m) => m.default),
1321
+ generate: () => Promise.resolve().then(() => (init_generate(), generate_exports)).then((m) => m.default),
1322
+ validate: () => Promise.resolve().then(() => (init_validate(), validate_exports)).then((m) => m.default),
1323
+ preview: () => Promise.resolve().then(() => (init_preview(), preview_exports)).then((m) => m.default),
1324
+ add: () => Promise.resolve().then(() => (init_add(), add_exports)).then((m) => m.default),
1325
+ figma: () => Promise.resolve().then(() => (init_figma(), figma_exports)).then((m) => m.default),
1326
+ eject: () => Promise.resolve().then(() => (init_eject(), eject_exports)).then((m) => m.default)
1327
+ }
1328
+ });
1329
+
1330
+ // src/bin.ts
1331
+ (0, import_citty12.runMain)(main);
1332
+ //# sourceMappingURL=bin.cjs.map