starwind 1.8.0 → 1.10.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-HDAZQTOL.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,863 @@ 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-white);
952
+ --foreground: var(--color-neutral-950);
953
+ --card: var(--color-white);
954
+ --card-foreground: var(--color-neutral-950);
955
+ --popover: var(--color-white);
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-100);
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-neutral-400);
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-900);
983
+ --card-foreground: var(--color-neutral-50);
984
+ --popover: var(--color-neutral-800);
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-800);
991
+ --muted-foreground: var(--color-neutral-400);
992
+ --accent: var(--color-neutral-700);
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: --alpha(var(--color-neutral-50) / 10%);
1003
+ --input: --alpha(var(--color-neutral-50) / 15%);
1004
+ --outline: var(--color-neutral-500);
1005
+ }
1006
+
1007
+ * {
1008
+ @apply border-border;
1009
+ }
1010
+ html {
1011
+ @apply bg-background text-foreground scheme-light dark:scheme-dark;
1012
+ }
1013
+ button {
1014
+ @apply cursor-pointer;
1015
+ }
1016
+ }
1017
+ `;
1018
+
1019
+ // src/utils/astro-config.ts
1020
+ import * as p4 from "@clack/prompts";
1021
+ import fs3 from "fs-extra";
1022
+ import semver3 from "semver";
1023
+ var CONFIG_EXTENSIONS = ["ts", "js", "mjs", "cjs"];
1024
+ async function findAstroConfig() {
1025
+ for (const ext of CONFIG_EXTENSIONS) {
1026
+ const configPath = `astro.config.${ext}`;
1027
+ if (await fileExists(configPath)) {
1028
+ return configPath;
1029
+ }
1030
+ }
1031
+ return null;
1032
+ }
1033
+ async function getAstroVersion() {
1034
+ try {
1035
+ const pkg = await readJsonFile("package.json");
1036
+ if (pkg.dependencies?.astro) {
1037
+ const astroVersion = pkg.dependencies.astro.replace(/^\^|~/, "");
1038
+ return astroVersion;
1039
+ }
1040
+ p4.log.error(
1041
+ highlighter.error(
1042
+ "Astro seems not installed in your project, please check your package.json"
1043
+ )
1044
+ );
1045
+ return null;
1046
+ } catch (error) {
1047
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
1048
+ p4.log.error(highlighter.error(`Failed to check Astro version: ${errorMessage}`));
1049
+ return null;
1050
+ }
1051
+ }
1052
+ async function setupAstroConfig() {
1053
+ try {
1054
+ let configPath = await findAstroConfig();
1055
+ let content = "";
1056
+ if (configPath) {
1057
+ content = await fs3.readFile(configPath, "utf-8");
1058
+ } else {
1059
+ configPath = "astro.config.ts";
1060
+ content = `import { defineConfig } from "astro/config";
1061
+
1062
+ export default defineConfig({});
1063
+ `;
1064
+ }
1065
+ if (!content.includes('import tailwindcss from "@tailwindcss/vite"')) {
1066
+ content = `import tailwindcss from "@tailwindcss/vite";
1067
+ ${content}`;
1068
+ }
1069
+ const configStart = content.indexOf("defineConfig(") + "defineConfig(".length;
1070
+ const configEnd = content.lastIndexOf(");");
1071
+ let config = content.slice(configStart, configEnd);
1072
+ config = config.trim().replace(/^{|}$/g, "").trim();
1073
+ const astroVersion = await getAstroVersion();
1074
+ if (astroVersion && semver3.lt(astroVersion, "5.7.0")) {
1075
+ if (!config.includes("experimental")) {
1076
+ config += `
1077
+ experimental: {
1078
+ svg: true,
1079
+ },`;
1080
+ } else if (!config.includes("svg: true") && !config.includes("svg: {")) {
1081
+ const expEnd = config.indexOf("experimental:") + "experimental:".length;
1082
+ const blockStart = config.indexOf("{", expEnd) + 1;
1083
+ config = config.slice(0, blockStart) + `
1084
+ svg: true,` + config.slice(blockStart);
1085
+ }
1086
+ }
1087
+ if (!config.includes("vite:")) {
1088
+ config += `
1089
+ vite: {
1090
+ plugins: [tailwindcss()],
1091
+ },`;
1092
+ } else if (!config.includes("plugins: [")) {
1093
+ const viteEnd = config.indexOf("vite:") + "vite:".length;
1094
+ const blockStart = config.indexOf("{", viteEnd) + 1;
1095
+ config = config.slice(0, blockStart) + `
1096
+ plugins: [tailwindcss()],` + config.slice(blockStart);
1097
+ } else if (!config.includes("tailwindcss()")) {
1098
+ const pluginsStart = config.indexOf("plugins:") + "plugins:".length;
1099
+ const arrayStart = config.indexOf("[", pluginsStart) + 1;
1100
+ config = config.slice(0, arrayStart) + `tailwindcss(), ` + config.slice(arrayStart);
1101
+ }
1102
+ const newContent = `${content.slice(0, configStart)}{
1103
+ ${config}
1104
+ }${content.slice(configEnd)}`;
1105
+ await fs3.writeFile(configPath, newContent, "utf-8");
1106
+ return true;
1107
+ } catch (error) {
1108
+ const errorMessage = error instanceof Error ? error.message : "An unknown error occurred";
1109
+ p4.log.error(highlighter.error(`Failed to setup Astro config: ${errorMessage}`));
1110
+ return false;
1111
+ }
1112
+ }
1113
+
1114
+ // src/commands/init.ts
1115
+ async function init(withinAdd = false, options) {
1116
+ if (!withinAdd) {
1117
+ p5.intro(highlighter.title(" Welcome to the Starwind CLI "));
1118
+ }
1119
+ try {
1120
+ if (!await fileExists("package.json")) {
1121
+ throw new Error(
1122
+ "No package.json found. Please run this command in the root of your project."
1123
+ );
1124
+ }
1125
+ const pkg = await readJsonFile("package.json");
1126
+ const installTasks = [];
1127
+ const configTasks = [];
1128
+ let configChoices;
1129
+ if (options?.defaults) {
1130
+ configChoices = {
1131
+ installLocation: PATHS.LOCAL_COMPONENTS_DIR,
1132
+ cssFile: PATHS.LOCAL_CSS_FILE,
1133
+ twBaseColor: "neutral"
1134
+ };
1135
+ if (!withinAdd) {
1136
+ p5.log.info("Using default configuration values");
1137
+ }
1138
+ } else {
1139
+ configChoices = await p5.group(
1140
+ {
1141
+ // ask where to install components
1142
+ installLocation: () => p5.text({
1143
+ message: "What is your components directory?",
1144
+ placeholder: PATHS.LOCAL_COMPONENTS_DIR,
1145
+ initialValue: PATHS.LOCAL_COMPONENTS_DIR,
1146
+ validate(value) {
1147
+ if (value.length === 0) return `Value is required!`;
1148
+ if (path2.isAbsolute(value)) return `Please use a relative path`;
1149
+ if (value.includes("..")) return `Path traversal is not allowed`;
1150
+ const invalidChars = /[<>:"|?*]/;
1151
+ if (invalidChars.test(value)) return `Path contains invalid characters`;
1152
+ const systemDirs = ["windows", "program files", "system32"];
1153
+ if (systemDirs.some((dir) => value.toLowerCase().startsWith(dir))) {
1154
+ return `Cannot install in system directories`;
1155
+ }
1156
+ }
1157
+ }),
1158
+ // ask where to add the css file
1159
+ cssFile: () => p5.text({
1160
+ message: `Where would you like to add the Tailwind ${highlighter.info(".css")} file?`,
1161
+ placeholder: PATHS.LOCAL_CSS_FILE,
1162
+ initialValue: PATHS.LOCAL_CSS_FILE,
1163
+ validate(value) {
1164
+ if (value.length === 0) return `Value is required!`;
1165
+ if (!value.endsWith(".css")) return `File must end with .css extension`;
1166
+ if (path2.isAbsolute(value)) return `Please use a relative path`;
1167
+ if (value.includes("..")) return `Path traversal is not allowed`;
1168
+ const invalidChars = /[<>:"|?*]/;
1169
+ if (invalidChars.test(value)) return `Path contains invalid characters`;
1170
+ const systemDirs = ["windows", "program files", "system32"];
1171
+ if (systemDirs.some((dir) => value.toLowerCase().startsWith(dir))) {
1172
+ return `Cannot use system directories`;
1173
+ }
1174
+ const basename = path2.basename(value, ".css");
1175
+ if (!basename || basename.trim().length === 0) {
1176
+ return `Invalid filename`;
1177
+ }
1178
+ }
1179
+ }),
1180
+ twBaseColor: () => p5.select({
1181
+ message: "What Tailwind base color would you like to use?",
1182
+ initialValue: "neutral",
1183
+ options: [
1184
+ { label: "Neutral (default)", value: "neutral" },
1185
+ { label: "Stone", value: "stone" },
1186
+ { label: "Zinc", value: "zinc" },
1187
+ { label: "Gray", value: "gray" },
1188
+ { label: "Slate", value: "slate" }
1189
+ ]
1190
+ })
1191
+ },
1192
+ {
1193
+ // On Cancel callback that wraps the group
1194
+ // So if the user cancels one of the prompts in the group this function will be called
1195
+ onCancel: () => {
1196
+ p5.cancel("Operation cancelled.");
1197
+ process.exit(0);
1198
+ }
1199
+ }
1200
+ );
1201
+ }
1202
+ const cssFileDir = path2.dirname(configChoices.cssFile);
1203
+ const componentInstallDir = path2.join(configChoices.installLocation, "starwind");
1204
+ configTasks.push({
1205
+ title: "Creating project structure",
1206
+ task: async () => {
1207
+ await ensureDirectory(componentInstallDir);
1208
+ await ensureDirectory(cssFileDir);
1209
+ await sleep(250);
1210
+ return "Created project structure";
1211
+ }
1212
+ });
1213
+ configTasks.push({
1214
+ title: "Setup Astro config file",
1215
+ task: async () => {
1216
+ const success = await setupAstroConfig();
1217
+ if (!success) {
1218
+ throw new Error("Failed to setup Astro config");
1219
+ }
1220
+ await sleep(250);
1221
+ return "Astro config setup completed";
1222
+ }
1223
+ });
1224
+ const cssFileExists = await fileExists(configChoices.cssFile);
1225
+ let updatedTailwindConfig = tailwindConfig;
1226
+ if (configChoices.twBaseColor !== "neutral") {
1227
+ updatedTailwindConfig = updatedTailwindConfig.replace(
1228
+ /--color-neutral-/g,
1229
+ `--color-${configChoices.twBaseColor}-`
1230
+ );
1231
+ }
1232
+ if (cssFileExists) {
1233
+ const shouldOverride = options?.defaults ? true : await p5.confirm({
1234
+ message: `${highlighter.info(configChoices.cssFile)} already exists. Do you want to override it?`
1235
+ });
1236
+ if (p5.isCancel(shouldOverride)) {
1237
+ p5.cancel("Operation cancelled");
1238
+ return process.exit(0);
1239
+ }
1240
+ if (!shouldOverride) {
1241
+ p5.log.info("Skipping Tailwind CSS configuration");
1242
+ } else {
1243
+ configTasks.push({
1244
+ title: "Creating Tailwind CSS configuration",
1245
+ task: async () => {
1246
+ await writeCssFile(configChoices.cssFile, updatedTailwindConfig);
1247
+ await sleep(250);
1248
+ return "Created Tailwind configuration";
1249
+ }
1250
+ });
1251
+ }
1252
+ } else {
1253
+ configTasks.push({
1254
+ title: "Creating Tailwind CSS configuration",
1255
+ task: async () => {
1256
+ await writeCssFile(configChoices.cssFile, updatedTailwindConfig);
1257
+ await sleep(250);
1258
+ return "Created Tailwind configuration";
1259
+ }
1260
+ });
1261
+ }
1262
+ configTasks.push({
1263
+ title: "Updating project configuration",
1264
+ task: async () => {
1265
+ await updateConfig({
1266
+ tailwind: {
1267
+ css: configChoices.cssFile,
1268
+ baseColor: configChoices.twBaseColor,
1269
+ cssVariables: true
1270
+ },
1271
+ // aliases: {
1272
+ // components: "@/components",
1273
+ // },
1274
+ componentDir: configChoices.installLocation,
1275
+ components: []
1276
+ });
1277
+ await sleep(250);
1278
+ return "Updated project starwind configuration";
1279
+ }
1280
+ });
1281
+ if (options?.pro) {
1282
+ const alreadyHasPro = await hasStarwindProRegistry();
1283
+ if (!alreadyHasPro) {
1284
+ if (!withinAdd) {
1285
+ p5.log.info(highlighter.info("Setting up Starwind Pro configuration..."));
1286
+ }
1287
+ configTasks.push({
1288
+ title: "Setting up Starwind Pro registry",
1289
+ task: async () => {
1290
+ await setupShadcnProConfig(configChoices.cssFile, configChoices.twBaseColor);
1291
+ await sleep(250);
1292
+ return "Configured Starwind Pro registry in components.json";
1293
+ }
1294
+ });
1295
+ } else {
1296
+ if (!withinAdd) {
1297
+ p5.log.info(highlighter.info("Starwind Pro registry already configured"));
1298
+ }
1299
+ }
1300
+ }
1301
+ const pm = options?.defaults ? await getDefaultPackageManager() : await requestPackageManager();
1302
+ if (pkg.dependencies?.astro) {
1303
+ const astroVersion = pkg.dependencies.astro.replace(/^\^|~/, "");
1304
+ if (!semver4.gte(astroVersion, MIN_ASTRO_VERSION)) {
1305
+ const shouldUpgrade = options?.defaults ? true : await p5.confirm({
1306
+ message: `Starwind requires Astro v${MIN_ASTRO_VERSION} or higher. Would you like to upgrade from v${astroVersion}?`,
1307
+ initialValue: true
1308
+ });
1309
+ if (p5.isCancel(shouldUpgrade)) {
1310
+ p5.cancel("Operation cancelled");
1311
+ return process.exit(0);
1312
+ }
1313
+ if (!shouldUpgrade) {
1314
+ p5.cancel("Astro v5 or higher is required to use Starwind");
1315
+ return process.exit(1);
1316
+ }
1317
+ installTasks.push({
1318
+ title: "Upgrading Astro",
1319
+ task: async () => {
1320
+ await installDependencies([ASTRO_PACKAGES.core], pm);
1321
+ return "Upgraded Astro successfully";
1322
+ }
1323
+ });
1324
+ }
1325
+ } else {
1326
+ const shouldInstall2 = options?.defaults ? true : await p5.confirm({
1327
+ message: `Starwind requires Astro v${MIN_ASTRO_VERSION} or higher. Would you like to install it?`,
1328
+ initialValue: true
1329
+ });
1330
+ if (p5.isCancel(shouldInstall2)) {
1331
+ p5.cancel("Operation cancelled");
1332
+ return process.exit(0);
1333
+ }
1334
+ if (!shouldInstall2) {
1335
+ p5.cancel("Astro is required to use Starwind");
1336
+ return process.exit(1);
1337
+ }
1338
+ installTasks.push({
1339
+ title: `Installing ${ASTRO_PACKAGES.core}`,
1340
+ task: async () => {
1341
+ await installDependencies([ASTRO_PACKAGES.core], pm);
1342
+ return `Installed ${highlighter.info(ASTRO_PACKAGES.core)} successfully`;
1343
+ }
1344
+ });
1345
+ }
1346
+ const otherPackages = getOtherPackages();
1347
+ const shouldInstall = options?.defaults ? true : await p5.confirm({
1348
+ message: `Install ${highlighter.info(otherPackages.join(", "))} using ${highlighter.info(pm)}?`
1349
+ });
1350
+ if (p5.isCancel(shouldInstall)) {
1351
+ p5.cancel("Operation cancelled");
1352
+ return process.exit(0);
1353
+ }
1354
+ if (shouldInstall) {
1355
+ installTasks.push({
1356
+ title: `Installing packages`,
1357
+ task: async () => {
1358
+ await installDependencies(getOtherPackages(), pm, false, false);
1359
+ return `${highlighter.info("Packages installed successfully")}`;
1360
+ }
1361
+ });
1362
+ } else {
1363
+ p5.log.warn(
1364
+ highlighter.warn(`Skipped installation of packages. Make sure to install them manually`)
1365
+ );
1366
+ }
1367
+ if (installTasks.length > 0) {
1368
+ await p5.tasks(installTasks);
1369
+ }
1370
+ if (configTasks.length > 0) {
1371
+ await p5.tasks(configTasks);
1372
+ }
1373
+ await sleep(250);
1374
+ let nextStepsMessage = `Make sure your layout imports the ${highlighter.infoBright(configChoices.cssFile)} file`;
1375
+ if (options?.pro) {
1376
+ nextStepsMessage += `
1377
+
1378
+ Starwind Pro is now configured! You can install pro components using:
1379
+ ${highlighter.info("npx starwind@latest add @starwind-pro/component-name")}
1380
+
1381
+ Make sure to set your ${highlighter.infoBright("STARWIND_LICENSE_KEY")} environment variable.`;
1382
+ }
1383
+ p5.note(nextStepsMessage, "Next steps");
1384
+ if (!withinAdd) {
1385
+ sleep(1e3);
1386
+ const outroMessage = options?.pro ? "Enjoy using Starwind UI with Pro components! \u{1F680}\u2728" : "Enjoy using Starwind UI \u{1F680}";
1387
+ p5.outro(outroMessage);
1388
+ }
1389
+ } catch (error) {
1390
+ p5.log.error(error instanceof Error ? error.message : "Failed to add components");
1391
+ p5.cancel("Operation cancelled");
1392
+ process.exit(1);
1393
+ }
1394
+ }
1395
+
290
1396
  // src/commands/add.ts
291
- var { init: init2 } = await import("./init-NBNT5V74.js");
292
1397
  async function add(components, options) {
293
1398
  try {
294
- p2.intro(highlighter.title(" Welcome to the Starwind CLI "));
1399
+ p6.intro(highlighter.title(" Welcome to the Starwind CLI "));
295
1400
  const configExists = await fileExists(PATHS.LOCAL_CONFIG_FILE);
296
1401
  if (!configExists) {
297
- const shouldInit = await p2.confirm({
1402
+ const shouldInit = await p6.confirm({
298
1403
  message: `Starwind configuration not found. Would you like to run ${highlighter.info("starwind init")} now?`,
299
1404
  initialValue: true
300
1405
  });
301
- if (p2.isCancel(shouldInit)) {
302
- p2.cancel("Operation cancelled");
1406
+ if (p6.isCancel(shouldInit)) {
1407
+ p6.cancel("Operation cancelled");
303
1408
  process.exit(0);
304
1409
  }
305
1410
  if (shouldInit) {
306
- await init2(true);
1411
+ await init(true);
307
1412
  } else {
308
- p2.log.error(
1413
+ p6.log.error(
309
1414
  `Please initialize starwind with ${highlighter.info("starwind init")} before adding components`
310
1415
  );
311
1416
  process.exit(1);
312
1417
  }
313
1418
  }
314
1419
  let componentsToInstall = [];
1420
+ const registryComponents = [];
1421
+ let registryResults = null;
315
1422
  if (options?.all) {
316
1423
  const availableComponents = await getAllComponents();
317
1424
  componentsToInstall = availableComponents.map((c) => c.name);
318
- p2.log.info(`Adding all ${componentsToInstall.length} available components...`);
1425
+ p6.log.info(`Adding all ${componentsToInstall.length} available components...`);
319
1426
  } 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);
1427
+ const regularComponents = [];
1428
+ for (const component of components) {
1429
+ if (component.startsWith("@")) {
1430
+ registryComponents.push(component);
1431
+ } else {
1432
+ regularComponents.push(component);
1433
+ }
1434
+ }
1435
+ if (registryComponents.length > 0) {
1436
+ const hasProRegistry = await hasStarwindProRegistry();
1437
+ if (!hasProRegistry) {
1438
+ const shouldSetupPro = await p6.confirm({
1439
+ message: `Starwind Pro registry not configured. Would you like to set it up now to install ${registryComponents.join(", ")}?`,
1440
+ initialValue: true
1441
+ });
1442
+ if (p6.isCancel(shouldSetupPro)) {
1443
+ p6.cancel("Operation cancelled");
1444
+ process.exit(0);
1445
+ }
1446
+ if (shouldSetupPro) {
1447
+ p6.log.info(highlighter.info("Setting up Starwind Pro configuration..."));
1448
+ let cssFile = PATHS.LOCAL_CSS_FILE;
1449
+ let baseColor = "neutral";
1450
+ try {
1451
+ const config = await getConfig();
1452
+ cssFile = config.tailwind?.css || PATHS.LOCAL_CSS_FILE;
1453
+ baseColor = config.tailwind?.baseColor || "neutral";
1454
+ } catch {
1455
+ }
1456
+ await setupShadcnProConfig(cssFile, baseColor);
1457
+ p6.log.success("Starwind Pro registry configured successfully!");
327
1458
  } else {
328
- acc.invalid.push(component);
1459
+ p6.log.error("Cannot install registry components without Starwind Pro configuration");
1460
+ p6.cancel("Operation cancelled");
1461
+ process.exit(1);
329
1462
  }
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
- );
1463
+ }
1464
+ p6.log.info(`Installing registry components: ${registryComponents.join(", ")}`);
1465
+ const [command, baseArgs] = await getShadcnCommand();
1466
+ registryResults = {
1467
+ success: [],
1468
+ failed: []
1469
+ };
1470
+ for (const registryComponent of registryComponents) {
1471
+ try {
1472
+ p6.log.info(`Installing ${highlighter.info(registryComponent)} via shadcn...`);
1473
+ await execa2(command, [...baseArgs, "add", registryComponent], {
1474
+ stdio: "inherit",
1475
+ cwd: process.cwd()
1476
+ });
1477
+ registryResults.success.push(registryComponent);
1478
+ } catch (error) {
1479
+ registryResults.failed.push(registryComponent);
1480
+ }
1481
+ }
339
1482
  }
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);
1483
+ if (regularComponents.length > 0) {
1484
+ const availableComponents = await getAllComponents();
1485
+ const { valid, invalid } = await regularComponents.reduce(
1486
+ async (accPromise, component) => {
1487
+ const acc = await accPromise;
1488
+ const isValid = await isValidComponent(component, availableComponents);
1489
+ if (isValid) {
1490
+ acc.valid.push(component);
1491
+ } else {
1492
+ acc.invalid.push(component);
1493
+ }
1494
+ return acc;
1495
+ },
1496
+ Promise.resolve({ valid: [], invalid: [] })
1497
+ );
1498
+ if (invalid.length > 0) {
1499
+ p6.log.warn(
1500
+ `${highlighter.warn("Invalid components found:")}
1501
+ ${invalid.map((name) => ` ${name}`).join("\n")}`
1502
+ );
1503
+ }
1504
+ if (valid.length > 0) {
1505
+ componentsToInstall = valid;
1506
+ } else if (registryComponents.length === 0) {
1507
+ p6.log.warn(`${highlighter.warn("No valid components to install")}`);
1508
+ p6.cancel("Operation cancelled");
1509
+ return process.exit(0);
1510
+ }
346
1511
  }
347
1512
  } else {
348
1513
  const selected = await selectComponents();
349
1514
  if (!selected) {
350
- p2.cancel("No components selected");
1515
+ p6.cancel("No components selected");
351
1516
  return process.exit(0);
352
1517
  }
353
1518
  componentsToInstall = selected;
354
1519
  }
355
- if (componentsToInstall.length === 0) {
356
- p2.log.warn(`${highlighter.warn("No components selected")}`);
357
- p2.cancel("Operation cancelled");
1520
+ if (componentsToInstall.length === 0 && registryComponents.length === 0) {
1521
+ p6.log.warn(`${highlighter.warn("No components selected")}`);
1522
+ p6.cancel("Operation cancelled");
358
1523
  return process.exit(0);
359
1524
  }
360
1525
  const results = {
@@ -369,12 +1534,42 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
369
1534
  case "installed":
370
1535
  results.installed.push(result);
371
1536
  installedComponents.push({ name: result.name, version: result.version });
1537
+ if (result.dependencyResults) {
1538
+ for (const depResult of result.dependencyResults) {
1539
+ switch (depResult.status) {
1540
+ case "installed":
1541
+ results.installed.push(depResult);
1542
+ break;
1543
+ case "skipped":
1544
+ results.skipped.push(depResult);
1545
+ break;
1546
+ case "failed":
1547
+ results.failed.push(depResult);
1548
+ break;
1549
+ }
1550
+ }
1551
+ }
372
1552
  break;
373
1553
  case "skipped":
374
1554
  results.skipped.push(result);
375
1555
  break;
376
1556
  case "failed":
377
1557
  results.failed.push(result);
1558
+ if (result.dependencyResults) {
1559
+ for (const depResult of result.dependencyResults) {
1560
+ switch (depResult.status) {
1561
+ case "installed":
1562
+ results.installed.push(depResult);
1563
+ break;
1564
+ case "skipped":
1565
+ results.skipped.push(depResult);
1566
+ break;
1567
+ case "failed":
1568
+ results.failed.push(depResult);
1569
+ break;
1570
+ }
1571
+ }
1572
+ }
378
1573
  break;
379
1574
  }
380
1575
  }
@@ -382,68 +1577,82 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
382
1577
  try {
383
1578
  await updateConfig({ components: installedComponents }, { appendComponents: true });
384
1579
  } catch (error) {
385
- p2.log.error(
1580
+ p6.log.error(
386
1581
  `Failed to update config: ${error instanceof Error ? error.message : "Unknown error"}`
387
1582
  );
388
1583
  process.exit(1);
389
1584
  }
390
1585
  }
391
- p2.log.message(`
1586
+ p6.log.message(`
392
1587
 
393
1588
  ${highlighter.underline("Installation Summary")}`);
