starwind 1.7.3 → 1.9.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 CHANGED
@@ -1,27 +1,128 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- PATHS,
4
- fileExists,
5
- getConfig,
6
- highlighter,
7
- init,
8
- installDependencies,
9
- requestPackageManager,
10
- sleep,
11
- updateConfig
12
- } from "./chunk-NN3HLNWX.js";
13
2
 
14
3
  // src/index.ts
15
4
  import { Command } from "commander";
16
5
 
17
6
  // src/commands/add.ts
18
- import * as p2 from "@clack/prompts";
7
+ import * as p6 from "@clack/prompts";
8
+ import { execa as execa2 } from "execa";
9
+
10
+ // src/utils/constants.ts
11
+ var MIN_ASTRO_VERSION = "5.0.0";
12
+ var PATHS = {
13
+ STARWIND_CORE: "@starwind-ui/core",
14
+ STARWIND_CORE_COMPONENTS: "src/components",
15
+ STARWIND_REMOTE_COMPONENT_REGISTRY: "https://starwind.dev/registry.json",
16
+ STARWIND_PRO_REGISTRY: "https://pro.starwind.dev/r/{name}",
17
+ LOCAL_CSS_FILE: "src/styles/starwind.css",
18
+ LOCAL_CONFIG_FILE: "starwind.config.json",
19
+ LOCAL_STYLES_DIR: "src/styles",
20
+ LOCAL_COMPONENTS_DIR: "src/components"
21
+ };
22
+ var ASTRO_PACKAGES = {
23
+ core: "astro@latest"
24
+ };
25
+ var OTHER_PACKAGES = {
26
+ tailwindCore: "tailwindcss@^4",
27
+ tailwindVite: "@tailwindcss/vite@^4",
28
+ tailwindForms: "@tailwindcss/forms@^0.5",
29
+ tailwindAnimate: "tw-animate-css@^1",
30
+ tailwindVariants: "tailwind-variants@^2",
31
+ tailwindMerge: "tailwind-merge@^3",
32
+ tablerIcons: "@tabler/icons@^3"
33
+ };
34
+ function getOtherPackages() {
35
+ return Object.values(OTHER_PACKAGES);
36
+ }
37
+
38
+ // src/utils/fs.ts
39
+ import fs from "fs-extra";
40
+ async function ensureDirectory(dir) {
41
+ await fs.ensureDir(dir);
42
+ }
43
+ async function readJsonFile(filePath) {
44
+ return fs.readJson(filePath);
45
+ }
46
+ async function writeJsonFile(filePath, data) {
47
+ await fs.writeJson(filePath, data, { spaces: 2 });
48
+ }
49
+ async function fileExists(filePath) {
50
+ return fs.pathExists(filePath);
51
+ }
52
+ async function writeCssFile(filePath, content) {
53
+ await fs.writeFile(filePath, content, "utf-8");
54
+ }
55
+
56
+ // src/utils/config.ts
57
+ var defaultConfig = {
58
+ $schema: "https://starwind.dev/config-schema.json",
59
+ tailwind: {
60
+ css: "src/styles/starwind.css",
61
+ baseColor: "neutral",
62
+ cssVariables: true
63
+ },
64
+ // aliases: {
65
+ // components: "@/components",
66
+ // },
67
+ componentDir: "src/components/starwind",
68
+ components: []
69
+ };
70
+ async function getConfig() {
71
+ try {
72
+ if (await fileExists(PATHS.LOCAL_CONFIG_FILE)) {
73
+ const config = await readJsonFile(PATHS.LOCAL_CONFIG_FILE);
74
+ return {
75
+ ...defaultConfig,
76
+ ...config,
77
+ components: Array.isArray(config.components) ? config.components : []
78
+ };
79
+ }
80
+ } catch (error) {
81
+ console.error("Error reading config:", error);
82
+ }
83
+ return defaultConfig;
84
+ }
85
+ async function updateConfig(updates, options = { appendComponents: true }) {
86
+ const currentConfig = await getConfig();
87
+ const currentComponents = Array.isArray(currentConfig.components) ? currentConfig.components : [];
88
+ const newConfig = {
89
+ ...currentConfig,
90
+ tailwind: {
91
+ ...currentConfig.tailwind,
92
+ ...updates.tailwind || {}
93
+ },
94
+ componentDir: updates.componentDir ? updates.componentDir : currentConfig.componentDir,
95
+ components: updates.components ? options.appendComponents ? [...currentComponents, ...updates.components] : updates.components : currentComponents
96
+ };
97
+ try {
98
+ await writeJsonFile(PATHS.LOCAL_CONFIG_FILE, newConfig);
99
+ } catch (error) {
100
+ throw new Error(
101
+ `Failed to update config: ${error instanceof Error ? error.message : "Unknown error"}`
102
+ );
103
+ }
104
+ }
105
+
106
+ // src/utils/highlighter.ts
107
+ import chalk from "chalk";
108
+ var highlighter = {
109
+ error: chalk.red,
110
+ warn: chalk.yellow,
111
+ info: chalk.cyan,
112
+ infoBright: chalk.cyanBright,
113
+ success: chalk.greenBright,
114
+ underline: chalk.underline,
115
+ title: chalk.bgBlue
116
+ };
117
+
118
+ // src/utils/install.ts
119
+ import * as p3 from "@clack/prompts";
19
120
 
20
121
  // src/utils/component.ts
21
122
  import * as path from "path";
22
123
  import { fileURLToPath } from "url";
23
124
  import * as p from "@clack/prompts";
24
- import fs from "fs-extra";
125
+ import fs2 from "fs-extra";
25
126
  import semver from "semver";
26
127
 
27
128
  // src/utils/registry.ts
@@ -96,18 +197,18 @@ async function copyComponent(name, overwrite = false) {
96
197
  }
97
198
  const componentDir = path.join(config.componentDir, "starwind", name);
