voidui-cli 0.1.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/README.md +1 -0
- package/dist/commands/add.d.ts +28 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +104 -0
- package/dist/commands/augment.d.ts +2 -0
- package/dist/commands/augment.d.ts.map +1 -0
- package/dist/commands/augment.js +97 -0
- package/dist/commands/diff.d.ts +19 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +233 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/snapshot.d.ts +2 -0
- package/dist/commands/snapshot.d.ts.map +1 -0
- package/dist/commands/snapshot.js +99 -0
- package/dist/commands/update.d.ts +28 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +203 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +89 -0
- package/dist/types/changelog.d.ts +17 -0
- package/dist/types/changelog.d.ts.map +1 -0
- package/dist/types/changelog.js +1 -0
- package/dist/types/lock-file.d.ts +41 -0
- package/dist/types/lock-file.d.ts.map +1 -0
- package/dist/types/lock-file.js +4 -0
- package/dist/types/registry.d.ts +84 -0
- package/dist/types/registry.d.ts.map +1 -0
- package/dist/types/registry.js +4 -0
- package/dist/utils/checksum.d.ts +27 -0
- package/dist/utils/checksum.d.ts.map +1 -0
- package/dist/utils/checksum.js +50 -0
- package/dist/utils/component-locator.d.ts +23 -0
- package/dist/utils/component-locator.d.ts.map +1 -0
- package/dist/utils/component-locator.js +59 -0
- package/dist/utils/diff-formatter.d.ts +47 -0
- package/dist/utils/diff-formatter.d.ts.map +1 -0
- package/dist/utils/diff-formatter.js +186 -0
- package/dist/utils/file-operations.d.ts +7 -0
- package/dist/utils/file-operations.d.ts.map +1 -0
- package/dist/utils/file-operations.js +37 -0
- package/dist/utils/lock-file.d.ts +60 -0
- package/dist/utils/lock-file.d.ts.map +1 -0
- package/dist/utils/lock-file.js +110 -0
- package/dist/utils/merge.d.ts +54 -0
- package/dist/utils/merge.d.ts.map +1 -0
- package/dist/utils/merge.js +84 -0
- package/dist/utils/prompts.d.ts +8 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +49 -0
- package/dist/utils/registry.d.ts +32 -0
- package/dist/utils/registry.d.ts.map +1 -0
- package/dist/utils/registry.js +139 -0
- package/dist/utils/shadcn.d.ts +30 -0
- package/dist/utils/shadcn.d.ts.map +1 -0
- package/dist/utils/shadcn.js +67 -0
- package/dist/validators/changelog.d.ts +50 -0
- package/dist/validators/changelog.d.ts.map +1 -0
- package/dist/validators/changelog.js +31 -0
- package/dist/validators/lock-file.d.ts +32 -0
- package/dist/validators/lock-file.d.ts.map +1 -0
- package/dist/validators/lock-file.js +39 -0
- package/package.json +56 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command implementation
|
|
3
|
+
* Updates components with smart merge support
|
|
4
|
+
*/
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import prompts from "prompts";
|
|
7
|
+
import { readFile, writeFile } from "fs/promises";
|
|
8
|
+
import { locateComponent } from "../utils/component-locator.js";
|
|
9
|
+
import { computeChecksum, compareChecksums } from "../utils/checksum.js";
|
|
10
|
+
import { fetchRegistryItem, fetchComponentVersion, extractComponentCode, } from "../utils/registry.js";
|
|
11
|
+
import { readLockFile, writeLockFile, updateComponentEntry, getComponentEntry, } from "../utils/lock-file.js";
|
|
12
|
+
import { threeWayMerge, formatMergeMessage } from "../utils/merge.js";
|
|
13
|
+
import { formatDiff } from "../utils/diff-formatter.js";
|
|
14
|
+
const DEFAULT_REGISTRY_URL = "https://voidui.dev/r";
|
|
15
|
+
/**
|
|
16
|
+
* Main update command handler
|
|
17
|
+
* Updates a component to latest version with merge support
|
|
18
|
+
*
|
|
19
|
+
* @param component - Component name
|
|
20
|
+
* @param options - Command options
|
|
21
|
+
*/
|
|
22
|
+
export async function updateCommand(component, options) {
|
|
23
|
+
const registryUrl = options.registry || DEFAULT_REGISTRY_URL;
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
// Validate component name
|
|
26
|
+
if (!component) {
|
|
27
|
+
console.error(chalk.red("ā Component name is required"));
|
|
28
|
+
console.error(chalk.gray("\nUsage:"));
|
|
29
|
+
console.error(chalk.gray(" voidui update <component>"));
|
|
30
|
+
console.error(chalk.gray(" voidui update <component> --force"));
|
|
31
|
+
console.error(chalk.gray(" voidui update <component> --merge"));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
console.log(chalk.blue(`\nš Updating ${component}...\n`));
|
|
35
|
+
// 1. Read lock file
|
|
36
|
+
const lockFile = await readLockFile(cwd);
|
|
37
|
+
if (!lockFile) {
|
|
38
|
+
console.error(chalk.red("ā No lock file found"));
|
|
39
|
+
console.error(chalk.gray("\n Lock file will be created when you run:"));
|
|
40
|
+
console.error(chalk.gray(` voidui add ${component}`));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const entry = getComponentEntry(lockFile, component);
|
|
44
|
+
if (!entry) {
|
|
45
|
+
console.error(chalk.red(`ā Component "${component}" is not tracked in lock file`));
|
|
46
|
+
console.error(chalk.gray(`\n Add tracking with:`));
|
|
47
|
+
console.error(chalk.gray(` voidui add ${component} --scan`));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
// 2. Fetch latest version from registry
|
|
51
|
+
console.log(chalk.gray("Checking for updates..."));
|
|
52
|
+
const registryItem = await fetchRegistryItem(component, registryUrl);
|
|
53
|
+
if (!registryItem) {
|
|
54
|
+
console.error(chalk.red(`ā Component "${component}" not found in registry`));
|
|
55
|
+
console.error(chalk.gray(` Registry: ${registryUrl}`));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
const versioning = registryItem.meta?.versioning;
|
|
59
|
+
if (!versioning) {
|
|
60
|
+
console.error(chalk.red(`ā Component "${component}" does not have version tracking`));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const latestVersion = versioning.currentVersion;
|
|
64
|
+
// 3. Locate component file
|
|
65
|
+
const location = await locateComponent(component, cwd);
|
|
66
|
+
if (!location.exists) {
|
|
67
|
+
console.error(chalk.red(`ā Component file not found at: ${location.path}`));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
// 4. Check for local modifications (even if on latest version)
|
|
71
|
+
const currentChecksum = await computeChecksum(location.path);
|
|
72
|
+
const isModified = !compareChecksums(entry.checksum, currentChecksum);
|
|
73
|
+
// Check if already on latest
|
|
74
|
+
if (entry.installedVersion === latestVersion) {
|
|
75
|
+
if (isModified) {
|
|
76
|
+
console.log(chalk.green(`ā Already on the latest version (${latestVersion})`));
|
|
77
|
+
console.log(chalk.yellow("\nā ļø However, local modifications detected"));
|
|
78
|
+
console.log(chalk.gray("\n Your file has been modified since installation."));
|
|
79
|
+
console.log(chalk.gray(" To reset to the original version, run:"));
|
|
80
|
+
console.log(chalk.gray(` voidui add ${component} --force`));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log(chalk.green(`ā Already on the latest version (${latestVersion})`));
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.log(chalk.gray(` Current: ${entry.installedVersion} ā Latest: ${latestVersion}`));
|
|
88
|
+
// Already have location, currentChecksum, isModified from above
|
|
89
|
+
if (isModified) {
|
|
90
|
+
console.log(chalk.yellow("\nā ļø Local modifications detected"));
|
|
91
|
+
}
|
|
92
|
+
// 5. Determine update strategy
|
|
93
|
+
let updateStrategy = "overwrite";
|
|
94
|
+
if (isModified && !options.force && !options.merge) {
|
|
95
|
+
// Interactive prompt
|
|
96
|
+
const response = await prompts({
|
|
97
|
+
type: "select",
|
|
98
|
+
name: "strategy",
|
|
99
|
+
message: "How would you like to update?",
|
|
100
|
+
choices: [
|
|
101
|
+
{
|
|
102
|
+
title: "Attempt 3-way merge (preserve your changes)",
|
|
103
|
+
value: "merge",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
title: "Overwrite (lose your local changes)",
|
|
107
|
+
value: "overwrite",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
title: "Show diff and abort",
|
|
111
|
+
value: "diff",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
title: "Cancel",
|
|
115
|
+
value: "abort",
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
initial: 0,
|
|
119
|
+
});
|
|
120
|
+
if (!response.strategy || response.strategy === "abort") {
|
|
121
|
+
console.log(chalk.gray("\nUpdate cancelled"));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (response.strategy === "diff") {
|
|
125
|
+
// Show diff and exit
|
|
126
|
+
const localContent = await readFile(location.path, "utf-8");
|
|
127
|
+
const latestContent = extractComponentCode(registryItem);
|
|
128
|
+
const diff = formatDiff(localContent, latestContent, `${component}@${entry.installedVersion}.tsx`, `${component}@${latestVersion}.tsx`);
|
|
129
|
+
console.log("\n" + diff);
|
|
130
|
+
console.log(chalk.gray(`\nRun \`voidui update ${component} --merge\` to attempt merge`));
|
|
131
|
+
console.log(chalk.gray(`Run \`voidui update ${component} --force\` to overwrite`));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
updateStrategy = response.strategy;
|
|
135
|
+
}
|
|
136
|
+
else if (options.force) {
|
|
137
|
+
updateStrategy = "overwrite";
|
|
138
|
+
}
|
|
139
|
+
else if (options.merge) {
|
|
140
|
+
updateStrategy = "merge";
|
|
141
|
+
}
|
|
142
|
+
// 6. Perform update
|
|
143
|
+
let newContent;
|
|
144
|
+
let mergeSuccess = true;
|
|
145
|
+
if (updateStrategy === "merge" && isModified) {
|
|
146
|
+
// 3-way merge
|
|
147
|
+
console.log(chalk.gray("\n Performing 3-way merge..."));
|
|
148
|
+
const baseContent = await fetchComponentVersion(component, entry.installedVersion, registryUrl);
|
|
149
|
+
const oursContent = await readFile(location.path, "utf-8");
|
|
150
|
+
const theirsContent = extractComponentCode(registryItem);
|
|
151
|
+
if (!baseContent) {
|
|
152
|
+
console.error(chalk.yellow(`\nā ļø Could not fetch base version (${entry.installedVersion})`));
|
|
153
|
+
console.error(chalk.gray(" Falling back to overwrite. Use --force to confirm."));
|
|
154
|
+
const confirmResponse = await prompts({
|
|
155
|
+
type: "confirm",
|
|
156
|
+
name: "overwrite",
|
|
157
|
+
message: "Overwrite local file with latest version?",
|
|
158
|
+
initial: false,
|
|
159
|
+
});
|
|
160
|
+
if (!confirmResponse.overwrite) {
|
|
161
|
+
console.log(chalk.gray("\nUpdate cancelled"));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
newContent = theirsContent;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const mergeResult = threeWayMerge(baseContent, oursContent, theirsContent, {
|
|
168
|
+
ours: "your changes",
|
|
169
|
+
theirs: `v${latestVersion}`,
|
|
170
|
+
});
|
|
171
|
+
newContent = mergeResult.content;
|
|
172
|
+
mergeSuccess = mergeResult.success;
|
|
173
|
+
console.log(formatMergeMessage(mergeResult, location.path));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// Simple overwrite
|
|
178
|
+
newContent = extractComponentCode(registryItem);
|
|
179
|
+
if (isModified) {
|
|
180
|
+
console.log(chalk.yellow("\nā ļø Overwriting local changes with latest version"));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// 7. Write updated content
|
|
184
|
+
await writeFile(location.path, newContent, "utf-8");
|
|
185
|
+
// 8. Update lock file
|
|
186
|
+
const newChecksum = await computeChecksum(location.path);
|
|
187
|
+
const updatedLockFile = updateComponentEntry(lockFile, component, {
|
|
188
|
+
...entry,
|
|
189
|
+
installedVersion: latestVersion,
|
|
190
|
+
installedAt: new Date().toISOString(),
|
|
191
|
+
checksum: newChecksum,
|
|
192
|
+
});
|
|
193
|
+
await writeLockFile(cwd, updatedLockFile);
|
|
194
|
+
// 9. Success message
|
|
195
|
+
if (mergeSuccess) {
|
|
196
|
+
console.log(chalk.green(`\nā Updated ${component} from ${entry.installedVersion} to ${latestVersion}`));
|
|
197
|
+
console.log(chalk.gray(` Location: ${location.path}`));
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
console.log(chalk.yellow(`\nā ļø Updated ${component} with conflicts. Please resolve manually.`));
|
|
201
|
+
}
|
|
202
|
+
console.log("");
|
|
203
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { snapshotCommand } from "./commands/snapshot.js";
|
|
4
|
+
import { augmentCommand } from "./commands/augment.js";
|
|
5
|
+
import { diffCommand } from "./commands/diff.js";
|
|
6
|
+
import { addCommand } from "./commands/add.js";
|
|
7
|
+
import { updateCommand } from "./commands/update.js";
|
|
8
|
+
import pkg from "../package.json" with { type: "json" };
|
|
9
|
+
const program = new Command();
|
|
10
|
+
program
|
|
11
|
+
.name("voidui")
|
|
12
|
+
.description("CLI tools for voidui component versioning")
|
|
13
|
+
.version(pkg.version);
|
|
14
|
+
program
|
|
15
|
+
.command("snapshot")
|
|
16
|
+
.description("Create a version snapshot of a component")
|
|
17
|
+
.argument("<component>", "Component name (e.g., separator)")
|
|
18
|
+
.argument("<version>", "Version number in semver format (e.g., 1.0.0)")
|
|
19
|
+
.action(async (component, version) => {
|
|
20
|
+
try {
|
|
21
|
+
await snapshotCommand(component, version);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error("Error:", error);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
program
|
|
29
|
+
.command("augment")
|
|
30
|
+
.description("Augment registry output with versioning metadata")
|
|
31
|
+
.action(async () => {
|
|
32
|
+
try {
|
|
33
|
+
await augmentCommand();
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error("Error:", error);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
program
|
|
41
|
+
.command("diff")
|
|
42
|
+
.description("Compare local components against registry versions")
|
|
43
|
+
.argument("[component]", "Component name (e.g., separator)")
|
|
44
|
+
.argument("[from-version]", "Source version (optional)")
|
|
45
|
+
.argument("[to-version]", "Target version (optional)")
|
|
46
|
+
.option("--code", "Show full code diff with syntax highlighting")
|
|
47
|
+
.option("--registry <url>", "Registry URL", "https://voidui.dev/r")
|
|
48
|
+
.action(async (component, fromVersion, toVersion, options) => {
|
|
49
|
+
try {
|
|
50
|
+
await diffCommand(component, fromVersion, toVersion, options);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.error("Error:", error);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
program
|
|
58
|
+
.command("add")
|
|
59
|
+
.description("Install a component with version tracking")
|
|
60
|
+
.argument("[component]", "Component name (e.g., separator)")
|
|
61
|
+
.option("--scan", "Add tracking to existing component without reinstalling")
|
|
62
|
+
.option("--force", "Reinstall and update lock file")
|
|
63
|
+
.option("--registry <url>", "Registry URL", "https://voidui.dev/r")
|
|
64
|
+
.action(async (component, options) => {
|
|
65
|
+
try {
|
|
66
|
+
await addCommand(component, options);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error("Error:", error);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
program
|
|
74
|
+
.command("update")
|
|
75
|
+
.description("Update a component to the latest version")
|
|
76
|
+
.argument("[component]", "Component name (e.g., separator)")
|
|
77
|
+
.option("--force", "Overwrite local changes")
|
|
78
|
+
.option("--merge", "Automatically attempt 3-way merge")
|
|
79
|
+
.option("--registry <url>", "Registry URL", "https://voidui.dev/r")
|
|
80
|
+
.action(async (component, options) => {
|
|
81
|
+
try {
|
|
82
|
+
await updateCommand(component, options);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
console.error("Error:", error);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
program.parse();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ChangeType = "added" | "changed" | "deprecated" | "removed" | "fixed" | "security";
|
|
2
|
+
export interface ChangelogChange {
|
|
3
|
+
type: ChangeType;
|
|
4
|
+
description: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ChangelogEntry {
|
|
7
|
+
version: string;
|
|
8
|
+
date: string;
|
|
9
|
+
changes: ChangelogChange[];
|
|
10
|
+
breaking?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ComponentChangelog {
|
|
13
|
+
component: string;
|
|
14
|
+
currentVersion: string;
|
|
15
|
+
entries: ChangelogEntry[];
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=changelog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/types/changelog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAClB,OAAO,GACP,SAAS,GACT,YAAY,GACZ,SAAS,GACT,OAAO,GACP,UAAU,CAAC;AAEf,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock file types for tracking installed component versions
|
|
3
|
+
*/
|
|
4
|
+
export interface ComponentLockEntry {
|
|
5
|
+
/**
|
|
6
|
+
* The installed version of the component (semver format)
|
|
7
|
+
* @example "1.2.0"
|
|
8
|
+
*/
|
|
9
|
+
installedVersion: string;
|
|
10
|
+
/**
|
|
11
|
+
* ISO 8601 timestamp of when the component was installed
|
|
12
|
+
* @example "2025-01-20T10:30:00Z"
|
|
13
|
+
*/
|
|
14
|
+
installedAt: string;
|
|
15
|
+
/**
|
|
16
|
+
* SHA-256 checksum of the installed component file
|
|
17
|
+
* @example "sha256:abc123..."
|
|
18
|
+
*/
|
|
19
|
+
checksum: string;
|
|
20
|
+
/**
|
|
21
|
+
* Optional custom registry URL if not using default
|
|
22
|
+
* @example "https://custom-registry.dev/r"
|
|
23
|
+
*/
|
|
24
|
+
registryUrl?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface LockFile {
|
|
27
|
+
/**
|
|
28
|
+
* Optional JSON schema reference
|
|
29
|
+
*/
|
|
30
|
+
$schema?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Lock file format version
|
|
33
|
+
* @example "1.0"
|
|
34
|
+
*/
|
|
35
|
+
version: string;
|
|
36
|
+
/**
|
|
37
|
+
* Map of component names to their lock entries
|
|
38
|
+
*/
|
|
39
|
+
components: Record<string, ComponentLockEntry>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=lock-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock-file.d.ts","sourceRoot":"","sources":["../../src/types/lock-file.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;CAChD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry API types for fetching component metadata
|
|
3
|
+
*/
|
|
4
|
+
import type { ComponentChangelog } from "./changelog.js";
|
|
5
|
+
export interface RegistryItemVersioning {
|
|
6
|
+
/**
|
|
7
|
+
* Current/latest version of the component
|
|
8
|
+
* @example "1.2.0"
|
|
9
|
+
*/
|
|
10
|
+
currentVersion: string;
|
|
11
|
+
/**
|
|
12
|
+
* Complete changelog with all version entries
|
|
13
|
+
*/
|
|
14
|
+
changelog: ComponentChangelog;
|
|
15
|
+
/**
|
|
16
|
+
* List of all available versions (sorted descending)
|
|
17
|
+
* @example ["1.2.0", "1.1.0", "1.0.0"]
|
|
18
|
+
*/
|
|
19
|
+
availableVersions: string[];
|
|
20
|
+
}
|
|
21
|
+
export interface RegistryFile {
|
|
22
|
+
/**
|
|
23
|
+
* Relative path of the file in the component
|
|
24
|
+
* @example "registry/components/ui/separator.tsx"
|
|
25
|
+
*/
|
|
26
|
+
path: string;
|
|
27
|
+
/**
|
|
28
|
+
* File content (source code)
|
|
29
|
+
*/
|
|
30
|
+
content: string;
|
|
31
|
+
/**
|
|
32
|
+
* File type
|
|
33
|
+
* @example "registry:ui"
|
|
34
|
+
*/
|
|
35
|
+
type: string;
|
|
36
|
+
/**
|
|
37
|
+
* Target path where the file should be copied
|
|
38
|
+
* @example "components/ui/separator.tsx"
|
|
39
|
+
*/
|
|
40
|
+
target?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface RegistryItem {
|
|
43
|
+
/**
|
|
44
|
+
* Component name
|
|
45
|
+
* @example "separator"
|
|
46
|
+
*/
|
|
47
|
+
name: string;
|
|
48
|
+
/**
|
|
49
|
+
* Component type
|
|
50
|
+
* @example "registry:ui"
|
|
51
|
+
*/
|
|
52
|
+
type: string;
|
|
53
|
+
/**
|
|
54
|
+
* Display title
|
|
55
|
+
* @example "Separator"
|
|
56
|
+
*/
|
|
57
|
+
title?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Component description
|
|
60
|
+
*/
|
|
61
|
+
description?: string;
|
|
62
|
+
/**
|
|
63
|
+
* List of files included in this component
|
|
64
|
+
*/
|
|
65
|
+
files: RegistryFile[];
|
|
66
|
+
/**
|
|
67
|
+
* Optional metadata including versioning info
|
|
68
|
+
*/
|
|
69
|
+
meta?: {
|
|
70
|
+
/**
|
|
71
|
+
* Versioning metadata (voidui extension)
|
|
72
|
+
*/
|
|
73
|
+
versioning?: RegistryItemVersioning;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Dependencies required by this component
|
|
77
|
+
*/
|
|
78
|
+
dependencies?: string[];
|
|
79
|
+
/**
|
|
80
|
+
* Dev dependencies
|
|
81
|
+
*/
|
|
82
|
+
devDependencies?: string[];
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/types/registry.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,SAAS,EAAE,kBAAkB,CAAC;IAE9B;;;OAGG;IACH,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,KAAK,EAAE,YAAY,EAAE,CAAC;IAEtB;;OAEG;IACH,IAAI,CAAC,EAAE;QACL;;WAEG;QACH,UAAU,CAAC,EAAE,sBAAsB,CAAC;KACrC,CAAC;IAEF;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for computing and comparing SHA-256 checksums
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Compute SHA-256 checksum of a file
|
|
6
|
+
* Normalizes line endings (CRLF -> LF) for cross-platform consistency
|
|
7
|
+
*
|
|
8
|
+
* @param filePath - Absolute path to the file
|
|
9
|
+
* @returns Checksum in format: "sha256:abc123..."
|
|
10
|
+
*/
|
|
11
|
+
export declare function computeChecksum(filePath: string): Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Compare two checksums for equality
|
|
14
|
+
*
|
|
15
|
+
* @param expected - Expected checksum
|
|
16
|
+
* @param actual - Actual checksum
|
|
17
|
+
* @returns True if checksums match
|
|
18
|
+
*/
|
|
19
|
+
export declare function compareChecksums(expected: string, actual: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Format a checksum for display (truncated)
|
|
22
|
+
*
|
|
23
|
+
* @param checksum - Full checksum string
|
|
24
|
+
* @returns Truncated checksum for display (e.g., "sha256:abc123...def789")
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatChecksum(checksum: string): string;
|
|
27
|
+
//# sourceMappingURL=checksum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checksum.d.ts","sourceRoot":"","sources":["../../src/utils/checksum.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWvE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAcvD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for computing and comparing SHA-256 checksums
|
|
3
|
+
*/
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
import { readFile } from "fs/promises";
|
|
6
|
+
/**
|
|
7
|
+
* Compute SHA-256 checksum of a file
|
|
8
|
+
* Normalizes line endings (CRLF -> LF) for cross-platform consistency
|
|
9
|
+
*
|
|
10
|
+
* @param filePath - Absolute path to the file
|
|
11
|
+
* @returns Checksum in format: "sha256:abc123..."
|
|
12
|
+
*/
|
|
13
|
+
export async function computeChecksum(filePath) {
|
|
14
|
+
const content = await readFile(filePath, "utf-8");
|
|
15
|
+
// Normalize line endings (CRLF -> LF) for cross-platform consistency
|
|
16
|
+
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
17
|
+
const hash = createHash("sha256");
|
|
18
|
+
hash.update(normalizedContent, "utf-8");
|
|
19
|
+
const digest = hash.digest("hex");
|
|
20
|
+
return `sha256:${digest}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Compare two checksums for equality
|
|
24
|
+
*
|
|
25
|
+
* @param expected - Expected checksum
|
|
26
|
+
* @param actual - Actual checksum
|
|
27
|
+
* @returns True if checksums match
|
|
28
|
+
*/
|
|
29
|
+
export function compareChecksums(expected, actual) {
|
|
30
|
+
return expected === actual;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Format a checksum for display (truncated)
|
|
34
|
+
*
|
|
35
|
+
* @param checksum - Full checksum string
|
|
36
|
+
* @returns Truncated checksum for display (e.g., "sha256:abc123...def789")
|
|
37
|
+
*/
|
|
38
|
+
export function formatChecksum(checksum) {
|
|
39
|
+
if (!checksum.startsWith("sha256:")) {
|
|
40
|
+
return checksum;
|
|
41
|
+
}
|
|
42
|
+
const hash = checksum.substring(7); // Remove "sha256:" prefix
|
|
43
|
+
if (hash.length <= 16) {
|
|
44
|
+
return checksum;
|
|
45
|
+
}
|
|
46
|
+
// Show first 6 and last 6 characters
|
|
47
|
+
const start = hash.substring(0, 6);
|
|
48
|
+
const end = hash.substring(hash.length - 6);
|
|
49
|
+
return `sha256:${start}...${end}`;
|
|
50
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for locating components in user projects
|
|
3
|
+
*/
|
|
4
|
+
export interface ComponentLocation {
|
|
5
|
+
/**
|
|
6
|
+
* Absolute path to the component file
|
|
7
|
+
*/
|
|
8
|
+
path: string;
|
|
9
|
+
/**
|
|
10
|
+
* Whether the component file exists
|
|
11
|
+
*/
|
|
12
|
+
exists: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Locate a component file in the user's project
|
|
16
|
+
* Tries common paths and reads components.json for path aliases
|
|
17
|
+
*
|
|
18
|
+
* @param componentName - Name of the component (e.g., "separator")
|
|
19
|
+
* @param cwd - Current working directory
|
|
20
|
+
* @returns Component location with path and existence flag
|
|
21
|
+
*/
|
|
22
|
+
export declare function locateComponent(componentName: string, cwd: string): Promise<ComponentLocation>;
|
|
23
|
+
//# sourceMappingURL=component-locator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-locator.d.ts","sourceRoot":"","sources":["../../src/utils/component-locator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB;AAUD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,iBAAiB,CAAC,CA0D5B"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for locating components in user projects
|
|
3
|
+
*/
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileExists, readJsonFile } from "./file-operations.js";
|
|
6
|
+
/**
|
|
7
|
+
* Locate a component file in the user's project
|
|
8
|
+
* Tries common paths and reads components.json for path aliases
|
|
9
|
+
*
|
|
10
|
+
* @param componentName - Name of the component (e.g., "separator")
|
|
11
|
+
* @param cwd - Current working directory
|
|
12
|
+
* @returns Component location with path and existence flag
|
|
13
|
+
*/
|
|
14
|
+
export async function locateComponent(componentName, cwd) {
|
|
15
|
+
const fileName = `${componentName}.tsx`;
|
|
16
|
+
// Try to read components.json for aliases
|
|
17
|
+
let componentAlias;
|
|
18
|
+
const configPath = path.join(cwd, "components.json");
|
|
19
|
+
if (await fileExists(configPath)) {
|
|
20
|
+
const config = await readJsonFile(configPath);
|
|
21
|
+
if (config?.aliases?.ui) {
|
|
22
|
+
componentAlias = config.aliases.ui;
|
|
23
|
+
}
|
|
24
|
+
else if (config?.aliases?.components) {
|
|
25
|
+
componentAlias = config.aliases.components;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Build list of paths to try
|
|
29
|
+
const pathsToTry = [];
|
|
30
|
+
// If we have an alias from config, try that first
|
|
31
|
+
if (componentAlias) {
|
|
32
|
+
// Handle path aliases like "@/components/ui"
|
|
33
|
+
const cleanAlias = componentAlias.replace(/^@\//, "");
|
|
34
|
+
pathsToTry.push(path.join(cwd, cleanAlias, fileName));
|
|
35
|
+
// Also try with "src/" prefix if alias doesn't start with it
|
|
36
|
+
if (!cleanAlias.startsWith("src/")) {
|
|
37
|
+
pathsToTry.push(path.join(cwd, "src", cleanAlias, fileName));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Common fallback paths
|
|
41
|
+
pathsToTry.push(path.join(cwd, "components", "ui", fileName), path.join(cwd, "src", "components", "ui", fileName), path.join(cwd, "app", "components", "ui", fileName), path.join(cwd, "lib", "components", "ui", fileName));
|
|
42
|
+
// Try each path in order
|
|
43
|
+
for (const tryPath of pathsToTry) {
|
|
44
|
+
if (await fileExists(tryPath)) {
|
|
45
|
+
return {
|
|
46
|
+
path: tryPath,
|
|
47
|
+
exists: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Component not found, return the most common default path
|
|
52
|
+
const defaultPath = componentAlias
|
|
53
|
+
? path.join(cwd, componentAlias.replace(/^@\//, ""), fileName)
|
|
54
|
+
: path.join(cwd, "components", "ui", fileName);
|
|
55
|
+
return {
|
|
56
|
+
path: defaultPath,
|
|
57
|
+
exists: false,
|
|
58
|
+
};
|
|
59
|
+
}
|