vectorvesper 0.1.1 → 0.2.1
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 +665 -257
- package/package.json +8 -5
package/dist/index.js
CHANGED
|
@@ -144,7 +144,17 @@ var RegistryComponentSchema = z.object({
|
|
|
144
144
|
supportsReducedMotion: z.boolean(),
|
|
145
145
|
usesWebGL: z.boolean(),
|
|
146
146
|
usesPointer: z.boolean(),
|
|
147
|
-
usesScroll: z.boolean()
|
|
147
|
+
usesScroll: z.boolean(),
|
|
148
|
+
// Optional metadata — kept in sync with the registry builder schema.
|
|
149
|
+
// Declared here (rather than relying on .passthrough()) so the CLI can
|
|
150
|
+
// read them with full type-safety for post-install guidance.
|
|
151
|
+
entry: z.string().optional(),
|
|
152
|
+
exportName: z.string().optional(),
|
|
153
|
+
usesTailwind: z.boolean().optional(),
|
|
154
|
+
fallbacks: z.array(z.string()).optional(),
|
|
155
|
+
recipes: z.array(z.string()).optional(),
|
|
156
|
+
docsUrl: z.string().optional(),
|
|
157
|
+
previewUrl: z.string().optional()
|
|
148
158
|
}).passthrough();
|
|
149
159
|
var IndexComponentSchema = z.object({
|
|
150
160
|
slug: z.string(),
|
|
@@ -168,7 +178,10 @@ var IndexComponentSchema = z.object({
|
|
|
168
178
|
supportsReducedMotion: z.boolean(),
|
|
169
179
|
usesWebGL: z.boolean(),
|
|
170
180
|
usesPointer: z.boolean(),
|
|
171
|
-
usesScroll: z.boolean()
|
|
181
|
+
usesScroll: z.boolean(),
|
|
182
|
+
entry: z.string().optional(),
|
|
183
|
+
exportName: z.string().optional(),
|
|
184
|
+
usesTailwind: z.boolean().optional()
|
|
172
185
|
}).passthrough();
|
|
173
186
|
var RegistryIndexSchema = z.object({
|
|
174
187
|
version: z.string(),
|
|
@@ -218,7 +231,7 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
218
231
|
raw = JSON.parse(content);
|
|
219
232
|
} catch {
|
|
220
233
|
throw new Error(
|
|
221
|
-
`Failed to parse vv.config.json \u2014 the file contains invalid JSON. Delete it and run "
|
|
234
|
+
`Failed to parse vv.config.json \u2014 the file contains invalid JSON. Delete it and run "npx vectorvesper init" to regenerate.`
|
|
222
235
|
);
|
|
223
236
|
}
|
|
224
237
|
const result = VVConfigSchema.safeParse(raw);
|
|
@@ -227,7 +240,7 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
227
240
|
throw new Error(
|
|
228
241
|
`Invalid vv.config.json \u2014 schema validation failed:
|
|
229
242
|
${issues}
|
|
230
|
-
Delete the file and run "
|
|
243
|
+
Delete the file and run "npx vectorvesper init" to regenerate.`
|
|
231
244
|
);
|
|
232
245
|
}
|
|
233
246
|
return result.data;
|
|
@@ -385,7 +398,7 @@ async function initCommand(options = {}) {
|
|
|
385
398
|
\u2022 Target directory created at ${pc2.cyan(targetLocalDir)}
|
|
386
399
|
\u2022 Manifest created at ${pc2.cyan("vv-manifest.json")}
|
|
387
400
|
|
|
388
|
-
\u{1F449} Next, run: ${pc2.bold(pc2.cyan("
|
|
401
|
+
\u{1F449} Next, run: ${pc2.bold(pc2.cyan("npx vectorvesper add <slug>"))} to add a component.`
|
|
389
402
|
);
|
|
390
403
|
} catch (error) {
|
|
391
404
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -414,25 +427,25 @@ function handleHttpError(status, url) {
|
|
|
414
427
|
case 401:
|
|
415
428
|
throw new Error(
|
|
416
429
|
`Authentication required. This component requires a valid license key.
|
|
417
|
-
\u{1F449} Run "
|
|
430
|
+
\u{1F449} Run "npx vectorvesper login <your-license-key>" to authenticate.
|
|
418
431
|
\u{1F511} Get a license at https://vectorvesper.dev/pricing`
|
|
419
432
|
);
|
|
420
433
|
case 402:
|
|
421
434
|
throw new Error(
|
|
422
|
-
`This is a Pro component and requires
|
|
423
|
-
\u{1F449}
|
|
424
|
-
\u{1F4A1} Run "
|
|
435
|
+
`This is a Pro component and requires a license.
|
|
436
|
+
\u{1F449} Get lifetime access at https://vectorvesper.dev/pricing
|
|
437
|
+
\u{1F4A1} Run "npx vectorvesper list" to see all free components.`
|
|
425
438
|
);
|
|
426
439
|
case 403:
|
|
427
440
|
throw new Error(
|
|
428
|
-
`Access denied. Your license
|
|
441
|
+
`Access denied. Your license doesn't include this component.
|
|
429
442
|
\u{1F449} Check your account at https://vectorvesper.dev/account
|
|
430
|
-
\u{1F4A1} Run "
|
|
443
|
+
\u{1F4A1} Run "npx vectorvesper whoami" to see your current access.`
|
|
431
444
|
);
|
|
432
445
|
case 404:
|
|
433
446
|
throw new Error(
|
|
434
447
|
`Component not found in the registry.
|
|
435
|
-
\u{1F449} Run "
|
|
448
|
+
\u{1F449} Run "npx vectorvesper list" to see all available components.`
|
|
436
449
|
);
|
|
437
450
|
case 429:
|
|
438
451
|
throw new Error(
|
|
@@ -586,10 +599,10 @@ async function listCommand() {
|
|
|
586
599
|
console.log(`
|
|
587
600
|
${pc3.green(String(freeCount))} free components${proCount > 0 ? ` \xB7 ${pc3.magenta(String(proCount))} pro components` : ""}`);
|
|
588
601
|
if (proCount > 0 && !hasAuth) {
|
|
589
|
-
console.log(` \u{1F511} ${pc3.dim("Unlock pro components:")} ${pc3.cyan("
|
|
602
|
+
console.log(` \u{1F511} ${pc3.dim("Unlock pro components:")} ${pc3.cyan("npx vectorvesper login <key>")} ${pc3.dim("\xB7")} ${pc3.cyan("https://vectorvesper.dev/pricing")}`);
|
|
590
603
|
}
|
|
591
604
|
console.log(`
|
|
592
|
-
\u{1F449} Run ${pc3.bold(pc3.cyan("
|
|
605
|
+
\u{1F449} Run ${pc3.bold(pc3.cyan("npx vectorvesper add <slug>"))} to add a component to your project.
|
|
593
606
|
`);
|
|
594
607
|
} catch (error) {
|
|
595
608
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -599,43 +612,281 @@ async function listCommand() {
|
|
|
599
612
|
}
|
|
600
613
|
|
|
601
614
|
// src/commands/add.ts
|
|
615
|
+
import fs7 from "fs";
|
|
616
|
+
import path7 from "path";
|
|
617
|
+
import ora2 from "ora";
|
|
618
|
+
import pc6 from "picocolors";
|
|
619
|
+
import { confirm as confirm2, isCancel as isCancel2 } from "@clack/prompts";
|
|
620
|
+
|
|
621
|
+
// src/utils/installer.ts
|
|
602
622
|
import fs5 from "fs";
|
|
603
623
|
import path5 from "path";
|
|
604
|
-
import ora2 from "ora";
|
|
605
624
|
import pc4 from "picocolors";
|
|
606
|
-
|
|
625
|
+
var MAX_DEPTH = 20;
|
|
607
626
|
function toForwardSlash(p) {
|
|
608
627
|
return p.replace(/\\/g, "/");
|
|
609
628
|
}
|
|
610
|
-
function
|
|
611
|
-
const
|
|
612
|
-
|
|
629
|
+
async function resolveComponentTree(slug) {
|
|
630
|
+
const resolved = [];
|
|
631
|
+
const visited = /* @__PURE__ */ new Set();
|
|
632
|
+
async function walk(componentSlug, depth) {
|
|
633
|
+
if (visited.has(componentSlug)) return;
|
|
634
|
+
if (depth > MAX_DEPTH) {
|
|
635
|
+
throw new Error(
|
|
636
|
+
`Dependency resolution exceeded maximum depth (${MAX_DEPTH}). Possible circular dependency involving "${componentSlug}".`
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
visited.add(componentSlug);
|
|
640
|
+
const component = await fetchComponent(componentSlug);
|
|
641
|
+
if (component.tier === "pro" && !getAuthToken()) {
|
|
642
|
+
throw new Error(
|
|
643
|
+
`"${componentSlug}" is a Pro component and requires a license.
|
|
644
|
+
\u{1F449} Run ${pc4.cyan("npx vectorvesper login <your-license-key>")} to unlock pro components.
|
|
645
|
+
\u{1F511} Get a license at ${pc4.cyan("https://vectorvesper.dev/pricing")}`
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
for (const depSlug of component.registryDependencies ?? []) {
|
|
649
|
+
await walk(depSlug, depth + 1);
|
|
650
|
+
}
|
|
651
|
+
resolved.push(component);
|
|
652
|
+
}
|
|
653
|
+
await walk(slug, 0);
|
|
654
|
+
return resolved;
|
|
655
|
+
}
|
|
656
|
+
function planFiles(components, installDir, projectRoot) {
|
|
657
|
+
const planned = [];
|
|
658
|
+
const compDir = path5.join(projectRoot, installDir);
|
|
659
|
+
for (const component of components) {
|
|
660
|
+
for (const file of component.files) {
|
|
661
|
+
const absolutePath = path5.resolve(compDir, file.target);
|
|
662
|
+
const relativeFromRoot = path5.relative(projectRoot, absolutePath);
|
|
663
|
+
if (relativeFromRoot.startsWith("..") || path5.isAbsolute(relativeFromRoot)) {
|
|
664
|
+
throw new Error(
|
|
665
|
+
`Security Error: component target tries to escape project root: ${file.target}`
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
planned.push({
|
|
669
|
+
absolutePath,
|
|
670
|
+
relativePath: relativeFromRoot,
|
|
671
|
+
target: file.target,
|
|
672
|
+
content: file.content,
|
|
673
|
+
exists: fs5.existsSync(absolutePath)
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return planned;
|
|
678
|
+
}
|
|
679
|
+
function writeFiles(files) {
|
|
680
|
+
for (const file of files) {
|
|
681
|
+
const folder = path5.dirname(file.absolutePath);
|
|
682
|
+
if (!fs5.existsSync(folder)) {
|
|
683
|
+
fs5.mkdirSync(folder, { recursive: true });
|
|
684
|
+
}
|
|
685
|
+
fs5.writeFileSync(file.absolutePath, file.content, "utf-8");
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
var MANIFEST_NAME = "vv-manifest.json";
|
|
689
|
+
function getManifestPath(projectRoot) {
|
|
690
|
+
return path5.join(projectRoot, MANIFEST_NAME);
|
|
691
|
+
}
|
|
692
|
+
function readManifest(projectRoot) {
|
|
693
|
+
const manifestPath = getManifestPath(projectRoot);
|
|
694
|
+
if (!fs5.existsSync(manifestPath)) {
|
|
695
|
+
return { components: {} };
|
|
696
|
+
}
|
|
697
|
+
const parsed = JSON.parse(fs5.readFileSync(manifestPath, "utf-8"));
|
|
698
|
+
if (!parsed.components) parsed.components = {};
|
|
699
|
+
return parsed;
|
|
700
|
+
}
|
|
701
|
+
function writeManifest(projectRoot, manifest) {
|
|
702
|
+
fs5.writeFileSync(getManifestPath(projectRoot), JSON.stringify(manifest, null, 2), "utf-8");
|
|
703
|
+
}
|
|
704
|
+
function recordInstall(components, installDir, projectRoot) {
|
|
705
|
+
const manifest = readManifest(projectRoot);
|
|
706
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
707
|
+
for (const component of components) {
|
|
708
|
+
manifest.components[component.slug] = {
|
|
709
|
+
version: component.version,
|
|
710
|
+
installedAt: toForwardSlash(path5.join(installDir, component.slug)),
|
|
711
|
+
installedOn: today,
|
|
712
|
+
files: component.files.map((f) => f.target)
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
writeManifest(projectRoot, manifest);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// src/utils/install.ts
|
|
719
|
+
import fs6 from "fs";
|
|
720
|
+
import path6 from "path";
|
|
721
|
+
import { spawnSync } from "child_process";
|
|
722
|
+
var NPM_NAME_REGEX = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/i;
|
|
723
|
+
function isValidPackageName(name) {
|
|
724
|
+
return NPM_NAME_REGEX.test(name);
|
|
725
|
+
}
|
|
726
|
+
function getMissingDependencies(components, projectRoot) {
|
|
727
|
+
const pkgPath = path6.join(projectRoot, "package.json");
|
|
728
|
+
if (!fs6.existsSync(pkgPath)) return [];
|
|
729
|
+
let installed = {};
|
|
613
730
|
try {
|
|
614
|
-
const pkg = JSON.parse(
|
|
615
|
-
|
|
731
|
+
const pkg = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
|
|
732
|
+
installed = {
|
|
616
733
|
...pkg.dependencies,
|
|
617
734
|
...pkg.devDependencies,
|
|
618
735
|
...pkg.peerDependencies
|
|
619
736
|
};
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
}
|
|
628
|
-
}
|
|
737
|
+
} catch {
|
|
738
|
+
return [];
|
|
739
|
+
}
|
|
740
|
+
const missing = /* @__PURE__ */ new Set();
|
|
741
|
+
for (const component of components) {
|
|
742
|
+
for (const dep of component.dependencies ?? []) {
|
|
743
|
+
if (!installed[dep]) missing.add(dep);
|
|
629
744
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
745
|
+
}
|
|
746
|
+
return Array.from(missing);
|
|
747
|
+
}
|
|
748
|
+
function getInstallCommand(pm, deps) {
|
|
749
|
+
const depsStr = deps.join(" ");
|
|
750
|
+
switch (pm) {
|
|
751
|
+
case "pnpm":
|
|
752
|
+
return `pnpm add ${depsStr}`;
|
|
753
|
+
case "yarn":
|
|
754
|
+
return `yarn add ${depsStr}`;
|
|
755
|
+
case "bun":
|
|
756
|
+
return `bun add ${depsStr}`;
|
|
757
|
+
default:
|
|
758
|
+
return `npm install ${depsStr}`;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function installDependencies(deps, pm, projectRoot) {
|
|
762
|
+
const invalid = deps.filter((d) => !isValidPackageName(d));
|
|
763
|
+
if (invalid.length > 0) {
|
|
764
|
+
throw new Error(`Refusing to install \u2014 invalid package name(s): ${invalid.join(", ")}`);
|
|
765
|
+
}
|
|
766
|
+
const command = getInstallCommand(pm, deps);
|
|
767
|
+
const result = spawnSync(command, {
|
|
768
|
+
cwd: projectRoot,
|
|
769
|
+
stdio: "inherit",
|
|
770
|
+
shell: true
|
|
771
|
+
});
|
|
772
|
+
return result.status === 0;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/utils/guidance.ts
|
|
776
|
+
import pc5 from "picocolors";
|
|
777
|
+
function toPascalCase(input) {
|
|
778
|
+
return input.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
779
|
+
}
|
|
780
|
+
function toImportPath(alias, entryTarget) {
|
|
781
|
+
let p = entryTarget.replace(/\.(tsx|ts|jsx|js)$/i, "");
|
|
782
|
+
p = p.replace(/\/index$/i, "");
|
|
783
|
+
const base = alias.replace(/\/+$/, "");
|
|
784
|
+
return `${base}/${p}`;
|
|
785
|
+
}
|
|
786
|
+
function buildGuidance(component, ctx) {
|
|
787
|
+
const entryTarget = component.entry ?? component.files[0]?.target ?? `${component.slug}/index.ts`;
|
|
788
|
+
const importPath = toImportPath(ctx.alias, entryTarget);
|
|
789
|
+
const exportName = component.exportName ?? "default";
|
|
790
|
+
const isDefault = exportName === "default";
|
|
791
|
+
const varName = isDefault ? toPascalCase(component.name || component.slug) : exportName;
|
|
792
|
+
const importSnippet = isDefault ? `import ${varName} from "${importPath}";` : `import { ${exportName} } from "${importPath}";`;
|
|
793
|
+
const guidance = {
|
|
794
|
+
importSnippet,
|
|
795
|
+
notes: [],
|
|
796
|
+
warnings: [],
|
|
797
|
+
docsUrl: component.docsUrl
|
|
798
|
+
};
|
|
799
|
+
if (component.usesWebGL) {
|
|
800
|
+
const resolver = isDefault ? `() => import("${importPath}")` : `() => import("${importPath}").then((m) => m.${exportName})`;
|
|
801
|
+
if (ctx.framework === "next") {
|
|
802
|
+
guidance.ssrSnippet = `"use client";
|
|
803
|
+
import dynamic from "next/dynamic";
|
|
804
|
+
|
|
805
|
+
const ${varName} = dynamic(
|
|
806
|
+
${resolver},
|
|
807
|
+
{ ssr: false }
|
|
808
|
+
);`;
|
|
809
|
+
} else if (ctx.framework === "unknown") {
|
|
810
|
+
guidance.notes.push(
|
|
811
|
+
"This is a WebGL component. If you server-render (e.g. Next.js), load it with `next/dynamic` and `{ ssr: false }` to avoid hydration errors."
|
|
812
|
+
);
|
|
637
813
|
}
|
|
638
|
-
}
|
|
814
|
+
}
|
|
815
|
+
if (component.supportsReducedMotion) {
|
|
816
|
+
guidance.notes.push("Honors `prefers-reduced-motion` automatically \u2014 no extra setup needed.");
|
|
817
|
+
}
|
|
818
|
+
if (component.usesTailwind && !ctx.hasTailwind) {
|
|
819
|
+
guidance.warnings.push(
|
|
820
|
+
"Styled with Tailwind CSS, which wasn't detected in this project. Install Tailwind or swap the classNames for your own styles."
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
return guidance;
|
|
824
|
+
}
|
|
825
|
+
function printGuidance(component, ctx) {
|
|
826
|
+
const g = buildGuidance(component, ctx);
|
|
827
|
+
console.log(pc5.bold(pc5.white(`
|
|
828
|
+
Using ${component.title || component.name}:`)));
|
|
829
|
+
if (g.ssrSnippet) {
|
|
830
|
+
console.log(pc5.dim(" // SSR-safe (Next.js) \u2014 disables server rendering for WebGL:"));
|
|
831
|
+
for (const line of g.ssrSnippet.split("\n")) {
|
|
832
|
+
console.log(line ? ` ${pc5.cyan(line)}` : "");
|
|
833
|
+
}
|
|
834
|
+
} else {
|
|
835
|
+
console.log(` ${pc5.cyan(g.importSnippet)}`);
|
|
836
|
+
}
|
|
837
|
+
for (const note of g.notes) {
|
|
838
|
+
console.log(` ${pc5.green("\u2022")} ${pc5.dim(note)}`);
|
|
839
|
+
}
|
|
840
|
+
for (const warning of g.warnings) {
|
|
841
|
+
console.log(` ${pc5.yellow("\u26A0")} ${pc5.yellow(warning)}`);
|
|
842
|
+
}
|
|
843
|
+
if (g.docsUrl) {
|
|
844
|
+
console.log(` ${pc5.dim("Docs:")} ${pc5.cyan(g.docsUrl)}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// src/commands/add.ts
|
|
849
|
+
async function handleDependencies(components, projectRoot, projectInfo, options) {
|
|
850
|
+
const missing = getMissingDependencies(components, projectRoot);
|
|
851
|
+
if (missing.length === 0) return;
|
|
852
|
+
const installCmd = getInstallCommand(projectInfo.packageManager, missing);
|
|
853
|
+
const depsLabel = missing.map((d) => pc6.cyan(d)).join(", ");
|
|
854
|
+
if (options.install === false) {
|
|
855
|
+
console.log(pc6.yellow(`
|
|
856
|
+
\u26A0\uFE0F Missing dependencies: ${depsLabel}`));
|
|
857
|
+
console.log(` Run: ${pc6.bold(pc6.cyan(installCmd))}`);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
let shouldInstall = options.yes === true;
|
|
861
|
+
if (!shouldInstall) {
|
|
862
|
+
console.log("");
|
|
863
|
+
const answer = await confirm2({
|
|
864
|
+
message: `Install ${missing.length} missing ${missing.length === 1 ? "dependency" : "dependencies"} (${missing.join(", ")}) with ${projectInfo.packageManager}?`,
|
|
865
|
+
initialValue: true
|
|
866
|
+
});
|
|
867
|
+
if (isCancel2(answer)) {
|
|
868
|
+
console.log(pc6.dim(` Skipped. Run: ${pc6.cyan(installCmd)}`));
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
shouldInstall = answer;
|
|
872
|
+
}
|
|
873
|
+
if (!shouldInstall) {
|
|
874
|
+
console.log(pc6.dim(` Skipped. Run: ${pc6.cyan(installCmd)} when ready.`));
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
console.log(pc6.dim(`
|
|
878
|
+
Installing dependencies with ${projectInfo.packageManager}...`));
|
|
879
|
+
try {
|
|
880
|
+
const ok = installDependencies(missing, projectInfo.packageManager, projectRoot);
|
|
881
|
+
if (ok) {
|
|
882
|
+
console.log(pc6.green(`\u2714 Dependencies installed.`));
|
|
883
|
+
} else {
|
|
884
|
+
console.log(pc6.yellow(`\u26A0\uFE0F Dependency install did not complete. Run manually: ${pc6.cyan(installCmd)}`));
|
|
885
|
+
}
|
|
886
|
+
} catch (error) {
|
|
887
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
888
|
+
console.log(pc6.yellow(`\u26A0\uFE0F ${message}`));
|
|
889
|
+
console.log(` Run manually: ${pc6.bold(pc6.cyan(installCmd))}`);
|
|
639
890
|
}
|
|
640
891
|
}
|
|
641
892
|
async function addCommand(slug, options = {}) {
|
|
@@ -643,7 +894,7 @@ async function addCommand(slug, options = {}) {
|
|
|
643
894
|
validateSlug(slug);
|
|
644
895
|
} catch (error) {
|
|
645
896
|
const message = error instanceof Error ? error.message : String(error);
|
|
646
|
-
console.error(`${
|
|
897
|
+
console.error(`${pc6.red(pc6.bold("Error:"))} ${message}`);
|
|
647
898
|
process.exit(1);
|
|
648
899
|
}
|
|
649
900
|
let config;
|
|
@@ -651,173 +902,324 @@ async function addCommand(slug, options = {}) {
|
|
|
651
902
|
config = loadConfig();
|
|
652
903
|
} catch (error) {
|
|
653
904
|
const message = error instanceof Error ? error.message : String(error);
|
|
654
|
-
console.error(`${
|
|
905
|
+
console.error(`${pc6.red(pc6.bold("Error:"))} ${message}`);
|
|
655
906
|
process.exit(1);
|
|
656
907
|
}
|
|
657
908
|
if (!config) {
|
|
658
909
|
console.error(
|
|
659
|
-
`${
|
|
660
|
-
\u{1F449} Run ${
|
|
910
|
+
`${pc6.red(pc6.bold("Error:"))} No ${pc6.cyan("vv.config.json")} found in this directory.
|
|
911
|
+
\u{1F449} Run ${pc6.bold(pc6.cyan("npx vectorvesper init"))} first to set up your project.`
|
|
661
912
|
);
|
|
662
913
|
process.exit(1);
|
|
663
914
|
return;
|
|
664
915
|
}
|
|
665
916
|
const projectInfo = detectProject();
|
|
666
917
|
const componentInstallDir = resolveAlias(config.aliases.vv, projectInfo.hasSrcDir);
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}).start();
|
|
671
|
-
const resolvedComponents = [];
|
|
672
|
-
const visited = /* @__PURE__ */ new Set();
|
|
673
|
-
async function resolveComponentTree(componentSlug, depth = 0) {
|
|
674
|
-
if (visited.has(componentSlug)) return;
|
|
675
|
-
if (depth > 20) {
|
|
676
|
-
throw new Error(`Dependency resolution exceeded maximum depth (20). Possible circular dependency involving "${componentSlug}".`);
|
|
677
|
-
}
|
|
678
|
-
visited.add(componentSlug);
|
|
679
|
-
const component = await fetchComponent(componentSlug);
|
|
680
|
-
if (component.tier === "pro" && !getAuthToken()) {
|
|
681
|
-
throw new Error(
|
|
682
|
-
`"${componentSlug}" is a Pro component and requires authentication.
|
|
683
|
-
\u{1F449} Run ${pc4.cyan("vv login <your-license-key>")} to unlock pro components.
|
|
684
|
-
\u{1F511} Get a key at ${pc4.cyan("https://vectorvesper.dev/pricing")}`
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
if (component.registryDependencies && component.registryDependencies.length > 0) {
|
|
688
|
-
for (const depSlug of component.registryDependencies) {
|
|
689
|
-
await resolveComponentTree(depSlug, depth + 1);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
resolvedComponents.push(component);
|
|
693
|
-
}
|
|
918
|
+
const projectRoot = process.cwd();
|
|
919
|
+
const spinner = ora2({ text: `Resolving component ${pc6.cyan(slug)}...`, color: "cyan" }).start();
|
|
920
|
+
let resolvedComponents;
|
|
694
921
|
try {
|
|
695
|
-
await resolveComponentTree(slug);
|
|
922
|
+
resolvedComponents = await resolveComponentTree(slug);
|
|
696
923
|
} catch (error) {
|
|
697
924
|
const message = error instanceof Error ? error.message : String(error);
|
|
698
|
-
spinner.fail(
|
|
925
|
+
spinner.fail(pc6.red(`Failed to resolve component "${slug}": ${message}`));
|
|
699
926
|
process.exit(1);
|
|
927
|
+
return;
|
|
700
928
|
}
|
|
701
929
|
spinner.text = "Checking file conflicts...";
|
|
930
|
+
let planned;
|
|
931
|
+
try {
|
|
932
|
+
planned = planFiles(resolvedComponents, componentInstallDir, projectRoot);
|
|
933
|
+
} catch (error) {
|
|
934
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
935
|
+
spinner.fail(pc6.red(message));
|
|
936
|
+
process.exit(1);
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
702
939
|
const filesToWrite = [];
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
if (relativeFromRoot.startsWith("..") || path5.isAbsolute(relativeFromRoot)) {
|
|
710
|
-
spinner.fail(
|
|
711
|
-
`${pc4.red(pc4.bold("Security Error:"))} Component target file path tries to escape project root: ${file.target}`
|
|
712
|
-
);
|
|
713
|
-
process.exit(1);
|
|
714
|
-
}
|
|
715
|
-
if (fs5.existsSync(fileWritePath)) {
|
|
716
|
-
if (options.dryRun) {
|
|
717
|
-
} else if (!options.overwrite && !options.yes) {
|
|
718
|
-
spinner.stop();
|
|
719
|
-
const relativeDisplayPath = path5.relative(projectRoot, fileWritePath);
|
|
720
|
-
const shouldOverwrite = await confirm2({
|
|
721
|
-
message: pc4.yellow(`File already exists: "${relativeDisplayPath}". Overwrite?`),
|
|
722
|
-
initialValue: false
|
|
723
|
-
});
|
|
724
|
-
if (isCancel2(shouldOverwrite) || !shouldOverwrite) {
|
|
725
|
-
console.log(pc4.dim(` Skipping existing file: ${relativeDisplayPath}`));
|
|
726
|
-
spinner.start("Continuing installation...");
|
|
727
|
-
continue;
|
|
728
|
-
}
|
|
729
|
-
spinner.start("Continuing installation...");
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
filesToWrite.push({
|
|
733
|
-
absolutePath: fileWritePath,
|
|
734
|
-
relativePath: relativeFromRoot,
|
|
735
|
-
content: file.content
|
|
940
|
+
for (const file of planned) {
|
|
941
|
+
if (file.exists && !options.dryRun && !options.overwrite && !options.yes) {
|
|
942
|
+
spinner.stop();
|
|
943
|
+
const answer = await confirm2({
|
|
944
|
+
message: pc6.yellow(`File already exists: "${file.relativePath}". Overwrite?`),
|
|
945
|
+
initialValue: false
|
|
736
946
|
});
|
|
947
|
+
if (isCancel2(answer) || !answer) {
|
|
948
|
+
console.log(pc6.dim(` Skipping existing file: ${file.relativePath}`));
|
|
949
|
+
spinner.start("Continuing installation...");
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
spinner.start("Continuing installation...");
|
|
737
953
|
}
|
|
954
|
+
filesToWrite.push({
|
|
955
|
+
absolutePath: file.absolutePath,
|
|
956
|
+
relativePath: file.relativePath,
|
|
957
|
+
content: file.content
|
|
958
|
+
});
|
|
738
959
|
}
|
|
960
|
+
const targetComponent = resolvedComponents[resolvedComponents.length - 1];
|
|
961
|
+
const installPath = toForwardSlash(path7.join(componentInstallDir, targetComponent.slug));
|
|
739
962
|
if (options.dryRun) {
|
|
740
|
-
spinner.succeed(
|
|
741
|
-
console.log(
|
|
963
|
+
spinner.succeed(pc6.yellow("Dry-run: simulation completed. No files were written."));
|
|
964
|
+
console.log(pc6.bold(pc6.yellow("\n[Dry Run] Files that would be created/modified:")));
|
|
742
965
|
for (const file of filesToWrite) {
|
|
743
|
-
const status =
|
|
744
|
-
console.log(` ${
|
|
966
|
+
const status = fs7.existsSync(file.absolutePath) ? pc6.yellow("(exists, would overwrite)") : pc6.green("(new)");
|
|
967
|
+
console.log(` ${pc6.cyan(file.relativePath)} ${status}`);
|
|
968
|
+
}
|
|
969
|
+
const missing = getMissingDependencies(resolvedComponents, projectRoot);
|
|
970
|
+
if (missing.length > 0) {
|
|
971
|
+
console.log(pc6.yellow(`
|
|
972
|
+
\u26A0\uFE0F Would need dependencies: ${missing.map((d) => pc6.cyan(d)).join(", ")}`));
|
|
973
|
+
console.log(` ${pc6.dim(getInstallCommand(projectInfo.packageManager, missing))}`);
|
|
745
974
|
}
|
|
746
|
-
reportMissingDependencies(resolvedComponents, projectRoot, projectInfo);
|
|
747
|
-
const targetComponent2 = resolvedComponents[resolvedComponents.length - 1];
|
|
748
|
-
console.log("");
|
|
749
|
-
console.log(
|
|
750
|
-
pc4.bold(pc4.yellow("\u2714 [Dry Run] Would add ")) + pc4.bold(pc4.cyan(targetComponent2.slug)) + pc4.bold(pc4.yellow(` to ${toForwardSlash(path5.join(componentInstallDir, targetComponent2.slug))}`))
|
|
751
|
-
);
|
|
752
975
|
console.log("");
|
|
753
|
-
console.log(
|
|
754
|
-
console.log(pc4.gray("\u{1F680} Need premium WebGL effects? Get the Pixel Library: https://vectorvesper.dev/library"));
|
|
976
|
+
console.log(pc6.bold(pc6.yellow(`\u2714 [Dry Run] Would add ${pc6.cyan(targetComponent.slug)} to ${installPath}`)));
|
|
755
977
|
console.log("");
|
|
756
978
|
return;
|
|
757
979
|
}
|
|
758
980
|
if (filesToWrite.length === 0) {
|
|
759
|
-
spinner.succeed(
|
|
981
|
+
spinner.succeed(pc6.green("All files skipped (already up to date)."));
|
|
760
982
|
return;
|
|
761
983
|
}
|
|
762
984
|
spinner.text = "Writing component files...";
|
|
763
985
|
try {
|
|
764
|
-
|
|
765
|
-
const folder = path5.dirname(file.absolutePath);
|
|
766
|
-
if (!fs5.existsSync(folder)) {
|
|
767
|
-
fs5.mkdirSync(folder, { recursive: true });
|
|
768
|
-
}
|
|
769
|
-
fs5.writeFileSync(file.absolutePath, file.content, "utf-8");
|
|
770
|
-
}
|
|
986
|
+
writeFiles(filesToWrite);
|
|
771
987
|
} catch (error) {
|
|
772
988
|
const message = error instanceof Error ? error.message : String(error);
|
|
773
|
-
spinner.fail(
|
|
989
|
+
spinner.fail(pc6.red(`Failed to write files: ${message}`));
|
|
774
990
|
process.exit(1);
|
|
991
|
+
return;
|
|
775
992
|
}
|
|
776
993
|
try {
|
|
777
|
-
|
|
778
|
-
let manifest = { components: {} };
|
|
779
|
-
if (fs5.existsSync(manifestPath)) {
|
|
780
|
-
manifest = JSON.parse(fs5.readFileSync(manifestPath, "utf-8"));
|
|
781
|
-
}
|
|
782
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
783
|
-
for (const component of resolvedComponents) {
|
|
784
|
-
manifest.components[component.slug] = {
|
|
785
|
-
version: component.version,
|
|
786
|
-
installedAt: toForwardSlash(path5.join(componentInstallDir, component.slug)),
|
|
787
|
-
installedOn: today,
|
|
788
|
-
files: component.files.map((f) => f.target)
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
fs5.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
994
|
+
recordInstall(resolvedComponents, componentInstallDir, projectRoot);
|
|
792
995
|
} catch (error) {
|
|
793
996
|
const message = error instanceof Error ? error.message : String(error);
|
|
794
997
|
logger.warn(`Failed to update vv-manifest.json: ${message}`);
|
|
795
|
-
logger.warn("Component was installed but may not appear in '
|
|
998
|
+
logger.warn("Component was installed but may not appear in 'npx vectorvesper info' or 'npx vectorvesper diff'.");
|
|
796
999
|
}
|
|
797
|
-
spinner.succeed(
|
|
798
|
-
console.log(
|
|
1000
|
+
spinner.succeed(pc6.green("Files written successfully!"));
|
|
1001
|
+
console.log(pc6.dim("\nCreated files:"));
|
|
799
1002
|
for (const file of filesToWrite) {
|
|
800
|
-
console.log(` ${
|
|
1003
|
+
console.log(` ${pc6.green("\u2714")} ${pc6.cyan(file.relativePath)}`);
|
|
801
1004
|
}
|
|
802
|
-
|
|
803
|
-
|
|
1005
|
+
await handleDependencies(resolvedComponents, projectRoot, projectInfo, options);
|
|
1006
|
+
printGuidance(targetComponent, {
|
|
1007
|
+
alias: config.aliases.vv,
|
|
1008
|
+
framework: projectInfo.framework,
|
|
1009
|
+
hasTailwind: projectInfo.hasTailwind
|
|
1010
|
+
});
|
|
804
1011
|
console.log("");
|
|
805
|
-
console.log(
|
|
806
|
-
|
|
807
|
-
);
|
|
1012
|
+
console.log(pc6.bold(pc6.green(`\u2714 Added ${pc6.cyan(targetComponent.slug)} to ${installPath}`)));
|
|
1013
|
+
console.log("");
|
|
1014
|
+
console.log(pc6.gray("\u{1F449} Loving Vector Vesper? Star the repo: https://github.com/vectorvesper/vv-components"));
|
|
1015
|
+
console.log(pc6.gray("\u{1F680} Premium shader & pixel systems: https://vectorvesper.dev/pricing"));
|
|
1016
|
+
console.log("");
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// src/commands/update.ts
|
|
1020
|
+
import pc7 from "picocolors";
|
|
1021
|
+
import ora3 from "ora";
|
|
1022
|
+
async function updateCommand(slug, options = {}) {
|
|
1023
|
+
const projectRoot = process.cwd();
|
|
1024
|
+
let config;
|
|
1025
|
+
try {
|
|
1026
|
+
config = loadConfig();
|
|
1027
|
+
} catch (error) {
|
|
1028
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1029
|
+
console.error(`${pc7.red(pc7.bold("Error:"))} ${message}`);
|
|
1030
|
+
process.exit(1);
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
if (!config) {
|
|
1034
|
+
console.error(
|
|
1035
|
+
`${pc7.red(pc7.bold("Error:"))} No ${pc7.cyan("vv.config.json")} found.
|
|
1036
|
+
\u{1F449} Run ${pc7.bold(pc7.cyan("npx vectorvesper init"))} first.`
|
|
1037
|
+
);
|
|
1038
|
+
process.exit(1);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
const projectInfo = detectProject();
|
|
1042
|
+
const installDir = resolveAlias(config.aliases.vv, projectInfo.hasSrcDir);
|
|
1043
|
+
const manifest = readManifest(projectRoot);
|
|
1044
|
+
const installedSlugs = Object.keys(manifest.components);
|
|
1045
|
+
if (installedSlugs.length === 0) {
|
|
1046
|
+
console.log(pc7.yellow(`
|
|
1047
|
+
\u26A0\uFE0F No components are installed in this project.`));
|
|
1048
|
+
console.log(`\u{1F449} Add one with ${pc7.bold(pc7.cyan("npx vectorvesper add <slug>"))}
|
|
1049
|
+
`);
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
let targets;
|
|
1053
|
+
if (slug) {
|
|
1054
|
+
try {
|
|
1055
|
+
validateSlug(slug);
|
|
1056
|
+
} catch (error) {
|
|
1057
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1058
|
+
console.error(pc7.red(`
|
|
1059
|
+
\u274C ${message}
|
|
1060
|
+
`));
|
|
1061
|
+
process.exit(1);
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
if (!manifest.components[slug]) {
|
|
1065
|
+
console.log(pc7.red(`
|
|
1066
|
+
\u274C Component "${slug}" is not installed in this project.`));
|
|
1067
|
+
console.log(`\u{1F449} Run ${pc7.cyan(`npx vectorvesper add ${slug}`)} to install it.
|
|
1068
|
+
`);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
targets = [slug];
|
|
1072
|
+
} else {
|
|
1073
|
+
targets = installedSlugs;
|
|
1074
|
+
}
|
|
1075
|
+
const spinner = ora3({ text: "Checking for updates...", color: "cyan" }).start();
|
|
1076
|
+
const updated = [];
|
|
1077
|
+
const upToDate = [];
|
|
1078
|
+
const failed = [];
|
|
1079
|
+
for (const target of targets) {
|
|
1080
|
+
spinner.text = `Updating ${pc7.cyan(target)}...`;
|
|
1081
|
+
try {
|
|
1082
|
+
const components = await resolveComponentTree(target);
|
|
1083
|
+
const remote = components[components.length - 1];
|
|
1084
|
+
const localVersion = manifest.components[target]?.version;
|
|
1085
|
+
if (localVersion === remote.version && !options.force) {
|
|
1086
|
+
upToDate.push(target);
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
const planned = planFiles(components, installDir, projectRoot);
|
|
1090
|
+
writeFiles(planned.map((f) => ({ absolutePath: f.absolutePath, content: f.content })));
|
|
1091
|
+
recordInstall(components, installDir, projectRoot);
|
|
1092
|
+
updated.push(`${target} \u2192 ${remote.version}`);
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1095
|
+
failed.push({ slug: target, reason: message });
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
spinner.stop();
|
|
1099
|
+
console.log(pc7.bold(pc7.cyan("\nVector Vesper Update\n")));
|
|
1100
|
+
if (updated.length > 0) {
|
|
1101
|
+
console.log(pc7.bold(pc7.green(` Updated (${updated.length}):`)));
|
|
1102
|
+
for (const u of updated) console.log(` ${pc7.green("\u2714")} ${u}`);
|
|
1103
|
+
console.log(pc7.dim(" Note: updating overwrites local edits to these files."));
|
|
1104
|
+
}
|
|
1105
|
+
if (upToDate.length > 0) {
|
|
1106
|
+
console.log(pc7.bold(pc7.gray(`
|
|
1107
|
+
Already up to date (${upToDate.length}):`)));
|
|
1108
|
+
console.log(` ${pc7.dim(upToDate.join(", "))}`);
|
|
1109
|
+
}
|
|
1110
|
+
if (failed.length > 0) {
|
|
1111
|
+
console.log(pc7.bold(pc7.red(`
|
|
1112
|
+
Failed (${failed.length}):`)));
|
|
1113
|
+
for (const f of failed) console.log(` ${pc7.red("\u2716")} ${f.slug}: ${pc7.dim(f.reason)}`);
|
|
1114
|
+
}
|
|
808
1115
|
console.log("");
|
|
809
|
-
|
|
810
|
-
|
|
1116
|
+
if (failed.length > 0) process.exit(1);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// src/commands/remove.ts
|
|
1120
|
+
import fs8 from "fs";
|
|
1121
|
+
import path8 from "path";
|
|
1122
|
+
import pc8 from "picocolors";
|
|
1123
|
+
import { confirm as confirm3, isCancel as isCancel3 } from "@clack/prompts";
|
|
1124
|
+
async function removeCommand(slug, options = {}) {
|
|
1125
|
+
try {
|
|
1126
|
+
validateSlug(slug);
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1129
|
+
console.error(`${pc8.red(pc8.bold("Error:"))} ${message}`);
|
|
1130
|
+
process.exit(1);
|
|
1131
|
+
return;
|
|
1132
|
+
}
|
|
1133
|
+
const projectRoot = process.cwd();
|
|
1134
|
+
let config;
|
|
1135
|
+
try {
|
|
1136
|
+
config = loadConfig();
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1139
|
+
console.error(`${pc8.red(pc8.bold("Error:"))} ${message}`);
|
|
1140
|
+
process.exit(1);
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
if (!config) {
|
|
1144
|
+
console.error(
|
|
1145
|
+
`${pc8.red(pc8.bold("Error:"))} No ${pc8.cyan("vv.config.json")} found.
|
|
1146
|
+
\u{1F449} Run ${pc8.bold(pc8.cyan("npx vectorvesper init"))} first.`
|
|
1147
|
+
);
|
|
1148
|
+
process.exit(1);
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
const manifest = readManifest(projectRoot);
|
|
1152
|
+
const entry = manifest.components[slug];
|
|
1153
|
+
if (!entry) {
|
|
1154
|
+
console.log(pc8.yellow(`
|
|
1155
|
+
\u26A0\uFE0F Component "${slug}" is not installed (not in vv-manifest.json).`));
|
|
1156
|
+
console.log(`\u{1F449} See installed components with ${pc8.bold(pc8.cyan("npx vectorvesper info"))}
|
|
1157
|
+
`);
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
const projectInfo = detectProject();
|
|
1161
|
+
const installDir = resolveAlias(config.aliases.vv, projectInfo.hasSrcDir);
|
|
1162
|
+
const baseDir = path8.join(projectRoot, installDir);
|
|
1163
|
+
const targets = entry.files ?? [];
|
|
1164
|
+
const absoluteFiles = targets.map((t) => path8.resolve(baseDir, t));
|
|
1165
|
+
console.log(pc8.bold(pc8.cyan(`
|
|
1166
|
+
Remove ${slug}
|
|
1167
|
+
`)));
|
|
1168
|
+
console.log(pc8.dim(" The following files will be deleted:"));
|
|
1169
|
+
for (const f of absoluteFiles) {
|
|
1170
|
+
console.log(` ${fs8.existsSync(f) ? pc8.red("\u2212") : pc8.dim("\xB7")} ${pc8.cyan(path8.relative(projectRoot, f))}`);
|
|
1171
|
+
}
|
|
1172
|
+
if (!options.yes) {
|
|
1173
|
+
const answer = await confirm3({
|
|
1174
|
+
message: pc8.yellow(`Delete ${absoluteFiles.length} file(s) for "${slug}"?`),
|
|
1175
|
+
initialValue: false
|
|
1176
|
+
});
|
|
1177
|
+
if (isCancel3(answer) || !answer) {
|
|
1178
|
+
console.log(pc8.dim("\n Cancelled. Nothing was removed.\n"));
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
let deleted = 0;
|
|
1183
|
+
for (const f of absoluteFiles) {
|
|
1184
|
+
try {
|
|
1185
|
+
if (fs8.existsSync(f)) {
|
|
1186
|
+
fs8.rmSync(f);
|
|
1187
|
+
deleted++;
|
|
1188
|
+
}
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1191
|
+
console.log(pc8.yellow(` \u26A0\uFE0F Could not delete ${path8.relative(projectRoot, f)}: ${message}`));
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
const componentDir = path8.resolve(baseDir, slug);
|
|
1195
|
+
try {
|
|
1196
|
+
if (fs8.existsSync(componentDir) && fs8.readdirSync(componentDir).length === 0) {
|
|
1197
|
+
fs8.rmdirSync(componentDir);
|
|
1198
|
+
}
|
|
1199
|
+
} catch {
|
|
1200
|
+
}
|
|
1201
|
+
delete manifest.components[slug];
|
|
1202
|
+
try {
|
|
1203
|
+
writeManifest(projectRoot, manifest);
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1206
|
+
console.log(pc8.yellow(` \u26A0\uFE0F Removed files but failed to update vv-manifest.json: ${message}`));
|
|
1207
|
+
}
|
|
1208
|
+
console.log("");
|
|
1209
|
+
console.log(pc8.bold(pc8.green(`\u2714 Removed ${pc8.cyan(slug)} (${deleted} file(s) deleted).`)));
|
|
1210
|
+
if ((entry.files ?? []).length === 0) {
|
|
1211
|
+
console.log(pc8.dim(" No files were tracked for this component; manifest entry cleared."));
|
|
1212
|
+
}
|
|
811
1213
|
console.log("");
|
|
812
1214
|
}
|
|
813
1215
|
|
|
814
1216
|
// src/commands/info.ts
|
|
815
|
-
import
|
|
816
|
-
import
|
|
817
|
-
import
|
|
818
|
-
var CLI_VERSION = true ? "0.
|
|
1217
|
+
import fs9 from "fs";
|
|
1218
|
+
import path9 from "path";
|
|
1219
|
+
import pc9 from "picocolors";
|
|
1220
|
+
var CLI_VERSION = true ? "0.2.1" : "0.0.0-dev";
|
|
819
1221
|
async function infoCommand() {
|
|
820
|
-
console.log(
|
|
1222
|
+
console.log(pc9.bold(pc9.cyan("\nVector Vesper Diagnostics\n")));
|
|
821
1223
|
const projectInfo = detectProject();
|
|
822
1224
|
let config;
|
|
823
1225
|
try {
|
|
@@ -825,95 +1227,95 @@ async function infoCommand() {
|
|
|
825
1227
|
} catch {
|
|
826
1228
|
config = null;
|
|
827
1229
|
}
|
|
828
|
-
console.log(
|
|
829
|
-
console.log(` \u2022 CLI Version: ${
|
|
830
|
-
console.log(` \u2022 Node Version: ${
|
|
831
|
-
console.log(` \u2022 OS: ${
|
|
832
|
-
console.log(` \u2022 Package Manager: ${
|
|
833
|
-
console.log(` \u2022 Framework: ${
|
|
834
|
-
console.log(` \u2022 TypeScript: ${
|
|
835
|
-
console.log(` \u2022 Has src/ folder: ${
|
|
836
|
-
console.log(` \u2022 Tailwind CSS: ${
|
|
1230
|
+
console.log(pc9.bold(pc9.white("Environment:")));
|
|
1231
|
+
console.log(` \u2022 CLI Version: ${pc9.cyan(CLI_VERSION)}`);
|
|
1232
|
+
console.log(` \u2022 Node Version: ${pc9.cyan(process.version)}`);
|
|
1233
|
+
console.log(` \u2022 OS: ${pc9.cyan(`${process.platform} ${process.arch}`)}`);
|
|
1234
|
+
console.log(` \u2022 Package Manager: ${pc9.cyan(projectInfo.packageManager)}`);
|
|
1235
|
+
console.log(` \u2022 Framework: ${pc9.cyan(projectInfo.framework)}`);
|
|
1236
|
+
console.log(` \u2022 TypeScript: ${pc9.cyan(projectInfo.isTypeScript ? "Yes" : "No")}`);
|
|
1237
|
+
console.log(` \u2022 Has src/ folder: ${pc9.cyan(projectInfo.hasSrcDir ? "Yes" : "No")}`);
|
|
1238
|
+
console.log(` \u2022 Tailwind CSS: ${pc9.cyan(projectInfo.hasTailwind ? "Yes" : "No")}`);
|
|
837
1239
|
console.log("");
|
|
838
|
-
console.log(
|
|
1240
|
+
console.log(pc9.bold(pc9.white("Authentication:")));
|
|
839
1241
|
const token = getAuthToken();
|
|
840
1242
|
if (token) {
|
|
841
1243
|
const maskedKey = token.slice(0, 6) + "\u2026" + token.slice(-4);
|
|
842
|
-
console.log(` \u2022 Status: ${
|
|
843
|
-
console.log(` \u2022 Key: ${
|
|
1244
|
+
console.log(` \u2022 Status: ${pc9.green("Authenticated")}`);
|
|
1245
|
+
console.log(` \u2022 Key: ${pc9.cyan(maskedKey)}`);
|
|
844
1246
|
} else {
|
|
845
|
-
console.log(` \u2022 Status: ${
|
|
846
|
-
console.log(` ${
|
|
1247
|
+
console.log(` \u2022 Status: ${pc9.yellow("Anonymous (free tier)")}`);
|
|
1248
|
+
console.log(` ${pc9.dim(" Run")} ${pc9.cyan("npx vectorvesper login <key>")} ${pc9.dim("to unlock pro components")}`);
|
|
847
1249
|
}
|
|
848
1250
|
console.log("");
|
|
849
|
-
console.log(
|
|
1251
|
+
console.log(pc9.bold(pc9.white("Configuration (vv.config.json):")));
|
|
850
1252
|
if (config) {
|
|
851
|
-
console.log(` \u2022 Framework Set: ${
|
|
852
|
-
console.log(` \u2022 TypeScript Set: ${
|
|
853
|
-
console.log(` \u2022 Component Alias: ${
|
|
1253
|
+
console.log(` \u2022 Framework Set: ${pc9.cyan(config.framework)}`);
|
|
1254
|
+
console.log(` \u2022 TypeScript Set: ${pc9.cyan(config.tsx ? "Yes" : "No")}`);
|
|
1255
|
+
console.log(` \u2022 Component Alias: ${pc9.cyan(config.aliases.vv)}`);
|
|
854
1256
|
} else {
|
|
855
|
-
console.log(` ${
|
|
1257
|
+
console.log(` ${pc9.yellow("\u26A0\uFE0F No vv.config.json found in this directory.")}`);
|
|
856
1258
|
}
|
|
857
1259
|
console.log("");
|
|
858
|
-
console.log(
|
|
859
|
-
const manifestPath =
|
|
860
|
-
if (
|
|
1260
|
+
console.log(pc9.bold(pc9.white("Installed Components (vv-manifest.json):")));
|
|
1261
|
+
const manifestPath = path9.join(process.cwd(), "vv-manifest.json");
|
|
1262
|
+
if (fs9.existsSync(manifestPath)) {
|
|
861
1263
|
try {
|
|
862
|
-
const manifest = JSON.parse(
|
|
1264
|
+
const manifest = JSON.parse(fs9.readFileSync(manifestPath, "utf-8"));
|
|
863
1265
|
const components = manifest.components || {};
|
|
864
1266
|
const slugs = Object.keys(components);
|
|
865
1267
|
if (slugs.length === 0) {
|
|
866
|
-
console.log(` ${
|
|
1268
|
+
console.log(` ${pc9.dim("No components installed yet.")}`);
|
|
867
1269
|
} else {
|
|
868
|
-
console.log(
|
|
869
|
-
console.log(` ${
|
|
870
|
-
console.log(
|
|
1270
|
+
console.log(pc9.dim(" ------------------------------------------------------------"));
|
|
1271
|
+
console.log(` ${pc9.bold(pc9.white("Component".padEnd(25)))} ${pc9.bold(pc9.white("Version".padEnd(10)))} ${pc9.bold(pc9.white("Installed At"))}`);
|
|
1272
|
+
console.log(pc9.dim(" ------------------------------------------------------------"));
|
|
871
1273
|
for (const slug of slugs) {
|
|
872
1274
|
const info = components[slug];
|
|
873
1275
|
const version2 = info?.version ?? "unknown";
|
|
874
1276
|
const installedAt = info?.installedAt ?? "unknown";
|
|
875
|
-
console.log(` ${
|
|
1277
|
+
console.log(` ${pc9.cyan(slug.padEnd(25))} ${pc9.green(version2.padEnd(10))} ${pc9.gray(installedAt)}`);
|
|
876
1278
|
}
|
|
877
|
-
console.log(
|
|
1279
|
+
console.log(pc9.dim(" ------------------------------------------------------------"));
|
|
878
1280
|
}
|
|
879
1281
|
} catch {
|
|
880
|
-
console.log(` ${
|
|
1282
|
+
console.log(` ${pc9.red("\u274C Error reading vv-manifest.json: Invalid JSON format.")}`);
|
|
881
1283
|
}
|
|
882
1284
|
} else {
|
|
883
|
-
console.log(` ${
|
|
1285
|
+
console.log(` ${pc9.dim("No components installed yet (vv-manifest.json not found).")}`);
|
|
884
1286
|
}
|
|
885
1287
|
console.log("");
|
|
886
1288
|
}
|
|
887
1289
|
|
|
888
1290
|
// src/commands/diff.ts
|
|
889
|
-
import
|
|
890
|
-
import
|
|
891
|
-
import
|
|
892
|
-
import
|
|
1291
|
+
import fs10 from "fs";
|
|
1292
|
+
import path10 from "path";
|
|
1293
|
+
import pc10 from "picocolors";
|
|
1294
|
+
import ora4 from "ora";
|
|
893
1295
|
async function diffCommand(slug) {
|
|
894
1296
|
const projectRoot = process.cwd();
|
|
895
|
-
const manifestPath =
|
|
896
|
-
if (!
|
|
897
|
-
console.log(
|
|
1297
|
+
const manifestPath = path10.join(projectRoot, "vv-manifest.json");
|
|
1298
|
+
if (!fs10.existsSync(manifestPath)) {
|
|
1299
|
+
console.log(pc10.yellow(`
|
|
898
1300
|
\u26A0\uFE0F No vv-manifest.json found in this directory.`));
|
|
899
|
-
console.log(`\u{1F449} Add components first using ${
|
|
1301
|
+
console.log(`\u{1F449} Add components first using ${pc10.bold(pc10.cyan("npx vectorvesper add <slug>"))}
|
|
900
1302
|
`);
|
|
901
1303
|
return;
|
|
902
1304
|
}
|
|
903
1305
|
let manifest;
|
|
904
1306
|
try {
|
|
905
|
-
manifest = JSON.parse(
|
|
1307
|
+
manifest = JSON.parse(fs10.readFileSync(manifestPath, "utf-8"));
|
|
906
1308
|
} catch {
|
|
907
|
-
console.error(
|
|
1309
|
+
console.error(pc10.red(`\u274C Error reading vv-manifest.json: Invalid JSON format.`));
|
|
908
1310
|
process.exit(1);
|
|
909
1311
|
return;
|
|
910
1312
|
}
|
|
911
1313
|
const installedComponents = manifest.components || {};
|
|
912
1314
|
const installedSlugs = Object.keys(installedComponents);
|
|
913
1315
|
if (installedSlugs.length === 0) {
|
|
914
|
-
console.log(
|
|
1316
|
+
console.log(pc10.yellow(`
|
|
915
1317
|
\u26A0\uFE0F No components are registered as installed in vv-manifest.json.`));
|
|
916
|
-
console.log(`\u{1F449} Add components using ${
|
|
1318
|
+
console.log(`\u{1F449} Add components using ${pc10.bold(pc10.cyan("npx vectorvesper add <slug>"))}
|
|
917
1319
|
`);
|
|
918
1320
|
return;
|
|
919
1321
|
}
|
|
@@ -922,21 +1324,21 @@ async function diffCommand(slug) {
|
|
|
922
1324
|
validateSlug(slug);
|
|
923
1325
|
} catch (error) {
|
|
924
1326
|
const message = error instanceof Error ? error.message : String(error);
|
|
925
|
-
console.error(
|
|
1327
|
+
console.error(pc10.red(`
|
|
926
1328
|
\u274C ${message}
|
|
927
1329
|
`));
|
|
928
1330
|
process.exit(1);
|
|
929
1331
|
}
|
|
930
1332
|
if (!installedComponents[slug]) {
|
|
931
|
-
console.log(
|
|
1333
|
+
console.log(pc10.red(`
|
|
932
1334
|
\u274C Component "${slug}" is not installed in this project.`));
|
|
933
|
-
console.log(`\u{1F449} Run ${
|
|
1335
|
+
console.log(`\u{1F449} Run ${pc10.cyan(`npx vectorvesper add ${slug}`)} to install it.
|
|
934
1336
|
`);
|
|
935
1337
|
return;
|
|
936
1338
|
}
|
|
937
1339
|
const localInfo = installedComponents[slug];
|
|
938
|
-
const spinner =
|
|
939
|
-
text: `Checking updates for ${
|
|
1340
|
+
const spinner = ora4({
|
|
1341
|
+
text: `Checking updates for ${pc10.cyan(slug)}...`,
|
|
940
1342
|
color: "cyan"
|
|
941
1343
|
}).start();
|
|
942
1344
|
try {
|
|
@@ -944,39 +1346,39 @@ async function diffCommand(slug) {
|
|
|
944
1346
|
spinner.stop();
|
|
945
1347
|
const localVersion = localInfo.version ?? "unknown";
|
|
946
1348
|
const remoteVersion = remoteComponent.version;
|
|
947
|
-
console.log(
|
|
1349
|
+
console.log(pc10.bold(pc10.cyan(`
|
|
948
1350
|
Component Diff: ${slug}`)));
|
|
949
|
-
console.log(` \u2022 Local version: ${
|
|
950
|
-
console.log(` \u2022 Latest version: ${
|
|
1351
|
+
console.log(` \u2022 Local version: ${pc10.yellow(localVersion)}`);
|
|
1352
|
+
console.log(` \u2022 Latest version: ${pc10.green(remoteVersion)}`);
|
|
951
1353
|
if (localVersion === remoteVersion) {
|
|
952
|
-
console.log(` \u2022 Status: ${
|
|
1354
|
+
console.log(` \u2022 Status: ${pc10.green("\u2714 Up to date")}
|
|
953
1355
|
`);
|
|
954
1356
|
} else {
|
|
955
|
-
console.log(` \u2022 Status: ${
|
|
956
|
-
console.log(` \u2022 Run ${
|
|
1357
|
+
console.log(` \u2022 Status: ${pc10.bold(pc10.yellow("\u26A0\uFE0F Update available!"))}`);
|
|
1358
|
+
console.log(` \u2022 Run ${pc10.bold(pc10.cyan(`npx vectorvesper add ${slug} --overwrite`))} to update.
|
|
957
1359
|
`);
|
|
958
1360
|
}
|
|
959
1361
|
} catch (error) {
|
|
960
1362
|
const message = error instanceof Error ? error.message : String(error);
|
|
961
|
-
spinner.fail(
|
|
1363
|
+
spinner.fail(pc10.red(`Failed to fetch remote component details: ${message}`));
|
|
962
1364
|
process.exit(1);
|
|
963
1365
|
}
|
|
964
1366
|
} else {
|
|
965
|
-
const spinner =
|
|
1367
|
+
const spinner = ora4({
|
|
966
1368
|
text: "Comparing local components with registry index...",
|
|
967
1369
|
color: "cyan"
|
|
968
1370
|
}).start();
|
|
969
1371
|
try {
|
|
970
1372
|
const registryIndex = await fetchRegistryIndex();
|
|
971
1373
|
spinner.stop();
|
|
972
|
-
console.log(
|
|
973
|
-
console.log(
|
|
1374
|
+
console.log(pc10.bold(pc10.cyan("\nVector Vesper Component Version Comparison\n")));
|
|
1375
|
+
console.log(pc10.dim(" ----------------------------------------------------------------------"));
|
|
974
1376
|
console.log(
|
|
975
|
-
` ${
|
|
976
|
-
|
|
977
|
-
)} ${
|
|
1377
|
+
` ${pc10.bold(pc10.white("Component".padEnd(25)))} ${pc10.bold(
|
|
1378
|
+
pc10.white("Local".padEnd(10))
|
|
1379
|
+
)} ${pc10.bold(pc10.white("Latest".padEnd(10)))} ${pc10.bold(pc10.white("Status"))}`
|
|
978
1380
|
);
|
|
979
|
-
console.log(
|
|
1381
|
+
console.log(pc10.dim(" ----------------------------------------------------------------------"));
|
|
980
1382
|
let updatesAvailableCount = 0;
|
|
981
1383
|
for (const instSlug of installedSlugs) {
|
|
982
1384
|
const localInfo = installedComponents[instSlug];
|
|
@@ -984,59 +1386,59 @@ Component Diff: ${slug}`)));
|
|
|
984
1386
|
const remoteInfo = registryIndex.components.find((c) => c.slug === instSlug);
|
|
985
1387
|
if (!remoteInfo) {
|
|
986
1388
|
console.log(
|
|
987
|
-
` ${
|
|
1389
|
+
` ${pc10.cyan(instSlug.padEnd(25))} ${pc10.yellow(
|
|
988
1390
|
localVersion.padEnd(10)
|
|
989
|
-
)} ${
|
|
1391
|
+
)} ${pc10.red("unknown".padEnd(10))} ${pc10.red("Not found in registry")}`
|
|
990
1392
|
);
|
|
991
1393
|
} else {
|
|
992
1394
|
const remoteVersion = remoteInfo.version;
|
|
993
1395
|
if (localVersion === remoteVersion) {
|
|
994
1396
|
console.log(
|
|
995
|
-
` ${
|
|
1397
|
+
` ${pc10.cyan(instSlug.padEnd(25))} ${pc10.gray(
|
|
996
1398
|
localVersion.padEnd(10)
|
|
997
|
-
)} ${
|
|
1399
|
+
)} ${pc10.gray(remoteVersion.padEnd(10))} ${pc10.green("\u2714 Up to date")}`
|
|
998
1400
|
);
|
|
999
1401
|
} else {
|
|
1000
1402
|
updatesAvailableCount++;
|
|
1001
1403
|
console.log(
|
|
1002
|
-
` ${
|
|
1404
|
+
` ${pc10.cyan(instSlug.padEnd(25))} ${pc10.yellow(
|
|
1003
1405
|
localVersion.padEnd(10)
|
|
1004
|
-
)} ${
|
|
1005
|
-
|
|
1406
|
+
)} ${pc10.green(remoteVersion.padEnd(10))} ${pc10.bold(
|
|
1407
|
+
pc10.yellow("\u26A0\uFE0F Update available")
|
|
1006
1408
|
)}`
|
|
1007
1409
|
);
|
|
1008
1410
|
}
|
|
1009
1411
|
}
|
|
1010
1412
|
}
|
|
1011
|
-
console.log(
|
|
1413
|
+
console.log(pc10.dim(" ----------------------------------------------------------------------"));
|
|
1012
1414
|
if (updatesAvailableCount > 0) {
|
|
1013
1415
|
console.log(
|
|
1014
1416
|
`
|
|
1015
|
-
\u{1F4E2} ${
|
|
1417
|
+
\u{1F4E2} ${pc10.bold(pc10.yellow(String(updatesAvailableCount)))} component(s) have updates available.`
|
|
1016
1418
|
);
|
|
1017
|
-
console.log(`\u{1F449} Run ${
|
|
1419
|
+
console.log(`\u{1F449} Run ${pc10.bold(pc10.cyan("npx vectorvesper add <slug> --overwrite"))} to update a component.
|
|
1018
1420
|
`);
|
|
1019
1421
|
} else {
|
|
1020
1422
|
console.log(`
|
|
1021
|
-
${
|
|
1423
|
+
${pc10.green("\u2714")} All components are up to date!
|
|
1022
1424
|
`);
|
|
1023
1425
|
}
|
|
1024
1426
|
} catch (error) {
|
|
1025
1427
|
const message = error instanceof Error ? error.message : String(error);
|
|
1026
|
-
spinner.fail(
|
|
1428
|
+
spinner.fail(pc10.red(`Failed to check updates: ${message}`));
|
|
1027
1429
|
process.exit(1);
|
|
1028
1430
|
}
|
|
1029
1431
|
}
|
|
1030
1432
|
}
|
|
1031
1433
|
|
|
1032
1434
|
// src/commands/auth.ts
|
|
1033
|
-
import
|
|
1435
|
+
import pc11 from "picocolors";
|
|
1034
1436
|
async function loginCommand(key) {
|
|
1035
1437
|
if (!key || key.trim().length === 0) {
|
|
1036
1438
|
logger.error("Please provide a license key.");
|
|
1037
1439
|
console.log(`
|
|
1038
|
-
Usage: ${
|
|
1039
|
-
console.log(` \u{1F511} Get a key at ${
|
|
1440
|
+
Usage: ${pc11.cyan("npx vectorvesper login <your-license-key>")}`);
|
|
1441
|
+
console.log(` \u{1F511} Get a key at ${pc11.cyan("https://vectorvesper.dev/pricing")}
|
|
1040
1442
|
`);
|
|
1041
1443
|
process.exit(1);
|
|
1042
1444
|
}
|
|
@@ -1044,7 +1446,7 @@ async function loginCommand(key) {
|
|
|
1044
1446
|
if (trimmedKey.length < 8) {
|
|
1045
1447
|
logger.error("That doesn't look like a valid license key.");
|
|
1046
1448
|
console.log(`
|
|
1047
|
-
\u{1F511} License keys are available at ${
|
|
1449
|
+
\u{1F511} License keys are available at ${pc11.cyan("https://vectorvesper.dev/pricing")}
|
|
1048
1450
|
`);
|
|
1049
1451
|
process.exit(1);
|
|
1050
1452
|
}
|
|
@@ -1061,18 +1463,18 @@ async function loginCommand(key) {
|
|
|
1061
1463
|
process.exit(1);
|
|
1062
1464
|
}
|
|
1063
1465
|
console.log("");
|
|
1064
|
-
console.log(` ${
|
|
1065
|
-
console.log(` ${
|
|
1466
|
+
console.log(` ${pc11.green(pc11.bold("\u2714"))} License key saved successfully!`);
|
|
1467
|
+
console.log(` ${pc11.dim("Stored at:")} ${pc11.cyan(getGlobalConfigPath())}`);
|
|
1066
1468
|
console.log("");
|
|
1067
|
-
console.log(` ${
|
|
1068
|
-
console.log(` ${
|
|
1469
|
+
console.log(` ${pc11.dim("Pro components are now available when you run")} ${pc11.cyan("npx vectorvesper add <slug>")}`);
|
|
1470
|
+
console.log(` ${pc11.dim("To verify your account:")} ${pc11.cyan("npx vectorvesper whoami")}`);
|
|
1069
1471
|
console.log("");
|
|
1070
1472
|
}
|
|
1071
1473
|
async function logoutCommand() {
|
|
1072
1474
|
const config = loadGlobalConfig();
|
|
1073
1475
|
if (!config.auth?.token) {
|
|
1074
1476
|
console.log(`
|
|
1075
|
-
${
|
|
1477
|
+
${pc11.dim("You're not logged in. Nothing to do.")}
|
|
1076
1478
|
`);
|
|
1077
1479
|
return;
|
|
1078
1480
|
}
|
|
@@ -1085,46 +1487,46 @@ async function logoutCommand() {
|
|
|
1085
1487
|
process.exit(1);
|
|
1086
1488
|
}
|
|
1087
1489
|
console.log("");
|
|
1088
|
-
console.log(` ${
|
|
1089
|
-
console.log(` ${
|
|
1090
|
-
console.log(` ${
|
|
1490
|
+
console.log(` ${pc11.green(pc11.bold("\u2714"))} Logged out successfully.`);
|
|
1491
|
+
console.log(` ${pc11.dim("Your license key has been removed from")} ${pc11.cyan(getGlobalConfigPath())}`);
|
|
1492
|
+
console.log(` ${pc11.dim("Free components remain available.")}`);
|
|
1091
1493
|
console.log("");
|
|
1092
1494
|
}
|
|
1093
1495
|
async function whoamiCommand() {
|
|
1094
1496
|
const config = loadGlobalConfig();
|
|
1095
1497
|
console.log("");
|
|
1096
|
-
console.log(
|
|
1498
|
+
console.log(pc11.bold(pc11.cyan(" Vector Vesper Account Status\n")));
|
|
1097
1499
|
if (config.auth?.token) {
|
|
1098
1500
|
const maskedKey = config.auth.token.slice(0, 6) + "\u2026" + config.auth.token.slice(-4);
|
|
1099
1501
|
const tier = config.auth.tier || "pro";
|
|
1100
|
-
console.log(` ${
|
|
1101
|
-
console.log(` ${
|
|
1102
|
-
console.log(` ${
|
|
1502
|
+
console.log(` ${pc11.bold("Status:")} ${pc11.green("Authenticated")}`);
|
|
1503
|
+
console.log(` ${pc11.bold("Key:")} ${pc11.cyan(maskedKey)}`);
|
|
1504
|
+
console.log(` ${pc11.bold("Tier:")} ${pc11.magenta(tier)}`);
|
|
1103
1505
|
if (config.auth.email) {
|
|
1104
|
-
console.log(` ${
|
|
1506
|
+
console.log(` ${pc11.bold("Email:")} ${pc11.cyan(config.auth.email)}`);
|
|
1105
1507
|
}
|
|
1106
1508
|
if (config.auth.expiresAt) {
|
|
1107
1509
|
const expiry = new Date(config.auth.expiresAt);
|
|
1108
1510
|
const now = /* @__PURE__ */ new Date();
|
|
1109
1511
|
const isExpired = expiry < now;
|
|
1110
|
-
console.log(` ${
|
|
1512
|
+
console.log(` ${pc11.bold("Expires:")} ${isExpired ? pc11.red("EXPIRED") : pc11.green(expiry.toLocaleDateString())}`);
|
|
1111
1513
|
}
|
|
1112
1514
|
console.log("");
|
|
1113
|
-
console.log(` ${
|
|
1114
|
-
console.log(` ${
|
|
1515
|
+
console.log(` ${pc11.dim("Config:")} ${pc11.cyan(getGlobalConfigPath())}`);
|
|
1516
|
+
console.log(` ${pc11.dim("Manage your account at")} ${pc11.cyan("https://vectorvesper.dev/account")}`);
|
|
1115
1517
|
} else {
|
|
1116
|
-
console.log(` ${
|
|
1117
|
-
console.log(` ${
|
|
1518
|
+
console.log(` ${pc11.bold("Status:")} ${pc11.yellow("Anonymous (free tier)")}`);
|
|
1519
|
+
console.log(` ${pc11.bold("Access:")} ${pc11.dim("Free components only")}`);
|
|
1118
1520
|
console.log("");
|
|
1119
|
-
console.log(` ${
|
|
1120
|
-
console.log(` ${
|
|
1121
|
-
console.log(` \u{1F511} ${
|
|
1521
|
+
console.log(` ${pc11.dim("To unlock pro components:")}`);
|
|
1522
|
+
console.log(` ${pc11.cyan("npx vectorvesper login <your-license-key>")}`);
|
|
1523
|
+
console.log(` \u{1F511} ${pc11.dim("Get a key at")} ${pc11.cyan("https://vectorvesper.dev/pricing")}`);
|
|
1122
1524
|
}
|
|
1123
1525
|
console.log("");
|
|
1124
1526
|
}
|
|
1125
1527
|
|
|
1126
1528
|
// src/index.ts
|
|
1127
|
-
var version = true ? "0.
|
|
1529
|
+
var version = true ? "0.2.1" : "0.0.0-dev";
|
|
1128
1530
|
var program = new Command();
|
|
1129
1531
|
program.name("vv").description("Vector Vesper CLI \u2014 add visual components to your React project").version(version).option("--verbose", "Show detailed debug output").hook("preAction", (thisCommand) => {
|
|
1130
1532
|
const opts = thisCommand.opts();
|
|
@@ -1138,9 +1540,15 @@ program.command("init").description("Initialize Vector Vesper configuration in y
|
|
|
1138
1540
|
program.command("list").description("List all available components in the registry").action(async () => {
|
|
1139
1541
|
await listCommand();
|
|
1140
1542
|
});
|
|
1141
|
-
program.command("add <slug>").description("Add a component to your project").option("-o, --overwrite", "Overwrite existing files without prompting").option("-y, --yes", "Accept all default prompts").option("-d, --dry-run", "Simulate the installation without writing files").action(async (slug, options) => {
|
|
1543
|
+
program.command("add <slug>").description("Add a component to your project").option("-o, --overwrite", "Overwrite existing files without prompting").option("-y, --yes", "Accept all default prompts").option("-d, --dry-run", "Simulate the installation without writing files").option("--no-install", "Skip auto-installing missing dependencies").action(async (slug, options) => {
|
|
1142
1544
|
await addCommand(slug, options);
|
|
1143
1545
|
});
|
|
1546
|
+
program.command("update [slug]").description("Update installed components to the latest registry version").option("-f, --force", "Re-write files even if the version already matches").action(async (slug, options) => {
|
|
1547
|
+
await updateCommand(slug, options);
|
|
1548
|
+
});
|
|
1549
|
+
program.command("remove <slug>").alias("rm").description("Remove an installed component and its files").option("-y, --yes", "Skip the confirmation prompt").action(async (slug, options) => {
|
|
1550
|
+
await removeCommand(slug, options);
|
|
1551
|
+
});
|
|
1144
1552
|
program.command("info").description("Show project diagnostics and list installed components").action(async () => {
|
|
1145
1553
|
await infoCommand();
|
|
1146
1554
|
});
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vectorvesper",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Add
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Add WebGL, React Three Fiber & advanced motion components to your project via CLI — motion, engineered.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"bin": {
|
|
9
|
-
"vv": "
|
|
9
|
+
"vv": "dist/index.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
@@ -41,7 +41,9 @@
|
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsup",
|
|
43
43
|
"dev": "tsup --watch",
|
|
44
|
-
"typecheck": "tsc --noEmit"
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"test:watch": "vitest"
|
|
45
47
|
},
|
|
46
48
|
"dependencies": {
|
|
47
49
|
"@clack/prompts": "^0.7.0",
|
|
@@ -53,6 +55,7 @@
|
|
|
53
55
|
"devDependencies": {
|
|
54
56
|
"@types/node": "^25.9.2",
|
|
55
57
|
"tsup": "^8.0.2",
|
|
56
|
-
"typescript": "^6.0.3"
|
|
58
|
+
"typescript": "^6.0.3",
|
|
59
|
+
"vitest": "^2.1.8"
|
|
57
60
|
}
|
|
58
61
|
}
|