98
199
  try {
99
- await fs.ensureDir(componentDir);
200
+ await fs2.ensureDir(componentDir);
100
201
  const pkgUrl = import.meta.resolve?.(PATHS.STARWIND_CORE);
101
202
  if (!pkgUrl) {
102
203
  throw new Error(`Could not resolve ${PATHS.STARWIND_CORE} package, is it installed?`);
103
204
  }
104
205
  const coreDir = path.dirname(fileURLToPath(pkgUrl));
105
206
  const sourceDir = path.join(coreDir, PATHS.STARWIND_CORE_COMPONENTS, name);
106
- const files = await fs.readdir(sourceDir);
207
+ const files = await fs2.readdir(sourceDir);
107
208
  for (const file of files) {
108
209
  const sourcePath = path.join(sourceDir, file);
109
210
  const destPath = path.join(componentDir, file);
110
- await fs.copy(sourcePath, destPath, { overwrite: true });
211
+ await fs2.copy(sourcePath, destPath, { overwrite: true });
111
212
  }
112
213
  const registry = await getRegistry();
113
214
  const componentInfo = registry.find((c) => c.name === name);
@@ -130,8 +231,8 @@ async function copyComponent(name, overwrite = false) {
130
231
  async function removeComponent(name, componentDir) {
131
232
  try {
132
233
  const componentPath = path.join(componentDir, "starwind", name);
133
- if (await fs.pathExists(componentPath)) {
134
- await fs.remove(componentPath);
234
+ if (await fs2.pathExists(componentPath)) {
235
+ await fs2.remove(componentPath);
135
236
  return { name, status: "removed" };
136
237
  } else {
137
238
  return {
@@ -220,6 +321,241 @@ async function updateComponent(name, currentVersion, skipConfirm) {
220
321
  }
221
322
  }
222
323
 
324
+ // src/utils/dependency-resolver.ts
325
+ import semver2 from "semver";
326
+ async function filterUninstalledDependencies(dependencies) {
327
+ try {
328
+ const pkg = await readJsonFile("package.json");
329
+ const installedDeps = { ...pkg.dependencies || {}, ...pkg.devDependencies || {} };
330
+ const dependenciesToInstall = [];
331
+ for (const dep of dependencies) {
332
+ let packageName;
333
+ let requiredVersion;
334
+ if (dep.startsWith("@")) {
335
+ const lastAtIndex = dep.lastIndexOf("@");
336
+ if (lastAtIndex > 0) {
337
+ packageName = dep.substring(0, lastAtIndex);
338
+ requiredVersion = dep.substring(lastAtIndex + 1);
339
+ } else {
340
+ packageName = dep;
341
+ requiredVersion = "*";
342
+ }
343
+ } else {
344
+ const atIndex = dep.indexOf("@");
345
+ if (atIndex > 0) {
346
+ packageName = dep.substring(0, atIndex);
347
+ requiredVersion = dep.substring(atIndex + 1);
348
+ } else {
349
+ packageName = dep;
350
+ requiredVersion = "*";
351
+ }
352
+ }
353
+ const installedVersion = installedDeps[packageName];
354
+ if (!installedVersion) {
355
+ dependenciesToInstall.push(dep);
356
+ } else if (requiredVersion && requiredVersion !== "*") {
357
+ const cleanInstalledVersion = semver2.clean(installedVersion) || installedVersion.replace(/^[\^~>=<= ]+/, "");
358
+ try {
359
+ if (!semver2.satisfies(cleanInstalledVersion, requiredVersion)) {
360
+ dependenciesToInstall.push(dep);
361
+ }
362
+ } catch (error) {
363
+ dependenciesToInstall.push(dep);
364
+ }
365
+ }
366
+ }
367
+ return dependenciesToInstall;
368
+ } catch (error) {
369
+ return dependencies;
370
+ }
371
+ }
372
+ function parseStarwindDependency(dependency) {
373
+ const starwindPattern = /^@starwind-ui\/core\/([^@]+)@(.+)$/;
374
+ const match = dependency.match(starwindPattern);
375
+ if (!match) {
376
+ return null;
377
+ }
378
+ const [, name, version] = match;
379
+ return {
380
+ name,
381
+ version,
382
+ originalSpec: dependency
383
+ };
384
+ }
385
+ function isStarwindDependency(dependency) {
386
+ return parseStarwindDependency(dependency) !== null;
387
+ }
388
+ async function getInstalledComponentVersion(componentName) {
389
+ try {
390
+ const config = await getConfig();
391
+ const installedComponent = config.components.find((comp) => comp.name === componentName);
392
+ return installedComponent?.version;
393
+ } catch {
394
+ return void 0;
395
+ }
396
+ }
397
+ async function resolveStarwindDependency(dependency) {
398
+ const parsed = parseStarwindDependency(dependency);
399
+ if (!parsed) {
400
+ return {
401
+ component: dependency,
402
+ requiredVersion: "",
403
+ needsInstall: false,
404
+ needsUpdate: false,
405
+ isStarwindComponent: false
406
+ };
407
+ }
408
+ const currentVersion = await getInstalledComponentVersion(parsed.name);
409
+ const registryComponent = await getComponent(parsed.name);
410
+ if (!registryComponent) {
411
+ throw new Error(`Starwind component "${parsed.name}" not found in registry`);
412
+ }
413
+ let needsInstall = false;
414
+ let needsUpdate = false;
415
+ if (!currentVersion) {
416
+ needsInstall = true;
417
+ } else {
418
+ if (!semver2.satisfies(currentVersion, parsed.version)) {
419
+ if (semver2.satisfies(registryComponent.version, parsed.version)) {
420
+ needsUpdate = true;
421
+ } else {
422
+ throw new Error(
423
+ `No version of "${parsed.name}" satisfies requirement "${parsed.version}". Latest available: ${registryComponent.version}, currently installed: ${currentVersion}`
424
+ );
425
+ }
426
+ }
427
+ }
428
+ return {
429
+ component: parsed.name,
430
+ currentVersion,
431
+ requiredVersion: parsed.version,
432
+ needsInstall,
433
+ needsUpdate,
434
+ isStarwindComponent: true
435
+ };
436
+ }
437
+ async function resolveAllStarwindDependencies(componentNames, resolved = /* @__PURE__ */ new Set()) {
438
+ const resolutions = [];
439
+ for (const componentName of componentNames) {
440
+ if (resolved.has(componentName)) {
441
+ continue;
442
+ }
443
+ resolved.add(componentName);
444
+ const component = await getComponent(componentName);
445
+ if (!component) {
446
+ throw new Error(`Component "${componentName}" not found in registry`);
447
+ }
448
+ for (const dependency of component.dependencies) {
449
+ const resolution = await resolveStarwindDependency(dependency);
450
+ if (resolution && resolution.isStarwindComponent) {
451
+ if (resolution.needsInstall || resolution.needsUpdate) {
452
+ resolutions.push(resolution);
453
+ }
454
+ const nestedResolutions = await resolveAllStarwindDependencies(
455
+ [resolution.component],
456
+ resolved
457
+ );
458
+ resolutions.push(...nestedResolutions);
459
+ }
460
+ }
461
+ }
462
+ const uniqueResolutions = /* @__PURE__ */ new Map();
463
+ for (const resolution of resolutions) {
464
+ const existing = uniqueResolutions.get(resolution.component);
465
+ if (!existing) {
466
+ uniqueResolutions.set(resolution.component, resolution);
467
+ } else {
468
+ if (resolution.needsInstall && !existing.needsInstall) {
469
+ uniqueResolutions.set(resolution.component, resolution);
470
+ }
471
+ }
472
+ }
473
+ return Array.from(uniqueResolutions.values());
474
+ }
475
+ function separateDependencies(dependencies) {
476
+ const starwindDependencies = [];
477
+ const npmDependencies = [];
478
+ for (const dependency of dependencies) {
479
+ if (isStarwindDependency(dependency)) {
480
+ starwindDependencies.push(dependency);
481
+ } else {
482
+ npmDependencies.push(dependency);
483
+ }
484
+ }
485
+ return { starwindDependencies, npmDependencies };
486
+ }
487
+
488
+ // src/utils/package-manager.ts
489
+ import * as p2 from "@clack/prompts";
490
+ import { execa } from "execa";
491
+ async function requestPackageManager() {
492
+ const pm = await p2.select({
493
+ message: "Select your preferred package manager",
494
+ options: [
495
+ { value: "pnpm", label: "pnpm", hint: "Default" },
496
+ { value: "npm", label: "npm" },
497
+ { value: "yarn", label: "yarn" },
498
+ { value: "bun", label: "bun" }
499
+ ]
500
+ });
501
+ if (p2.isCancel(pm)) {
502
+ p2.log.warn("No package manager selected, defaulting to npm");
503
+ return "npm";
504
+ }
505
+ return pm;
506
+ }
507
+ function getCurrentPackageManager() {
508
+ const userAgent = process.env.npm_config_user_agent;
509
+ if (userAgent) {
510
+ if (userAgent.includes("pnpm")) {
511
+ return "pnpm";
512
+ } else if (userAgent.includes("yarn")) {
513
+ return "yarn";
514
+ } else if (userAgent.includes("npm")) {
515
+ return "npm";
516
+ } else if (userAgent.includes("bun")) {
517
+ return "bun";
518
+ }
519
+ }
520
+ return null;
521
+ }
522
+ async function getDefaultPackageManager() {
523
+ const current = getCurrentPackageManager();
524
+ if (current) {
525
+ return current;
526
+ }
527
+ if (await fileExists("yarn.lock")) {
528
+ return "yarn";
529
+ } else if (await fileExists("pnpm-lock.yaml")) {
530
+ return "pnpm";
531
+ } else {
532
+ return "npm";
533
+ }
534
+ }
535
+ async function getShadcnCommand() {
536
+ const pm = await getDefaultPackageManager();
537
+ switch (pm) {
538
+ case "pnpm":
539
+ return ["pnpm", ["dlx", "shadcn@3"]];
540
+ case "yarn":
541
+ return ["yarn", ["dlx", "shadcn@3"]];
542
+ case "bun":
543
+ return ["bunx", ["shadcn@3"]];
544
+ case "npm":
545
+ default:
546
+ return ["npx", ["shadcn@3"]];
547
+ }
548
+ }
549
+ async function installDependencies(packages, pm, dev = false, force = false) {
550
+ const args = [
551
+ pm === "npm" ? "install" : "add",
552
+ ...packages,
553
+ dev ? pm === "npm" || pm === "pnpm" ? "-D" : "--dev" : "",
554
+ force ? "--force" : ""
555
+ ].filter(Boolean);
556
+ await execa(pm, args);
557
+ }
558
+
223
559
  // src/utils/prompts.ts
224
560
  import { confirm as confirm2, multiselect } from "@clack/prompts";
225
561
  async function selectComponents() {
@@ -237,15 +573,74 @@ async function selectComponents() {
237
573
  }
238
574
  return selected;
239
575
  }
576
+ async function confirmStarwindDependencies(componentNames) {
577
+ try {
578
+ const resolutions = await resolveAllStarwindDependencies(componentNames);
579
+ if (resolutions.length === 0) {
580
+ return true;
581
+ }
582
+ const toInstall = resolutions.filter((r) => r.needsInstall);
583
+ const toUpdate = resolutions.filter((r) => r.needsUpdate);
584
+ let message = "This component has Starwind component dependencies:\n\n";
585
+ if (toInstall.length > 0) {
586
+ message += `${highlighter.info("Components to install:")}
587
+ `;
588
+ for (const dep of toInstall) {
589
+ message += ` \u2022 ${dep.component} (requires ${dep.requiredVersion})
590
+ `;
591
+ }
592
+ message += "\n";
593
+ }
594
+ if (toUpdate.length > 0) {
595
+ message += `${highlighter.warn("Components to update:")}
596
+ `;
597
+ for (const dep of toUpdate) {
598
+ message += ` \u2022 ${dep.component} (${dep.currentVersion} \u2192 latest, requires ${dep.requiredVersion})
599
+ `;
600
+ }
601
+ message += "\n";
602
+ }
603
+ message += "Proceed with installation?";
604
+ const confirmed = await confirm2({ message });
605
+ if (typeof confirmed === "symbol") {
606
+ return false;
607
+ }
608
+ return confirmed;
609
+ } catch (error) {
610
+ console.error("Error resolving Starwind dependencies:", error);
611
+ const confirmed = await confirm2({
612
+ message: `Error resolving dependencies: ${error instanceof Error ? error.message : "Unknown error"}. Continue anyway?`
613
+ });
614
+ if (typeof confirmed === "symbol") {
615
+ return false;
616
+ }
617
+ return confirmed;
618
+ }
619
+ }
620
+ async function getStarwindDependencyResolutions(componentNames) {
621
+ return resolveAllStarwindDependencies(componentNames);
622
+ }
240
623
  async function confirmInstall(component) {
241
624
  if (component.dependencies.length === 0) return true;
242
- const confirmed = await confirm2({
243
- message: `This component requires the following dependencies: ${component.dependencies.join(", ")}. Install them?`
244
- });
245
- if (typeof confirmed === "symbol") {
246
- return false;
625
+ const { starwindDependencies, npmDependencies } = separateDependencies(component.dependencies);
626
+ if (npmDependencies.length > 0) {
627
+ const dependenciesToInstall = await filterUninstalledDependencies(npmDependencies);
628
+ if (dependenciesToInstall.length > 0) {
629
+ const confirmed = await confirm2({
630
+ message: `This component requires the following npm dependencies: ${dependenciesToInstall.join(", ")}. Install them?`
631
+ });
632
+ if (typeof confirmed === "symbol" || !confirmed) {
633
+ return false;
634
+ }
635
+ }
247
636
  }
248
- return confirmed;
637
+ if (starwindDependencies.length > 0) {
638
+ const confirmed = await confirmStarwindDependencies([component.name]);
639
+ if (!confirmed) {
640
+ return false;
641
+ }
642
+ }
643
+ return true;
249
644
  }
250
645
 
251
646
  // src/utils/install.ts
@@ -258,6 +653,7 @@ async function installComponent(name) {
258
653
  error: "Component not found in registry"
259
654
  };
260
655
  }
656
+ let dependencyResults = [];
261
657
  if (component.dependencies.length > 0) {
262
658
  const confirmed = await confirmInstall(component);
263
659
  if (!confirmed) {
@@ -267,94 +663,876 @@ async function installComponent(name) {
267
663
  error: "Installation cancelled by user"
268
664
  };
269
665
  }
666
+ const { starwindDependencies, npmDependencies } = separateDependencies(component.dependencies);
667
+ if (npmDependencies.length > 0) {
668
+ try {
669
+ const dependenciesToInstall = await filterUninstalledDependencies(npmDependencies);
670
+ if (dependenciesToInstall.length > 0) {
671
+ const pm = await requestPackageManager();
672
+ const installTasks = [
673
+ {
674
+ title: `Installing ${dependenciesToInstall.length === 1 ? "dependency" : "dependencies"}`,
675
+ task: async () => {
676
+ await installDependencies(dependenciesToInstall, pm);
677
+ return `${highlighter.info("Dependencies installed successfully")}`;
678
+ }
679
+ }
680
+ ];
681
+ await p3.tasks(installTasks);
682
+ } else {
683
+ p3.log.info(
684
+ `${highlighter.info("All npm dependencies are already installed with valid versions")}`
685
+ );
686
+ }
687
+ } catch (error) {
688
+ return {
689
+ status: "failed",
690
+ name,
691
+ error: `Failed to install npm dependencies: ${error instanceof Error ? error.message : String(error)}`
692
+ };
693
+ }
694
+ }
695
+ if (starwindDependencies.length > 0) {
696
+ let resolutions = [];
697
+ try {
698
+ resolutions = await getStarwindDependencyResolutions([name]);
699
+ } catch (error) {
700
+ console.warn(
701
+ "Proceeding without Starwind dependency installs due to resolution error:",
702
+ error
703
+ );
704
+ resolutions = [];
705
+ }
706
+ if (resolutions.length > 0) {
707
+ const installResults = await installStarwindDependencies(resolutions);
708
+ dependencyResults = installResults;
709
+ const failedDeps = installResults.filter((r) => r.status === "failed");
710
+ if (failedDeps.length > 0) {
711
+ return {
712
+ status: "failed",
713
+ name,
714
+ error: `Failed to install Starwind dependencies: ${failedDeps.map((r) => r.name).join(", ")}`,
715
+ dependencyResults
716
+ };
717
+ }
718
+ }
719
+ }
720
+ }
721
+ const result = await copyComponent(name);
722
+ if (dependencyResults.length > 0) {
723
+ return {
724
+ ...result,
725
+ dependencyResults
726
+ };
727
+ }
728
+ return result;
729
+ }
730
+ async function installStarwindDependencies(resolutions) {
731
+ const results = [];
732
+ const componentsToInstall = [];
733
+ const componentsToUpdate = [];
734
+ for (const resolution of resolutions) {
735
+ if (resolution.needsInstall) {
736
+ const result = await copyComponent(resolution.component);
737
+ results.push(result);
738
+ if (result.status === "installed" && result.version) {
739
+ componentsToInstall.push({ name: result.name, version: result.version });
740
+ }
741
+ } else if (resolution.needsUpdate) {
742
+ const result = await copyComponent(resolution.component, true);
743
+ results.push(result);
744
+ if (result.status === "installed" && result.version) {
745
+ componentsToUpdate.push({ name: result.name, version: result.version });
746
+ }
747
+ }
748
+ }
749
+ if (componentsToInstall.length > 0) {
270
750
  try {
271
- const pm = await requestPackageManager();
272
- await installDependencies(component.dependencies, pm);
751
+ await updateConfig({ components: componentsToInstall }, { appendComponents: true });
273
752
  } catch (error) {
274
- return {
275
- status: "failed",
276
- name,
277
- error: `Failed to install dependencies: ${error instanceof Error ? error.message : String(error)}`
753
+ console.error("Failed to update config after installing new dependencies:", error);
754
+ }
755
+ }
756
+ if (componentsToUpdate.length > 0) {
757
+ try {
758
+ await updateExistingComponents(componentsToUpdate);
759
+ } catch (error) {
760
+ console.error("Failed to update config after updating dependencies:", error);
761
+ }
762
+ }
763
+ return results;
764
+ }
765
+ async function updateExistingComponents(componentsToUpdate) {
766
+ const config = await getConfig();
767
+ const updatedComponents = [...config.components];
768
+ for (const componentUpdate of componentsToUpdate) {
769
+ const existingIndex = updatedComponents.findIndex((comp) => comp.name === componentUpdate.name);
770
+ if (existingIndex >= 0) {
771
+ updatedComponents[existingIndex] = {
772
+ name: componentUpdate.name,
773
+ version: componentUpdate.version
278
774
  };
775
+ } else {
776
+ updatedComponents.push(componentUpdate);
777
+ }
778
+ }
779
+ await updateConfig({ components: updatedComponents }, { appendComponents: false });
780
+ }
781
+
782
+ // src/utils/shadcn-config.ts
783
+ var COMPONENTS_JSON_PATH = "components.json";
784
+ function createDefaultShadcnConfig(cssFilePath, baseColor = "neutral") {
785
+ return {
786
+ $schema: "https://ui.shadcn.com/schema.json",
787
+ registries: {
788
+ "@starwind-pro": {
789
+ url: PATHS.STARWIND_PRO_REGISTRY,
790
+ headers: {
791
+ Authorization: "Bearer ${STARWIND_LICENSE_KEY}"
792
+ }
793
+ }
794
+ },
795
+ aliases: {
796
+ components: "@/components",
797
+ utils: "@/lib/utils"
798
+ },
799
+ tailwind: {
800
+ config: "",
801
+ css: cssFilePath,
802
+ baseColor,
803
+ cssVariables: true
804
+ },
805
+ style: "default",
806
+ rsc: true
807
+ };
808
+ }
809
+ async function componentsJsonExists() {
810
+ return fileExists(COMPONENTS_JSON_PATH);
811
+ }
812
+ async function readComponentsJson() {
813
+ try {
814
+ return await readJsonFile(COMPONENTS_JSON_PATH);
815
+ } catch (error) {
816
+ throw new Error(`Failed to read components.json: ${error}`);
817
+ }
818
+ }
819
+ async function writeComponentsJson(config) {
820
+ try {
821
+ await writeJsonFile(COMPONENTS_JSON_PATH, config);
822
+ } catch (error) {
823
+ throw new Error(`Failed to write components.json: ${error}`);
824
+ }
825
+ }
826
+ function addStarwindProRegistry(config) {
827
+ const updatedConfig = { ...config };
828
+ if (!updatedConfig.registries) {
829
+ updatedConfig.registries = {};
830
+ }
831
+ updatedConfig.registries["@starwind-pro"] = {
832
+ url: PATHS.STARWIND_PRO_REGISTRY,
833
+ headers: {
834
+ Authorization: "Bearer ${STARWIND_LICENSE_KEY}"
835
+ }
836
+ };
837
+ return updatedConfig;
838
+ }
839
+ async function setupShadcnProConfig(cssFilePath, baseColor = "neutral") {
840
+ const exists = await componentsJsonExists();
841
+ if (!exists) {
842
+ const config = createDefaultShadcnConfig(cssFilePath, baseColor);
843
+ await writeComponentsJson(config);
844
+ } else {
845
+ const existingConfig = await readComponentsJson();
846
+ const updatedConfig = addStarwindProRegistry(existingConfig);
847
+ await writeComponentsJson(updatedConfig);
848
+ }
849
+ }
850
+ async function hasStarwindProRegistry() {
851
+ if (!await componentsJsonExists()) {
852
+ return false;
853
+ }
854
+ try {
855
+ const config = await readComponentsJson();
856
+ const starwindProRegistry = config.registries?.["@starwind-pro"];
857
+ if (!starwindProRegistry?.url) {
858
+ return false;
279
859
  }
860
+ const url = starwindProRegistry.url;
861
+ const isAuthorized = url.startsWith("http://localhost") || url.startsWith("https://pro.starwind.dev");
862
+ return isAuthorized;
863
+ } catch {
864
+ return false;
280
865
  }
281
- return await copyComponent(name);
282
866
  }
283
867
 
868
+ // src/utils/sleep.ts
869
+ var sleep = async (ms) => {
870
+ await new Promise((resolve) => setTimeout(resolve, ms));
871
+ };
872
+
284
873
  // src/utils/validate.ts
285
874
  async function isValidComponent(component, availableComponents) {
286
875
  const components = availableComponents || await getAllComponents();
287
876
  return components.some((c) => c.name === component);
288
877
  }
289
878
 
879
+ // src/commands/init.ts
880
+ import path2 from "path";
881
+ import * as p5 from "@clack/prompts";
882
+ import semver4 from "semver";
883
+
884
+ // src/templates/starwind.css.ts
885
+ var tailwindConfig = `@import "tailwindcss";
886
+ @import "tw-animate-css";
887
+ @plugin "@tailwindcss/forms";
888
+ @variant dark (&:where(.dark, .dark *));
889
+
890
+ @theme {
891
+ --animate-accordion-down: accordion-down 0.2s ease-out;
892
+ --animate-accordion-up: accordion-up 0.2s ease-out;
893
+
894
+ @keyframes accordion-down {
895
+ from {
896
+ height: 0;
897
+ }
898
+ to {
899
+ height: var(--starwind-accordion-content-height);
900
+ }
901
+ }
902
+
903
+ @keyframes accordion-up {
904
+ from {
905
+ height: var(--starwind-accordion-content-height);
906
+ }
907
+ to {
908
+ height: 0;
909
+ }
910
+ }
911
+ }
912
+
913
+ @theme inline {
914
+ --color-background: var(--background);
915
+ --color-foreground: var(--foreground);
916
+ --color-card: var(--card);
917
+ --color-card-foreground: var(--card-foreground);
918
+ --color-popover: var(--popover);
919
+ --color-popover-foreground: var(--popover-foreground);
920
+ --color-primary: var(--primary);
921
+ --color-primary-foreground: var(--primary-foreground);
922
+ --color-secondary: var(--secondary);
923
+ --color-secondary-foreground: var(--secondary-foreground);
924
+ --color-muted: var(--muted);
925
+ --color-muted-foreground: var(--muted-foreground);
926
+ --color-accent: var(--accent);
927
+ --color-accent-foreground: var(--accent-foreground);
928
+ --color-info: var(--info);
929
+ --color-info-foreground: var(--info-foreground);
930
+ --color-success: var(--success);
931
+ --color-success-foreground: var(--success-foreground);
932
+ --color-warning: var(--warning);
933
+ --color-warning-foreground: var(--warning-foreground);
934
+ --color-error: var(--error);
935
+ --color-error-foreground: var(--error-foreground);
936
+ --color-border: var(--border);
937
+ --color-input: var(--input);
938
+ --color-outline: var(--outline);
939
+
940
+ --radius-xs: calc(var(--radius) - 0.375rem);
941
+ --radius-sm: calc(var(--radius) - 0.25rem);
942
+ --radius-md: calc(var(--radius) - 0.125rem);
943
+ --radius-lg: var(--radius);
944
+ --radius-xl: calc(var(--radius) + 0.25rem);
945
+ --radius-2xl: calc(var(--radius) + 0.5rem);
946
+ --radius-3xl: calc(var(--radius) + 1rem);
947
+ }
948
+
949
+ @layer base {
950
+ :root {
951
+ --background: var(--color-neutral-50);
952
+ --foreground: var(--color-neutral-950);
953
+ --card: var(--color-neutral-50);
954
+ --card-foreground: var(--color-neutral-950);
955
+ --popover: var(--color-neutral-50);
956
+ --popover-foreground: var(--color-neutral-950);
957
+ --primary: var(--color-blue-700);
958
+ --primary-foreground: var(--color-neutral-50);
959
+ --secondary: var(--color-fuchsia-700);
960
+ --secondary-foreground: var(--color-neutral-50);
961
+ --muted: var(--color-neutral-100);
962
+ --muted-foreground: var(--color-neutral-600);
963
+ --accent: var(--color-neutral-200);
964
+ --accent-foreground: var(--color-neutral-900);
965
+ --info: var(--color-sky-300);
966
+ --info-foreground: var(--color-sky-950);
967
+ --success: var(--color-green-300);
968
+ --success-foreground: var(--color-green-950);
969
+ --warning: var(--color-amber-300);
970
+ --warning-foreground: var(--color-amber-950);
971
+ --error: var(--color-red-700);
972
+ --error-foreground: var(--color-neutral-50);
973
+ --border: var(--color-neutral-200);
974
+ --input: var(--color-neutral-200);
975
+ --outline: var(--color-blue-600);
976
+ --radius: 0.625rem;
977
+ }
978
+
979
+ .dark {
980
+ --background: var(--color-neutral-950);
981
+ --foreground: var(--color-neutral-50);
982
+ --card: var(--color-neutral-950);
983
+ --card-foreground: var(--color-neutral-50);
984
+ --popover: var(--color-neutral-950);
985
+ --popover-foreground: var(--color-neutral-50);
986
+ --primary: var(--color-blue-700);
987
+ --primary-foreground: var(--color-neutral-50);
988
+ --secondary: var(--color-fuchsia-300);
989
+ --secondary-foreground: var(--color-neutral-950);
990
+ --muted: var(--color-neutral-900);
991
+ --muted-foreground: var(--color-neutral-400);
992
+ --accent: var(--color-neutral-900);
993
+ --accent-foreground: var(--color-neutral-100);
994
+ --info: var(--color-sky-300);
995
+ --info-foreground: var(--color-sky-950);
996
+ --success: var(--color-green-300);
997
+ --success-foreground: var(--color-green-950);
998
+ --warning: var(--color-amber-300);
999
+ --warning-foreground: var(--color-amber-950);
1000
+ --error: var(--color-red-800);
1001
+ --error-foreground: var(--color-neutral-50);
1002
+ --border: var(--color-neutral-800);
1003
+ --input: var(--color-neutral-800);
1004
+ --outline: var(--color-blue-600);
1005
+ }
1006
+
1007
+ * {
1008
+ @apply border-border;
1009
+ }
1010
+ *:focus-visible {
1011
+ @apply outline-outline;
1012
+ }
1013
+ html {
1014
+ @apply bg-background text-foreground scheme-light dark:scheme-dark;
1015
+ }
1016
+ button {
1017
+ @apply cursor-pointer;
1018
+ }
1019
+ }
1020
+
1021
+ @layer utilities {
1022
+ /* transition-colors but without outline-color transition property */
1023
+ .starwind-transition-colors {
1024
+ transition-property: color, background-color, border-color, text-decoration-color, fill, stroke,
1025
+ --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
1026
+ transition-timing-function: var(--default-transition-timing-function);
1027
+ transition-duration: var(--default-transition-duration);
1028
+ }
1029
+ }
1030
+ `;
1031
+
1032
+ // src/utils/astro-config.ts
1033
+ import * as p4 from "@clack/prompts";
1034
+ import fs3 from "fs-extra";
1035
+ import semver3 from "semver";
1036
+ var CONFIG_EXTENSIONS = ["ts", "js", "mjs", "cjs"];
1037
+ async function findAstroConfig() {
1038
+ for (const ext of CONFIG_EXTENSIONS) {
1039
+ const configPath = `astro.config.${ext}`;
1040
+ if (await fileExists(configPath)) {
1041
+ return configPath;
1042
+ }
1043
+ }
1044
+ return null;
1045
+ }
1046
+ async function getAstroVersion() {
1047
+ try {
1048
+ const pkg = await readJsonFile("package.json");
1049
+ if (pkg.dependencies?.astro) {
1050
+ const astroVersion = pkg.dependencies.astro.replace(/^\^|~/, "");
1051
+ return astroVersion;
1052
+ }
1053
+ p4.log.error(
1054
+ highlighter.error(
1055
+ "Astro seems not installed in your project, please check your package.json"
1056
+ )
1057
+ );
1058
+ return null;
1059
+ } catch (error) {
1060
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
1061
+ p4.log.error(highlighter.error(`Failed to check Astro version: ${errorMessage}`));
1062
+ return null;
1063
+ }
1064
+ }
1065
+ async function setupAstroConfig() {
1066
+ try {
1067
+ let configPath = await findAstroConfig();
1068
+ let content = "";
1069
+ if (configPath) {
1070
+ content = await fs3.readFile(configPath, "utf-8");
1071
+ } else {
1072
+ configPath = "astro.config.ts";
1073
+ content = `import { defineConfig } from "astro/config";
1074
+
1075
+ export default defineConfig({});
1076
+ `;
1077
+ }
1078
+ if (!content.includes('import tailwindcss from "@tailwindcss/vite"')) {
1079
+ content = `import tailwindcss from "@tailwindcss/vite";
1080
+ ${content}`;
1081
+ }
1082
+ const configStart = content.indexOf("defineConfig(") + "defineConfig(".length;
1083
+ const configEnd = content.lastIndexOf(");");
1084
+ let config = content.slice(configStart, configEnd);
1085
+ config = config.trim().replace(/^{|}$/g, "").trim();
1086
+ const astroVersion = await getAstroVersion();
1087
+ if (astroVersion && semver3.lt(astroVersion, "5.7.0")) {
1088
+ if (!config.includes("experimental")) {
1089
+ config += `
1090
+ experimental: {
1091
+ svg: true,
1092
+ },`;
1093
+ } else if (!config.includes("svg: true") && !config.includes("svg: {")) {
1094
+ const expEnd = config.indexOf("experimental:") + "experimental:".length;
1095
+ const blockStart = config.indexOf("{", expEnd) + 1;
1096
+ config = config.slice(0, blockStart) + `
1097
+ svg: true,` + config.slice(blockStart);
1098
+ }
1099
+ }
1100
+ if (!config.includes("vite:")) {
1101
+ config += `
1102
+ vite: {
1103
+ plugins: [tailwindcss()],
1104
+ },`;
1105
+ } else if (!config.includes("plugins: [")) {
1106
+ const viteEnd = config.indexOf("vite:") + "vite:".length;
1107
+ const blockStart = config.indexOf("{", viteEnd) + 1;
1108
+ config = config.slice(0, blockStart) + `
1109
+ plugins: [tailwindcss()],` + config.slice(blockStart);
1110
+ } else if (!config.includes("tailwindcss()")) {
1111
+ const pluginsStart = config.indexOf("plugins:") + "plugins:".length;
1112
+ const arrayStart = config.indexOf("[", pluginsStart) + 1;
1113
+ config = config.slice(0, arrayStart) + `tailwindcss(), ` + config.slice(arrayStart);
1114
+ }
1115
+ const newContent = `${content.slice(0, configStart)}{
1116
+ ${config}
1117
+ }${content.slice(configEnd)}`;
1118
+ await fs3.writeFile(configPath, newContent, "utf-8");
1119
+ return true;
1120
+ } catch (error) {
1121
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
1122
+ p4.log.error(highlighter.error(`Failed to setup Astro config: ${errorMessage}`));
1123
+ return false;
1124
+ }
1125
+ }
1126
+
1127
+ // src/commands/init.ts
1128
+ async function init(withinAdd = false, options) {
1129
+ if (!withinAdd) {
1130
+ p5.intro(highlighter.title(" Welcome to the Starwind CLI "));
1131
+ }
1132
+ try {
1133
+ if (!await fileExists("package.json")) {
1134
+ throw new Error(
1135
+ "No package.json found. Please run this command in the root of your project."
1136
+ );
1137
+ }
1138
+ const pkg = await readJsonFile("package.json");
1139
+ const installTasks = [];
1140
+ const configTasks = [];
1141
+ let configChoices;
1142
+ if (options?.defaults) {
1143
+ configChoices = {
1144
+ installLocation: PATHS.LOCAL_COMPONENTS_DIR,
1145
+ cssFile: PATHS.LOCAL_CSS_FILE,
1146
+ twBaseColor: "neutral"
1147
+ };
1148
+ if (!withinAdd) {
1149
+ p5.log.info("Using default configuration values");
1150
+ }
1151
+ } else {
1152
+ configChoices = await p5.group(
1153
+ {
1154
+ // ask where to install components
1155
+ installLocation: () => p5.text({
1156
+ message: "What is your components directory?",
1157
+ placeholder: PATHS.LOCAL_COMPONENTS_DIR,
1158
+ initialValue: PATHS.LOCAL_COMPONENTS_DIR,
1159
+ validate(value) {
1160
+ if (value.length === 0) return `Value is required!`;
1161
+ if (path2.isAbsolute(value)) return `Please use a relative path`;
1162
+ if (value.includes("..")) return `Path traversal is not allowed`;
1163
+ const invalidChars = /[<>:"|?*]/;
1164
+ if (invalidChars.test(value)) return `Path contains invalid characters`;
1165
+ const systemDirs = ["windows", "program files", "system32"];
1166
+ if (systemDirs.some((dir) => value.toLowerCase().startsWith(dir))) {
1167
+ return `Cannot install in system directories`;
1168
+ }
1169
+ }
1170
+ }),
1171
+ // ask where to add the css file
1172
+ cssFile: () => p5.text({
1173
+ message: `Where would you like to add the Tailwind ${highlighter.info(".css")} file?`,
1174
+ placeholder: PATHS.LOCAL_CSS_FILE,
1175
+ initialValue: PATHS.LOCAL_CSS_FILE,
1176
+ validate(value) {
1177
+ if (value.length === 0) return `Value is required!`;
1178
+ if (!value.endsWith(".css")) return `File must end with .css extension`;
1179
+ if (path2.isAbsolute(value)) return `Please use a relative path`;
1180
+ if (value.includes("..")) return `Path traversal is not allowed`;
1181
+ const invalidChars = /[<>:"|?*]/;
1182
+ if (invalidChars.test(value)) return `Path contains invalid characters`;
1183
+ const systemDirs = ["windows", "program files", "system32"];
1184
+ if (systemDirs.some((dir) => value.toLowerCase().startsWith(dir))) {
1185
+ return `Cannot use system directories`;
1186
+ }
1187
+ const basename = path2.basename(value, ".css");
1188
+ if (!basename || basename.trim().length === 0) {
1189
+ return `Invalid filename`;
1190
+ }
1191
+ }
1192
+ }),
1193
+ twBaseColor: () => p5.select({
1194
+ message: "What Tailwind base color would you like to use?",
1195
+ initialValue: "neutral",
1196
+ options: [
1197
+ { label: "Neutral (default)", value: "neutral" },
1198
+ { label: "Stone", value: "stone" },
1199
+ { label: "Zinc", value: "zinc" },
1200
+ { label: "Gray", value: "gray" },
1201
+ { label: "Slate", value: "slate" }
1202
+ ]
1203
+ })
1204
+ },
1205
+ {
1206
+ // On Cancel callback that wraps the group
1207
+ // So if the user cancels one of the prompts in the group this function will be called
1208
+ onCancel: () => {
1209
+ p5.cancel("Operation cancelled.");
1210
+ process.exit(0);
1211
+ }
1212
+ }
1213
+ );
1214
+ }
1215
+ const cssFileDir = path2.dirname(configChoices.cssFile);
1216
+ const componentInstallDir = path2.join(configChoices.installLocation, "starwind");
1217
+ configTasks.push({
1218
+ title: "Creating project structure",
1219
+ task: async () => {
1220
+ await ensureDirectory(componentInstallDir);
1221
+ await ensureDirectory(cssFileDir);
1222
+ await sleep(250);
1223
+ return "Created project structure";
1224
+ }
1225
+ });
1226
+ configTasks.push({
1227
+ title: "Setup Astro config file",
1228
+ task: async () => {
1229
+ const success = await setupAstroConfig();
1230
+ if (!success) {
1231
+ throw new Error("Failed to setup Astro config");
1232
+ }
1233
+ await sleep(250);
1234
+ return "Astro config setup completed";
1235
+ }
1236
+ });
1237
+ const cssFileExists = await fileExists(configChoices.cssFile);
1238
+ let updatedTailwindConfig = tailwindConfig;
1239
+ if (configChoices.twBaseColor !== "neutral") {
1240
+ updatedTailwindConfig = updatedTailwindConfig.replace(
1241
+ /--color-neutral-/g,
1242
+ `--color-${configChoices.twBaseColor}-`
1243
+ );
1244
+ }
1245
+ if (cssFileExists) {
1246
+ const shouldOverride = options?.defaults ? true : await p5.confirm({
1247
+ message: `${highlighter.info(configChoices.cssFile)} already exists. Do you want to override it?`
1248
+ });
1249
+ if (p5.isCancel(shouldOverride)) {
1250
+ p5.cancel("Operation cancelled");
1251
+ return process.exit(0);
1252
+ }
1253
+ if (!shouldOverride) {
1254
+ p5.log.info("Skipping Tailwind CSS configuration");
1255
+ } else {
1256
+ configTasks.push({
1257
+ title: "Creating Tailwind CSS configuration",
1258
+ task: async () => {
1259
+ await writeCssFile(configChoices.cssFile, updatedTailwindConfig);
1260
+ await sleep(250);
1261
+ return "Created Tailwind configuration";
1262
+ }
1263
+ });
1264
+ }
1265
+ } else {
1266
+ configTasks.push({
1267
+ title: "Creating Tailwind CSS configuration",
1268
+ task: async () => {
1269
+ await writeCssFile(configChoices.cssFile, updatedTailwindConfig);
1270
+ await sleep(250);
1271
+ return "Created Tailwind configuration";
1272
+ }
1273
+ });
1274
+ }
1275
+ configTasks.push({
1276
+ title: "Updating project configuration",
1277
+ task: async () => {
1278
+ await updateConfig({
1279
+ tailwind: {
1280
+ css: configChoices.cssFile,
1281
+ baseColor: configChoices.twBaseColor,
1282
+ cssVariables: true
1283
+ },
1284
+ // aliases: {
1285
+ // components: "@/components",
1286
+ // },
1287
+ componentDir: configChoices.installLocation,
1288
+ components: []
1289
+ });
1290
+ await sleep(250);
1291
+ return "Updated project starwind configuration";
1292
+ }
1293
+ });
1294
+ if (options?.pro) {
1295
+ const alreadyHasPro = await hasStarwindProRegistry();
1296
+ if (!alreadyHasPro) {
1297
+ if (!withinAdd) {
1298
+ p5.log.info(highlighter.info("Setting up Starwind Pro configuration..."));
1299
+ }
1300
+ configTasks.push({
1301
+ title: "Setting up Starwind Pro registry",
1302
+ task: async () => {
1303
+ await setupShadcnProConfig(configChoices.cssFile, configChoices.twBaseColor);
1304
+ await sleep(250);
1305
+ return "Configured Starwind Pro registry in components.json";
1306
+ }
1307
+ });
1308
+ } else {
1309
+ if (!withinAdd) {
1310
+ p5.log.info(highlighter.info("Starwind Pro registry already configured"));
1311
+ }
1312
+ }
1313
+ }
1314
+ const pm = options?.defaults ? await getDefaultPackageManager() : await requestPackageManager();
1315
+ if (pkg.dependencies?.astro) {
1316
+ const astroVersion = pkg.dependencies.astro.replace(/^\^|~/, "");
1317
+ if (!semver4.gte(astroVersion, MIN_ASTRO_VERSION)) {
1318
+ const shouldUpgrade = options?.defaults ? true : await p5.confirm({
1319
+ message: `Starwind requires Astro v${MIN_ASTRO_VERSION} or higher. Would you like to upgrade from v${astroVersion}?`,
1320
+ initialValue: true
1321
+ });
1322
+ if (p5.isCancel(shouldUpgrade)) {
1323
+ p5.cancel("Operation cancelled");
1324
+ return process.exit(0);
1325
+ }
1326
+ if (!shouldUpgrade) {
1327
+ p5.cancel("Astro v5 or higher is required to use Starwind");
1328
+ return process.exit(1);
1329
+ }
1330
+ installTasks.push({
1331
+ title: "Upgrading Astro",
1332
+ task: async () => {
1333
+ await installDependencies([ASTRO_PACKAGES.core], pm);
1334
+ return "Upgraded Astro successfully";
1335
+ }
1336
+ });
1337
+ }
1338
+ } else {
1339
+ const shouldInstall2 = options?.defaults ? true : await p5.confirm({
1340
+ message: `Starwind requires Astro v${MIN_ASTRO_VERSION} or higher. Would you like to install it?`,
1341
+ initialValue: true
1342
+ });
1343
+ if (p5.isCancel(shouldInstall2)) {
1344
+ p5.cancel("Operation cancelled");
1345
+ return process.exit(0);
1346
+ }
1347
+ if (!shouldInstall2) {
1348
+ p5.cancel("Astro is required to use Starwind");
1349
+ return process.exit(1);
1350
+ }
1351
+ installTasks.push({
1352
+ title: `Installing ${ASTRO_PACKAGES.core}`,
1353
+ task: async () => {
1354
+ await installDependencies([ASTRO_PACKAGES.core], pm);
1355
+ return `Installed ${highlighter.info(ASTRO_PACKAGES.core)} successfully`;
1356
+ }
1357
+ });
1358
+ }
1359
+ const otherPackages = getOtherPackages();
1360
+ const shouldInstall = options?.defaults ? true : await p5.confirm({
1361
+ message: `Install ${highlighter.info(otherPackages.join(", "))} using ${highlighter.info(pm)}?`
1362
+ });
1363
+ if (p5.isCancel(shouldInstall)) {
1364
+ p5.cancel("Operation cancelled");
1365
+ return process.exit(0);
1366
+ }
1367
+ if (shouldInstall) {
1368
+ installTasks.push({
1369
+ title: `Installing packages`,
1370
+ task: async () => {
1371
+ await installDependencies(getOtherPackages(), pm, false, false);
1372
+ return `${highlighter.info("Packages installed successfully")}`;
1373
+ }
1374
+ });
1375
+ } else {
1376
+ p5.log.warn(
1377
+ highlighter.warn(`Skipped installation of packages. Make sure to install them manually`)
1378
+ );
1379
+ }
1380
+ if (installTasks.length > 0) {
1381
+ await p5.tasks(installTasks);
1382
+ }
1383
+ if (configTasks.length > 0) {
1384
+ await p5.tasks(configTasks);
1385
+ }
1386
+ await sleep(250);
1387
+ let nextStepsMessage = `Make sure your layout imports the ${highlighter.infoBright(configChoices.cssFile)} file`;
1388
+ if (options?.pro) {
1389
+ nextStepsMessage += `
1390
+
1391
+ Starwind Pro is now configured! You can install pro components using:
1392
+ ${highlighter.info("npx starwind@latest add @starwind-pro/component-name")}
1393
+
1394
+ Make sure to set your ${highlighter.infoBright("STARWIND_LICENSE_KEY")} environment variable.`;
1395
+ }
1396
+ p5.note(nextStepsMessage, "Next steps");
1397
+ if (!withinAdd) {
1398
+ sleep(1e3);
1399
+ const outroMessage = options?.pro ? "Enjoy using Starwind UI with Pro components! \u{1F680}\u2728" : "Enjoy using Starwind UI \u{1F680}";
1400
+ p5.outro(outroMessage);
1401
+ }
1402
+ } catch (error) {
1403
+ p5.log.error(error instanceof Error ? error.message : "Failed to add components");
1404
+ p5.cancel("Operation cancelled");
1405
+ process.exit(1);
1406
+ }
1407
+ }
1408
+
290
1409
  // src/commands/add.ts
291
- var { init: init2 } = await import("./init-VWGSAZG5.js");
292
1410
  async function add(components, options) {
293
1411
  try {
294
- p2.intro(highlighter.title(" Welcome to the Starwind CLI "));
1412
+ p6.intro(highlighter.title(" Welcome to the Starwind CLI "));
295
1413
  const configExists = await fileExists(PATHS.LOCAL_CONFIG_FILE);
296
1414
  if (!configExists) {
297
- const shouldInit = await p2.confirm({
1415
+ const shouldInit = await p6.confirm({
298
1416
  message: `Starwind configuration not found. Would you like to run ${highlighter.info("starwind init")} now?`,
299
1417
  initialValue: true
300
1418
  });
301
- if (p2.isCancel(shouldInit)) {
302
- p2.cancel("Operation cancelled");
1419
+ if (p6.isCancel(shouldInit)) {
1420
+ p6.cancel("Operation cancelled");
303
1421
  process.exit(0);
304
1422
  }
305
1423
  if (shouldInit) {
306
- await init2(true);
1424
+ await init(true);
307
1425
  } else {
308
- p2.log.error(
1426
+ p6.log.error(
309
1427
  `Please initialize starwind with ${highlighter.info("starwind init")} before adding components`
310
1428
  );
311
1429
  process.exit(1);
312
1430
  }
313
1431
  }
314
1432
  let componentsToInstall = [];
1433
+ const registryComponents = [];
1434
+ let registryResults = null;
315
1435
  if (options?.all) {
316
1436
  const availableComponents = await getAllComponents();
317
1437
  componentsToInstall = availableComponents.map((c) => c.name);
318
- p2.log.info(`Adding all ${componentsToInstall.length} available components...`);
1438
+ p6.log.info(`Adding all ${componentsToInstall.length} available components...`);
319
1439
  } else if (components && components.length > 0) {
320
- const availableComponents = await getAllComponents();
321
- const { valid, invalid } = await components.reduce(
322
- async (accPromise, component) => {
323
- const acc = await accPromise;
324
- const isValid = await isValidComponent(component, availableComponents);
325
- if (isValid) {
326
- acc.valid.push(component);
1440
+ const regularComponents = [];
1441
+ for (const component of components) {
1442
+ if (component.startsWith("@")) {
1443
+ registryComponents.push(component);
1444
+ } else {
1445
+ regularComponents.push(component);
1446
+ }
1447
+ }
1448
+ if (registryComponents.length > 0) {
1449
+ const hasProRegistry = await hasStarwindProRegistry();
1450
+ if (!hasProRegistry) {
1451
+ const shouldSetupPro = await p6.confirm({
1452
+ message: `Starwind Pro registry not configured. Would you like to set it up now to install ${registryComponents.join(", ")}?`,
1453
+ initialValue: true
1454
+ });
1455
+ if (p6.isCancel(shouldSetupPro)) {
1456
+ p6.cancel("Operation cancelled");
1457
+ process.exit(0);
1458
+ }
1459
+ if (shouldSetupPro) {
1460
+ p6.log.info(highlighter.info("Setting up Starwind Pro configuration..."));
1461
+ let cssFile = PATHS.LOCAL_CSS_FILE;
1462
+ let baseColor = "neutral";
1463
+ try {
1464
+ const config = await getConfig();
1465
+ cssFile = config.tailwind?.css || PATHS.LOCAL_CSS_FILE;
1466
+ baseColor = config.tailwind?.baseColor || "neutral";
1467
+ } catch {
1468
+ }
1469
+ await setupShadcnProConfig(cssFile, baseColor);
1470
+ p6.log.success("Starwind Pro registry configured successfully!");
327
1471
  } else {
328
- acc.invalid.push(component);
1472
+ p6.log.error("Cannot install registry components without Starwind Pro configuration");
1473
+ p6.cancel("Operation cancelled");
1474
+ process.exit(1);
329
1475
  }
330
- return acc;
331
- },
332
- Promise.resolve({ valid: [], invalid: [] })
333
- );
334
- if (invalid.length > 0) {
335
- p2.log.warn(
336
- `${highlighter.warn("Invalid components found:")}
337
- ${invalid.map((name) => ` ${name}`).join("\n")}`
338
- );
1476
+ }
1477
+ p6.log.info(`Installing registry components: ${registryComponents.join(", ")}`);
1478
+ const [command, baseArgs] = await getShadcnCommand();
1479
+ registryResults = {
1480
+ success: [],
1481
+ failed: []
1482
+ };
1483
+ for (const registryComponent of registryComponents) {
1484
+ try {
1485
+ p6.log.info(`Installing ${highlighter.info(registryComponent)} via shadcn...`);
1486
+ await execa2(command, [...baseArgs, "add", registryComponent], {
1487
+ stdio: "inherit",
1488
+ cwd: process.cwd()
1489
+ });
1490
+ registryResults.success.push(registryComponent);
1491
+ } catch (error) {
1492
+ registryResults.failed.push(registryComponent);
1493
+ }
1494
+ }
339
1495
  }
340
- if (valid.length > 0) {
341
- componentsToInstall = valid;
342
- } else {
343
- p2.log.warn(`${highlighter.warn("No valid components to install")}`);
344
- p2.cancel("Operation cancelled");
345
- return process.exit(0);
1496
+ if (regularComponents.length > 0) {
1497
+ const availableComponents = await getAllComponents();
1498
+ const { valid, invalid } = await regularComponents.reduce(
1499
+ async (accPromise, component) => {
1500
+ const acc = await accPromise;
1501
+ const isValid = await isValidComponent(component, availableComponents);
1502
+ if (isValid) {
1503
+ acc.valid.push(component);
1504
+ } else {
1505
+ acc.invalid.push(component);
1506
+ }
1507
+ return acc;
1508
+ },
1509
+ Promise.resolve({ valid: [], invalid: [] })
1510
+ );
1511
+ if (invalid.length > 0) {
1512
+ p6.log.warn(
1513
+ `${highlighter.warn("Invalid components found:")}
1514
+ ${invalid.map((name) => ` ${name}`).join("\n")}`
1515
+ );
1516
+ }
1517
+ if (valid.length > 0) {
1518
+ componentsToInstall = valid;
1519
+ } else if (registryComponents.length === 0) {
1520
+ p6.log.warn(`${highlighter.warn("No valid components to install")}`);
1521
+ p6.cancel("Operation cancelled");
1522
+ return process.exit(0);
1523
+ }
346
1524
  }
347
1525
  } else {
348
1526
  const selected = await selectComponents();
349
1527
  if (!selected) {
350
- p2.cancel("No components selected");
1528
+ p6.cancel("No components selected");
351
1529
  return process.exit(0);
352
1530
  }
353
1531
  componentsToInstall = selected;
354
1532
  }
355
- if (componentsToInstall.length === 0) {
356
- p2.log.warn(`${highlighter.warn("No components selected")}`);
357
- p2.cancel("Operation cancelled");
1533
+ if (componentsToInstall.length === 0 && registryComponents.length === 0) {
1534
+ p6.log.warn(`${highlighter.warn("No components selected")}`);
1535
+ p6.cancel("Operation cancelled");
358
1536
  return process.exit(0);
359
1537
  }
360
1538
  const results = {
@@ -369,12 +1547,42 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
369
1547
  case "installed":
370
1548
  results.installed.push(result);
371
1549
  installedComponents.push({ name: result.name, version: result.version });
1550
+ if (result.dependencyResults) {
1551
+ for (const depResult of result.dependencyResults) {
1552
+ switch (depResult.status) {
1553
+ case "installed":
1554
+ results.installed.push(depResult);
1555
+ break;
1556
+ case "skipped":
1557
+ results.skipped.push(depResult);
1558
+ break;
1559
+ case "failed":
1560
+ results.failed.push(depResult);
1561
+ break;
1562
+ }
1563
+ }
1564
+ }
372
1565
  break;
373
1566
  case "skipped":
374
1567
  results.skipped.push(result);
375
1568
  break;
376
1569
  case "failed":
377
1570
  results.failed.push(result);
1571
+ if (result.dependencyResults) {
1572
+ for (const depResult of result.dependencyResults) {
1573
+ switch (depResult.status) {
1574
+ case "installed":
1575
+ results.installed.push(depResult);
1576
+ break;
1577
+ case "skipped":
1578
+ results.skipped.push(depResult);
1579
+ break;
1580
+ case "failed":
1581
+ results.failed.push(depResult);
1582
+ break;
1583
+ }
1584
+ }
1585
+ }
378
1586
  break;
379
1587
  }
380
1588
  }
@@ -382,68 +1590,82 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
382
1590
  try {
383
1591
  await updateConfig({ components: installedComponents }, { appendComponents: true });
384
1592
  } catch (error) {
385
- p2.log.error(
1593
+ p6.log.error(
386
1594
  `Failed to update config: ${error instanceof Error ? error.message : "Unknown error"}`
387
1595
  );
388
1596
  process.exit(1);
389
1597
  }
390
1598
  }
391
- p2.log.message(`
1599
+ p6.log.message(`
392
1600
 
393
1601
  ${highlighter.underline("Installation Summary")}`);
394
1602
  if (results.failed.length > 0) {
395
- p2.log.error(
1603
+ p6.log.error(
396
1604
  `${highlighter.error("Failed to install components:")}
397
1605
  ${results.failed.map((r) => ` ${r.name} - ${r.error}`).join("\n")}`
398
1606
  );
399
1607
  }
400
1608
  if (results.skipped.length > 0) {
401
- p2.log.warn(
1609
+ p6.log.warn(
402
1610
  `${highlighter.warn("Skipped components (already installed):")}
403
1611
  ${results.skipped.map((r) => ` ${r.name} v${r.version}`).join("\n")}`
404
1612
  );
405
1613
  }
406
1614
  if (results.installed.length > 0) {
407
- p2.log.success(
1615
+ p6.log.success(
408
1616
  `${highlighter.success("Successfully installed components:")}
409
1617
  ${results.installed.map((r) => ` ${r.name} v${r.version}`).join("\n")}`
410
1618
  );
411
1619
  }
1620
+ if (registryResults) {
1621
+ if (registryResults.failed.length > 0) {
1622
+ p6.log.error(
1623
+ `${highlighter.error("Failed to install registry components:")}
1624
+ ${registryResults.failed.map((name) => ` ${name} - see the error message above for further details`).join("\n")}`
1625
+ );
1626
+ }
1627
+ if (registryResults.success.length > 0) {
1628
+ p6.log.success(
1629
+ `${highlighter.success("Successfully installed registry components:")}
1630
+ ${registryResults.success.map((name) => ` ${name}`).join("\n")}`
1631
+ );
1632
+ }
1633
+ }
412
1634
  await sleep(1e3);
413
- p2.outro("Enjoy using Starwind UI \u{1F680}");
1635
+ p6.outro("Enjoy using Starwind UI \u{1F680}");
414
1636
  } catch (error) {
415
- p2.log.error(error instanceof Error ? error.message : "Failed to add components");
416
- p2.cancel("Operation cancelled");
1637
+ p6.log.error(error instanceof Error ? error.message : "Failed to add components");
1638
+ p6.cancel("Operation cancelled");
417
1639
  process.exit(1);
418
1640
  }
419
1641
  }
420
1642
 
421
1643
  // src/commands/remove.ts
422
- import * as p3 from "@clack/prompts";
1644
+ import * as p7 from "@clack/prompts";
423
1645
  async function remove(components, options) {
424
1646
  try {
425
- p3.intro(highlighter.title(" Welcome to the Starwind CLI "));
1647
+ p7.intro(highlighter.title(" Welcome to the Starwind CLI "));
426
1648
  const configExists = await fileExists(PATHS.LOCAL_CONFIG_FILE);
427
1649
  if (!configExists) {
428
- p3.log.error("No Starwind configuration found. Please run starwind init first.");
1650
+ p7.log.error("No Starwind configuration found. Please run starwind init first.");
429
1651
  process.exit(1);
430
1652
  }
431
1653
  const config = await getConfig();
432
1654
  const installedComponents = config.components;
433
1655
  if (installedComponents.length === 0) {
434
- p3.log.warn("No components are currently installed.");
1656
+ p7.log.warn("No components are currently installed.");
435
1657
  process.exit(0);
436
1658
  }
437
1659
  let componentsToRemove = [];
438
1660
  if (options?.all) {
439
1661
  componentsToRemove = installedComponents.map((comp) => comp.name);
440
- p3.log.info(`Removing all ${componentsToRemove.length} installed components...`);
1662
+ p7.log.info(`Removing all ${componentsToRemove.length} installed components...`);
441
1663
  } else if (components && components.length > 0) {
442
1664
  const invalid = components.filter(
443
1665
  (comp) => !installedComponents.some((ic) => ic.name === comp)
444
1666
  );
445
1667
  if (invalid.length > 0) {
446
- p3.log.warn(
1668
+ p7.log.warn(
447
1669
  `${highlighter.warn("Components not found:")}
448
1670
  ${invalid.map((name) => ` ${name}`).join("\n")}`
449
1671
  );
@@ -452,7 +1674,7 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
452
1674
  (comp) => installedComponents.some((ic) => ic.name === comp)
453
1675
  );
454
1676
  if (componentsToRemove.length === 0) {
455
- p3.log.warn("No valid components to remove");
1677
+ p7.log.warn("No valid components to remove");
456
1678
  process.exit(0);
457
1679
  }
458
1680
  } else {
@@ -460,25 +1682,25 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
460
1682
  value: comp.name,
461
1683
  label: comp.name
462
1684
  }));
463
- const selected = await p3.multiselect({
1685
+ const selected = await p7.multiselect({
464
1686
  message: "Select components to remove",
465
1687
  options: choices
466
1688
  });
467
- if (p3.isCancel(selected)) {
468
- p3.cancel("Operation cancelled");
1689
+ if (p7.isCancel(selected)) {
1690
+ p7.cancel("Operation cancelled");
469
1691
  process.exit(0);
470
1692
  }
471
1693
  componentsToRemove = selected;
472
1694
  }
473
1695
  if (componentsToRemove.length === 0) {
474
- p3.log.warn("No components selected for removal");
1696
+ p7.log.warn("No components selected for removal");
475
1697
  process.exit(0);
476
1698
  }
477
- const confirmed = await p3.confirm({
1699
+ const confirmed = await p7.confirm({
478
1700
  message: `Remove ${componentsToRemove.map((comp) => highlighter.info(comp)).join(", ")} ${componentsToRemove.length > 1 ? "components" : "component"}?`
479
1701
  });
480
- if (!confirmed || p3.isCancel(confirmed)) {
481
- p3.cancel("Operation cancelled");
1702
+ if (!confirmed || p7.isCancel(confirmed)) {
1703
+ p7.cancel("Operation cancelled");
482
1704
  process.exit(0);
483
1705
  }
484
1706
  const results = {
@@ -503,61 +1725,61 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
503
1725
  },
504
1726
  { appendComponents: false }
505
1727
  );
506
- p3.log.message(`
1728
+ p7.log.message(`
507
1729
 
508
1730
  ${highlighter.underline("Removal Summary")}`);
509
1731
  if (results.failed.length > 0) {
510
- p3.log.error(
1732
+ p7.log.error(
511
1733
  `${highlighter.error("Failed to remove components:")}
512
1734
  ${results.failed.map((r) => ` ${r.name} - ${r.error}`).join("\n")}`
513
1735
  );
514
1736
  }
515
1737
  if (results.removed.length > 0) {
516
- p3.log.success(
1738
+ p7.log.success(
517
1739
  `${highlighter.success("Successfully removed components:")}
518
1740
  ${results.removed.map((r) => ` ${r.name}`).join("\n")}`
519
1741
  );
520
1742
  }
521
1743
  await sleep(1e3);
522
1744
  if (results.removed.length > 0) {
523
- p3.outro("Components removed successfully \u{1F5D1}\uFE0F");
1745
+ p7.outro("Components removed successfully \u{1F5D1}\uFE0F");
524
1746
  } else {
525
- p3.cancel("Errors occurred while removing components");
1747
+ p7.cancel("Errors occurred while removing components");
526
1748
  process.exit(1);
527
1749
  }
528
1750
  } catch (error) {
529
- p3.log.error(error instanceof Error ? error.message : "Failed to remove components");
530
- p3.cancel("Operation cancelled");
1751
+ p7.log.error(error instanceof Error ? error.message : "Failed to remove components");
1752
+ p7.cancel("Operation cancelled");
531
1753
  process.exit(1);
532
1754
  }
533
1755
  }
534
1756
 
535
1757
  // src/commands/update.ts
536
- import * as p4 from "@clack/prompts";
1758
+ import * as p8 from "@clack/prompts";
537
1759
  async function update(components, options) {
538
1760
  try {
539
- p4.intro(highlighter.title(" Welcome to the Starwind CLI "));
1761
+ p8.intro(highlighter.title(" Welcome to the Starwind CLI "));
540
1762
  const configExists = await fileExists(PATHS.LOCAL_CONFIG_FILE);
541
1763
  if (!configExists) {
542
- p4.log.error("No Starwind configuration found. Please run starwind init first.");
1764
+ p8.log.error("No Starwind configuration found. Please run starwind init first.");
543
1765
  process.exit(1);
544
1766
  }
545
1767
  const config = await getConfig();
546
1768
  const installedComponents = config.components;
547
1769
  if (installedComponents.length === 0) {
548
- p4.log.warn("No components are currently installed.");
1770
+ p8.log.warn("No components are currently installed.");
549
1771
  process.exit(0);
550
1772
  }
551
1773
  let componentsToUpdate = [];
552
1774
  if (options?.all) {
553
1775
  componentsToUpdate = installedComponents.map((comp) => comp.name);
554
- p4.log.info(`Checking updates for all ${componentsToUpdate.length} installed components...`);
1776
+ p8.log.info(`Checking updates for all ${componentsToUpdate.length} installed components...`);
555
1777
  } else if (components && components.length > 0) {
556
1778
  const invalid = components.filter(
557
1779
  (comp) => !installedComponents.some((ic) => ic.name === comp)
558
1780
  );
559
1781
  if (invalid.length > 0) {
560
- p4.log.warn(
1782
+ p8.log.warn(
561
1783
  `${highlighter.warn("Components not found in project:")}
562
1784
  ${invalid.map((name) => ` ${name}`).join("\n")}`
563
1785
  );
@@ -566,7 +1788,7 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
566
1788
  (comp) => installedComponents.some((ic) => ic.name === comp)
567
1789
  );
568
1790
  if (componentsToUpdate.length === 0) {
569
- p4.log.warn("No valid components to update");
1791
+ p8.log.warn("No valid components to update");
570
1792
  process.exit(0);
571
1793
  }
572
1794
  } else {
@@ -574,18 +1796,18 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
574
1796
  value: comp.name,
575
1797
  label: comp.name
576
1798
  }));
577
- const selected = await p4.multiselect({
1799
+ const selected = await p8.multiselect({
578
1800
  message: "Select components to update",
579
1801
  options: choices
580
1802
  });
581
- if (p4.isCancel(selected)) {
582
- p4.cancel("Operation cancelled");
1803
+ if (p8.isCancel(selected)) {
1804
+ p8.cancel("Operation cancelled");
583
1805
  process.exit(0);
584
1806
  }
585
1807
  componentsToUpdate = selected;
586
1808
  }
587
1809
  if (componentsToUpdate.length === 0) {
588
- p4.log.warn("No components selected for update");
1810
+ p8.log.warn("No components selected for update");
589
1811
  process.exit(0);
590
1812
  }
591
1813
  const results = {
@@ -636,52 +1858,52 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
636
1858
  { appendComponents: false }
637
1859
  );
638
1860
  } catch (error) {
639
- p4.log.error(
1861
+ p8.log.error(
640
1862
  `Failed to update config: ${error instanceof Error ? error.message : "Unknown error"}`
641
1863
  );
642
1864
  process.exit(1);
643
1865
  }
644
1866
  }
645
- p4.log.message(`
1867
+ p8.log.message(`
646
1868
 
647
1869
  ${highlighter.underline("Update Summary")}`);
648
1870
  if (results.failed.length > 0) {
649
- p4.log.error(
1871
+ p8.log.error(
650
1872
  `${highlighter.error("Failed to update components:")}
651
1873
  ${results.failed.map((r) => ` ${r.name} - ${r.error}`).join("\n")}`
652
1874
  );
653
1875
  }
654
1876
  if (results.skipped.length > 0) {
655
- p4.log.info(
1877
+ p8.log.info(
656
1878
  `${highlighter.info("Components already up to date or skipped:")}
657
1879
  ${results.skipped.map((r) => ` ${r.name} (${r.oldVersion})`).join("\n")}`
658
1880
  );
659
1881
  }
660
1882
  if (results.updated.length > 0) {
661
- p4.log.success(
1883
+ p8.log.success(
662
1884
  `${highlighter.success("Successfully updated components:")}
663
1885
  ${results.updated.map((r) => ` ${r.name} (${r.oldVersion} \u2192 ${r.newVersion})`).join("\n")}`
664
1886
  );
665
1887
  }
666
1888
  await sleep(1e3);
667
1889
  if (results.updated.length > 0) {
668
- p4.outro("Components updated successfully \u{1F680}");
1890
+ p8.outro("Components updated successfully \u{1F680}");
669
1891
  } else if (results.skipped.length > 0 && results.failed.length === 0) {
670
- p4.outro("Components already up to date or skipped \u2728");
1892
+ p8.outro("Components already up to date or skipped \u2728");
671
1893
  } else {
672
- p4.cancel("No components were updated");
1894
+ p8.cancel("No components were updated");
673
1895
  process.exit(1);
674
1896
  }
675
1897
  } catch (error) {
676
- p4.log.error(error instanceof Error ? error.message : "Failed to update components");
677
- p4.cancel("Operation cancelled");
1898
+ p8.log.error(error instanceof Error ? error.message : "Failed to update components");
1899
+ p8.cancel("Operation cancelled");
678
1900
  process.exit(1);
679
1901
  }
680
1902
  }
681
1903
 
682
1904
  // src/index.ts
683
1905
  var program = new Command().name("starwind").description("Add beautifully designed components to your Astro applications").version("1.7.3");
684
- program.command("init").description("Initialize your project with Starwind").option("-d, --defaults", "Use default values for all prompts").action((options) => init(false, { defaults: options.defaults }));
1906
+ program.command("init").description("Initialize your project with Starwind").option("-d, --defaults", "Use default values for all prompts").option("-p, --pro", "Initialize with Starwind Pro setup").action((options) => init(false, { defaults: options.defaults, pro: options.pro }));
685
1907
  program.command("add").description("Add Starwind components to your project").argument("[components...]", "The components to add (space separated)").allowExcessArguments().option("-a, --all", "Add all available components").action(add);
686
1908
  program.command("update").description("Update Starwind components to their latest versions").argument("[components...]", "The components to update (space separated)").allowExcessArguments().option("-a, --all", "Update all installed components").option("-y, --yes", "Skip confirmation prompts").action(update);
687
1909
  program.command("remove").description("Remove Starwind components from your project").argument("[components...]", "The components to remove (space separated)").allowExcessArguments().option("-a, --all", "Remove all installed components").action(remove);