starwind 1.8.0 → 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 +1339 -117
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
- package/dist/chunk-HDAZQTOL.js +0 -668
- package/dist/chunk-HDAZQTOL.js.map +0 -1
- package/dist/init-NBNT5V74.js +0 -7
- package/dist/init-NBNT5V74.js.map +0 -1
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
134
|
-
await
|
|
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
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
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
|
-
|
|
272
|
-
await installDependencies(component.dependencies, pm);
|
|
751
|
+
await updateConfig({ components: componentsToInstall }, { appendComponents: true });
|
|
273
752
|
} catch (error) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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-NBNT5V74.js");
|
|
292
1410
|
async function add(components, options) {
|
|
293
1411
|
try {
|
|
294
|
-
|
|
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
|
|
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 (
|
|
302
|
-
|
|
1419
|
+
if (p6.isCancel(shouldInit)) {
|
|
1420
|
+
p6.cancel("Operation cancelled");
|
|
303
1421
|
process.exit(0);
|
|
304
1422
|
}
|
|
305
1423
|
if (shouldInit) {
|
|
306
|
-
await
|
|
1424
|
+
await init(true);
|
|
307
1425
|
} else {
|
|
308
|
-
|
|
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
|
-
|
|
1438
|
+
p6.log.info(`Adding all ${componentsToInstall.length} available components...`);
|
|
319
1439
|
} else if (components && components.length > 0) {
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
1472
|
+
p6.log.error("Cannot install registry components without Starwind Pro configuration");
|
|
1473
|
+
p6.cancel("Operation cancelled");
|
|
1474
|
+
process.exit(1);
|
|
329
1475
|
}
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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 (
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
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
|
-
|
|
357
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1599
|
+
p6.log.message(`
|
|
392
1600
|
|
|
393
1601
|
${highlighter.underline("Installation Summary")}`);
|
|
394
1602
|
if (results.failed.length > 0) {
|
|
395
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1635
|
+
p6.outro("Enjoy using Starwind UI \u{1F680}");
|
|
414
1636
|
} catch (error) {
|
|
415
|
-
|
|
416
|
-
|
|
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
|
|
1644
|
+
import * as p7 from "@clack/prompts";
|
|
423
1645
|
async function remove(components, options) {
|
|
424
1646
|
try {
|
|
425
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1685
|
+
const selected = await p7.multiselect({
|
|
464
1686
|
message: "Select components to remove",
|
|
465
1687
|
options: choices
|
|
466
1688
|
});
|
|
467
|
-
if (
|
|
468
|
-
|
|
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
|
-
|
|
1696
|
+
p7.log.warn("No components selected for removal");
|
|
475
1697
|
process.exit(0);
|
|
476
1698
|
}
|
|
477
|
-
const confirmed = await
|
|
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 ||
|
|
481
|
-
|
|
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
|
-
|
|
1728
|
+
p7.log.message(`
|
|
507
1729
|
|
|
508
1730
|
${highlighter.underline("Removal Summary")}`);
|
|
509
1731
|
if (results.failed.length > 0) {
|
|
510
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1745
|
+
p7.outro("Components removed successfully \u{1F5D1}\uFE0F");
|
|
524
1746
|
} else {
|
|
525
|
-
|
|
1747
|
+
p7.cancel("Errors occurred while removing components");
|
|
526
1748
|
process.exit(1);
|
|
527
1749
|
}
|
|
528
1750
|
} catch (error) {
|
|
529
|
-
|
|
530
|
-
|
|
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
|
|
1758
|
+
import * as p8 from "@clack/prompts";
|
|
537
1759
|
async function update(components, options) {
|
|
538
1760
|
try {
|
|
539
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1799
|
+
const selected = await p8.multiselect({
|
|
578
1800
|
message: "Select components to update",
|
|
579
1801
|
options: choices
|
|
580
1802
|
});
|
|
581
|
-
if (
|
|
582
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1867
|
+
p8.log.message(`
|
|
646
1868
|
|
|
647
1869
|
${highlighter.underline("Update Summary")}`);
|
|
648
1870
|
if (results.failed.length > 0) {
|
|
649
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1890
|
+
p8.outro("Components updated successfully \u{1F680}");
|
|
669
1891
|
} else if (results.skipped.length > 0 && results.failed.length === 0) {
|
|
670
|
-
|
|
1892
|
+
p8.outro("Components already up to date or skipped \u2728");
|
|
671
1893
|
} else {
|
|
672
|
-
|
|
1894
|
+
p8.cancel("No components were updated");
|
|
673
1895
|
process.exit(1);
|
|
674
1896
|
}
|
|
675
1897
|
} catch (error) {
|
|
676
|
-
|
|
677
|
-
|
|
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);
|