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 +1326 -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,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
|
-
|
|
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-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
|
-
|
|
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
|
|
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 (
|
|
302
|
-
|
|
1406
|
+
if (p6.isCancel(shouldInit)) {
|
|
1407
|
+
p6.cancel("Operation cancelled");
|
|
303
1408
|
process.exit(0);
|
|
304
1409
|
}
|
|
305
1410
|
if (shouldInit) {
|
|
306
|
-
await
|
|
1411
|
+
await init(true);
|
|
307
1412
|
} else {
|
|
308
|
-
|
|
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
|
-
|
|
1425
|
+
p6.log.info(`Adding all ${componentsToInstall.length} available components...`);
|
|
319
1426
|
} else if (components && components.length > 0) {
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
1459
|
+
p6.log.error("Cannot install registry components without Starwind Pro configuration");
|
|
1460
|
+
p6.cancel("Operation cancelled");
|
|
1461
|
+
process.exit(1);
|
|
329
1462
|
}
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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 (
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
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
|
-
|
|
357
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1586
|
+
p6.log.message(`
|
|
392
1587
|
|
|
393
1588
|
${highlighter.underline("Installation Summary")}`);
|
|
394
1589
|
if (results.failed.length > 0) {
|
|
395
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1622
|
+
p6.outro("Enjoy using Starwind UI \u{1F680}");
|
|
414
1623
|
} catch (error) {
|
|
415
|
-
|
|
416
|
-
|
|
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
|
|
1631
|
+
import * as p7 from "@clack/prompts";
|
|
423
1632
|
async function remove(components, options) {
|
|
424
1633
|
try {
|
|
425
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1672
|
+
const selected = await p7.multiselect({
|
|
464
1673
|
message: "Select components to remove",
|
|
465
1674
|
options: choices
|
|
466
1675
|
});
|
|
467
|
-
if (
|
|
468
|
-
|
|
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
|
-
|
|
1683
|
+
p7.log.warn("No components selected for removal");
|
|
475
1684
|
process.exit(0);
|
|
476
1685
|
}
|
|
477
|
-
const confirmed = await
|
|
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 ||
|
|
481
|
-
|
|
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
|
-
|
|
1715
|
+
p7.log.message(`
|
|
507
1716
|
|
|
508
1717
|
${highlighter.underline("Removal Summary")}`);
|
|
509
1718
|
if (results.failed.length > 0) {
|
|
510
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1732
|
+
p7.outro("Components removed successfully \u{1F5D1}\uFE0F");
|
|
524
1733
|
} else {
|
|
525
|
-
|
|
1734
|
+
p7.cancel("Errors occurred while removing components");
|
|
526
1735
|
process.exit(1);
|
|
527
1736
|
}
|
|
528
1737
|
} catch (error) {
|
|
529
|
-
|
|
530
|
-
|
|
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
|
|
1745
|
+
import * as p8 from "@clack/prompts";
|
|
537
1746
|
async function update(components, options) {
|
|
538
1747
|
try {
|
|
539
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1786
|
+
const selected = await p8.multiselect({
|
|
578
1787
|
message: "Select components to update",
|
|
579
1788
|
options: choices
|
|
580
1789
|
});
|
|
581
|
-
if (
|
|
582
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1854
|
+
p8.log.message(`
|
|
646
1855
|
|
|
647
1856
|
${highlighter.underline("Update Summary")}`);
|
|
648
1857
|
if (results.failed.length > 0) {
|
|
649
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1877
|
+
p8.outro("Components updated successfully \u{1F680}");
|
|
669
1878
|
} else if (results.skipped.length > 0 && results.failed.length === 0) {
|
|
670
|
-
|
|
1879
|
+
p8.outro("Components already up to date or skipped \u2728");
|
|
671
1880
|
} else {
|
|
672
|
-
|
|
1881
|
+
p8.cancel("No components were updated");
|
|
673
1882
|
process.exit(1);
|
|
674
1883
|
}
|
|
675
1884
|
} catch (error) {
|
|
676
|
-
|
|
677
|
-
|
|
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);
|