selia 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1386 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { program } from "commander";
5
+
6
+ // src/commands/add.ts
7
+ import { Command } from "commander";
8
+ import { intro, outro, spinner as spinner2, log as log2, select } from "@clack/prompts";
9
+ import fs5 from "fs/promises";
10
+ import path5 from "path";
11
+
12
+ // src/lib/load-config.ts
13
+ import fs from "fs/promises";
14
+ import path from "path";
15
+
16
+ // src/schemas/config-schema.ts
17
+ import { z } from "zod";
18
+ var ConfigSchema = z.object({
19
+ framework: z.string().optional(),
20
+ paths: z.record(z.string(), z.string()),
21
+ imports: z.record(z.string(), z.string()),
22
+ registries: z.object({
23
+ default: z.string().optional(),
24
+ sources: z.record(
25
+ z.string(),
26
+ z.object({
27
+ name: z.string(),
28
+ url: z.url(),
29
+ homepage: z.url().optional()
30
+ })
31
+ ).optional()
32
+ }).optional()
33
+ });
34
+
35
+ // src/lib/default-config.ts
36
+ var defaultConfig = {
37
+ framework: "react",
38
+ paths: {
39
+ components: "components/selia",
40
+ utils: "lib/utils.ts"
41
+ },
42
+ imports: {
43
+ utils: "@/lib/utils",
44
+ components: "@/components/selia"
45
+ },
46
+ registries: {
47
+ default: "selia",
48
+ sources: {
49
+ selia: {
50
+ name: "selia",
51
+ url: "http://localhost:5173/registry"
52
+ }
53
+ }
54
+ }
55
+ };
56
+
57
+ // src/lib/load-config.ts
58
+ import defu from "defu";
59
+ import { log } from "@clack/prompts";
60
+ import z2 from "zod";
61
+ async function loadConfig(cwd = process.cwd()) {
62
+ const configPath = path.join(cwd, "selia.json");
63
+ try {
64
+ const content = await fs.readFile(configPath, "utf-8");
65
+ const data = JSON.parse(content);
66
+ const merged = defu(data, defaultConfig);
67
+ const config = ConfigSchema.parse(merged);
68
+ log.info(`Loaded config from ${path.relative(cwd, configPath)}`);
69
+ return config;
70
+ } catch (error) {
71
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
72
+ log.warn(
73
+ `No config file found at ${path.relative(cwd, configPath)} (using default config)`
74
+ );
75
+ return defaultConfig;
76
+ }
77
+ if (error instanceof SyntaxError) {
78
+ throw new Error(
79
+ `Invalid JSON in config file ${configPath}: ${error.message}`
80
+ );
81
+ }
82
+ if (error instanceof z2.ZodError) {
83
+ throw new Error(
84
+ `Invalid config format:
85
+ ${error.issues.map((e) => ` - ${e.path.join(".")}: ${e.message}`).join("\n")}`
86
+ );
87
+ }
88
+ throw new Error(
89
+ `Failed to load config from ${configPath}: ${error instanceof Error ? error.message : "Unknown error"}`
90
+ );
91
+ }
92
+ }
93
+
94
+ // src/schemas/item-schema.ts
95
+ import { z as z3 } from "zod";
96
+ var ItemSchema = z3.object({
97
+ name: z3.string(),
98
+ type: z3.union([
99
+ z3.enum(["component", "block", "hook", "util", "config"]),
100
+ z3.string()
101
+ ]),
102
+ dependencies: z3.object({
103
+ npm: z3.record(z3.string(), z3.string()).optional(),
104
+ items: z3.array(z3.string()).optional()
105
+ }).optional(),
106
+ files: z3.array(
107
+ z3.object({
108
+ name: z3.string(),
109
+ content: z3.string().optional(),
110
+ target: z3.string(),
111
+ path: z3.string().optional(),
112
+ type: z3.union([
113
+ z3.enum(["component", "block", "hook", "util", "config"]),
114
+ z3.string()
115
+ ]).optional()
116
+ })
117
+ )
118
+ });
119
+
120
+ // src/lib/fetch-item.ts
121
+ async function fetchItem(registryUrl, itemName) {
122
+ const url = `${registryUrl}/${itemName}.json`;
123
+ try {
124
+ const response = await fetch(url);
125
+ if (!response.ok) {
126
+ if (response.status === 404) {
127
+ throw new Error(`No item found.`);
128
+ }
129
+ throw new Error(
130
+ `Failed to fetch item "${itemName}" from registry: ${response.statusText}`
131
+ );
132
+ }
133
+ const data = await response.json();
134
+ return ItemSchema.parse(data);
135
+ } catch (error) {
136
+ if (error instanceof Error) {
137
+ throw new Error(`Failed to fetch item "${itemName}": ${error.message}`);
138
+ }
139
+ throw error;
140
+ }
141
+ }
142
+ async function fetchItems(registryUrl, itemNames) {
143
+ const items = await Promise.all(
144
+ itemNames.map((name) => fetchItem(registryUrl, name))
145
+ );
146
+ return items;
147
+ }
148
+
149
+ // src/lib/resolve-dependencies.ts
150
+ async function resolveDependencies(items, registryUrl, visited = /* @__PURE__ */ new Set()) {
151
+ const result = {
152
+ items: /* @__PURE__ */ new Map(),
153
+ npmPackages: {}
154
+ };
155
+ for (const item of items) {
156
+ if (visited.has(item.name)) continue;
157
+ visited.add(item.name);
158
+ result.items.set(item.name, item);
159
+ if (item.dependencies?.npm) {
160
+ Object.assign(result.npmPackages, item.dependencies.npm);
161
+ }
162
+ if (item.dependencies?.items && item.dependencies.items.length > 0) {
163
+ const depItems = await Promise.all(
164
+ item.dependencies.items.map(
165
+ (depName) => fetchItem(registryUrl, depName)
166
+ )
167
+ );
168
+ const depResolved = await resolveDependencies(
169
+ depItems,
170
+ registryUrl,
171
+ visited
172
+ );
173
+ for (const [name, depItem] of depResolved.items) {
174
+ result.items.set(name, depItem);
175
+ }
176
+ Object.assign(result.npmPackages, depResolved.npmPackages);
177
+ }
178
+ }
179
+ return result;
180
+ }
181
+
182
+ // src/lib/resolve-import.ts
183
+ import path2 from "path";
184
+ function resolveImportAlias(content, config) {
185
+ let resolved = content;
186
+ const regex = /\bfrom\s+(['"`])(#(\w+)(?:\/[^'"`]*)?)\1/g;
187
+ resolved = resolved.replace(regex, (match, quote, fullPath, key) => {
188
+ const target = config.imports[key];
189
+ if (!target) {
190
+ return match;
191
+ }
192
+ const replaced = fullPath.replace(`#${key}`, target);
193
+ return `from ${quote}${replaced}${quote}`;
194
+ });
195
+ return resolved;
196
+ }
197
+ function resolveTargetPath(target, config, cwd = process.cwd()) {
198
+ const configPath = config.paths[target];
199
+ if (!configPath) {
200
+ throw new Error(
201
+ `Unknown target "${target}". Available targets: ${Object.keys(config.paths).join(", ")}`
202
+ );
203
+ }
204
+ return path2.join(cwd, configPath);
205
+ }
206
+
207
+ // src/lib/install-dependencies.ts
208
+ import { execa } from "execa";
209
+
210
+ // src/lib/package-manager.ts
211
+ import fs2 from "fs/promises";
212
+ import { existsSync } from "fs";
213
+ import path3 from "path";
214
+ async function detectPackageManager(cwd = process.cwd()) {
215
+ if (existsSync(path3.join(cwd, "bun.lock"))) return "bun";
216
+ if (existsSync(path3.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
217
+ if (existsSync(path3.join(cwd, "yarn.lock"))) return "yarn";
218
+ if (existsSync(path3.join(cwd, "package-lock.json"))) return "npm";
219
+ try {
220
+ const pkgPath = path3.join(cwd, "package.json");
221
+ const pkg = JSON.parse(await fs2.readFile(pkgPath, "utf-8"));
222
+ if (pkg.packageManager) {
223
+ if (pkg.packageManager.startsWith("bun")) return "bun";
224
+ if (pkg.packageManager.startsWith("pnpm")) return "pnpm";
225
+ if (pkg.packageManager.startsWith("yarn")) return "yarn";
226
+ }
227
+ } catch {
228
+ }
229
+ return "npm";
230
+ }
231
+ function getInstallCommand(pm) {
232
+ const commands = {
233
+ npm: "npm install",
234
+ yarn: "yarn add",
235
+ pnpm: "pnpm add",
236
+ bun: "bun add"
237
+ };
238
+ return commands[pm];
239
+ }
240
+
241
+ // src/lib/install-dependencies.ts
242
+ import { spinner } from "@clack/prompts";
243
+
244
+ // src/lib/check-workspace.ts
245
+ import fs3 from "fs/promises";
246
+ import path4 from "path";
247
+ import { parse } from "yaml";
248
+ async function isSinglePackageWorkspace(cwd) {
249
+ return true;
250
+ try {
251
+ const wsPath = path4.join(cwd, "pnpm-workspace.yaml");
252
+ const raw = await fs3.readFile(wsPath, "utf-8");
253
+ console.log(raw);
254
+ const ws = parse(raw);
255
+ const packages = ws?.packages ?? [];
256
+ return packages.length === 1 && (packages[0] === "." || packages[0] === "./");
257
+ } catch {
258
+ return false;
259
+ }
260
+ }
261
+ async function isPnpmWorkspaceRoot(cwd) {
262
+ try {
263
+ await fs3.access(path4.join(cwd, "pnpm-workspace.yaml"));
264
+ return true;
265
+ } catch {
266
+ return false;
267
+ }
268
+ }
269
+
270
+ // src/lib/install-dependencies.ts
271
+ async function installDependencies(packages, cwd = process.cwd()) {
272
+ if (Object.keys(packages).length === 0) return;
273
+ const pm = await detectPackageManager(cwd);
274
+ const packagesString = Object.entries(packages).map(([pkg, version]) => `${pkg}@${version}`).join(" ");
275
+ const s = spinner();
276
+ s.start(`Installing dependencies with ${pm}...`);
277
+ try {
278
+ const [command, ...args] = getInstallCommand(pm).split(" ");
279
+ if (pm === "pnpm" && await isPnpmWorkspaceRoot(cwd)) {
280
+ if (!await isSinglePackageWorkspace(cwd)) {
281
+ throw new Error("You need to install dependencies manually.");
282
+ }
283
+ args.push("-w");
284
+ }
285
+ await execa(command, [...args, ...packagesString.split(" ")], {
286
+ cwd,
287
+ args,
288
+ stdio: "pipe"
289
+ });
290
+ s.stop("Dependencies installed");
291
+ } catch (error) {
292
+ s.stop("Failed to install dependencies");
293
+ throw new Error(
294
+ `${error instanceof Error ? error.message : "Unknown error"}`
295
+ );
296
+ }
297
+ }
298
+
299
+ // src/lib/utils.ts
300
+ import fs4 from "fs/promises";
301
+ import { cancel, isCancel } from "@clack/prompts";
302
+ async function isRegistryExists() {
303
+ try {
304
+ await fs4.access("./registry.json");
305
+ return true;
306
+ } catch (e) {
307
+ return false;
308
+ }
309
+ }
310
+ function getRegistryFromConfig(config) {
311
+ if (!config.registries?.default || !config.registries.sources) {
312
+ return null;
313
+ }
314
+ const registry = config.registries.sources[config.registries.default];
315
+ if (!registry) {
316
+ return null;
317
+ }
318
+ return registry;
319
+ }
320
+ function abortIfCancel(value) {
321
+ if (isCancel(value)) {
322
+ cancel("Setup aborted by user.");
323
+ process.exit(1);
324
+ }
325
+ }
326
+
327
+ // src/commands/add.ts
328
+ import picocolors from "picocolors";
329
+ import { existsSync as existsSync2 } from "fs";
330
+ var addCommand = new Command().name("add").description("Add components to your project").argument("<items...>", "Items to add").option("-y, --yes", "Skip confirmation prompts").option("--no-install", "Skip installing dependencies").option("--overwrite", "Overwrite existing files without asking").action(async (itemNames, options) => {
331
+ console.log();
332
+ intro(picocolors.bgBlue(picocolors.blackBright(" Add Item ")));
333
+ log2.warn(
334
+ picocolors.yellow(
335
+ "The CLI is still in development, report any issues on GitHub!"
336
+ )
337
+ );
338
+ try {
339
+ const config = await loadConfig();
340
+ const s = spinner2();
341
+ const registry = getRegistryFromConfig(config);
342
+ if (!registry?.url) {
343
+ log2.error(picocolors.red("Registry not found"));
344
+ return;
345
+ }
346
+ const items = await fetchItems(registry.url, itemNames);
347
+ s.start("Resolving dependencies...");
348
+ const resolved = await resolveDependencies(items, registry.url);
349
+ s.stop(
350
+ `Resolved ${resolved.items.size} item(s) and ${Object.keys(resolved.npmPackages).length} npm package(s)`
351
+ );
352
+ const allItems = Array.from(resolved.items.values());
353
+ const npmPackages = resolved.npmPackages;
354
+ const existingFiles = [];
355
+ const filesToWrite = [];
356
+ for (const item of allItems) {
357
+ for (const file of item.files) {
358
+ const basePath = resolveTargetPath(
359
+ file.target,
360
+ config,
361
+ process.cwd()
362
+ );
363
+ const targetPath = path5.join(basePath, file.name);
364
+ let content = file.content || "";
365
+ content = resolveImportAlias(content, config);
366
+ filesToWrite.push({ item, file, targetPath, content });
367
+ if (existsSync2(targetPath)) {
368
+ existingFiles.push(path5.relative(process.cwd(), targetPath));
369
+ }
370
+ }
371
+ }
372
+ if (existingFiles.length > 0 && !options.overwrite) {
373
+ log2.warn(`Found ${existingFiles.length} existing file(s):`);
374
+ existingFiles.forEach(
375
+ (f) => console.log(` ${picocolors.yellow("\u2022")} ${f}`)
376
+ );
377
+ console.log();
378
+ const overwriteChoice = await select({
379
+ message: "How do you want to proceed?",
380
+ initialValue: "skip",
381
+ options: [
382
+ { value: "overwrite", label: "Overwrite all existing files" },
383
+ { value: "skip", label: "Skip existing files" },
384
+ { value: "cancel", label: "Cancel operation" }
385
+ ]
386
+ });
387
+ abortIfCancel(overwriteChoice);
388
+ if (overwriteChoice === "cancel") {
389
+ outro("Cancelled");
390
+ process.exit(0);
391
+ }
392
+ if (overwriteChoice === "skip") {
393
+ const skippedCount = filesToWrite.length;
394
+ filesToWrite.splice(
395
+ 0,
396
+ filesToWrite.length,
397
+ ...filesToWrite.filter((f) => !existsSync2(f.targetPath))
398
+ );
399
+ log2.info(
400
+ `Skipping ${skippedCount - filesToWrite.length} existing file(s)`
401
+ );
402
+ }
403
+ }
404
+ if (filesToWrite.length === 0) {
405
+ log2.warn("No files to write");
406
+ outro("Done");
407
+ return;
408
+ }
409
+ s.start("Writing files...");
410
+ let filesWritten = 0;
411
+ for (const { targetPath, content } of filesToWrite) {
412
+ await fs5.mkdir(path5.dirname(targetPath), { recursive: true });
413
+ await fs5.writeFile(targetPath, content, "utf-8");
414
+ filesWritten++;
415
+ }
416
+ s.stop(`Wrote ${filesWritten} file(s)`);
417
+ if (options.install && Object.keys(npmPackages).length > 0) {
418
+ await installDependencies(npmPackages);
419
+ }
420
+ outro("Components added successfully! \u2713");
421
+ } catch (error) {
422
+ log2.error(
423
+ picocolors.red(
424
+ error instanceof Error ? error.message : "An unknown error occurred"
425
+ )
426
+ );
427
+ process.exit(1);
428
+ }
429
+ });
430
+
431
+ // src/commands/init.ts
432
+ import { Command as Command2 } from "commander";
433
+ import { intro as intro2, outro as outro2, log as log4, spinner as spinner3, note, confirm as confirm2 } from "@clack/prompts";
434
+ import picocolors3 from "picocolors";
435
+
436
+ // src/schemas/setup-schema.ts
437
+ import { z as z4 } from "zod";
438
+ var ConditionAtomSchema = z4.discriminatedUnion("type", [
439
+ z4.object({
440
+ type: z4.literal("file-exists"),
441
+ path: z4.string()
442
+ }),
443
+ z4.object({
444
+ type: z4.literal("file-contains"),
445
+ path: z4.string(),
446
+ pattern: z4.string()
447
+ }),
448
+ z4.object({
449
+ type: z4.literal("dependency"),
450
+ name: z4.string()
451
+ }),
452
+ z4.object({
453
+ type: z4.literal("env"),
454
+ key: z4.string()
455
+ }),
456
+ z4.object({
457
+ type: z4.literal("framework"),
458
+ value: z4.string()
459
+ })
460
+ ]);
461
+ var ConditionSchema = z4.lazy(
462
+ () => z4.union([
463
+ ConditionAtomSchema,
464
+ z4.object({
465
+ all: z4.array(ConditionSchema)
466
+ }),
467
+ z4.object({
468
+ any: z4.array(ConditionSchema)
469
+ }),
470
+ z4.object({
471
+ not: ConditionSchema
472
+ })
473
+ ])
474
+ );
475
+ var BaseStepSchema = z4.object({
476
+ condition: z4.object({
477
+ if: ConditionSchema.optional(),
478
+ unless: ConditionSchema.optional()
479
+ }).optional()
480
+ });
481
+ var SetupStepSchema = z4.discriminatedUnion("type", [
482
+ BaseStepSchema.extend({
483
+ type: z4.literal("dependencies"),
484
+ packages: z4.record(z4.string(), z4.string())
485
+ }),
486
+ // Detect framework
487
+ BaseStepSchema.extend({
488
+ type: z4.literal("detect-framework"),
489
+ name: z4.string(),
490
+ saveAs: z4.string().optional()
491
+ }),
492
+ // Detect workdir
493
+ BaseStepSchema.extend({
494
+ type: z4.literal("detect-workdir"),
495
+ name: z4.string(),
496
+ saveAs: z4.string().optional()
497
+ }),
498
+ // Context update
499
+ BaseStepSchema.extend({
500
+ type: z4.literal("context-update"),
501
+ name: z4.string(),
502
+ data: z4.record(z4.string(), z4.any())
503
+ }),
504
+ // Assert
505
+ BaseStepSchema.extend({
506
+ type: z4.literal("assert"),
507
+ name: z4.string(),
508
+ check: z4.discriminatedUnion("type", [
509
+ z4.object({
510
+ type: z4.literal("dependency"),
511
+ packages: z4.array(z4.string())
512
+ }),
513
+ z4.object({
514
+ type: z4.literal("file-exists"),
515
+ path: z4.string()
516
+ }),
517
+ z4.object({
518
+ type: z4.literal("framework"),
519
+ value: z4.array(z4.string())
520
+ }),
521
+ z4.object({
522
+ type: z4.literal("env"),
523
+ key: z4.string()
524
+ })
525
+ ]),
526
+ onFail: z4.object({
527
+ exit: z4.boolean().default(true),
528
+ message: z4.union([z4.string(), z4.array(z4.string())])
529
+ })
530
+ }),
531
+ // Prompt
532
+ BaseStepSchema.extend({
533
+ type: z4.literal("prompt"),
534
+ name: z4.string(),
535
+ promptType: z4.enum(["text", "select", "confirm", "file-search"]),
536
+ message: z4.string(),
537
+ saveAs: z4.string(),
538
+ // where to save in config
539
+ default: z4.any().optional(),
540
+ // For file-search
541
+ pattern: z4.string().optional(),
542
+ exclude: z4.array(z4.string()).optional(),
543
+ // For select
544
+ options: z4.array(
545
+ z4.object({
546
+ value: z4.string(),
547
+ label: z4.string()
548
+ })
549
+ ).optional(),
550
+ // Validation
551
+ validate: z4.object({
552
+ pattern: z4.string().optional(),
553
+ required: z4.boolean().optional()
554
+ }).optional()
555
+ }),
556
+ // File append
557
+ BaseStepSchema.extend({
558
+ type: z4.literal("file-append"),
559
+ target: z4.string(),
560
+ // supports {{variable}}
561
+ content: z4.string().optional(),
562
+ contentPath: z4.string().optional(),
563
+ // from registry
564
+ saveTargetAs: z4.union([z4.string(), z4.array(z4.string())]).optional()
565
+ }),
566
+ // File create
567
+ BaseStepSchema.extend({
568
+ type: z4.literal("file-create"),
569
+ target: z4.string(),
570
+ content: z4.string().optional(),
571
+ contentPath: z4.string().optional(),
572
+ overwrite: z4.boolean().optional().default(false),
573
+ saveTargetAs: z4.union([z4.string(), z4.array(z4.string())]).optional()
574
+ }),
575
+ // File update
576
+ BaseStepSchema.extend({
577
+ type: z4.literal("file-update"),
578
+ target: z4.string(),
579
+ search: z4.string(),
580
+ // regex pattern
581
+ replace: z4.string(),
582
+ // replacement string
583
+ saveTargetAs: z4.union([z4.string(), z4.array(z4.string())]).optional()
584
+ }),
585
+ // File update JSON
586
+ BaseStepSchema.extend({
587
+ type: z4.literal("file-update-json"),
588
+ target: z4.string(),
589
+ content: z4.record(z4.string(), z4.any()),
590
+ merge: z4.enum(["shallow", "deep"]).optional().default("deep"),
591
+ saveTargetAs: z4.union([z4.string(), z4.array(z4.string())]).optional()
592
+ })
593
+ ]);
594
+ var SetupSchema = z4.object({
595
+ steps: z4.array(SetupStepSchema)
596
+ });
597
+
598
+ // src/lib/fetch-setup.ts
599
+ async function fetchSetup(registryUrl) {
600
+ const url = `${registryUrl}/setup.json`;
601
+ try {
602
+ const response = await fetch(url);
603
+ if (!response.ok) {
604
+ throw new Error(`${response.statusText}`);
605
+ }
606
+ const data = await response.json();
607
+ return SetupSchema.parse(data);
608
+ } catch (error) {
609
+ throw new Error(
610
+ `Failed to fetch setup configuration: ${error instanceof Error ? error.message : "Unknown error"}`
611
+ );
612
+ }
613
+ }
614
+
615
+ // src/lib/setup-executor.ts
616
+ import fs7 from "fs/promises";
617
+ import { existsSync as existsSync4 } from "fs";
618
+ import path7 from "path";
619
+ import { glob as glob2 } from "glob";
620
+ import { text, select as select2, confirm, log as log3 } from "@clack/prompts";
621
+ import { defu as defu2 } from "defu";
622
+
623
+ // src/lib/detect-framework.ts
624
+ import fs6 from "fs/promises";
625
+ import path6 from "path";
626
+ import { glob } from "glob";
627
+ async function exists(p) {
628
+ try {
629
+ await fs6.access(p);
630
+ return true;
631
+ } catch {
632
+ return false;
633
+ }
634
+ }
635
+ async function detectFramework(cwd = process.cwd()) {
636
+ try {
637
+ const pkgPath = path6.join(cwd, "package.json");
638
+ const pkg = JSON.parse(await fs6.readFile(pkgPath, "utf-8"));
639
+ const deps = {
640
+ ...pkg.dependencies,
641
+ ...pkg.devDependencies
642
+ };
643
+ if (deps["next"]) {
644
+ const files = await glob("{pages,src/pages}/_app.{js,ts,jsx,tsx}", {
645
+ cwd
646
+ });
647
+ if (files.length > 0) {
648
+ return ["next-pages", "Next.js Pages"];
649
+ }
650
+ return ["next", "Next.js"];
651
+ }
652
+ if (deps["react-router"] && deps["@react-router/dev"]) {
653
+ return ["react-router", "React Router v7"];
654
+ }
655
+ if (deps["@remix-run/react"]) return ["remix", "Remix"];
656
+ if (deps["@tanstack/react-start"])
657
+ return ["tanstack-start", "TanStack Start"];
658
+ if (deps["@tanstack/react-router"])
659
+ return ["tanstack-router", "TanStack Router"];
660
+ if (deps["astro"]) return ["astro", "Astro"];
661
+ if (deps["vite"] && (await exists(path6.join(cwd, "vite.config.ts")) || await exists(path6.join(cwd, "vite.config.js")))) {
662
+ return ["vite", "Vite"];
663
+ }
664
+ if (await exists(path6.join(cwd, "artisan")) && await exists(path6.join(cwd, "composer.json"))) {
665
+ return ["laravel", "Laravel"];
666
+ }
667
+ if (deps["react"]) return ["react", "React"];
668
+ return ["unknown", "Unknown"];
669
+ } catch {
670
+ return ["unknown", "Unknown"];
671
+ }
672
+ }
673
+
674
+ // src/lib/detect-workdir.ts
675
+ import { join } from "path";
676
+ import { existsSync as existsSync3 } from "fs";
677
+ function detectWorkdir(cwd) {
678
+ if (existsSync3(join(cwd, "src"))) {
679
+ return "./src/";
680
+ }
681
+ return "./";
682
+ }
683
+
684
+ // src/lib/setup-executor.ts
685
+ import picocolors2 from "picocolors";
686
+ var executable = [
687
+ "prompt",
688
+ "detect-framework",
689
+ "detect-workdir",
690
+ "assert",
691
+ "context-update"
692
+ ];
693
+ async function executeSetup(setup, options = {}) {
694
+ const context = {};
695
+ const cwd = options.cwd || process.cwd();
696
+ for (const step of setup.steps) {
697
+ if (!executable.includes(step.type)) {
698
+ continue;
699
+ }
700
+ if (await shouldRunStep(step, context, cwd)) {
701
+ await executeStep(step, context, cwd);
702
+ }
703
+ }
704
+ return context;
705
+ }
706
+ async function executeSetupActions(setup, context, options = {}) {
707
+ const cwd = options.cwd || process.cwd();
708
+ for (const step of setup.steps) {
709
+ if (executable.includes(step.type)) {
710
+ continue;
711
+ }
712
+ if (await shouldRunStep(step, context, cwd)) {
713
+ await executeStep(step, context, cwd);
714
+ }
715
+ }
716
+ }
717
+ async function previewSetupActions(setup, context, options = {}) {
718
+ const actions = [];
719
+ const cwd = options.cwd || process.cwd();
720
+ for (const step of setup.steps) {
721
+ if (executable.includes(step.type)) {
722
+ continue;
723
+ }
724
+ if (!await shouldRunStep(step, context, cwd)) {
725
+ continue;
726
+ }
727
+ if (step.type === "dependencies") {
728
+ const count = Object.keys(step.packages).length;
729
+ actions.push(`Install ${count} required package${count > 1 ? "s" : ""}`);
730
+ } else if (step.type === "file-create") {
731
+ const target = interpolate(step.target, context);
732
+ actions.push(`Create \`${target}\``);
733
+ } else if (step.type === "file-append") {
734
+ const target = interpolate(step.target, context);
735
+ actions.push(`Append to \`${target}\``);
736
+ } else if (step.type === "file-update") {
737
+ const target = interpolate(step.target, context);
738
+ actions.push(`Update \`${target}\``);
739
+ } else if (step.type === "file-update-json") {
740
+ const target = interpolate(step.target, context);
741
+ actions.push(`Update \`${target}\``);
742
+ }
743
+ }
744
+ return actions;
745
+ }
746
+ async function executeStep(step, context, cwd) {
747
+ switch (step.type) {
748
+ case "assert":
749
+ await executeAssert(step, context, cwd);
750
+ break;
751
+ case "dependencies":
752
+ await executeDependencies(step, cwd);
753
+ break;
754
+ case "detect-framework":
755
+ await executeDetectFramework(step, context, cwd);
756
+ break;
757
+ case "detect-workdir":
758
+ await executeDetectWorkdir(step, context, cwd);
759
+ break;
760
+ case "prompt":
761
+ await executePrompt(step, context, cwd);
762
+ break;
763
+ case "file-append":
764
+ await executeFileAppend(step, context, cwd);
765
+ break;
766
+ case "file-create":
767
+ await executeFileCreate(step, context, cwd);
768
+ break;
769
+ case "file-update":
770
+ await executeFileUpdate(step, context, cwd);
771
+ break;
772
+ case "file-update-json":
773
+ await executeFileUpdateJson(step, context, cwd);
774
+ break;
775
+ case "context-update":
776
+ await executeContextUpdate(step, context, cwd);
777
+ break;
778
+ }
779
+ }
780
+ async function executeContextUpdate(step, context, cwd) {
781
+ for (const [key, value] of Object.entries(step.data)) {
782
+ const interpolatedValue = typeof value === "string" ? interpolate(value, context) : value;
783
+ setNestedValue(context, key, interpolatedValue);
784
+ }
785
+ }
786
+ async function executeAssert(step, context, cwd) {
787
+ const ok = await runAssertCheck(step.check, context, cwd);
788
+ if (!ok) {
789
+ const message = Array.isArray(step.onFail.message) ? step.onFail.message.join("\n") : step.onFail.message;
790
+ if (step.onFail?.exit !== false) {
791
+ throw new Error(message);
792
+ }
793
+ }
794
+ }
795
+ async function runAssertCheck(check, context, cwd) {
796
+ switch (check.type) {
797
+ case "dependency":
798
+ return checkDependencies(check.packages, cwd);
799
+ case "file-exists":
800
+ return existsSync4(path7.join(cwd, check.path));
801
+ case "framework":
802
+ return check.value.includes(context.framework);
803
+ case "env":
804
+ return process.env[check.key] !== void 0;
805
+ default:
806
+ throw new Error(`Unknown assert check type: ${check.type}`);
807
+ }
808
+ }
809
+ async function checkDependencies(packages, cwd) {
810
+ try {
811
+ const pkgPath = path7.join(cwd, "package.json");
812
+ const pkg = JSON.parse(await fs7.readFile(pkgPath, "utf-8"));
813
+ const deps = {
814
+ ...pkg.dependencies,
815
+ ...pkg.devDependencies
816
+ };
817
+ return packages.every((name) => deps[name]);
818
+ } catch {
819
+ return false;
820
+ }
821
+ }
822
+ async function executeDependencies(step, cwd) {
823
+ await installDependencies(step.packages, cwd);
824
+ }
825
+ async function executeDetectWorkdir(step, context, cwd) {
826
+ const workdir = await detectWorkdir(cwd);
827
+ const saveAs = step.saveAs || step.name;
828
+ setNestedValue(context, saveAs, workdir);
829
+ }
830
+ async function executeDetectFramework(step, context, cwd) {
831
+ const [framework, frameworkLabel] = await detectFramework(cwd);
832
+ log3.info(
833
+ `Framework: ${picocolors2.bgWhiteBright(picocolors2.black(` ${frameworkLabel} `))}`
834
+ );
835
+ const saveAs = step.saveAs || step.name;
836
+ setNestedValue(context, saveAs, framework);
837
+ }
838
+ async function executePrompt(step, context, cwd) {
839
+ let answer;
840
+ if (step.promptType === "text") {
841
+ const input = await text({
842
+ message: step.message,
843
+ placeholder: step.default,
844
+ validate: (value) => {
845
+ const actualValue = value || step.default;
846
+ if (step.validate?.required && !actualValue) {
847
+ return "This field is required";
848
+ }
849
+ if (step.validate?.pattern && actualValue) {
850
+ const regex = new RegExp(step.validate.pattern);
851
+ if (!regex.test(actualValue)) {
852
+ return `Must match pattern: ${step.validate.pattern}`;
853
+ }
854
+ }
855
+ }
856
+ });
857
+ abortIfCancel(input);
858
+ answer = input || step.default;
859
+ } else if (step.promptType === "select" && step.options) {
860
+ answer = await select2({
861
+ message: step.message,
862
+ options: step.options
863
+ });
864
+ abortIfCancel(answer);
865
+ } else if (step.promptType === "confirm") {
866
+ answer = await confirm({
867
+ message: step.message
868
+ });
869
+ abortIfCancel(answer);
870
+ } else if (step.promptType === "file-search") {
871
+ const pattern = step.pattern || "**/*";
872
+ const exclude = step.exclude || ["node_modules", "dist", ".git"];
873
+ const files = await glob2(pattern, {
874
+ cwd,
875
+ ignore: exclude,
876
+ dot: false,
877
+ absolute: false
878
+ });
879
+ if (files.length === 0) {
880
+ answer = await text({
881
+ message: step.message,
882
+ defaultValue: step.default
883
+ });
884
+ } else {
885
+ const sortedFiles = files.sort((a, b) => a.localeCompare(b)).slice(0, 20);
886
+ const options = sortedFiles.map((f) => ({
887
+ value: f,
888
+ label: f
889
+ }));
890
+ options.push({
891
+ value: "__custom__",
892
+ label: "Enter custom path..."
893
+ });
894
+ const selected = await select2({
895
+ message: step.message,
896
+ options
897
+ });
898
+ abortIfCancel(selected);
899
+ if (selected === "__custom__") {
900
+ answer = await text({
901
+ message: "Enter file path:",
902
+ defaultValue: step.default
903
+ });
904
+ abortIfCancel(answer);
905
+ } else {
906
+ answer = selected;
907
+ }
908
+ }
909
+ }
910
+ if (answer !== void 0) {
911
+ setNestedValue(context, step.saveAs || step.name, answer);
912
+ }
913
+ }
914
+ async function executeFileAppend(step, context, cwd) {
915
+ const target = interpolate(step.target, context);
916
+ const targetPath = path7.join(cwd, target);
917
+ if (!existsSync4(targetPath)) {
918
+ throw new Error(`File not found: ${target}`);
919
+ }
920
+ const content = step.content || await getContent(step.contentPath);
921
+ const existing = await fs7.readFile(targetPath, "utf-8");
922
+ const newContent = existing + "\n" + content;
923
+ await fs7.writeFile(targetPath, newContent, "utf-8");
924
+ if (step.saveTargetAs) {
925
+ saveTargetAs(context, step.saveTargetAs, target);
926
+ }
927
+ }
928
+ async function executeFileCreate(step, context, cwd) {
929
+ const target = interpolate(step.target, context);
930
+ const targetPath = path7.join(cwd, target);
931
+ if (existsSync4(targetPath) && !step.overwrite) {
932
+ const shouldOverwrite = await confirm({
933
+ message: `File \`${target}\` already exists. Overwrite?`,
934
+ initialValue: false
935
+ });
936
+ if (!shouldOverwrite) {
937
+ return;
938
+ }
939
+ }
940
+ const content = step.content || await getContent(step.contentPath);
941
+ await fs7.mkdir(path7.dirname(targetPath), { recursive: true });
942
+ await fs7.writeFile(targetPath, content, "utf-8");
943
+ if (step.saveTargetAs) {
944
+ saveTargetAs(context, step.saveTargetAs, target);
945
+ }
946
+ }
947
+ async function executeFileUpdate(step, context, cwd) {
948
+ const target = interpolate(step.target, context);
949
+ const targetPath = path7.join(cwd, target);
950
+ if (!existsSync4(targetPath)) {
951
+ throw new Error(`File not found: ${target}`);
952
+ }
953
+ let content = await fs7.readFile(targetPath, "utf-8");
954
+ const searchRegex = new RegExp(step.search, "gms");
955
+ if (!searchRegex.test(content)) {
956
+ return;
957
+ }
958
+ const newContent = content.replace(searchRegex, step.replace);
959
+ await fs7.writeFile(targetPath, newContent, "utf-8");
960
+ log3.success(`Updated ${target}`);
961
+ if (step.saveTargetAs) {
962
+ saveTargetAs(context, step.saveTargetAs, target);
963
+ }
964
+ }
965
+ async function executeFileUpdateJson(step, context, cwd) {
966
+ const target = interpolate(step.target, context);
967
+ const targetPath = path7.join(cwd, target);
968
+ let existing = {};
969
+ if (existsSync4(targetPath)) {
970
+ const content = await fs7.readFile(targetPath, "utf-8");
971
+ existing = JSON.parse(content);
972
+ }
973
+ const merged = step.merge === "deep" ? defu2(step.content, existing) : { ...existing, ...step.content };
974
+ await fs7.writeFile(targetPath, JSON.stringify(merged, null, 2), "utf-8");
975
+ if (step.saveTargetAs) {
976
+ saveTargetAs(context, step.saveTargetAs, target);
977
+ }
978
+ }
979
+ function interpolate(str, context) {
980
+ return str.replace(/\{\{([^}]+)\}\}/g, (match, key) => {
981
+ const trimmedKey = key.trim();
982
+ let value = getNestedValue(context, trimmedKey);
983
+ if (value === void 0 && !trimmedKey.includes(".")) {
984
+ value = getNestedValue(context, `paths.${trimmedKey}`);
985
+ }
986
+ return value !== void 0 ? value : match;
987
+ });
988
+ }
989
+ async function getContent(contentPath) {
990
+ const filePath = path7.join(process.cwd(), contentPath);
991
+ try {
992
+ return await fs7.readFile(filePath, "utf-8");
993
+ } catch (error) {
994
+ throw new Error(
995
+ `Failed to read content from ${filePath}: ${error instanceof Error ? error.message : "Unknown error"}`
996
+ );
997
+ }
998
+ }
999
+ function getNestedValue(obj, path11) {
1000
+ return path11.split(".").reduce((current, key) => current?.[key], obj);
1001
+ }
1002
+ function setNestedValue(obj, path11, value) {
1003
+ const keys = path11.split(".");
1004
+ let current = obj;
1005
+ for (let i = 0; i < keys.length - 1; i++) {
1006
+ if (!current[keys[i]]) {
1007
+ current[keys[i]] = {};
1008
+ }
1009
+ current = current[keys[i]];
1010
+ }
1011
+ current[keys[keys.length - 1]] = value;
1012
+ }
1013
+ async function evaluateCondition(condition, context, cwd) {
1014
+ if ("all" in condition) {
1015
+ for (const c of condition.all) {
1016
+ if (!await evaluateCondition(c, context, cwd)) return false;
1017
+ }
1018
+ return true;
1019
+ }
1020
+ if ("any" in condition) {
1021
+ for (const c of condition.any) {
1022
+ if (await evaluateCondition(c, context, cwd)) return true;
1023
+ }
1024
+ return false;
1025
+ }
1026
+ if ("not" in condition) {
1027
+ return !await evaluateCondition(condition.not, context, cwd);
1028
+ }
1029
+ switch (condition.type) {
1030
+ case "file-exists": {
1031
+ return existsSync4(path7.join(cwd, interpolate(condition.path, context)));
1032
+ }
1033
+ case "file-contains": {
1034
+ const fullPath = path7.join(cwd, interpolate(condition.path, context));
1035
+ if (!existsSync4(fullPath)) return false;
1036
+ const content = await fs7.readFile(fullPath, "utf-8");
1037
+ const regex = new RegExp(condition.pattern, "m");
1038
+ return regex.test(content);
1039
+ }
1040
+ case "dependency": {
1041
+ try {
1042
+ const pkg = JSON.parse(
1043
+ await fs7.readFile(path7.join(cwd, "package.json"), "utf-8")
1044
+ );
1045
+ return pkg.dependencies?.[condition.name] || pkg.devDependencies?.[condition.name];
1046
+ } catch {
1047
+ return false;
1048
+ }
1049
+ }
1050
+ case "env":
1051
+ return process.env[condition.key] !== void 0;
1052
+ case "framework":
1053
+ return context.framework === condition.value;
1054
+ default:
1055
+ throw new Error(`Unknown condition type: ${condition.type}`);
1056
+ }
1057
+ }
1058
+ async function shouldRunStep(step, context, cwd) {
1059
+ if (!step.condition) return true;
1060
+ if (step.condition.if) {
1061
+ const ok = await evaluateCondition(step.condition.if, context, cwd);
1062
+ if (!ok) return false;
1063
+ }
1064
+ if (step.condition.unless) {
1065
+ const blocked = await evaluateCondition(
1066
+ step.condition.unless,
1067
+ context,
1068
+ cwd
1069
+ );
1070
+ if (blocked) return false;
1071
+ }
1072
+ return true;
1073
+ }
1074
+ function saveTargetAs(context, name, value) {
1075
+ if (Array.isArray(name)) {
1076
+ for (const n of name) {
1077
+ setNestedValue(context, n, value);
1078
+ }
1079
+ return;
1080
+ }
1081
+ setNestedValue(context, name, value);
1082
+ }
1083
+
1084
+ // src/lib/resolve-registry.ts
1085
+ import fs8 from "fs/promises";
1086
+ import path8 from "path";
1087
+ async function resolveRegistry(cwd, cliRegistry) {
1088
+ const configPath = path8.join(cwd, "selia.json");
1089
+ let existingConfig = null;
1090
+ try {
1091
+ existingConfig = JSON.parse(await fs8.readFile(configPath, "utf-8"));
1092
+ } catch {
1093
+ }
1094
+ const isDev = process.env.SELIA_DEV === "1" || process.env.NODE_ENV === "development";
1095
+ const defaultRegistry = isDev ? "http://localhost:5173/registry" : "https://selia.nauv.al/registry";
1096
+ const runtimeUrl = cliRegistry || existingConfig?.registries?.sources?.selia?.url || defaultRegistry;
1097
+ const persist = Boolean(cliRegistry) && !existingConfig?.registries?.sources?.selia;
1098
+ return {
1099
+ runtimeUrl,
1100
+ persist,
1101
+ existingConfig
1102
+ };
1103
+ }
1104
+
1105
+ // src/lib/write-config.ts
1106
+ import deepmerge from "deepmerge";
1107
+ import fs9 from "fs/promises";
1108
+ import path9 from "path";
1109
+ async function writeConfig(config) {
1110
+ let existingConfig;
1111
+ try {
1112
+ existingConfig = await fs9.readFile(
1113
+ path9.join(process.cwd(), "selia.json"),
1114
+ "utf-8"
1115
+ );
1116
+ } catch (error) {
1117
+ existingConfig = "{}";
1118
+ }
1119
+ const mergedConfig = deepmerge(JSON.parse(existingConfig), config);
1120
+ const configPath = path9.join(process.cwd(), "selia.json");
1121
+ await fs9.writeFile(
1122
+ configPath,
1123
+ JSON.stringify(mergedConfig, null, 2),
1124
+ "utf-8"
1125
+ );
1126
+ return configPath;
1127
+ }
1128
+
1129
+ // src/commands/init.ts
1130
+ var initCommand = new Command2().name("init").description("Initialize Selia in your project").option("-r, --registry <url>", "Registry URL").option("-y, --yes", "Skip confirmation").action(async (options) => {
1131
+ console.log();
1132
+ intro2(picocolors3.bgBlue(picocolors3.blackBright(" Initialize Selia ")));
1133
+ log4.warn(
1134
+ picocolors3.yellow(
1135
+ "The CLI is still in development, report any issues on GitHub!"
1136
+ )
1137
+ );
1138
+ if (options.registry) {
1139
+ log4.info(
1140
+ "This feature is not available yet.\nUse default Selia registry instead."
1141
+ );
1142
+ console.log();
1143
+ return;
1144
+ }
1145
+ try {
1146
+ const { runtimeUrl } = await resolveRegistry(
1147
+ process.cwd(),
1148
+ options.registry
1149
+ );
1150
+ const registryUrl = runtimeUrl;
1151
+ if (!registryUrl) {
1152
+ outro2("Cancelled.");
1153
+ return;
1154
+ }
1155
+ const s = spinner3();
1156
+ let setup;
1157
+ try {
1158
+ s.start("Fetching setup configuration...");
1159
+ setup = await fetchSetup(registryUrl);
1160
+ s.stop("Setup configuration loaded");
1161
+ } catch (error) {
1162
+ if (error instanceof Error) {
1163
+ s.stop(error.message);
1164
+ return;
1165
+ }
1166
+ s.stop("No setup configuration found");
1167
+ const finalConfig = {
1168
+ ...defaultConfig
1169
+ };
1170
+ await writeConfig(finalConfig);
1171
+ outro2(picocolors3.green("Config created \u2713"));
1172
+ log4.info(
1173
+ "Run " + picocolors3.cyan("selia add <component>") + " to add components"
1174
+ );
1175
+ console.log();
1176
+ return;
1177
+ }
1178
+ const context = await executeSetup(setup);
1179
+ const actions = await previewSetupActions(setup, context);
1180
+ actions.unshift("Create `selia.json`");
1181
+ log4.info("I will now perform the following actions:");
1182
+ actions.forEach((action) => {
1183
+ console.log(picocolors3.dim(" \u2022 ") + action);
1184
+ });
1185
+ if (!options.yes) {
1186
+ const shouldContinue = await confirm2({
1187
+ message: "Is this okay?",
1188
+ initialValue: true
1189
+ });
1190
+ abortIfCancel(shouldContinue);
1191
+ if (!shouldContinue) {
1192
+ outro2("Cancelled. Nothing was done.");
1193
+ process.exit(0);
1194
+ }
1195
+ }
1196
+ await executeSetupActions(setup, context);
1197
+ const config = context;
1198
+ s.start("Creating config file...");
1199
+ await writeConfig(config);
1200
+ s.stop("Config file created");
1201
+ note(picocolors3.dim("Config saved to: ") + picocolors3.cyan("selia.json"));
1202
+ log4.info(picocolors3.green("Selia initialized successfully! \u2713"));
1203
+ outro2(
1204
+ "Run " + picocolors3.cyan("selia add <component>") + " to add components"
1205
+ );
1206
+ console.log();
1207
+ } catch (error) {
1208
+ log4.error(
1209
+ error instanceof Error ? error.message : "An unknown error occurred"
1210
+ );
1211
+ console.log();
1212
+ process.exit(1);
1213
+ }
1214
+ });
1215
+
1216
+ // src/commands/build.ts
1217
+ import { Command as Command3 } from "commander";
1218
+ import { intro as intro3, outro as outro3, log as log6 } from "@clack/prompts";
1219
+ import fs12 from "fs/promises";
1220
+ import picocolors5 from "picocolors";
1221
+
1222
+ // src/schemas/registry-schema.ts
1223
+ import { z as z5 } from "zod";
1224
+ var RegistrySchema = z5.object({
1225
+ name: z5.string(),
1226
+ homepage: z5.url().optional(),
1227
+ items: z5.array(ItemSchema),
1228
+ setup: z5.union([SetupSchema, z5.string()]).optional()
1229
+ });
1230
+
1231
+ // src/lib/clean-build.ts
1232
+ import fs10 from "fs/promises";
1233
+ async function cleanBuild(output) {
1234
+ await fs10.rm(output, { recursive: true, force: true }).catch(() => {
1235
+ });
1236
+ await fs10.mkdir(output, { recursive: true }).catch(() => {
1237
+ });
1238
+ }
1239
+
1240
+ // src/lib/build-registry.ts
1241
+ import fs11 from "fs/promises";
1242
+ import path10 from "path";
1243
+ import { log as log5, spinner as spinner4 } from "@clack/prompts";
1244
+ import picocolors4 from "picocolors";
1245
+ async function buildRegistry(registry, options) {
1246
+ const s = spinner4();
1247
+ s.start("Building registry...");
1248
+ try {
1249
+ await buildRegistryFile(registry, options.output);
1250
+ for (let i = 0; i < registry.items.length; i++) {
1251
+ const item = registry.items[i];
1252
+ s.message(`Building ${item.name} (${i + 1}/${registry.items.length})`);
1253
+ await buildItem(item, options.output);
1254
+ }
1255
+ if (registry.setup) {
1256
+ s.message("Building setup configuration...");
1257
+ await buildSetup(registry.setup, options.output);
1258
+ }
1259
+ s.stop("Build complete");
1260
+ log5.success(
1261
+ picocolors4.green(`Built ${registry.items.length} items successfully`)
1262
+ );
1263
+ } catch (error) {
1264
+ s.stop("Build failed");
1265
+ const message = error instanceof Error ? error.message : "Unknown error";
1266
+ log5.error(picocolors4.red(message));
1267
+ process.exit(1);
1268
+ }
1269
+ }
1270
+ async function buildRegistryFile(registry, output) {
1271
+ const cleanedItems = registry.items.map((item) => ({
1272
+ ...item,
1273
+ files: item.files.map(({ path: _, ...file }) => file)
1274
+ }));
1275
+ const cleanedRegistry = {
1276
+ ...registry,
1277
+ items: cleanedItems
1278
+ };
1279
+ if (registry.setup) {
1280
+ cleanedRegistry.setup = "setup.json";
1281
+ }
1282
+ await fs11.mkdir(output, { recursive: true });
1283
+ await fs11.writeFile(
1284
+ path10.join(output, "registry.json"),
1285
+ JSON.stringify(cleanedRegistry, null, 2),
1286
+ "utf-8"
1287
+ );
1288
+ }
1289
+ async function buildItem(item, output) {
1290
+ const filesWithContent = await Promise.all(
1291
+ item.files.map(async (file) => {
1292
+ if (!file.path) {
1293
+ throw new Error(`Path is required for file in item "${item.name}"`);
1294
+ }
1295
+ const content = await fs11.readFile(file.path, "utf-8");
1296
+ const { path: _, ...rest } = file;
1297
+ return {
1298
+ ...rest,
1299
+ content
1300
+ };
1301
+ })
1302
+ );
1303
+ const itemWithContent = {
1304
+ ...item,
1305
+ files: filesWithContent
1306
+ };
1307
+ await fs11.mkdir(output, { recursive: true });
1308
+ await fs11.writeFile(
1309
+ path10.join(output, `${item.name}.json`),
1310
+ JSON.stringify(itemWithContent, null, 2),
1311
+ "utf-8"
1312
+ );
1313
+ }
1314
+ async function buildSetup(setup, output) {
1315
+ const processedSteps = await Promise.all(
1316
+ setup.steps.map(async (step) => {
1317
+ if ("contentPath" in step && step.contentPath && (step.type === "file-append" || step.type === "file-create")) {
1318
+ try {
1319
+ const content = await fs11.readFile(step.contentPath, "utf-8");
1320
+ const { contentPath: _, ...rest } = step;
1321
+ return {
1322
+ ...rest,
1323
+ content
1324
+ };
1325
+ } catch (error) {
1326
+ throw new Error(
1327
+ `Failed to read content for setup step from "${step.contentPath}": ${error instanceof Error ? error.message : "Unknown error"}`
1328
+ );
1329
+ }
1330
+ }
1331
+ return step;
1332
+ })
1333
+ );
1334
+ const processedSetup = {
1335
+ steps: processedSteps
1336
+ };
1337
+ await fs11.writeFile(
1338
+ path10.join(output, "setup.json"),
1339
+ JSON.stringify(processedSetup, null, 2),
1340
+ "utf-8"
1341
+ );
1342
+ }
1343
+
1344
+ // src/commands/build.ts
1345
+ var buildCommand = new Command3().name("build").description("Build the registry").option("-o, --output <path>", "Output directory", "./public/registry").action(async (options) => {
1346
+ intro3("Build Registry");
1347
+ try {
1348
+ if (!await isRegistryExists()) {
1349
+ log6.error(picocolors5.red("Registry file not found: registry.json"));
1350
+ process.exit(1);
1351
+ }
1352
+ const registryContent = await fs12.readFile("./registry.json", "utf-8");
1353
+ const parsedRegistry = JSON.parse(registryContent);
1354
+ const validatedRegistry = RegistrySchema.safeParse(parsedRegistry);
1355
+ if (!validatedRegistry.success) {
1356
+ console.log(validatedRegistry.error);
1357
+ log6.error(picocolors5.red("Invalid registry format:"));
1358
+ validatedRegistry.error.issues.forEach((err) => {
1359
+ log6.error(
1360
+ picocolors5.red(` - ${err.path.join(".")}: ${err.message}`)
1361
+ );
1362
+ });
1363
+ process.exit(1);
1364
+ }
1365
+ await cleanBuild(options.output);
1366
+ await buildRegistry(validatedRegistry.data, {
1367
+ output: options.output
1368
+ });
1369
+ outro3(picocolors5.green("Registry built successfully! \u2713"));
1370
+ } catch (error) {
1371
+ log6.error(
1372
+ picocolors5.red(
1373
+ error instanceof Error ? error.message : "An unknown error occurred"
1374
+ )
1375
+ );
1376
+ process.exit(1);
1377
+ }
1378
+ });
1379
+
1380
+ // src/index.ts
1381
+ program.version("0.0.1");
1382
+ program.addCommand(initCommand);
1383
+ program.addCommand(addCommand);
1384
+ program.addCommand(buildCommand);
1385
+ program.parse();
1386
+ //# sourceMappingURL=index.js.map