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
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# voidui CLI
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add command implementation
|
|
3
|
+
* Installs components with version tracking
|
|
4
|
+
*/
|
|
5
|
+
interface AddCommandOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Add tracking to existing component without reinstalling
|
|
8
|
+
*/
|
|
9
|
+
scan?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Force reinstall even if component exists
|
|
12
|
+
*/
|
|
13
|
+
force?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Registry URL
|
|
16
|
+
*/
|
|
17
|
+
registry?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Main add command handler
|
|
21
|
+
* Installs a component and tracks it in voidui.lock.json
|
|
22
|
+
*
|
|
23
|
+
* @param component - Component name
|
|
24
|
+
* @param options - Command options
|
|
25
|
+
*/
|
|
26
|
+
export declare function addCommand(component: string | undefined, options: AddCommandOptions): Promise<void>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=add.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAcH,UAAU,iBAAiB;IACzB;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CA8If"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add command implementation
|
|
3
|
+
* Installs components with version tracking
|
|
4
|
+
*/
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { locateComponent } from "../utils/component-locator.js";
|
|
7
|
+
import { computeChecksum } from "../utils/checksum.js";
|
|
8
|
+
import { fetchRegistryItem } from "../utils/registry.js";
|
|
9
|
+
import { execShadcnAdd } from "../utils/shadcn.js";
|
|
10
|
+
import { readOrCreateLockFile, writeLockFile, updateComponentEntry, isComponentTracked, } from "../utils/lock-file.js";
|
|
11
|
+
const DEFAULT_REGISTRY_URL = "https://voidui.dev/r";
|
|
12
|
+
/**
|
|
13
|
+
* Main add command handler
|
|
14
|
+
* Installs a component and tracks it in voidui.lock.json
|
|
15
|
+
*
|
|
16
|
+
* @param component - Component name
|
|
17
|
+
* @param options - Command options
|
|
18
|
+
*/
|
|
19
|
+
export async function addCommand(component, options) {
|
|
20
|
+
const registryUrl = options.registry || DEFAULT_REGISTRY_URL;
|
|
21
|
+
const cwd = process.cwd();
|
|
22
|
+
// Validate component name
|
|
23
|
+
if (!component) {
|
|
24
|
+
console.error(chalk.red("ā Component name is required"));
|
|
25
|
+
console.error(chalk.gray("\nUsage:"));
|
|
26
|
+
console.error(chalk.gray(" voidui add <component>"));
|
|
27
|
+
console.error(chalk.gray(" voidui add <component> --scan"));
|
|
28
|
+
console.error(chalk.gray(" voidui add <component> --force"));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
console.log(chalk.blue(`\nš¦ Adding ${component}...\n`));
|
|
32
|
+
// 1. Check if component already exists locally
|
|
33
|
+
const location = await locateComponent(component, cwd);
|
|
34
|
+
if (location.exists && !options.scan && !options.force) {
|
|
35
|
+
console.error(chalk.red(`ā Component "${component}" already exists in your project`));
|
|
36
|
+
console.error(chalk.gray(` Location: ${location.path}`));
|
|
37
|
+
console.error(chalk.gray("\n Use --scan to add version tracking without reinstalling"));
|
|
38
|
+
console.error(chalk.gray(" Use --force to reinstall and update tracking"));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
// 2. Fetch registry metadata to get current version
|
|
42
|
+
console.log(chalk.gray("Fetching registry metadata..."));
|
|
43
|
+
const registryItem = await fetchRegistryItem(component, registryUrl);
|
|
44
|
+
if (!registryItem) {
|
|
45
|
+
console.error(chalk.red(`ā Component "${component}" not found in registry`));
|
|
46
|
+
console.error(chalk.gray(` Registry: ${registryUrl}`));
|
|
47
|
+
console.error(chalk.gray(` Available at: ${registryUrl.replace("/r", "/components")}`));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const versioning = registryItem.meta?.versioning;
|
|
51
|
+
if (!versioning) {
|
|
52
|
+
console.error(chalk.red(`ā Component "${component}" does not have version tracking enabled`));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const currentVersion = versioning.currentVersion;
|
|
56
|
+
// 3. Install component via shadcn if needed
|
|
57
|
+
if (!options.scan) {
|
|
58
|
+
try {
|
|
59
|
+
await execShadcnAdd(component, { registryUrl });
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(chalk.red("\nā Installation failed:"));
|
|
63
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// 4. Locate the installed component
|
|
68
|
+
const componentLocation = await locateComponent(component, cwd);
|
|
69
|
+
if (!componentLocation.exists) {
|
|
70
|
+
console.error(chalk.red(`ā Component was not installed at expected location`));
|
|
71
|
+
console.error(chalk.gray(` Expected: ${componentLocation.path}`));
|
|
72
|
+
console.error(chalk.gray("\n This might be a configuration issue."));
|
|
73
|
+
console.error(chalk.gray(" Check your components.json or project structure."));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// 5. Compute checksum of installed file
|
|
77
|
+
console.log(chalk.gray("Computing checksum..."));
|
|
78
|
+
const checksum = await computeChecksum(componentLocation.path);
|
|
79
|
+
// 6. Read or create lock file
|
|
80
|
+
const lockFile = await readOrCreateLockFile(cwd);
|
|
81
|
+
// Check if already tracked
|
|
82
|
+
if (isComponentTracked(lockFile, component) && !options.force) {
|
|
83
|
+
console.log(chalk.yellow(`ā ļø Component "${component}" is already tracked in lock file`));
|
|
84
|
+
console.log(chalk.gray(" Use --force to update the tracking information"));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// 7. Update lock file
|
|
88
|
+
const updatedLockFile = updateComponentEntry(lockFile, component, {
|
|
89
|
+
installedVersion: currentVersion,
|
|
90
|
+
installedAt: new Date().toISOString(),
|
|
91
|
+
checksum: checksum,
|
|
92
|
+
registryUrl: registryUrl,
|
|
93
|
+
});
|
|
94
|
+
await writeLockFile(cwd, updatedLockFile);
|
|
95
|
+
// 8. Success message
|
|
96
|
+
console.log(chalk.green(`\nā Added ${component}@${currentVersion} with version tracking`));
|
|
97
|
+
console.log(chalk.gray(` Checksum: ${checksum.substring(0, 20)}...`));
|
|
98
|
+
console.log(chalk.gray(` Location: ${componentLocation.path}`));
|
|
99
|
+
if (options.scan) {
|
|
100
|
+
console.log(chalk.yellow("\n Note: Existing component was scanned. Checksum reflects current state."));
|
|
101
|
+
}
|
|
102
|
+
console.log(chalk.gray(`\nRun \`voidui diff ${component}\` to check for updates`));
|
|
103
|
+
console.log("");
|
|
104
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augment.d.ts","sourceRoot":"","sources":["../../src/commands/augment.ts"],"names":[],"mappings":"AA2CA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CA4HpD"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
import { fileExists, readJsonFile, writeJsonFile, readDir, } from "../utils/file-operations.js";
|
|
5
|
+
export async function augmentCommand() {
|
|
6
|
+
console.log(chalk.blue("\nš§ Augmenting registry with versioning metadata...\n"));
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
// Detect if running from monorepo root or www-docs
|
|
9
|
+
let registryPath;
|
|
10
|
+
let outputPath;
|
|
11
|
+
if (await fileExists(path.join(cwd, "registry/components/ui"))) {
|
|
12
|
+
// Running from www-docs
|
|
13
|
+
registryPath = path.join(cwd, "registry/components/ui");
|
|
14
|
+
outputPath = path.join(cwd, "public/r");
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
// Running from monorepo root
|
|
18
|
+
registryPath = path.join(cwd, "apps/www-docs/registry/components/ui");
|
|
19
|
+
outputPath = path.join(cwd, "apps/www-docs/public/r");
|
|
20
|
+
}
|
|
21
|
+
// Check if output directory exists
|
|
22
|
+
if (!(await fileExists(outputPath))) {
|
|
23
|
+
console.error(chalk.red('ā Registry output not found. Run "pnpm registry:build:raw" first.'));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
let augmentedCount = 0;
|
|
27
|
+
// Find all changelog files
|
|
28
|
+
const changelogPattern = path.join(registryPath, "*.changelog.json");
|
|
29
|
+
const changelogFiles = await glob(changelogPattern.replace(/\\/g, "/"));
|
|
30
|
+
for (const changelogFilePath of changelogFiles) {
|
|
31
|
+
const componentName = path
|
|
32
|
+
.basename(changelogFilePath)
|
|
33
|
+
.replace(".changelog.json", "");
|
|
34
|
+
// Read changelog
|
|
35
|
+
const changelog = await readJsonFile(changelogFilePath);
|
|
36
|
+
if (!changelog) {
|
|
37
|
+
console.warn(chalk.yellow(`ā ļø Could not read changelog for ${componentName}`));
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
// Find available versions
|
|
41
|
+
const versionsDir = path.join(registryPath, `${componentName}.versions`);
|
|
42
|
+
let availableVersions = [];
|
|
43
|
+
if (await fileExists(versionsDir)) {
|
|
44
|
+
const files = await readDir(versionsDir);
|
|
45
|
+
availableVersions = files
|
|
46
|
+
.filter((f) => f.endsWith(".tsx"))
|
|
47
|
+
.map((f) => f.replace(".tsx", ""))
|
|
48
|
+
.sort((a, b) => {
|
|
49
|
+
// Semantic version sort (descending)
|
|
50
|
+
const aParts = a.split(".").map(Number);
|
|
51
|
+
const bParts = b.split(".").map(Number);
|
|
52
|
+
for (let i = 0; i < 3; i++) {
|
|
53
|
+
const aVal = aParts[i] ?? 0;
|
|
54
|
+
const bVal = bParts[i] ?? 0;
|
|
55
|
+
if (bVal !== aVal) {
|
|
56
|
+
return bVal - aVal;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return 0;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Read generated registry item
|
|
63
|
+
const registryItemPath = path.join(outputPath, `${componentName}.json`);
|
|
64
|
+
const registryItem = await readJsonFile(registryItemPath);
|
|
65
|
+
if (!registryItem) {
|
|
66
|
+
console.warn(chalk.yellow(`ā ļø No registry output found for ${componentName}`));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Augment with meta field
|
|
70
|
+
registryItem.meta = {
|
|
71
|
+
versioning: {
|
|
72
|
+
currentVersion: changelog.currentVersion,
|
|
73
|
+
changelog,
|
|
74
|
+
availableVersions,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
// Write back
|
|
78
|
+
await writeJsonFile(registryItemPath, registryItem);
|
|
79
|
+
console.log(chalk.green(`ā Augmented: ${componentName}.json`));
|
|
80
|
+
augmentedCount++;
|
|
81
|
+
}
|
|
82
|
+
// Update main registry.json
|
|
83
|
+
const mainRegistryPath = path.join(outputPath, "registry.json");
|
|
84
|
+
const mainRegistry = await readJsonFile(mainRegistryPath);
|
|
85
|
+
if (mainRegistry && mainRegistry.items) {
|
|
86
|
+
for (const item of mainRegistry.items) {
|
|
87
|
+
const itemPath = path.join(outputPath, `${item.name}.json`);
|
|
88
|
+
const itemData = await readJsonFile(itemPath);
|
|
89
|
+
if (itemData?.meta) {
|
|
90
|
+
item.meta = itemData.meta;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
await writeJsonFile(mainRegistryPath, mainRegistry);
|
|
94
|
+
console.log(chalk.green("ā Augmented: registry.json"));
|
|
95
|
+
}
|
|
96
|
+
console.log(chalk.blue(`\n⨠Augmentation complete! ${augmentedCount} component(s) updated.\n`));
|
|
97
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff command implementation
|
|
3
|
+
* Compares local components against registry versions
|
|
4
|
+
*/
|
|
5
|
+
interface DiffCommandOptions {
|
|
6
|
+
code?: boolean;
|
|
7
|
+
registry?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Main diff command handler
|
|
11
|
+
*
|
|
12
|
+
* Supports three modes:
|
|
13
|
+
* 1. `voidui diff separator` - Check local vs latest
|
|
14
|
+
* 2. `voidui diff separator --code` - Show code diff
|
|
15
|
+
* 3. `voidui diff separator 1.0.0 1.2.0` - Compare registry versions
|
|
16
|
+
*/
|
|
17
|
+
export declare function diffCommand(component: string | undefined, fromVersion: string | undefined, toVersion: string | undefined, options: DiffCommandOptions): Promise<void>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAsBH,UAAU,kBAAkB;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff command implementation
|
|
3
|
+
* Compares local components against registry versions
|
|
4
|
+
*/
|
|
5
|
+
import path from "path";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { readFile } from "fs/promises";
|
|
8
|
+
import { fileExists, readJsonFile } from "../utils/file-operations.js";
|
|
9
|
+
import { locateComponent } from "../utils/component-locator.js";
|
|
10
|
+
import { computeChecksum, compareChecksums } from "../utils/checksum.js";
|
|
11
|
+
import { fetchRegistryItem, extractComponentCode, fetchComponentVersion, } from "../utils/registry.js";
|
|
12
|
+
import { formatDiff, formatChangelog, formatChangelogSummary, } from "../utils/diff-formatter.js";
|
|
13
|
+
import { lockFileSchema } from "../validators/lock-file.js";
|
|
14
|
+
const DEFAULT_REGISTRY_URL = "https://voidui.dev/r";
|
|
15
|
+
/**
|
|
16
|
+
* Main diff command handler
|
|
17
|
+
*
|
|
18
|
+
* Supports three modes:
|
|
19
|
+
* 1. `voidui diff separator` - Check local vs latest
|
|
20
|
+
* 2. `voidui diff separator --code` - Show code diff
|
|
21
|
+
* 3. `voidui diff separator 1.0.0 1.2.0` - Compare registry versions
|
|
22
|
+
*/
|
|
23
|
+
export async function diffCommand(component, fromVersion, toVersion, options) {
|
|
24
|
+
const registryUrl = options.registry || DEFAULT_REGISTRY_URL;
|
|
25
|
+
// Validate component name is provided
|
|
26
|
+
if (!component) {
|
|
27
|
+
console.error(chalk.red("ā Component name is required"));
|
|
28
|
+
console.error(chalk.gray("\nUsage:"));
|
|
29
|
+
console.error(chalk.gray(" voidui diff <component>"));
|
|
30
|
+
console.error(chalk.gray(" voidui diff <component> --code"));
|
|
31
|
+
console.error(chalk.gray(" voidui diff <component> <from-version> <to-version>"));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// Determine which mode to run
|
|
35
|
+
if (fromVersion && toVersion) {
|
|
36
|
+
// Mode 3: Compare two registry versions
|
|
37
|
+
await compareRegistryVersions(component, fromVersion, toVersion, registryUrl);
|
|
38
|
+
}
|
|
39
|
+
else if (fromVersion && !toVersion) {
|
|
40
|
+
// Invalid: only one version specified
|
|
41
|
+
console.error(chalk.red("ā Both from-version and to-version must be specified"));
|
|
42
|
+
console.error(chalk.gray("\nUsage:"));
|
|
43
|
+
console.error(chalk.gray(" voidui diff <component> <from-version> <to-version>"));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Mode 1 or 2: Check local vs registry
|
|
48
|
+
await compareLocalVsRegistry(component, registryUrl, options);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Mode 1 & 2: Compare local component against registry
|
|
53
|
+
*/
|
|
54
|
+
async function compareLocalVsRegistry(component, registryUrl, options) {
|
|
55
|
+
const cwd = process.cwd();
|
|
56
|
+
console.log(chalk.blue(`\nš Checking ${component}...\n`));
|
|
57
|
+
// 1. Locate component in user's project
|
|
58
|
+
const location = await locateComponent(component, cwd);
|
|
59
|
+
if (!location.exists) {
|
|
60
|
+
console.error(chalk.red(`ā Component "${component}" not found in your project`));
|
|
61
|
+
console.error(chalk.gray(` Expected at: ${location.path}`));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
// 2. Read lock file if exists
|
|
65
|
+
const lockFilePath = path.join(cwd, "voidui.lock.json");
|
|
66
|
+
const lockFileExists = await fileExists(lockFilePath);
|
|
67
|
+
let lockFile = null;
|
|
68
|
+
let installedVersion = null;
|
|
69
|
+
let expectedChecksum = null;
|
|
70
|
+
if (lockFileExists) {
|
|
71
|
+
const rawLockFile = await readJsonFile(lockFilePath);
|
|
72
|
+
if (rawLockFile) {
|
|
73
|
+
try {
|
|
74
|
+
const parsedLockFile = lockFileSchema.parse(rawLockFile);
|
|
75
|
+
lockFile = parsedLockFile;
|
|
76
|
+
const entry = parsedLockFile.components[component];
|
|
77
|
+
if (entry) {
|
|
78
|
+
installedVersion = entry.installedVersion;
|
|
79
|
+
expectedChecksum = entry.checksum;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
console.error(chalk.yellow("ā ļø Lock file is corrupted. Continuing without version tracking."));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// 3. Compute local file checksum
|
|
88
|
+
const actualChecksum = await computeChecksum(location.path);
|
|
89
|
+
const isModified = expectedChecksum && !compareChecksums(expectedChecksum, actualChecksum);
|
|
90
|
+
// 4. Fetch registry metadata
|
|
91
|
+
const registryItem = await fetchRegistryItem(component, registryUrl);
|
|
92
|
+
if (!registryItem) {
|
|
93
|
+
console.error(chalk.red(`ā Component "${component}" not found in registry`));
|
|
94
|
+
console.error(chalk.gray(` Registry: ${registryUrl}`));
|
|
95
|
+
console.error(chalk.gray(` Available at: ${registryUrl.replace("/r", "/components")}`));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const versioning = registryItem.meta?.versioning;
|
|
99
|
+
if (!versioning) {
|
|
100
|
+
console.error(chalk.red(`ā Component "${component}" does not have version tracking enabled`));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
const latestVersion = versioning.currentVersion;
|
|
104
|
+
// 5. Display version information
|
|
105
|
+
if (installedVersion) {
|
|
106
|
+
const modifiedText = isModified ? chalk.yellow(" (modified ā ļø )") : "";
|
|
107
|
+
console.log(`Your version: ${chalk.bold(installedVersion)}${modifiedText}`);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
if (!lockFileExists) {
|
|
111
|
+
console.log(chalk.yellow("ā ļø No lock file found. Lock file will be created when you run:"));
|
|
112
|
+
console.log(chalk.gray(` voidui add ${component}\n`));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
console.log(chalk.yellow(`ā ļø Component "${component}" not found in lock file. Install with:`));
|
|
116
|
+
console.log(chalk.gray(` voidui add ${component}\n`));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
console.log(`Latest: ${chalk.bold(latestVersion)}`);
|
|
120
|
+
// 6. Show code diff if requested
|
|
121
|
+
if (options.code) {
|
|
122
|
+
console.log(chalk.blue(`\nš Comparing ${installedVersion || "local"} ā ${latestVersion}\n`));
|
|
123
|
+
// Read local file content
|
|
124
|
+
const localContent = await readFile(location.path, "utf-8");
|
|
125
|
+
// Get latest version content from registry
|
|
126
|
+
const latestContent = extractComponentCode(registryItem);
|
|
127
|
+
// Generate and display diff
|
|
128
|
+
const diff = formatDiff(localContent, latestContent, `${component}@${installedVersion || "local"}.tsx`, `${component}@${latestVersion}.tsx`);
|
|
129
|
+
console.log(diff);
|
|
130
|
+
console.log(""); // Empty line
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// 7. Show changelog summary if versions differ
|
|
134
|
+
if (installedVersion && installedVersion !== latestVersion) {
|
|
135
|
+
console.log(chalk.blue(`\nChanges from ${installedVersion} to ${latestVersion}:\n`));
|
|
136
|
+
const relevantEntries = getEntriesBetweenVersions(versioning.changelog.entries, installedVersion, latestVersion);
|
|
137
|
+
if (relevantEntries.length > 0) {
|
|
138
|
+
for (const entry of relevantEntries) {
|
|
139
|
+
console.log(formatChangelogSummary(entry));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.log(chalk.gray("No changelog entries found."));
|
|
144
|
+
}
|
|
145
|
+
console.log(chalk.gray(`\nRun \`voidui diff ${component} --code\` to see code changes`));
|
|
146
|
+
console.log(chalk.gray(`Run \`voidui update ${component}\` to update to latest`));
|
|
147
|
+
}
|
|
148
|
+
else if (!installedVersion) {
|
|
149
|
+
// Show latest version info
|
|
150
|
+
console.log(chalk.blue("\nLatest changes:\n"));
|
|
151
|
+
const latestEntry = versioning.changelog.entries[0];
|
|
152
|
+
if (latestEntry) {
|
|
153
|
+
console.log(formatChangelogSummary(latestEntry));
|
|
154
|
+
}
|
|
155
|
+
console.log(chalk.gray(`\nRun \`voidui diff ${component} --code\` to see code`));
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// Versions are the same
|
|
159
|
+
console.log(chalk.green("\nā You're on the latest version!"));
|
|
160
|
+
if (isModified) {
|
|
161
|
+
console.log(chalk.yellow("\nNote: Your local file has been modified from the installed version."));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
console.log(""); // Empty line
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Mode 3: Compare two registry versions
|
|
168
|
+
*/
|
|
169
|
+
async function compareRegistryVersions(component, fromVersion, toVersion, registryUrl) {
|
|
170
|
+
console.log(chalk.blue(`\nš Comparing ${component} ${fromVersion} ā ${toVersion}\n`));
|
|
171
|
+
// Fetch registry metadata
|
|
172
|
+
const registryItem = await fetchRegistryItem(component, registryUrl);
|
|
173
|
+
if (!registryItem) {
|
|
174
|
+
console.error(chalk.red(`ā Component "${component}" not found in registry`));
|
|
175
|
+
console.error(chalk.gray(` Registry: ${registryUrl}`));
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
const versioning = registryItem.meta?.versioning;
|
|
179
|
+
if (!versioning) {
|
|
180
|
+
console.error(chalk.red(`ā Component "${component}" does not have version tracking enabled`));
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
// Validate versions exist
|
|
184
|
+
const availableVersions = versioning.availableVersions;
|
|
185
|
+
if (!availableVersions.includes(fromVersion)) {
|
|
186
|
+
console.error(chalk.red(`ā Version "${fromVersion}" not found for ${component}`));
|
|
187
|
+
console.error(chalk.gray(` Available: ${availableVersions.join(", ")}`));
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
if (!availableVersions.includes(toVersion)) {
|
|
191
|
+
console.error(chalk.red(`ā Version "${toVersion}" not found for ${component}`));
|
|
192
|
+
console.error(chalk.gray(` Available: ${availableVersions.join(", ")}`));
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
// Show changelog between versions
|
|
196
|
+
console.log(chalk.blue(`Changes from ${fromVersion} to ${toVersion}:\n`));
|
|
197
|
+
const changelog = formatChangelog(versioning.changelog.entries, fromVersion, toVersion);
|
|
198
|
+
console.log(changelog);
|
|
199
|
+
// Try to fetch code for both versions and show diff
|
|
200
|
+
const fromContent = await fetchComponentVersion(component, fromVersion, registryUrl);
|
|
201
|
+
const toContent = await fetchComponentVersion(component, toVersion, registryUrl);
|
|
202
|
+
if (fromContent && toContent) {
|
|
203
|
+
console.log(chalk.blue("\nš Code changes:\n"));
|
|
204
|
+
const diff = formatDiff(fromContent, toContent, `${component}@${fromVersion}.tsx`, `${component}@${toVersion}.tsx`);
|
|
205
|
+
console.log(diff);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// Show note about why we can't show code diff
|
|
209
|
+
console.log(chalk.gray("\nNote: Code diff for historical versions is not yet supported in production."));
|
|
210
|
+
console.log(chalk.gray(" This feature requires registry infrastructure for version endpoints."));
|
|
211
|
+
if (registryUrl.includes("localhost")) {
|
|
212
|
+
console.log(chalk.yellow("\n For local testing, make sure version files exist at:"));
|
|
213
|
+
console.log(chalk.gray(` registry/components/ui/${component}.versions/${fromVersion}.tsx`));
|
|
214
|
+
console.log(chalk.gray(` registry/components/ui/${component}.versions/${toVersion}.tsx`));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
console.log(""); // Empty line
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get changelog entries between two versions
|
|
221
|
+
*/
|
|
222
|
+
function getEntriesBetweenVersions(entries, fromVersion, toVersion) {
|
|
223
|
+
const fromIndex = entries.findIndex((e) => e.version === fromVersion);
|
|
224
|
+
const toIndex = entries.findIndex((e) => e.version === toVersion);
|
|
225
|
+
if (fromIndex === -1 || toIndex === -1) {
|
|
226
|
+
return [];
|
|
227
|
+
}
|
|
228
|
+
// Entries are sorted newest first, extract range between versions
|
|
229
|
+
const start = Math.min(fromIndex, toIndex);
|
|
230
|
+
const end = Math.max(fromIndex, toIndex);
|
|
231
|
+
// Return entries in chronological order (oldest first)
|
|
232
|
+
return entries.slice(start, end + 1).reverse();
|
|
233
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/commands/snapshot.ts"],"names":[],"mappings":"AAqBA,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAqHf"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { componentChangelogSchema } from "../validators/changelog.js";
|
|
4
|
+
import { fileExists, readJsonFile, writeJsonFile, copyFile, ensureDir, } from "../utils/file-operations.js";
|
|
5
|
+
import { promptForChange, promptForMore, promptForBreaking, } from "../utils/prompts.js";
|
|
6
|
+
export async function snapshotCommand(component, version) {
|
|
7
|
+
console.log(chalk.blue(`\nšø Creating snapshot for ${component}@${version}\n`));
|
|
8
|
+
// 1. Validate paths
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
// Detect if running from monorepo root or www-docs
|
|
11
|
+
let registryPath;
|
|
12
|
+
if (await fileExists(path.join(cwd, "registry/components/ui"))) {
|
|
13
|
+
// Running from www-docs
|
|
14
|
+
registryPath = path.join(cwd, "registry/components/ui");
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
// Running from monorepo root
|
|
18
|
+
registryPath = path.join(cwd, "apps/www-docs/registry/components/ui");
|
|
19
|
+
}
|
|
20
|
+
const componentFile = path.join(registryPath, `${component}.tsx`);
|
|
21
|
+
const changelogFile = path.join(registryPath, `${component}.changelog.json`);
|
|
22
|
+
const versionsDir = path.join(registryPath, `${component}.versions`);
|
|
23
|
+
// 2. Check component exists
|
|
24
|
+
if (!(await fileExists(componentFile))) {
|
|
25
|
+
console.error(chalk.red(`ā Component "${component}" not found`));
|
|
26
|
+
console.error(chalk.gray(` Expected: ${componentFile}`));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
// 3. Validate version format
|
|
30
|
+
if (!/^\d+\.\d+\.\d+$/.test(version)) {
|
|
31
|
+
console.error(chalk.red("ā Version must be in semver format (e.g., 1.0.0)"));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// 4. Create versions directory if needed
|
|
35
|
+
await ensureDir(versionsDir);
|
|
36
|
+
// 5. Check if version already exists
|
|
37
|
+
const versionFile = path.join(versionsDir, `${version}.tsx`);
|
|
38
|
+
if (await fileExists(versionFile)) {
|
|
39
|
+
console.error(chalk.red(`ā Version ${version} already exists`));
|
|
40
|
+
console.error(chalk.gray(` File: ${versionFile}`));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
// 6. Copy component file to versions folder
|
|
44
|
+
await copyFile(componentFile, versionFile);
|
|
45
|
+
console.log(chalk.green(`ā Created snapshot: ${versionFile}`));
|
|
46
|
+
// 7. Interactive changelog entry
|
|
47
|
+
console.log(chalk.blue("\nš Creating changelog entry...\n"));
|
|
48
|
+
const changeEntries = [];
|
|
49
|
+
let addMore = true;
|
|
50
|
+
while (addMore) {
|
|
51
|
+
const change = await promptForChange();
|
|
52
|
+
if (!change) {
|
|
53
|
+
console.error(chalk.red("\nā Cancelled"));
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
changeEntries.push(change);
|
|
57
|
+
if (changeEntries.length > 0) {
|
|
58
|
+
addMore = await promptForMore("Add another change?");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const breaking = await promptForBreaking();
|
|
62
|
+
// 8. Update or create changelog file
|
|
63
|
+
let changelog = await readJsonFile(changelogFile);
|
|
64
|
+
if (!changelog) {
|
|
65
|
+
// Create new changelog
|
|
66
|
+
changelog = {
|
|
67
|
+
component,
|
|
68
|
+
currentVersion: version,
|
|
69
|
+
entries: [],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// 9. Add new entry to the beginning
|
|
73
|
+
const newEntry = {
|
|
74
|
+
version,
|
|
75
|
+
date: new Date().toISOString(),
|
|
76
|
+
changes: changeEntries,
|
|
77
|
+
...(breaking && { breaking }),
|
|
78
|
+
};
|
|
79
|
+
changelog.entries.unshift(newEntry);
|
|
80
|
+
changelog.currentVersion = version;
|
|
81
|
+
// 10. Validate with Zod schema
|
|
82
|
+
try {
|
|
83
|
+
componentChangelogSchema.parse(changelog);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error(chalk.red("\nā Changelog validation failed:"));
|
|
87
|
+
console.error(error);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
// 11. Write changelog
|
|
91
|
+
await writeJsonFile(changelogFile, changelog);
|
|
92
|
+
console.log(chalk.green(`\nā Updated changelog: ${changelogFile}`));
|
|
93
|
+
// 12. Success message with next steps
|
|
94
|
+
console.log(chalk.blue("\n⨠Snapshot created successfully!\n"));
|
|
95
|
+
console.log(chalk.gray("Next steps:"));
|
|
96
|
+
console.log(chalk.gray(" 1. Review the changes"));
|
|
97
|
+
console.log(chalk.gray(" 2. Run: pnpm registry:build"));
|
|
98
|
+
console.log(chalk.gray(" 3. Commit the changes\n"));
|
|
99
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command implementation
|
|
3
|
+
* Updates components with smart merge support
|
|
4
|
+
*/
|
|
5
|
+
interface UpdateCommandOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Force overwrite (lose local changes)
|
|
8
|
+
*/
|
|
9
|
+
force?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Automatically attempt 3-way merge
|
|
12
|
+
*/
|
|
13
|
+
merge?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Registry URL
|
|
16
|
+
*/
|
|
17
|
+
registry?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Main update command handler
|
|
21
|
+
* Updates a component to latest version with merge support
|
|
22
|
+
*
|
|
23
|
+
* @param component - Component name
|
|
24
|
+
* @param options - Command options
|
|
25
|
+
*/
|
|
26
|
+
export declare function updateCommand(component: string | undefined, options: UpdateCommandOptions): Promise<void>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAsBH,UAAU,oBAAoB;IAC5B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Qf"}
|