vectorvesper 0.1.0 → 0.2.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 +658 -249
- 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(),
|
|
@@ -419,15 +432,15 @@ function handleHttpError(status, url) {
|
|
|
419
432
|
);
|
|
420
433
|
case 402:
|
|
421
434
|
throw new Error(
|
|
422
|
-
`This is a Pro component and requires
|
|
423
|
-
\u{1F449}
|
|
435
|
+
`This is a Pro component and requires a license.
|
|
436
|
+
\u{1F449} Get lifetime access at https://vectorvesper.dev/pricing
|
|
424
437
|
\u{1F4A1} Run "vv 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 "vv whoami" to see your current
|
|
443
|
+
\u{1F4A1} Run "vv whoami" to see your current access.`
|
|
431
444
|
);
|
|
432
445
|
case 404:
|
|
433
446
|
throw new Error(
|
|
@@ -451,7 +464,8 @@ async function fetchWithRetry(url) {
|
|
|
451
464
|
"User-Agent": "vv-cli",
|
|
452
465
|
"Accept": "application/json"
|
|
453
466
|
};
|
|
454
|
-
|
|
467
|
+
const isPublicRegistry = url.includes("raw.githubusercontent.com");
|
|
468
|
+
if (token && !isPublicRegistry) {
|
|
455
469
|
headers["Authorization"] = `Bearer ${token}`;
|
|
456
470
|
logger.debug(`Using auth token for request`);
|
|
457
471
|
}
|
|
@@ -598,43 +612,281 @@ async function listCommand() {
|
|
|
598
612
|
}
|
|
599
613
|
|
|
600
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
|
|
601
622
|
import fs5 from "fs";
|
|
602
623
|
import path5 from "path";
|
|
603
|
-
import ora2 from "ora";
|
|
604
624
|
import pc4 from "picocolors";
|
|
605
|
-
|
|
625
|
+
var MAX_DEPTH = 20;
|
|
606
626
|
function toForwardSlash(p) {
|
|
607
627
|
return p.replace(/\\/g, "/");
|
|
608
628
|
}
|
|
609
|
-
function
|
|
610
|
-
const
|
|
611
|
-
|
|
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("vv 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 = {};
|
|
612
730
|
try {
|
|
613
|
-
const pkg = JSON.parse(
|
|
614
|
-
|
|
731
|
+
const pkg = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
|
|
732
|
+
installed = {
|
|
615
733
|
...pkg.dependencies,
|
|
616
734
|
...pkg.devDependencies,
|
|
617
735
|
...pkg.peerDependencies
|
|
618
736
|
};
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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);
|
|
744
|
+
}
|
|
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
|
+
);
|
|
628
813
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
\
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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)}` : "");
|
|
636
833
|
}
|
|
637
|
-
}
|
|
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))}`);
|
|
638
890
|
}
|
|
639
891
|
}
|
|
640
892
|
async function addCommand(slug, options = {}) {
|
|
@@ -642,7 +894,7 @@ async function addCommand(slug, options = {}) {
|
|
|
642
894
|
validateSlug(slug);
|
|
643
895
|
} catch (error) {
|
|
644
896
|
const message = error instanceof Error ? error.message : String(error);
|
|
645
|
-
console.error(`${
|
|
897
|
+
console.error(`${pc6.red(pc6.bold("Error:"))} ${message}`);
|
|
646
898
|
process.exit(1);
|
|
647
899
|
}
|
|
648
900
|
let config;
|
|
@@ -650,173 +902,324 @@ async function addCommand(slug, options = {}) {
|
|
|
650
902
|
config = loadConfig();
|
|
651
903
|
} catch (error) {
|
|
652
904
|
const message = error instanceof Error ? error.message : String(error);
|
|
653
|
-
console.error(`${
|
|
905
|
+
console.error(`${pc6.red(pc6.bold("Error:"))} ${message}`);
|
|
654
906
|
process.exit(1);
|
|
655
907
|
}
|
|
656
908
|
if (!config) {
|
|
657
909
|
console.error(
|
|
658
|
-
`${
|
|
659
|
-
\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("vv init"))} first to set up your project.`
|
|
660
912
|
);
|
|
661
913
|
process.exit(1);
|
|
662
914
|
return;
|
|
663
915
|
}
|
|
664
916
|
const projectInfo = detectProject();
|
|
665
917
|
const componentInstallDir = resolveAlias(config.aliases.vv, projectInfo.hasSrcDir);
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
}).start();
|
|
670
|
-
const resolvedComponents = [];
|
|
671
|
-
const visited = /* @__PURE__ */ new Set();
|
|
672
|
-
async function resolveComponentTree(componentSlug, depth = 0) {
|
|
673
|
-
if (visited.has(componentSlug)) return;
|
|
674
|
-
if (depth > 20) {
|
|
675
|
-
throw new Error(`Dependency resolution exceeded maximum depth (20). Possible circular dependency involving "${componentSlug}".`);
|
|
676
|
-
}
|
|
677
|
-
visited.add(componentSlug);
|
|
678
|
-
const component = await fetchComponent(componentSlug);
|
|
679
|
-
if (component.tier === "pro" && !getAuthToken()) {
|
|
680
|
-
throw new Error(
|
|
681
|
-
`"${componentSlug}" is a Pro component and requires authentication.
|
|
682
|
-
\u{1F449} Run ${pc4.cyan("vv login <your-license-key>")} to unlock pro components.
|
|
683
|
-
\u{1F511} Get a key at ${pc4.cyan("https://vectorvesper.dev/pricing")}`
|
|
684
|
-
);
|
|
685
|
-
}
|
|
686
|
-
if (component.registryDependencies && component.registryDependencies.length > 0) {
|
|
687
|
-
for (const depSlug of component.registryDependencies) {
|
|
688
|
-
await resolveComponentTree(depSlug, depth + 1);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
resolvedComponents.push(component);
|
|
692
|
-
}
|
|
918
|
+
const projectRoot = process.cwd();
|
|
919
|
+
const spinner = ora2({ text: `Resolving component ${pc6.cyan(slug)}...`, color: "cyan" }).start();
|
|
920
|
+
let resolvedComponents;
|
|
693
921
|
try {
|
|
694
|
-
await resolveComponentTree(slug);
|
|
922
|
+
resolvedComponents = await resolveComponentTree(slug);
|
|
695
923
|
} catch (error) {
|
|
696
924
|
const message = error instanceof Error ? error.message : String(error);
|
|
697
|
-
spinner.fail(
|
|
925
|
+
spinner.fail(pc6.red(`Failed to resolve component "${slug}": ${message}`));
|
|
698
926
|
process.exit(1);
|
|
927
|
+
return;
|
|
699
928
|
}
|
|
700
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
|
+
}
|
|
701
939
|
const filesToWrite = [];
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
if (relativeFromRoot.startsWith("..") || path5.isAbsolute(relativeFromRoot)) {
|
|
709
|
-
spinner.fail(
|
|
710
|
-
`${pc4.red(pc4.bold("Security Error:"))} Component target file path tries to escape project root: ${file.target}`
|
|
711
|
-
);
|
|
712
|
-
process.exit(1);
|
|
713
|
-
}
|
|
714
|
-
if (fs5.existsSync(fileWritePath)) {
|
|
715
|
-
if (options.dryRun) {
|
|
716
|
-
} else if (!options.overwrite && !options.yes) {
|
|
717
|
-
spinner.stop();
|
|
718
|
-
const relativeDisplayPath = path5.relative(projectRoot, fileWritePath);
|
|
719
|
-
const shouldOverwrite = await confirm2({
|
|
720
|
-
message: pc4.yellow(`File already exists: "${relativeDisplayPath}". Overwrite?`),
|
|
721
|
-
initialValue: false
|
|
722
|
-
});
|
|
723
|
-
if (isCancel2(shouldOverwrite) || !shouldOverwrite) {
|
|
724
|
-
console.log(pc4.dim(` Skipping existing file: ${relativeDisplayPath}`));
|
|
725
|
-
spinner.start("Continuing installation...");
|
|
726
|
-
continue;
|
|
727
|
-
}
|
|
728
|
-
spinner.start("Continuing installation...");
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
filesToWrite.push({
|
|
732
|
-
absolutePath: fileWritePath,
|
|
733
|
-
relativePath: relativeFromRoot,
|
|
734
|
-
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
|
|
735
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...");
|
|
736
953
|
}
|
|
954
|
+
filesToWrite.push({
|
|
955
|
+
absolutePath: file.absolutePath,
|
|
956
|
+
relativePath: file.relativePath,
|
|
957
|
+
content: file.content
|
|
958
|
+
});
|
|
737
959
|
}
|
|
960
|
+
const targetComponent = resolvedComponents[resolvedComponents.length - 1];
|
|
961
|
+
const installPath = toForwardSlash(path7.join(componentInstallDir, targetComponent.slug));
|
|
738
962
|
if (options.dryRun) {
|
|
739
|
-
spinner.succeed(
|
|
740
|
-
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:")));
|
|
741
965
|
for (const file of filesToWrite) {
|
|
742
|
-
const status =
|
|
743
|
-
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))}`);
|
|
744
974
|
}
|
|
745
|
-
reportMissingDependencies(resolvedComponents, projectRoot, projectInfo);
|
|
746
|
-
const targetComponent2 = resolvedComponents[resolvedComponents.length - 1];
|
|
747
|
-
console.log("");
|
|
748
|
-
console.log(
|
|
749
|
-
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))}`))
|
|
750
|
-
);
|
|
751
975
|
console.log("");
|
|
752
|
-
console.log(
|
|
753
|
-
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}`)));
|
|
754
977
|
console.log("");
|
|
755
978
|
return;
|
|
756
979
|
}
|
|
757
980
|
if (filesToWrite.length === 0) {
|
|
758
|
-
spinner.succeed(
|
|
981
|
+
spinner.succeed(pc6.green("All files skipped (already up to date)."));
|
|
759
982
|
return;
|
|
760
983
|
}
|
|
761
984
|
spinner.text = "Writing component files...";
|
|
762
985
|
try {
|
|
763
|
-
|
|
764
|
-
const folder = path5.dirname(file.absolutePath);
|
|
765
|
-
if (!fs5.existsSync(folder)) {
|
|
766
|
-
fs5.mkdirSync(folder, { recursive: true });
|
|
767
|
-
}
|
|
768
|
-
fs5.writeFileSync(file.absolutePath, file.content, "utf-8");
|
|
769
|
-
}
|
|
986
|
+
writeFiles(filesToWrite);
|
|
770
987
|
} catch (error) {
|
|
771
988
|
const message = error instanceof Error ? error.message : String(error);
|
|
772
|
-
spinner.fail(
|
|
989
|
+
spinner.fail(pc6.red(`Failed to write files: ${message}`));
|
|
773
990
|
process.exit(1);
|
|
991
|
+
return;
|
|
774
992
|
}
|
|
775
993
|
try {
|
|
776
|
-
|
|
777
|
-
let manifest = { components: {} };
|
|
778
|
-
if (fs5.existsSync(manifestPath)) {
|
|
779
|
-
manifest = JSON.parse(fs5.readFileSync(manifestPath, "utf-8"));
|
|
780
|
-
}
|
|
781
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
782
|
-
for (const component of resolvedComponents) {
|
|
783
|
-
manifest.components[component.slug] = {
|
|
784
|
-
version: component.version,
|
|
785
|
-
installedAt: toForwardSlash(path5.join(componentInstallDir, component.slug)),
|
|
786
|
-
installedOn: today,
|
|
787
|
-
files: component.files.map((f) => f.target)
|
|
788
|
-
};
|
|
789
|
-
}
|
|
790
|
-
fs5.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
994
|
+
recordInstall(resolvedComponents, componentInstallDir, projectRoot);
|
|
791
995
|
} catch (error) {
|
|
792
996
|
const message = error instanceof Error ? error.message : String(error);
|
|
793
997
|
logger.warn(`Failed to update vv-manifest.json: ${message}`);
|
|
794
998
|
logger.warn("Component was installed but may not appear in 'vv info' or 'vv diff'.");
|
|
795
999
|
}
|
|
796
|
-
spinner.succeed(
|
|
797
|
-
console.log(
|
|
1000
|
+
spinner.succeed(pc6.green("Files written successfully!"));
|
|
1001
|
+
console.log(pc6.dim("\nCreated files:"));
|
|
798
1002
|
for (const file of filesToWrite) {
|
|
799
|
-
console.log(` ${
|
|
1003
|
+
console.log(` ${pc6.green("\u2714")} ${pc6.cyan(file.relativePath)}`);
|
|
800
1004
|
}
|
|
801
|
-
|
|
802
|
-
|
|
1005
|
+
await handleDependencies(resolvedComponents, projectRoot, projectInfo, options);
|
|
1006
|
+
printGuidance(targetComponent, {
|
|
1007
|
+
alias: config.aliases.vv,
|
|
1008
|
+
framework: projectInfo.framework,
|
|
1009
|
+
hasTailwind: projectInfo.hasTailwind
|
|
1010
|
+
});
|
|
803
1011
|
console.log("");
|
|
804
|
-
console.log(
|
|
805
|
-
|
|
806
|
-
);
|
|
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("vv 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("vv 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(`vv 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
|
+
}
|
|
1115
|
+
console.log("");
|
|
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("vv 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("vv 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
|
+
}
|
|
807
1208
|
console.log("");
|
|
808
|
-
console.log(
|
|
809
|
-
|
|
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
|
+
}
|
|
810
1213
|
console.log("");
|
|
811
1214
|
}
|
|
812
1215
|
|
|
813
1216
|
// src/commands/info.ts
|
|
814
|
-
import
|
|
815
|
-
import
|
|
816
|
-
import
|
|
817
|
-
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.0" : "0.0.0-dev";
|
|
818
1221
|
async function infoCommand() {
|
|
819
|
-
console.log(
|
|
1222
|
+
console.log(pc9.bold(pc9.cyan("\nVector Vesper Diagnostics\n")));
|
|
820
1223
|
const projectInfo = detectProject();
|
|
821
1224
|
let config;
|
|
822
1225
|
try {
|
|
@@ -824,95 +1227,95 @@ async function infoCommand() {
|
|
|
824
1227
|
} catch {
|
|
825
1228
|
config = null;
|
|
826
1229
|
}
|
|
827
|
-
console.log(
|
|
828
|
-
console.log(` \u2022 CLI Version: ${
|
|
829
|
-
console.log(` \u2022 Node Version: ${
|
|
830
|
-
console.log(` \u2022 OS: ${
|
|
831
|
-
console.log(` \u2022 Package Manager: ${
|
|
832
|
-
console.log(` \u2022 Framework: ${
|
|
833
|
-
console.log(` \u2022 TypeScript: ${
|
|
834
|
-
console.log(` \u2022 Has src/ folder: ${
|
|
835
|
-
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")}`);
|
|
836
1239
|
console.log("");
|
|
837
|
-
console.log(
|
|
1240
|
+
console.log(pc9.bold(pc9.white("Authentication:")));
|
|
838
1241
|
const token = getAuthToken();
|
|
839
1242
|
if (token) {
|
|
840
1243
|
const maskedKey = token.slice(0, 6) + "\u2026" + token.slice(-4);
|
|
841
|
-
console.log(` \u2022 Status: ${
|
|
842
|
-
console.log(` \u2022 Key: ${
|
|
1244
|
+
console.log(` \u2022 Status: ${pc9.green("Authenticated")}`);
|
|
1245
|
+
console.log(` \u2022 Key: ${pc9.cyan(maskedKey)}`);
|
|
843
1246
|
} else {
|
|
844
|
-
console.log(` \u2022 Status: ${
|
|
845
|
-
console.log(` ${
|
|
1247
|
+
console.log(` \u2022 Status: ${pc9.yellow("Anonymous (free tier)")}`);
|
|
1248
|
+
console.log(` ${pc9.dim(" Run")} ${pc9.cyan("vv login <key>")} ${pc9.dim("to unlock pro components")}`);
|
|
846
1249
|
}
|
|
847
1250
|
console.log("");
|
|
848
|
-
console.log(
|
|
1251
|
+
console.log(pc9.bold(pc9.white("Configuration (vv.config.json):")));
|
|
849
1252
|
if (config) {
|
|
850
|
-
console.log(` \u2022 Framework Set: ${
|
|
851
|
-
console.log(` \u2022 TypeScript Set: ${
|
|
852
|
-
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)}`);
|
|
853
1256
|
} else {
|
|
854
|
-
console.log(` ${
|
|
1257
|
+
console.log(` ${pc9.yellow("\u26A0\uFE0F No vv.config.json found in this directory.")}`);
|
|
855
1258
|
}
|
|
856
1259
|
console.log("");
|
|
857
|
-
console.log(
|
|
858
|
-
const manifestPath =
|
|
859
|
-
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)) {
|
|
860
1263
|
try {
|
|
861
|
-
const manifest = JSON.parse(
|
|
1264
|
+
const manifest = JSON.parse(fs9.readFileSync(manifestPath, "utf-8"));
|
|
862
1265
|
const components = manifest.components || {};
|
|
863
1266
|
const slugs = Object.keys(components);
|
|
864
1267
|
if (slugs.length === 0) {
|
|
865
|
-
console.log(` ${
|
|
1268
|
+
console.log(` ${pc9.dim("No components installed yet.")}`);
|
|
866
1269
|
} else {
|
|
867
|
-
console.log(
|
|
868
|
-
console.log(` ${
|
|
869
|
-
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(" ------------------------------------------------------------"));
|
|
870
1273
|
for (const slug of slugs) {
|
|
871
1274
|
const info = components[slug];
|
|
872
1275
|
const version2 = info?.version ?? "unknown";
|
|
873
1276
|
const installedAt = info?.installedAt ?? "unknown";
|
|
874
|
-
console.log(` ${
|
|
1277
|
+
console.log(` ${pc9.cyan(slug.padEnd(25))} ${pc9.green(version2.padEnd(10))} ${pc9.gray(installedAt)}`);
|
|
875
1278
|
}
|
|
876
|
-
console.log(
|
|
1279
|
+
console.log(pc9.dim(" ------------------------------------------------------------"));
|
|
877
1280
|
}
|
|
878
1281
|
} catch {
|
|
879
|
-
console.log(` ${
|
|
1282
|
+
console.log(` ${pc9.red("\u274C Error reading vv-manifest.json: Invalid JSON format.")}`);
|
|
880
1283
|
}
|
|
881
1284
|
} else {
|
|
882
|
-
console.log(` ${
|
|
1285
|
+
console.log(` ${pc9.dim("No components installed yet (vv-manifest.json not found).")}`);
|
|
883
1286
|
}
|
|
884
1287
|
console.log("");
|
|
885
1288
|
}
|
|
886
1289
|
|
|
887
1290
|
// src/commands/diff.ts
|
|
888
|
-
import
|
|
889
|
-
import
|
|
890
|
-
import
|
|
891
|
-
import
|
|
1291
|
+
import fs10 from "fs";
|
|
1292
|
+
import path10 from "path";
|
|
1293
|
+
import pc10 from "picocolors";
|
|
1294
|
+
import ora4 from "ora";
|
|
892
1295
|
async function diffCommand(slug) {
|
|
893
1296
|
const projectRoot = process.cwd();
|
|
894
|
-
const manifestPath =
|
|
895
|
-
if (!
|
|
896
|
-
console.log(
|
|
1297
|
+
const manifestPath = path10.join(projectRoot, "vv-manifest.json");
|
|
1298
|
+
if (!fs10.existsSync(manifestPath)) {
|
|
1299
|
+
console.log(pc10.yellow(`
|
|
897
1300
|
\u26A0\uFE0F No vv-manifest.json found in this directory.`));
|
|
898
|
-
console.log(`\u{1F449} Add components first using ${
|
|
1301
|
+
console.log(`\u{1F449} Add components first using ${pc10.bold(pc10.cyan("vv add <slug>"))}
|
|
899
1302
|
`);
|
|
900
1303
|
return;
|
|
901
1304
|
}
|
|
902
1305
|
let manifest;
|
|
903
1306
|
try {
|
|
904
|
-
manifest = JSON.parse(
|
|
1307
|
+
manifest = JSON.parse(fs10.readFileSync(manifestPath, "utf-8"));
|
|
905
1308
|
} catch {
|
|
906
|
-
console.error(
|
|
1309
|
+
console.error(pc10.red(`\u274C Error reading vv-manifest.json: Invalid JSON format.`));
|
|
907
1310
|
process.exit(1);
|
|
908
1311
|
return;
|
|
909
1312
|
}
|
|
910
1313
|
const installedComponents = manifest.components || {};
|
|
911
1314
|
const installedSlugs = Object.keys(installedComponents);
|
|
912
1315
|
if (installedSlugs.length === 0) {
|
|
913
|
-
console.log(
|
|
1316
|
+
console.log(pc10.yellow(`
|
|
914
1317
|
\u26A0\uFE0F No components are registered as installed in vv-manifest.json.`));
|
|
915
|
-
console.log(`\u{1F449} Add components using ${
|
|
1318
|
+
console.log(`\u{1F449} Add components using ${pc10.bold(pc10.cyan("vv add <slug>"))}
|
|
916
1319
|
`);
|
|
917
1320
|
return;
|
|
918
1321
|
}
|
|
@@ -921,21 +1324,21 @@ async function diffCommand(slug) {
|
|
|
921
1324
|
validateSlug(slug);
|
|
922
1325
|
} catch (error) {
|
|
923
1326
|
const message = error instanceof Error ? error.message : String(error);
|
|
924
|
-
console.error(
|
|
1327
|
+
console.error(pc10.red(`
|
|
925
1328
|
\u274C ${message}
|
|
926
1329
|
`));
|
|
927
1330
|
process.exit(1);
|
|
928
1331
|
}
|
|
929
1332
|
if (!installedComponents[slug]) {
|
|
930
|
-
console.log(
|
|
1333
|
+
console.log(pc10.red(`
|
|
931
1334
|
\u274C Component "${slug}" is not installed in this project.`));
|
|
932
|
-
console.log(`\u{1F449} Run ${
|
|
1335
|
+
console.log(`\u{1F449} Run ${pc10.cyan(`vv add ${slug}`)} to install it.
|
|
933
1336
|
`);
|
|
934
1337
|
return;
|
|
935
1338
|
}
|
|
936
1339
|
const localInfo = installedComponents[slug];
|
|
937
|
-
const spinner =
|
|
938
|
-
text: `Checking updates for ${
|
|
1340
|
+
const spinner = ora4({
|
|
1341
|
+
text: `Checking updates for ${pc10.cyan(slug)}...`,
|
|
939
1342
|
color: "cyan"
|
|
940
1343
|
}).start();
|
|
941
1344
|
try {
|
|
@@ -943,39 +1346,39 @@ async function diffCommand(slug) {
|
|
|
943
1346
|
spinner.stop();
|
|
944
1347
|
const localVersion = localInfo.version ?? "unknown";
|
|
945
1348
|
const remoteVersion = remoteComponent.version;
|
|
946
|
-
console.log(
|
|
1349
|
+
console.log(pc10.bold(pc10.cyan(`
|
|
947
1350
|
Component Diff: ${slug}`)));
|
|
948
|
-
console.log(` \u2022 Local version: ${
|
|
949
|
-
console.log(` \u2022 Latest version: ${
|
|
1351
|
+
console.log(` \u2022 Local version: ${pc10.yellow(localVersion)}`);
|
|
1352
|
+
console.log(` \u2022 Latest version: ${pc10.green(remoteVersion)}`);
|
|
950
1353
|
if (localVersion === remoteVersion) {
|
|
951
|
-
console.log(` \u2022 Status: ${
|
|
1354
|
+
console.log(` \u2022 Status: ${pc10.green("\u2714 Up to date")}
|
|
952
1355
|
`);
|
|
953
1356
|
} else {
|
|
954
|
-
console.log(` \u2022 Status: ${
|
|
955
|
-
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(`vv add ${slug} --overwrite`))} to update.
|
|
956
1359
|
`);
|
|
957
1360
|
}
|
|
958
1361
|
} catch (error) {
|
|
959
1362
|
const message = error instanceof Error ? error.message : String(error);
|
|
960
|
-
spinner.fail(
|
|
1363
|
+
spinner.fail(pc10.red(`Failed to fetch remote component details: ${message}`));
|
|
961
1364
|
process.exit(1);
|
|
962
1365
|
}
|
|
963
1366
|
} else {
|
|
964
|
-
const spinner =
|
|
1367
|
+
const spinner = ora4({
|
|
965
1368
|
text: "Comparing local components with registry index...",
|
|
966
1369
|
color: "cyan"
|
|
967
1370
|
}).start();
|
|
968
1371
|
try {
|
|
969
1372
|
const registryIndex = await fetchRegistryIndex();
|
|
970
1373
|
spinner.stop();
|
|
971
|
-
console.log(
|
|
972
|
-
console.log(
|
|
1374
|
+
console.log(pc10.bold(pc10.cyan("\nVector Vesper Component Version Comparison\n")));
|
|
1375
|
+
console.log(pc10.dim(" ----------------------------------------------------------------------"));
|
|
973
1376
|
console.log(
|
|
974
|
-
` ${
|
|
975
|
-
|
|
976
|
-
)} ${
|
|
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"))}`
|
|
977
1380
|
);
|
|
978
|
-
console.log(
|
|
1381
|
+
console.log(pc10.dim(" ----------------------------------------------------------------------"));
|
|
979
1382
|
let updatesAvailableCount = 0;
|
|
980
1383
|
for (const instSlug of installedSlugs) {
|
|
981
1384
|
const localInfo = installedComponents[instSlug];
|
|
@@ -983,59 +1386,59 @@ Component Diff: ${slug}`)));
|
|
|
983
1386
|
const remoteInfo = registryIndex.components.find((c) => c.slug === instSlug);
|
|
984
1387
|
if (!remoteInfo) {
|
|
985
1388
|
console.log(
|
|
986
|
-
` ${
|
|
1389
|
+
` ${pc10.cyan(instSlug.padEnd(25))} ${pc10.yellow(
|
|
987
1390
|
localVersion.padEnd(10)
|
|
988
|
-
)} ${
|
|
1391
|
+
)} ${pc10.red("unknown".padEnd(10))} ${pc10.red("Not found in registry")}`
|
|
989
1392
|
);
|
|
990
1393
|
} else {
|
|
991
1394
|
const remoteVersion = remoteInfo.version;
|
|
992
1395
|
if (localVersion === remoteVersion) {
|
|
993
1396
|
console.log(
|
|
994
|
-
` ${
|
|
1397
|
+
` ${pc10.cyan(instSlug.padEnd(25))} ${pc10.gray(
|
|
995
1398
|
localVersion.padEnd(10)
|
|
996
|
-
)} ${
|
|
1399
|
+
)} ${pc10.gray(remoteVersion.padEnd(10))} ${pc10.green("\u2714 Up to date")}`
|
|
997
1400
|
);
|
|
998
1401
|
} else {
|
|
999
1402
|
updatesAvailableCount++;
|
|
1000
1403
|
console.log(
|
|
1001
|
-
` ${
|
|
1404
|
+
` ${pc10.cyan(instSlug.padEnd(25))} ${pc10.yellow(
|
|
1002
1405
|
localVersion.padEnd(10)
|
|
1003
|
-
)} ${
|
|
1004
|
-
|
|
1406
|
+
)} ${pc10.green(remoteVersion.padEnd(10))} ${pc10.bold(
|
|
1407
|
+
pc10.yellow("\u26A0\uFE0F Update available")
|
|
1005
1408
|
)}`
|
|
1006
1409
|
);
|
|
1007
1410
|
}
|
|
1008
1411
|
}
|
|
1009
1412
|
}
|
|
1010
|
-
console.log(
|
|
1413
|
+
console.log(pc10.dim(" ----------------------------------------------------------------------"));
|
|
1011
1414
|
if (updatesAvailableCount > 0) {
|
|
1012
1415
|
console.log(
|
|
1013
1416
|
`
|
|
1014
|
-
\u{1F4E2} ${
|
|
1417
|
+
\u{1F4E2} ${pc10.bold(pc10.yellow(String(updatesAvailableCount)))} component(s) have updates available.`
|
|
1015
1418
|
);
|
|
1016
|
-
console.log(`\u{1F449} Run ${
|
|
1419
|
+
console.log(`\u{1F449} Run ${pc10.bold(pc10.cyan("vv add <slug> --overwrite"))} to update a component.
|
|
1017
1420
|
`);
|
|
1018
1421
|
} else {
|
|
1019
1422
|
console.log(`
|
|
1020
|
-
${
|
|
1423
|
+
${pc10.green("\u2714")} All components are up to date!
|
|
1021
1424
|
`);
|
|
1022
1425
|
}
|
|
1023
1426
|
} catch (error) {
|
|
1024
1427
|
const message = error instanceof Error ? error.message : String(error);
|
|
1025
|
-
spinner.fail(
|
|
1428
|
+
spinner.fail(pc10.red(`Failed to check updates: ${message}`));
|
|
1026
1429
|
process.exit(1);
|
|
1027
1430
|
}
|
|
1028
1431
|
}
|
|
1029
1432
|
}
|
|
1030
1433
|
|
|
1031
1434
|
// src/commands/auth.ts
|
|
1032
|
-
import
|
|
1435
|
+
import pc11 from "picocolors";
|
|
1033
1436
|
async function loginCommand(key) {
|
|
1034
1437
|
if (!key || key.trim().length === 0) {
|
|
1035
1438
|
logger.error("Please provide a license key.");
|
|
1036
1439
|
console.log(`
|
|
1037
|
-
Usage: ${
|
|
1038
|
-
console.log(` \u{1F511} Get a key at ${
|
|
1440
|
+
Usage: ${pc11.cyan("vv login <your-license-key>")}`);
|
|
1441
|
+
console.log(` \u{1F511} Get a key at ${pc11.cyan("https://vectorvesper.dev/pricing")}
|
|
1039
1442
|
`);
|
|
1040
1443
|
process.exit(1);
|
|
1041
1444
|
}
|
|
@@ -1043,7 +1446,7 @@ async function loginCommand(key) {
|
|
|
1043
1446
|
if (trimmedKey.length < 8) {
|
|
1044
1447
|
logger.error("That doesn't look like a valid license key.");
|
|
1045
1448
|
console.log(`
|
|
1046
|
-
\u{1F511} License keys are available at ${
|
|
1449
|
+
\u{1F511} License keys are available at ${pc11.cyan("https://vectorvesper.dev/pricing")}
|
|
1047
1450
|
`);
|
|
1048
1451
|
process.exit(1);
|
|
1049
1452
|
}
|
|
@@ -1060,18 +1463,18 @@ async function loginCommand(key) {
|
|
|
1060
1463
|
process.exit(1);
|
|
1061
1464
|
}
|
|
1062
1465
|
console.log("");
|
|
1063
|
-
console.log(` ${
|
|
1064
|
-
console.log(` ${
|
|
1466
|
+
console.log(` ${pc11.green(pc11.bold("\u2714"))} License key saved successfully!`);
|
|
1467
|
+
console.log(` ${pc11.dim("Stored at:")} ${pc11.cyan(getGlobalConfigPath())}`);
|
|
1065
1468
|
console.log("");
|
|
1066
|
-
console.log(` ${
|
|
1067
|
-
console.log(` ${
|
|
1469
|
+
console.log(` ${pc11.dim("Pro components are now available when you run")} ${pc11.cyan("vv add <slug>")}`);
|
|
1470
|
+
console.log(` ${pc11.dim("To verify your account:")} ${pc11.cyan("vv whoami")}`);
|
|
1068
1471
|
console.log("");
|
|
1069
1472
|
}
|
|
1070
1473
|
async function logoutCommand() {
|
|
1071
1474
|
const config = loadGlobalConfig();
|
|
1072
1475
|
if (!config.auth?.token) {
|
|
1073
1476
|
console.log(`
|
|
1074
|
-
${
|
|
1477
|
+
${pc11.dim("You're not logged in. Nothing to do.")}
|
|
1075
1478
|
`);
|
|
1076
1479
|
return;
|
|
1077
1480
|
}
|
|
@@ -1084,46 +1487,46 @@ async function logoutCommand() {
|
|
|
1084
1487
|
process.exit(1);
|
|
1085
1488
|
}
|
|
1086
1489
|
console.log("");
|
|
1087
|
-
console.log(` ${
|
|
1088
|
-
console.log(` ${
|
|
1089
|
-
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.")}`);
|
|
1090
1493
|
console.log("");
|
|
1091
1494
|
}
|
|
1092
1495
|
async function whoamiCommand() {
|
|
1093
1496
|
const config = loadGlobalConfig();
|
|
1094
1497
|
console.log("");
|
|
1095
|
-
console.log(
|
|
1498
|
+
console.log(pc11.bold(pc11.cyan(" Vector Vesper Account Status\n")));
|
|
1096
1499
|
if (config.auth?.token) {
|
|
1097
1500
|
const maskedKey = config.auth.token.slice(0, 6) + "\u2026" + config.auth.token.slice(-4);
|
|
1098
1501
|
const tier = config.auth.tier || "pro";
|
|
1099
|
-
console.log(` ${
|
|
1100
|
-
console.log(` ${
|
|
1101
|
-
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)}`);
|
|
1102
1505
|
if (config.auth.email) {
|
|
1103
|
-
console.log(` ${
|
|
1506
|
+
console.log(` ${pc11.bold("Email:")} ${pc11.cyan(config.auth.email)}`);
|
|
1104
1507
|
}
|
|
1105
1508
|
if (config.auth.expiresAt) {
|
|
1106
1509
|
const expiry = new Date(config.auth.expiresAt);
|
|
1107
1510
|
const now = /* @__PURE__ */ new Date();
|
|
1108
1511
|
const isExpired = expiry < now;
|
|
1109
|
-
console.log(` ${
|
|
1512
|
+
console.log(` ${pc11.bold("Expires:")} ${isExpired ? pc11.red("EXPIRED") : pc11.green(expiry.toLocaleDateString())}`);
|
|
1110
1513
|
}
|
|
1111
1514
|
console.log("");
|
|
1112
|
-
console.log(` ${
|
|
1113
|
-
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")}`);
|
|
1114
1517
|
} else {
|
|
1115
|
-
console.log(` ${
|
|
1116
|
-
console.log(` ${
|
|
1518
|
+
console.log(` ${pc11.bold("Status:")} ${pc11.yellow("Anonymous (free tier)")}`);
|
|
1519
|
+
console.log(` ${pc11.bold("Access:")} ${pc11.dim("Free components only")}`);
|
|
1117
1520
|
console.log("");
|
|
1118
|
-
console.log(` ${
|
|
1119
|
-
console.log(` ${
|
|
1120
|
-
console.log(` \u{1F511} ${
|
|
1521
|
+
console.log(` ${pc11.dim("To unlock pro components:")}`);
|
|
1522
|
+
console.log(` ${pc11.cyan("vv login <your-license-key>")}`);
|
|
1523
|
+
console.log(` \u{1F511} ${pc11.dim("Get a key at")} ${pc11.cyan("https://vectorvesper.dev/pricing")}`);
|
|
1121
1524
|
}
|
|
1122
1525
|
console.log("");
|
|
1123
1526
|
}
|
|
1124
1527
|
|
|
1125
1528
|
// src/index.ts
|
|
1126
|
-
var version = true ? "0.
|
|
1529
|
+
var version = true ? "0.2.0" : "0.0.0-dev";
|
|
1127
1530
|
var program = new Command();
|
|
1128
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) => {
|
|
1129
1532
|
const opts = thisCommand.opts();
|
|
@@ -1137,9 +1540,15 @@ program.command("init").description("Initialize Vector Vesper configuration in y
|
|
|
1137
1540
|
program.command("list").description("List all available components in the registry").action(async () => {
|
|
1138
1541
|
await listCommand();
|
|
1139
1542
|
});
|
|
1140
|
-
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) => {
|
|
1141
1544
|
await addCommand(slug, options);
|
|
1142
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
|
+
});
|
|
1143
1552
|
program.command("info").description("Show project diagnostics and list installed components").action(async () => {
|
|
1144
1553
|
await infoCommand();
|
|
1145
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.0",
|
|
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
|
}
|