394
1589
  if (results.failed.length > 0) {
395
- p2.log.error(
1590
+ p6.log.error(
396
1591
  `${highlighter.error("Failed to install components:")}
397
1592
  ${results.failed.map((r) => ` ${r.name} - ${r.error}`).join("\n")}`
398
1593
  );
399
1594
  }
400
1595
  if (results.skipped.length > 0) {
401
- p2.log.warn(
1596
+ p6.log.warn(
402
1597
  `${highlighter.warn("Skipped components (already installed):")}
403
1598
  ${results.skipped.map((r) => ` ${r.name} v${r.version}`).join("\n")}`
404
1599
  );
405
1600
  }
406
1601
  if (results.installed.length > 0) {
407
- p2.log.success(
1602
+ p6.log.success(
408
1603
  `${highlighter.success("Successfully installed components:")}
409
1604
  ${results.installed.map((r) => ` ${r.name} v${r.version}`).join("\n")}`
410
1605
  );
411
1606
  }
1607
+ if (registryResults) {
1608
+ if (registryResults.failed.length > 0) {
1609
+ p6.log.error(
1610
+ `${highlighter.error("Failed to install registry components:")}
1611
+ ${registryResults.failed.map((name) => ` ${name} - see the error message above for further details`).join("\n")}`
1612
+ );
1613
+ }
1614
+ if (registryResults.success.length > 0) {
1615
+ p6.log.success(
1616
+ `${highlighter.success("Successfully installed registry components:")}
1617
+ ${registryResults.success.map((name) => ` ${name}`).join("\n")}`
1618
+ );
1619
+ }
1620
+ }
412
1621
  await sleep(1e3);
413
- p2.outro("Enjoy using Starwind UI \u{1F680}");
1622
+ p6.outro("Enjoy using Starwind UI \u{1F680}");
414
1623
  } catch (error) {
415
- p2.log.error(error instanceof Error ? error.message : "Failed to add components");
416
- p2.cancel("Operation cancelled");
1624
+ p6.log.error(error instanceof Error ? error.message : "Failed to add components");
1625
+ p6.cancel("Operation cancelled");
417
1626
  process.exit(1);
418
1627
  }
419
1628
  }
420
1629
 
421
1630
  // src/commands/remove.ts
422
- import * as p3 from "@clack/prompts";
1631
+ import * as p7 from "@clack/prompts";
423
1632
  async function remove(components, options) {
424
1633
  try {
425
- p3.intro(highlighter.title(" Welcome to the Starwind CLI "));
1634
+ p7.intro(highlighter.title(" Welcome to the Starwind CLI "));
426
1635
  const configExists = await fileExists(PATHS.LOCAL_CONFIG_FILE);
427
1636
  if (!configExists) {
428
- p3.log.error("No Starwind configuration found. Please run starwind init first.");
1637
+ p7.log.error("No Starwind configuration found. Please run starwind init first.");
429
1638
  process.exit(1);
430
1639
  }
431
1640
  const config = await getConfig();
432
1641
  const installedComponents = config.components;
433
1642
  if (installedComponents.length === 0) {
434
- p3.log.warn("No components are currently installed.");
1643
+ p7.log.warn("No components are currently installed.");
435
1644
  process.exit(0);
436
1645
  }
437
1646
  let componentsToRemove = [];
438
1647
  if (options?.all) {
439
1648
  componentsToRemove = installedComponents.map((comp) => comp.name);
440
- p3.log.info(`Removing all ${componentsToRemove.length} installed components...`);
1649
+ p7.log.info(`Removing all ${componentsToRemove.length} installed components...`);
441
1650
  } else if (components && components.length > 0) {
442
1651
  const invalid = components.filter(
443
1652
  (comp) => !installedComponents.some((ic) => ic.name === comp)
444
1653
  );
445
1654
  if (invalid.length > 0) {
446
- p3.log.warn(
1655
+ p7.log.warn(
447
1656
  `${highlighter.warn("Components not found:")}
448
1657
  ${invalid.map((name) => ` ${name}`).join("\n")}`
449
1658
  );
@@ -452,7 +1661,7 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
452
1661
  (comp) => installedComponents.some((ic) => ic.name === comp)
453
1662
  );
454
1663
  if (componentsToRemove.length === 0) {
455
- p3.log.warn("No valid components to remove");
1664
+ p7.log.warn("No valid components to remove");
456
1665
  process.exit(0);
457
1666
  }
458
1667
  } else {
@@ -460,25 +1669,25 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
460
1669
  value: comp.name,
461
1670
  label: comp.name
462
1671
  }));
463
- const selected = await p3.multiselect({
1672
+ const selected = await p7.multiselect({
464
1673
  message: "Select components to remove",
465
1674
  options: choices
466
1675
  });
467
- if (p3.isCancel(selected)) {
468
- p3.cancel("Operation cancelled");
1676
+ if (p7.isCancel(selected)) {
1677
+ p7.cancel("Operation cancelled");
469
1678
  process.exit(0);
470
1679
  }
471
1680
  componentsToRemove = selected;
472
1681
  }
473
1682
  if (componentsToRemove.length === 0) {
474
- p3.log.warn("No components selected for removal");
1683
+ p7.log.warn("No components selected for removal");
475
1684
  process.exit(0);
476
1685
  }
477
- const confirmed = await p3.confirm({
1686
+ const confirmed = await p7.confirm({
478
1687
  message: `Remove ${componentsToRemove.map((comp) => highlighter.info(comp)).join(", ")} ${componentsToRemove.length > 1 ? "components" : "component"}?`
479
1688
  });
480
- if (!confirmed || p3.isCancel(confirmed)) {
481
- p3.cancel("Operation cancelled");
1689
+ if (!confirmed || p7.isCancel(confirmed)) {
1690
+ p7.cancel("Operation cancelled");
482
1691
  process.exit(0);
483
1692
  }
484
1693
  const results = {
@@ -503,61 +1712,61 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
503
1712
  },
504
1713
  { appendComponents: false }
505
1714
  );
506
- p3.log.message(`
1715
+ p7.log.message(`
507
1716
 
508
1717
  ${highlighter.underline("Removal Summary")}`);
509
1718
  if (results.failed.length > 0) {
510
- p3.log.error(
1719
+ p7.log.error(
511
1720
  `${highlighter.error("Failed to remove components:")}
512
1721
  ${results.failed.map((r) => ` ${r.name} - ${r.error}`).join("\n")}`
513
1722
  );
514
1723
  }
515
1724
  if (results.removed.length > 0) {
516
- p3.log.success(
1725
+ p7.log.success(
517
1726
  `${highlighter.success("Successfully removed components:")}
518
1727
  ${results.removed.map((r) => ` ${r.name}`).join("\n")}`
519
1728
  );
520
1729
  }
521
1730
  await sleep(1e3);
522
1731
  if (results.removed.length > 0) {
523
- p3.outro("Components removed successfully \u{1F5D1}\uFE0F");
1732
+ p7.outro("Components removed successfully \u{1F5D1}\uFE0F");
524
1733
  } else {
525
- p3.cancel("Errors occurred while removing components");
1734
+ p7.cancel("Errors occurred while removing components");
526
1735
  process.exit(1);
527
1736
  }
528
1737
  } catch (error) {
529
- p3.log.error(error instanceof Error ? error.message : "Failed to remove components");
530
- p3.cancel("Operation cancelled");
1738
+ p7.log.error(error instanceof Error ? error.message : "Failed to remove components");
1739
+ p7.cancel("Operation cancelled");
531
1740
  process.exit(1);
532
1741
  }
533
1742
  }
534
1743
 
535
1744
  // src/commands/update.ts
536
- import * as p4 from "@clack/prompts";
1745
+ import * as p8 from "@clack/prompts";
537
1746
  async function update(components, options) {
538
1747
  try {
539
- p4.intro(highlighter.title(" Welcome to the Starwind CLI "));
1748
+ p8.intro(highlighter.title(" Welcome to the Starwind CLI "));
540
1749
  const configExists = await fileExists(PATHS.LOCAL_CONFIG_FILE);
541
1750
  if (!configExists) {
542
- p4.log.error("No Starwind configuration found. Please run starwind init first.");
1751
+ p8.log.error("No Starwind configuration found. Please run starwind init first.");
543
1752
  process.exit(1);
544
1753
  }
545
1754
  const config = await getConfig();
546
1755
  const installedComponents = config.components;
547
1756
  if (installedComponents.length === 0) {
548
- p4.log.warn("No components are currently installed.");
1757
+ p8.log.warn("No components are currently installed.");
549
1758
  process.exit(0);
550
1759
  }
551
1760
  let componentsToUpdate = [];
552
1761
  if (options?.all) {
553
1762
  componentsToUpdate = installedComponents.map((comp) => comp.name);
554
- p4.log.info(`Checking updates for all ${componentsToUpdate.length} installed components...`);
1763
+ p8.log.info(`Checking updates for all ${componentsToUpdate.length} installed components...`);
555
1764
  } else if (components && components.length > 0) {
556
1765
  const invalid = components.filter(
557
1766
  (comp) => !installedComponents.some((ic) => ic.name === comp)
558
1767
  );
559
1768
  if (invalid.length > 0) {
560
- p4.log.warn(
1769
+ p8.log.warn(
561
1770
  `${highlighter.warn("Components not found in project:")}
562
1771
  ${invalid.map((name) => ` ${name}`).join("\n")}`
563
1772
  );
@@ -566,7 +1775,7 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
566
1775
  (comp) => installedComponents.some((ic) => ic.name === comp)
567
1776
  );
568
1777
  if (componentsToUpdate.length === 0) {
569
- p4.log.warn("No valid components to update");
1778
+ p8.log.warn("No valid components to update");
570
1779
  process.exit(0);
571
1780
  }
572
1781
  } else {
@@ -574,18 +1783,18 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
574
1783
  value: comp.name,
575
1784
  label: comp.name
576
1785
  }));
577
- const selected = await p4.multiselect({
1786
+ const selected = await p8.multiselect({
578
1787
  message: "Select components to update",
579
1788
  options: choices
580
1789
  });
581
- if (p4.isCancel(selected)) {
582
- p4.cancel("Operation cancelled");
1790
+ if (p8.isCancel(selected)) {
1791
+ p8.cancel("Operation cancelled");
583
1792
  process.exit(0);
584
1793
  }
585
1794
  componentsToUpdate = selected;
586
1795
  }
587
1796
  if (componentsToUpdate.length === 0) {
588
- p4.log.warn("No components selected for update");
1797
+ p8.log.warn("No components selected for update");
589
1798
  process.exit(0);
590
1799
  }
591
1800
  const results = {
@@ -636,52 +1845,52 @@ ${invalid.map((name) => ` ${name}`).join("\n")}`
636
1845
  { appendComponents: false }
637
1846
  );
638
1847
  } catch (error) {
639
- p4.log.error(
1848
+ p8.log.error(
640
1849
  `Failed to update config: ${error instanceof Error ? error.message : "Unknown error"}`
641
1850
  );
642
1851
  process.exit(1);
643
1852
  }
644
1853
  }
645
- p4.log.message(`
1854
+ p8.log.message(`
646
1855
 
647
1856
  ${highlighter.underline("Update Summary")}`);
648
1857
  if (results.failed.length > 0) {
649
- p4.log.error(
1858
+ p8.log.error(
650
1859
  `${highlighter.error("Failed to update components:")}
651
1860
  ${results.failed.map((r) => ` ${r.name} - ${r.error}`).join("\n")}`
652
1861
  );
653
1862
  }
654
1863
  if (results.skipped.length > 0) {
655
- p4.log.info(
1864
+ p8.log.info(
656
1865
  `${highlighter.info("Components already up to date or skipped:")}
657
1866
  ${results.skipped.map((r) => ` ${r.name} (${r.oldVersion})`).join("\n")}`
658
1867
  );
659
1868
  }
660
1869
  if (results.updated.length > 0) {
661
- p4.log.success(
1870
+ p8.log.success(
662
1871
  `${highlighter.success("Successfully updated components:")}
663
1872
  ${results.updated.map((r) => ` ${r.name} (${r.oldVersion} \u2192 ${r.newVersion})`).join("\n")}`
664
1873
  );
665
1874
  }
666
1875
  await sleep(1e3);
667
1876
  if (results.updated.length > 0) {
668
- p4.outro("Components updated successfully \u{1F680}");
1877
+ p8.outro("Components updated successfully \u{1F680}");
669
1878
  } else if (results.skipped.length > 0 && results.failed.length === 0) {
670
- p4.outro("Components already up to date or skipped \u2728");
1879
+ p8.outro("Components already up to date or skipped \u2728");
671
1880
  } else {
672
- p4.cancel("No components were updated");
1881
+ p8.cancel("No components were updated");
673
1882
  process.exit(1);
674
1883
  }
675
1884
  } catch (error) {
676
- p4.log.error(error instanceof Error ? error.message : "Failed to update components");
677
- p4.cancel("Operation cancelled");
1885
+ p8.log.error(error instanceof Error ? error.message : "Failed to update components");
1886
+ p8.cancel("Operation cancelled");
678
1887
  process.exit(1);
679
1888
  }
680
1889
  }
681
1890
 
682
1891
  // src/index.ts
683
1892
  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 }));
1893
+ 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
1894
  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
1895
  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
1896
  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);