@dalexto/lexsys-cli 0.0.3 → 0.0.4
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 +8 -15
- package/dist/commands/reset.d.ts +1 -0
- package/dist/config/config.d.ts +1 -1
- package/dist/config/installed.d.ts +6 -0
- package/dist/index.js +406 -276
- package/dist/install/component-drift.d.ts +4 -0
- package/dist/install/update-engine.d.ts +3 -1
- package/dist/registry/closure.d.ts +1 -1
- package/package.json +2 -2
- package/dist/utils/version.d.ts +0 -2
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
**Source of truth for:** Package role, command surface, core module boundaries
|
|
6
6
|
**Full CLI reference:** [docs/reference/cli/CLI.md](../../docs/reference/cli/CLI.md)
|
|
7
7
|
**Verified against:** `packages/cli/src/`
|
|
8
|
+
**Last reviewed:** 2026-05-30
|
|
8
9
|
|
|
9
10
|
---
|
|
10
11
|
|
|
@@ -50,8 +51,9 @@ Installed via `npm install -g lexsys` or run directly with `pnpm exec lexsys`.
|
|
|
50
51
|
| `lexsys init next [directory]` | Scaffold a new Next.js App Router consumer (pinned Next.js 15.3.3) |
|
|
51
52
|
| `lexsys add <component>` | Install one or more components into the consumer project |
|
|
52
53
|
| `lexsys update [component]` | Update installed components; `--sync`, `--utilities`, `--styles`, `--force` |
|
|
54
|
+
| `lexsys reset [component]` | Restore components from registry templates (backup + overwrite) |
|
|
53
55
|
| `lexsys list` | List available registry components |
|
|
54
|
-
| `lexsys status` | Show installed
|
|
56
|
+
| `lexsys status` | Show installed components and template drift vs registry |
|
|
55
57
|
| `lexsys doctor` | Check project health and config validity |
|
|
56
58
|
| `lexsys config` | Read or modify `lexsys.config.json` |
|
|
57
59
|
| `lexsys registry` | Inspect the active registry source |
|
|
@@ -69,20 +71,11 @@ Installed via `npm install -g lexsys` or run directly with `pnpm exec lexsys`.
|
|
|
69
71
|
|
|
70
72
|
---
|
|
71
73
|
|
|
72
|
-
##
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
| `core/installer.ts` | File copy, conflict detection, idempotent installs |
|
|
78
|
-
| `core/registry-provider.ts` | Selects local vs remote registry source |
|
|
79
|
-
| `core/registry-resolver.ts` | Resolves registry items, utilities, and styles from active registry |
|
|
80
|
-
| `core/tailwind-setup.ts` | Detects and wires Tailwind v4 CSS entrypoint |
|
|
81
|
-
| `core/vite-scaffold.ts` | Detects and patches Vite config for Tailwind plugin |
|
|
82
|
-
| `core/package-manager.ts` | Detects npm/pnpm/yarn and runs installs |
|
|
83
|
-
| `core/context.ts` | Process-level `cwd` override via `--cwd` flag |
|
|
84
|
-
| `core/flags.ts` | Shared flag parsing utilities |
|
|
85
|
-
| `core/cli-error.ts` | Typed CLI error class and top-level error handler |
|
|
74
|
+
## Source layout
|
|
75
|
+
|
|
76
|
+
Domain modules under `packages/cli/src/`: `config/`, `install/`, `registry/`,
|
|
77
|
+
`commands/`, `scaffold/`, `utils/`. Command behavior and config schema:
|
|
78
|
+
[docs/reference/cli/CLI.md](../../docs/reference/cli/CLI.md).
|
|
86
79
|
|
|
87
80
|
---
|
|
88
81
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const runReset: (args: string[]) => Promise<void>;
|
package/dist/config/config.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface LexsysConfig {
|
|
|
15
15
|
paths: LexsysPathsConfig;
|
|
16
16
|
aliases: LexsysAliasesConfig;
|
|
17
17
|
tailwind: LexsysTailwindConfig;
|
|
18
|
-
installed?:
|
|
18
|
+
installed?: string[];
|
|
19
19
|
registryUrl?: string | null;
|
|
20
20
|
}
|
|
21
21
|
export interface LexsysTailwindConfig {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const isLegacyInstalledRecord: (value: unknown) => boolean;
|
|
2
|
+
export declare const normalizeInstalled: (value: unknown) => string[];
|
|
3
|
+
export declare const isInstalled: (installed: string[], name: string) => boolean;
|
|
4
|
+
export declare const addInstalled: (installed: string[], itemName: string) => string[];
|
|
5
|
+
export declare const removeInstalled: (installed: string[], itemName: string) => string[];
|
|
6
|
+
export declare const findInstalledKey: (installed: string[], name: string) => string | undefined;
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,38 @@ var handleCliError = (error) => {
|
|
|
37
37
|
}
|
|
38
38
|
process.exit(1);
|
|
39
39
|
};
|
|
40
|
+
|
|
41
|
+
// src/config/installed.ts
|
|
42
|
+
var isLegacyInstalledRecord = (value) => {
|
|
43
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
44
|
+
};
|
|
45
|
+
var normalizeInstalled = (value) => {
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
return value.filter((entry) => typeof entry === "string");
|
|
48
|
+
}
|
|
49
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
50
|
+
return Object.keys(value);
|
|
51
|
+
}
|
|
52
|
+
return [];
|
|
53
|
+
};
|
|
54
|
+
var isInstalled = (installed, name) => {
|
|
55
|
+
const normalized = name.toLowerCase();
|
|
56
|
+
return installed.some((entry) => entry.toLowerCase() === normalized);
|
|
57
|
+
};
|
|
58
|
+
var addInstalled = (installed, itemName) => {
|
|
59
|
+
if (isInstalled(installed, itemName)) {
|
|
60
|
+
return installed;
|
|
61
|
+
}
|
|
62
|
+
return [...installed, itemName];
|
|
63
|
+
};
|
|
64
|
+
var removeInstalled = (installed, itemName) => {
|
|
65
|
+
const normalized = itemName.toLowerCase();
|
|
66
|
+
return installed.filter((entry) => entry.toLowerCase() !== normalized);
|
|
67
|
+
};
|
|
68
|
+
var findInstalledKey = (installed, name) => {
|
|
69
|
+
const normalized = name.toLowerCase();
|
|
70
|
+
return installed.find((entry) => entry.toLowerCase() === normalized);
|
|
71
|
+
};
|
|
40
72
|
var hashContent = (content) => {
|
|
41
73
|
return createHash("sha256").update(content, "utf-8").digest("hex");
|
|
42
74
|
};
|
|
@@ -98,7 +130,7 @@ var defaultConfig = {
|
|
|
98
130
|
paths: defaultPathsConfig,
|
|
99
131
|
aliases: defaultAliasesConfig,
|
|
100
132
|
tailwind: defaultTailwindConfig,
|
|
101
|
-
installed:
|
|
133
|
+
installed: [],
|
|
102
134
|
registryUrl: null
|
|
103
135
|
};
|
|
104
136
|
var getConfigPath = () => {
|
|
@@ -111,7 +143,7 @@ var loadConfig = async () => {
|
|
|
111
143
|
}
|
|
112
144
|
const content = await readFile(configPath, "utf-8");
|
|
113
145
|
const parsed = JSON.parse(content);
|
|
114
|
-
|
|
146
|
+
const config = {
|
|
115
147
|
...defaultConfig,
|
|
116
148
|
...parsed,
|
|
117
149
|
paths: {
|
|
@@ -125,8 +157,13 @@ var loadConfig = async () => {
|
|
|
125
157
|
tailwind: {
|
|
126
158
|
...defaultTailwindConfig,
|
|
127
159
|
...parsed.tailwind
|
|
128
|
-
}
|
|
160
|
+
},
|
|
161
|
+
installed: normalizeInstalled(parsed.installed)
|
|
129
162
|
};
|
|
163
|
+
if (isLegacyInstalledRecord(parsed.installed)) {
|
|
164
|
+
await saveConfig(config);
|
|
165
|
+
}
|
|
166
|
+
return config;
|
|
130
167
|
};
|
|
131
168
|
var saveConfig = async (config) => {
|
|
132
169
|
const configPath = getConfigPath();
|
|
@@ -738,7 +775,7 @@ var isRegistryItem = (value) => {
|
|
|
738
775
|
return false;
|
|
739
776
|
}
|
|
740
777
|
const item = value;
|
|
741
|
-
return typeof item.name === "string" && typeof item.canonicalName === "string" && typeof item.
|
|
778
|
+
return typeof item.name === "string" && typeof item.canonicalName === "string" && typeof item.type === "string" && typeof item.category === "string" && Array.isArray(item.aliases) && isStringArray(item.files) && isStringArray(item.dependencies) && isStringArray(item.registryDependencies) && isStringArray(item.utilities) && isStringArray(item.styles) && typeof item.target === "string";
|
|
742
779
|
};
|
|
743
780
|
var findInvalidRegistryItemIndex = (items) => {
|
|
744
781
|
return items.findIndex((item) => {
|
|
@@ -1106,7 +1143,7 @@ var runAdd = async (args2) => {
|
|
|
1106
1143
|
console.log("Dry run: no files or dependencies will be changed.\n");
|
|
1107
1144
|
console.log("Components:");
|
|
1108
1145
|
for (const item of resolvedItems) {
|
|
1109
|
-
console.log(`- ${item.canonicalName}
|
|
1146
|
+
console.log(`- ${item.canonicalName}`);
|
|
1110
1147
|
}
|
|
1111
1148
|
console.log("\nDependencies:");
|
|
1112
1149
|
for (const dependency of dependencies) {
|
|
@@ -1145,11 +1182,9 @@ var runAdd = async (args2) => {
|
|
|
1145
1182
|
}
|
|
1146
1183
|
console.log("");
|
|
1147
1184
|
}
|
|
1148
|
-
|
|
1149
|
-
...config.installed ?? {}
|
|
1150
|
-
};
|
|
1185
|
+
let installed = [...config.installed ?? []];
|
|
1151
1186
|
for (const item of successfullyInstalled) {
|
|
1152
|
-
installed
|
|
1187
|
+
installed = addInstalled(installed, item.name);
|
|
1153
1188
|
}
|
|
1154
1189
|
await saveConfig({
|
|
1155
1190
|
...config,
|
|
@@ -1253,13 +1288,13 @@ var runDoctor = async (options = {}) => {
|
|
|
1253
1288
|
if (registryFailed && options.noFallback) {
|
|
1254
1289
|
return;
|
|
1255
1290
|
}
|
|
1256
|
-
const installed = config.installed ??
|
|
1257
|
-
if (
|
|
1291
|
+
const installed = config.installed ?? [];
|
|
1292
|
+
if (installed.length) {
|
|
1258
1293
|
console.log("\nTracked components:");
|
|
1259
|
-
for (const
|
|
1294
|
+
for (const name of installed) {
|
|
1260
1295
|
const item = await findItem(name);
|
|
1261
1296
|
if (!item) {
|
|
1262
|
-
console.log(`\xD7 ${name}
|
|
1297
|
+
console.log(`\xD7 ${name} (missing from registry)`);
|
|
1263
1298
|
continue;
|
|
1264
1299
|
}
|
|
1265
1300
|
const componentPath = join(
|
|
@@ -1268,9 +1303,7 @@ var runDoctor = async (options = {}) => {
|
|
|
1268
1303
|
);
|
|
1269
1304
|
const exists = await fileExists(componentPath);
|
|
1270
1305
|
const layer = getInstallLayer(item) ?? "unknown";
|
|
1271
|
-
console.log(
|
|
1272
|
-
`${exists ? "\u2713" : "\xD7"} ${item.canonicalName} v${version} (${layer})`
|
|
1273
|
-
);
|
|
1306
|
+
console.log(`${exists ? "\u2713" : "\xD7"} ${item.canonicalName} (${layer})`);
|
|
1274
1307
|
}
|
|
1275
1308
|
}
|
|
1276
1309
|
};
|
|
@@ -1324,7 +1357,7 @@ Options
|
|
|
1324
1357
|
--all, -a Update all tracked components
|
|
1325
1358
|
--styles, -S Update generated token/theme CSS files
|
|
1326
1359
|
--utilities, -u Update shared utility files
|
|
1327
|
-
--sync Refresh tracked components even when
|
|
1360
|
+
--sync Refresh tracked components even when templates already match
|
|
1328
1361
|
--dry-run, -d Preview update without writing files
|
|
1329
1362
|
--force, -f Write conflicted updates after creating backups
|
|
1330
1363
|
--yes, -y Auto-confirm safe prompts
|
|
@@ -1363,6 +1396,24 @@ Usage
|
|
|
1363
1396
|
Options
|
|
1364
1397
|
--no-fallback Fail instead of falling back to local registry
|
|
1365
1398
|
--help, -h Show this help
|
|
1399
|
+
`,
|
|
1400
|
+
reset: `
|
|
1401
|
+
Usage
|
|
1402
|
+
lexsys reset [component...]
|
|
1403
|
+
|
|
1404
|
+
Options
|
|
1405
|
+
--dry-run, -d Preview reset without writing files
|
|
1406
|
+
--with-deps, -w Also reset installed registry dependencies in the closure
|
|
1407
|
+
--no-fallback Fail instead of falling back to local registry
|
|
1408
|
+
--cwd, -C <path> Run from a different project directory
|
|
1409
|
+
--help, -h Show this help
|
|
1410
|
+
|
|
1411
|
+
Run without arguments for guided reset picker.
|
|
1412
|
+
|
|
1413
|
+
Examples
|
|
1414
|
+
lexsys reset button
|
|
1415
|
+
lexsys reset sidebar --with-deps
|
|
1416
|
+
lexsys reset button --dry-run
|
|
1366
1417
|
`,
|
|
1367
1418
|
uninstall: `
|
|
1368
1419
|
Usage
|
|
@@ -1445,6 +1496,7 @@ Scaffold
|
|
|
1445
1496
|
Components
|
|
1446
1497
|
add <component...> Install components into your project [alias: a]
|
|
1447
1498
|
update [component...] Update installed components [alias: up]
|
|
1499
|
+
reset [component...] Restore components from registry templates
|
|
1448
1500
|
uninstall [component...] Remove installed components [alias: rm]
|
|
1449
1501
|
|
|
1450
1502
|
Inspect
|
|
@@ -2387,7 +2439,6 @@ var runList = async (options = {}) => {
|
|
|
2387
2439
|
const simplified = registryItems3.map((item) => ({
|
|
2388
2440
|
name: item.name,
|
|
2389
2441
|
canonicalName: item.canonicalName,
|
|
2390
|
-
version: item.version,
|
|
2391
2442
|
category: item.category,
|
|
2392
2443
|
layer: getInstallLayer(item)
|
|
2393
2444
|
}));
|
|
@@ -2409,9 +2460,7 @@ var runList = async (options = {}) => {
|
|
|
2409
2460
|
}
|
|
2410
2461
|
console.log(`${layerLabels[layer] ?? layer}:`);
|
|
2411
2462
|
for (const item of items) {
|
|
2412
|
-
console.log(
|
|
2413
|
-
`- ${item.canonicalName} v${item.version} (${item.category})`
|
|
2414
|
-
);
|
|
2463
|
+
console.log(`- ${item.canonicalName} (${item.category})`);
|
|
2415
2464
|
}
|
|
2416
2465
|
console.log("");
|
|
2417
2466
|
}
|
|
@@ -2435,7 +2484,7 @@ var printRegistrySummary = (result) => {
|
|
|
2435
2484
|
for (const item of result.items) {
|
|
2436
2485
|
const remoteFileCount = item.remoteFiles?.length ?? 0;
|
|
2437
2486
|
console.log(
|
|
2438
|
-
`- ${item.canonicalName}
|
|
2487
|
+
`- ${item.canonicalName} (${item.type}/${item.category}, remote files: ${remoteFileCount})`
|
|
2439
2488
|
);
|
|
2440
2489
|
}
|
|
2441
2490
|
};
|
|
@@ -2521,12 +2570,43 @@ var runRegistry = async (options = {}) => {
|
|
|
2521
2570
|
process.exitCode = 1;
|
|
2522
2571
|
}
|
|
2523
2572
|
};
|
|
2573
|
+
var getComponentDriftStatus = async (name) => {
|
|
2574
|
+
const item = await findItem(name);
|
|
2575
|
+
if (!item) {
|
|
2576
|
+
return "missing";
|
|
2577
|
+
}
|
|
2578
|
+
const hasDrift = await itemHasTemplateDrift(item);
|
|
2579
|
+
return hasDrift ? "drift" : "in-sync";
|
|
2580
|
+
};
|
|
2581
|
+
var itemHasTemplateDrift = async (item) => {
|
|
2582
|
+
const config = await loadConfig();
|
|
2583
|
+
const installTarget = resolveItemInstallTarget(config, item);
|
|
2584
|
+
for (const file of item.files) {
|
|
2585
|
+
const fileName = file.split("/").at(-1);
|
|
2586
|
+
if (!fileName) {
|
|
2587
|
+
return true;
|
|
2588
|
+
}
|
|
2589
|
+
const targetPath = join(getCwd(), installTarget, fileName);
|
|
2590
|
+
if (!await fileExists(targetPath)) {
|
|
2591
|
+
return true;
|
|
2592
|
+
}
|
|
2593
|
+
const preparedContent = prepareInstalledFileContent(
|
|
2594
|
+
await readFile(getRegistryTemplatePath(file), "utf-8"),
|
|
2595
|
+
item
|
|
2596
|
+
);
|
|
2597
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
2598
|
+
if (!hashesAreEqual(preparedContent, targetContent)) {
|
|
2599
|
+
return true;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
return false;
|
|
2603
|
+
};
|
|
2524
2604
|
|
|
2525
2605
|
// src/commands/status.ts
|
|
2526
2606
|
var runStatus = async (options = {}) => {
|
|
2527
2607
|
const config = await loadConfig();
|
|
2528
|
-
const installed = config.installed ??
|
|
2529
|
-
if (!
|
|
2608
|
+
const installed = config.installed ?? [];
|
|
2609
|
+
if (!installed.length) {
|
|
2530
2610
|
console.log("No Lexsys components are currently tracked.");
|
|
2531
2611
|
return;
|
|
2532
2612
|
}
|
|
@@ -2541,14 +2621,15 @@ var runStatus = async (options = {}) => {
|
|
|
2541
2621
|
return;
|
|
2542
2622
|
}
|
|
2543
2623
|
console.log("Installed Lexsys components:\n");
|
|
2544
|
-
for (const
|
|
2624
|
+
for (const name of installed) {
|
|
2545
2625
|
const item = await findItem(name);
|
|
2546
2626
|
if (!item) {
|
|
2547
|
-
console.log(`- ${name}
|
|
2627
|
+
console.log(`- ${name} (missing from registry)`);
|
|
2548
2628
|
continue;
|
|
2549
2629
|
}
|
|
2550
|
-
const
|
|
2551
|
-
|
|
2630
|
+
const driftStatus = await getComponentDriftStatus(name);
|
|
2631
|
+
const status = driftStatus === "drift" ? "out of sync with registry" : "up to date with registry";
|
|
2632
|
+
console.log(`- ${item.canonicalName} (${status})`);
|
|
2552
2633
|
}
|
|
2553
2634
|
};
|
|
2554
2635
|
|
|
@@ -2573,7 +2654,7 @@ var computeRegistryClosure = (rootNames, items) => {
|
|
|
2573
2654
|
return closure;
|
|
2574
2655
|
};
|
|
2575
2656
|
var findOrphanInstalledItems = (removedTargetNames, remainingInstalled, items) => {
|
|
2576
|
-
const remainingNames =
|
|
2657
|
+
const remainingNames = remainingInstalled;
|
|
2577
2658
|
const removedDependencyNames = /* @__PURE__ */ new Set();
|
|
2578
2659
|
for (const removedName of removedTargetNames) {
|
|
2579
2660
|
const closure = computeRegistryClosure([removedName], items);
|
|
@@ -2592,17 +2673,263 @@ var findOrphanInstalledItems = (removedTargetNames, remainingInstalled, items) =
|
|
|
2592
2673
|
)
|
|
2593
2674
|
).filter((item) => Boolean(item)).filter((item) => removedDependencyNames.has(item.name));
|
|
2594
2675
|
};
|
|
2676
|
+
var checkItemFiles = async (name) => {
|
|
2677
|
+
const item = await findItem(name);
|
|
2678
|
+
const config = await loadConfig();
|
|
2679
|
+
if (!item) {
|
|
2680
|
+
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
const installTarget = resolveItemInstallTarget(config, item);
|
|
2684
|
+
console.log(`File check for ${item.canonicalName}:`);
|
|
2685
|
+
for (const file of item.files) {
|
|
2686
|
+
const fileName = file.split("/").at(-1);
|
|
2687
|
+
if (!fileName) {
|
|
2688
|
+
console.log(`- invalid registry file path: ${file}`);
|
|
2689
|
+
continue;
|
|
2690
|
+
}
|
|
2691
|
+
const targetPath = join(getCwd(), installTarget, fileName);
|
|
2692
|
+
if (!await fileExists(targetPath)) {
|
|
2693
|
+
console.log(`- missing: ${targetPath}`);
|
|
2694
|
+
continue;
|
|
2695
|
+
}
|
|
2696
|
+
const preparedContent = prepareInstalledFileContent(
|
|
2697
|
+
await readFile(getRegistryTemplatePath(file), "utf-8"),
|
|
2698
|
+
item
|
|
2699
|
+
);
|
|
2700
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
2701
|
+
if (hashesAreEqual(preparedContent, targetContent)) {
|
|
2702
|
+
console.log(`- identical: ${targetPath}`);
|
|
2703
|
+
continue;
|
|
2704
|
+
}
|
|
2705
|
+
console.log(`- conflict: ${targetPath}`);
|
|
2706
|
+
}
|
|
2707
|
+
};
|
|
2708
|
+
var applyItemOverwrite = async (name, force) => {
|
|
2709
|
+
const item = await findItem(name);
|
|
2710
|
+
const config = await loadConfig();
|
|
2711
|
+
if (!item) {
|
|
2712
|
+
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2713
|
+
return false;
|
|
2714
|
+
}
|
|
2715
|
+
const installTarget = resolveItemInstallTarget(config, item);
|
|
2716
|
+
let hasConflict = false;
|
|
2717
|
+
console.log(`Applying update for ${item.canonicalName}:`);
|
|
2718
|
+
for (const file of item.files) {
|
|
2719
|
+
const sourcePath = getRegistryTemplatePath(file);
|
|
2720
|
+
const fileName = file.split("/").at(-1);
|
|
2721
|
+
if (!fileName) {
|
|
2722
|
+
console.log(`- invalid registry file path: ${file}`);
|
|
2723
|
+
hasConflict = true;
|
|
2724
|
+
continue;
|
|
2725
|
+
}
|
|
2726
|
+
const targetPath = join(getCwd(), installTarget, fileName);
|
|
2727
|
+
const preparedContent = prepareInstalledFileContent(
|
|
2728
|
+
await readFile(sourcePath, "utf-8"),
|
|
2729
|
+
item
|
|
2730
|
+
);
|
|
2731
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
2732
|
+
if (!await fileExists(targetPath)) {
|
|
2733
|
+
await writeFile(targetPath, preparedContent, "utf-8");
|
|
2734
|
+
console.log(`- restored missing file: ${targetPath}`);
|
|
2735
|
+
continue;
|
|
2736
|
+
}
|
|
2737
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
2738
|
+
if (hashesAreEqual(preparedContent, targetContent)) {
|
|
2739
|
+
console.log(`- identical: ${targetPath}`);
|
|
2740
|
+
continue;
|
|
2741
|
+
}
|
|
2742
|
+
if (force) {
|
|
2743
|
+
const backupPath = await createBackupFile(targetPath);
|
|
2744
|
+
if (backupPath) {
|
|
2745
|
+
console.log(`- backup created: ${backupPath}`);
|
|
2746
|
+
}
|
|
2747
|
+
await writeFile(targetPath, preparedContent, "utf-8");
|
|
2748
|
+
console.log(`- force updated file: ${targetPath}`);
|
|
2749
|
+
continue;
|
|
2750
|
+
}
|
|
2751
|
+
hasConflict = true;
|
|
2752
|
+
console.log(`- conflict (user modified): ${targetPath}`);
|
|
2753
|
+
}
|
|
2754
|
+
if (hasConflict) {
|
|
2755
|
+
console.log(
|
|
2756
|
+
"Update finished with conflicts. Review conflicted files before retrying."
|
|
2757
|
+
);
|
|
2758
|
+
return false;
|
|
2759
|
+
}
|
|
2760
|
+
console.log(`\u2714 ${item.canonicalName} updated successfully`);
|
|
2761
|
+
return true;
|
|
2762
|
+
};
|
|
2763
|
+
var checkItemUpdate = async (name, dryRun, force, sync = false) => {
|
|
2764
|
+
const item = await findItem(name);
|
|
2765
|
+
if (!item) {
|
|
2766
|
+
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2767
|
+
return false;
|
|
2768
|
+
}
|
|
2769
|
+
const driftStatus = await getComponentDriftStatus(name);
|
|
2770
|
+
if (driftStatus === "missing") {
|
|
2771
|
+
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2772
|
+
return false;
|
|
2773
|
+
}
|
|
2774
|
+
const hasDrift = driftStatus === "drift";
|
|
2775
|
+
if (!sync && !hasDrift) {
|
|
2776
|
+
console.log(`${item.canonicalName} is up to date with the registry.`);
|
|
2777
|
+
return false;
|
|
2778
|
+
}
|
|
2779
|
+
if (sync && !hasDrift) {
|
|
2780
|
+
console.log(
|
|
2781
|
+
`${item.canonicalName} template sync (already matches registry)`
|
|
2782
|
+
);
|
|
2783
|
+
} else {
|
|
2784
|
+
console.log(`${item.canonicalName} can be updated from registry templates`);
|
|
2785
|
+
}
|
|
2786
|
+
if (dryRun) {
|
|
2787
|
+
console.log("\nChanged file candidates:");
|
|
2788
|
+
for (const file of item.files) {
|
|
2789
|
+
console.log(`~ ${file}`);
|
|
2790
|
+
}
|
|
2791
|
+
console.log("\nDry run: no files will be changed.");
|
|
2792
|
+
console.log("Update plan:");
|
|
2793
|
+
if (force) {
|
|
2794
|
+
console.log(
|
|
2795
|
+
"- Force mode requested: conflicted files require backup before overwrite"
|
|
2796
|
+
);
|
|
2797
|
+
console.log(
|
|
2798
|
+
"- Dry run: backups would be created before forced overwrites"
|
|
2799
|
+
);
|
|
2800
|
+
}
|
|
2801
|
+
console.log(`- Check installed files for ${item.canonicalName}`);
|
|
2802
|
+
console.log("- Compare existing files with registry templates");
|
|
2803
|
+
console.log("- Report conflicts before writing changes");
|
|
2804
|
+
console.log("- Never overwrite user-modified files silently");
|
|
2805
|
+
await checkItemFiles(name);
|
|
2806
|
+
return false;
|
|
2807
|
+
}
|
|
2808
|
+
return await applyItemOverwrite(name, force);
|
|
2809
|
+
};
|
|
2810
|
+
var resetItem = async (name, dryRun) => {
|
|
2811
|
+
const item = await findItem(name);
|
|
2812
|
+
if (!item) {
|
|
2813
|
+
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2814
|
+
return false;
|
|
2815
|
+
}
|
|
2816
|
+
const hasDrift = await itemHasTemplateDrift(item);
|
|
2817
|
+
if (!hasDrift) {
|
|
2818
|
+
console.log(`${item.canonicalName} already matches registry templates.`);
|
|
2819
|
+
return false;
|
|
2820
|
+
}
|
|
2821
|
+
if (dryRun) {
|
|
2822
|
+
console.log(`Reset plan for ${item.canonicalName}:`);
|
|
2823
|
+
console.log("- Backups would be created before overwriting changed files");
|
|
2824
|
+
console.log("- Files would be restored from registry templates");
|
|
2825
|
+
for (const file of item.files) {
|
|
2826
|
+
console.log(`~ ${file}`);
|
|
2827
|
+
}
|
|
2828
|
+
console.log("\nDry run: no files will be changed.");
|
|
2829
|
+
await checkItemFiles(name);
|
|
2830
|
+
return false;
|
|
2831
|
+
}
|
|
2832
|
+
return await applyItemOverwrite(name, true);
|
|
2833
|
+
};
|
|
2595
2834
|
|
|
2596
|
-
// src/commands/
|
|
2597
|
-
var
|
|
2598
|
-
|
|
2835
|
+
// src/commands/reset.ts
|
|
2836
|
+
var resolveInstalledKey = async (name, installed) => {
|
|
2837
|
+
const direct = findInstalledKey(installed, name);
|
|
2838
|
+
if (direct) {
|
|
2839
|
+
return direct;
|
|
2840
|
+
}
|
|
2841
|
+
const item = await findItem(name);
|
|
2842
|
+
if (!item) {
|
|
2843
|
+
return void 0;
|
|
2844
|
+
}
|
|
2845
|
+
return findInstalledKey(installed, item.name);
|
|
2846
|
+
};
|
|
2847
|
+
var runReset = async (args2) => {
|
|
2848
|
+
const dryRun = hasFlag(args2, "--dry-run", "-d");
|
|
2849
|
+
const withDeps = hasFlag(args2, "--with-deps", "-w");
|
|
2850
|
+
const noFallback = hasFlag(args2, "--no-fallback");
|
|
2851
|
+
const targetArgs = removeFlagsWithValues(
|
|
2852
|
+
removeFlags(args2, [
|
|
2853
|
+
"--dry-run",
|
|
2854
|
+
"-d",
|
|
2855
|
+
"--with-deps",
|
|
2856
|
+
"-w",
|
|
2857
|
+
"--no-fallback"
|
|
2858
|
+
]),
|
|
2859
|
+
["--cwd", "-C"]
|
|
2860
|
+
);
|
|
2861
|
+
const config = await loadConfig();
|
|
2862
|
+
const installed = [...config.installed ?? []];
|
|
2863
|
+
if (!installed.length) {
|
|
2864
|
+
console.log("No components installed.");
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
try {
|
|
2868
|
+
await getRegistryProviderResult({
|
|
2869
|
+
fallback: !noFallback
|
|
2870
|
+
});
|
|
2871
|
+
} catch (error) {
|
|
2872
|
+
console.log("Failed to resolve registry.");
|
|
2873
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
2874
|
+
process.exitCode = 1;
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
if (!targetArgs.length) {
|
|
2878
|
+
const selected = await promptMultiselect(
|
|
2879
|
+
"Select components to reset",
|
|
2880
|
+
installed.map((name) => ({ title: name, value: name })),
|
|
2881
|
+
{ min: 1 }
|
|
2882
|
+
);
|
|
2883
|
+
if (!selected.length) return;
|
|
2884
|
+
targetArgs.push(...selected);
|
|
2885
|
+
}
|
|
2886
|
+
const resetNames = /* @__PURE__ */ new Set();
|
|
2887
|
+
for (const name of targetArgs) {
|
|
2888
|
+
const installedKey = await resolveInstalledKey(name, installed);
|
|
2889
|
+
if (!installedKey) {
|
|
2890
|
+
console.log(`Component "${name}" is not tracked as installed.`);
|
|
2891
|
+
continue;
|
|
2892
|
+
}
|
|
2893
|
+
resetNames.add(installedKey);
|
|
2894
|
+
}
|
|
2895
|
+
if (!resetNames.size) {
|
|
2896
|
+
console.log("No installed components matched the request.");
|
|
2897
|
+
return;
|
|
2898
|
+
}
|
|
2899
|
+
if (withDeps) {
|
|
2900
|
+
const allItems = await getRegistryItems({ fallback: !noFallback });
|
|
2901
|
+
for (const rootName of [...resetNames]) {
|
|
2902
|
+
const closure = computeRegistryClosure([rootName], allItems);
|
|
2903
|
+
for (const dependencyName of closure) {
|
|
2904
|
+
if (isInstalled(installed, dependencyName)) {
|
|
2905
|
+
resetNames.add(dependencyName);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
const resetItems = await resolveRegistryItems([...resetNames], {
|
|
2911
|
+
fallback: !noFallback
|
|
2912
|
+
});
|
|
2913
|
+
if (dryRun) {
|
|
2914
|
+
console.log("Dry run: no files will be changed.\n");
|
|
2915
|
+
console.log("Components:");
|
|
2916
|
+
for (const item of resetItems) {
|
|
2917
|
+
console.log(`- ${item.canonicalName}`);
|
|
2918
|
+
}
|
|
2919
|
+
console.log("");
|
|
2920
|
+
}
|
|
2921
|
+
for (const item of resetItems) {
|
|
2922
|
+
await resetItem(item.name, dryRun);
|
|
2923
|
+
console.log("");
|
|
2924
|
+
}
|
|
2599
2925
|
};
|
|
2926
|
+
|
|
2927
|
+
// src/commands/uninstall.ts
|
|
2600
2928
|
var resolveInstalledItems = async (installed) => {
|
|
2601
|
-
|
|
2602
|
-
if (!names.length) {
|
|
2929
|
+
if (!installed.length) {
|
|
2603
2930
|
return [];
|
|
2604
2931
|
}
|
|
2605
|
-
return resolveRegistryItems(
|
|
2932
|
+
return resolveRegistryItems(installed);
|
|
2606
2933
|
};
|
|
2607
2934
|
var collectOrphanedSharedResources = (removedItems, remainingItems) => {
|
|
2608
2935
|
const removedUtilities = collectUtilities(removedItems);
|
|
@@ -2618,14 +2945,6 @@ var collectOrphanedSharedResources = (removedItems, remainingItems) => {
|
|
|
2618
2945
|
})
|
|
2619
2946
|
};
|
|
2620
2947
|
};
|
|
2621
|
-
var removeInstalledKey = (installed, itemName) => {
|
|
2622
|
-
const installedKey = Object.keys(installed).find((key) => {
|
|
2623
|
-
return normalizeInstalledKey(key) === normalizeInstalledKey(itemName);
|
|
2624
|
-
});
|
|
2625
|
-
if (installedKey) {
|
|
2626
|
-
delete installed[installedKey];
|
|
2627
|
-
}
|
|
2628
|
-
};
|
|
2629
2948
|
var runUninstall = async (args2) => {
|
|
2630
2949
|
const dryRun = hasFlag(args2, "--dry-run", "-d");
|
|
2631
2950
|
const withDeps = hasFlag(args2, "--with-deps", "-w");
|
|
@@ -2642,7 +2961,7 @@ var runUninstall = async (args2) => {
|
|
|
2642
2961
|
);
|
|
2643
2962
|
if (!targetArgs.length) {
|
|
2644
2963
|
const preConfig = await loadConfig();
|
|
2645
|
-
const installedNames =
|
|
2964
|
+
const installedNames = preConfig.installed ?? [];
|
|
2646
2965
|
if (installedNames.length === 0) {
|
|
2647
2966
|
console.log("No components installed.");
|
|
2648
2967
|
return;
|
|
@@ -2656,7 +2975,7 @@ var runUninstall = async (args2) => {
|
|
|
2656
2975
|
targetArgs.push(...selected);
|
|
2657
2976
|
}
|
|
2658
2977
|
const config = await loadConfig();
|
|
2659
|
-
const installed = config.installed ??
|
|
2978
|
+
const installed = [...config.installed ?? []];
|
|
2660
2979
|
const resolvedTargets = [];
|
|
2661
2980
|
const notTracked = [];
|
|
2662
2981
|
for (const name of targetArgs) {
|
|
@@ -2665,10 +2984,7 @@ var runUninstall = async (args2) => {
|
|
|
2665
2984
|
console.log(`Component "${name}" not found in registry.`);
|
|
2666
2985
|
continue;
|
|
2667
2986
|
}
|
|
2668
|
-
|
|
2669
|
-
return normalizeInstalledKey(key) === normalizeInstalledKey(item.name);
|
|
2670
|
-
});
|
|
2671
|
-
if (!installedKey) {
|
|
2987
|
+
if (!isInstalled(installed, item.name)) {
|
|
2672
2988
|
notTracked.push(name);
|
|
2673
2989
|
console.log(`Component "${name}" is not tracked as installed.`);
|
|
2674
2990
|
continue;
|
|
@@ -2681,10 +2997,11 @@ var runUninstall = async (args2) => {
|
|
|
2681
2997
|
}
|
|
2682
2998
|
return;
|
|
2683
2999
|
}
|
|
2684
|
-
const remainingInstalled =
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
3000
|
+
const remainingInstalled = installed.filter((entry) => {
|
|
3001
|
+
return !resolvedTargets.some((item) => {
|
|
3002
|
+
return findInstalledKey([entry], item.name) !== void 0;
|
|
3003
|
+
});
|
|
3004
|
+
});
|
|
2688
3005
|
const allItems = await getRegistryItems({ fallback: !noFallback });
|
|
2689
3006
|
const orphanItems = withDeps ? findOrphanInstalledItems(
|
|
2690
3007
|
resolvedTargets.map((item) => item.name),
|
|
@@ -2706,9 +3023,7 @@ var runUninstall = async (args2) => {
|
|
|
2706
3023
|
console.log("Dry run: no files will be removed.\n");
|
|
2707
3024
|
console.log("Components:");
|
|
2708
3025
|
for (const item of resolvedTargets) {
|
|
2709
|
-
console.log(
|
|
2710
|
-
`- ${item.canonicalName} v${installed[item.name] ?? "unknown"}`
|
|
2711
|
-
);
|
|
3026
|
+
console.log(`- ${item.canonicalName}`);
|
|
2712
3027
|
}
|
|
2713
3028
|
if (withDeps && orphanItems.length) {
|
|
2714
3029
|
console.log("\nOrphan registry items (--with-deps):");
|
|
@@ -2724,10 +3039,12 @@ var runUninstall = async (args2) => {
|
|
|
2724
3039
|
console.log(`- ${item.canonicalName}`);
|
|
2725
3040
|
}
|
|
2726
3041
|
}
|
|
2727
|
-
const postOrphanRemaining =
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
3042
|
+
const postOrphanRemaining = remainingInstalled.filter((entry) => {
|
|
3043
|
+
if (!withDeps) {
|
|
3044
|
+
return true;
|
|
3045
|
+
}
|
|
3046
|
+
return !orphanItems.some((item) => isInstalled([entry], item.name));
|
|
3047
|
+
});
|
|
2731
3048
|
const dryRunOrphans = collectOrphanedSharedResources(
|
|
2732
3049
|
allRemovalTargets,
|
|
2733
3050
|
await resolveInstalledItems(postOrphanRemaining)
|
|
@@ -2761,11 +3078,11 @@ var runUninstall = async (args2) => {
|
|
|
2761
3078
|
}
|
|
2762
3079
|
console.log("");
|
|
2763
3080
|
}
|
|
2764
|
-
|
|
3081
|
+
let updatedInstalled = [...installed];
|
|
2765
3082
|
for (const item of successfullyUninstalled) {
|
|
2766
|
-
|
|
3083
|
+
updatedInstalled = removeInstalled(updatedInstalled, item.name);
|
|
2767
3084
|
}
|
|
2768
|
-
const remainingItems = await resolveInstalledItems(
|
|
3085
|
+
const remainingItems = await resolveInstalledItems(updatedInstalled);
|
|
2769
3086
|
const orphanedResources = collectOrphanedSharedResources(
|
|
2770
3087
|
successfullyUninstalled,
|
|
2771
3088
|
remainingItems
|
|
@@ -2785,12 +3102,6 @@ var runUninstall = async (args2) => {
|
|
|
2785
3102
|
}
|
|
2786
3103
|
const utilitiesResult = await uninstallUtilities(resolvedUtilities, config);
|
|
2787
3104
|
const stylesResult = await uninstallStyles(resolvedStyles, config);
|
|
2788
|
-
const updatedInstalled = {
|
|
2789
|
-
...installed
|
|
2790
|
-
};
|
|
2791
|
-
for (const item of successfullyUninstalled) {
|
|
2792
|
-
removeInstalledKey(updatedInstalled, item.name);
|
|
2793
|
-
}
|
|
2794
3105
|
await saveConfig({
|
|
2795
3106
|
...config,
|
|
2796
3107
|
installed: updatedInstalled
|
|
@@ -2810,172 +3121,18 @@ var runUninstall = async (args2) => {
|
|
|
2810
3121
|
}
|
|
2811
3122
|
};
|
|
2812
3123
|
|
|
2813
|
-
// src/utils/version.ts
|
|
2814
|
-
var compareVersions = (a, b) => {
|
|
2815
|
-
const pa = a.split(".").map(Number);
|
|
2816
|
-
const pb = b.split(".").map(Number);
|
|
2817
|
-
const length = Math.max(pa.length, pb.length);
|
|
2818
|
-
for (let i = 0; i < length; i++) {
|
|
2819
|
-
const va = pa[i] ?? 0;
|
|
2820
|
-
const vb = pb[i] ?? 0;
|
|
2821
|
-
if (va > vb) return 1;
|
|
2822
|
-
if (va < vb) return -1;
|
|
2823
|
-
}
|
|
2824
|
-
return 0;
|
|
2825
|
-
};
|
|
2826
|
-
var isUpdateAvailable = (installed, latest) => {
|
|
2827
|
-
return compareVersions(latest, installed) === 1;
|
|
2828
|
-
};
|
|
2829
|
-
|
|
2830
|
-
// src/install/update-engine.ts
|
|
2831
|
-
var checkItemFiles = async (name) => {
|
|
2832
|
-
const item = await findItem(name);
|
|
2833
|
-
const config = await loadConfig();
|
|
2834
|
-
if (!item) {
|
|
2835
|
-
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2836
|
-
return;
|
|
2837
|
-
}
|
|
2838
|
-
const installTarget = resolveItemInstallTarget(config, item);
|
|
2839
|
-
console.log(`File check for ${item.canonicalName}:`);
|
|
2840
|
-
for (const file of item.files) {
|
|
2841
|
-
const fileName = file.split("/").at(-1);
|
|
2842
|
-
if (!fileName) {
|
|
2843
|
-
console.log(`- invalid registry file path: ${file}`);
|
|
2844
|
-
continue;
|
|
2845
|
-
}
|
|
2846
|
-
const targetPath = join(getCwd(), installTarget, fileName);
|
|
2847
|
-
if (!await fileExists(targetPath)) {
|
|
2848
|
-
console.log(`- missing: ${targetPath}`);
|
|
2849
|
-
continue;
|
|
2850
|
-
}
|
|
2851
|
-
const preparedContent = prepareInstalledFileContent(
|
|
2852
|
-
await readFile(getRegistryTemplatePath(file), "utf-8"),
|
|
2853
|
-
item
|
|
2854
|
-
);
|
|
2855
|
-
const targetContent = await readFile(targetPath, "utf-8");
|
|
2856
|
-
if (hashesAreEqual(preparedContent, targetContent)) {
|
|
2857
|
-
console.log(`- identical: ${targetPath}`);
|
|
2858
|
-
continue;
|
|
2859
|
-
}
|
|
2860
|
-
console.log(`- conflict: ${targetPath}`);
|
|
2861
|
-
}
|
|
2862
|
-
};
|
|
2863
|
-
var applySafeItemUpdate = async (name, force) => {
|
|
2864
|
-
const item = await findItem(name);
|
|
2865
|
-
const config = await loadConfig();
|
|
2866
|
-
if (!item) {
|
|
2867
|
-
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2868
|
-
return false;
|
|
2869
|
-
}
|
|
2870
|
-
const installTarget = resolveItemInstallTarget(config, item);
|
|
2871
|
-
let hasConflict = false;
|
|
2872
|
-
console.log(`Applying update for ${item.canonicalName}:`);
|
|
2873
|
-
for (const file of item.files) {
|
|
2874
|
-
const sourcePath = getRegistryTemplatePath(file);
|
|
2875
|
-
const fileName = file.split("/").at(-1);
|
|
2876
|
-
if (!fileName) {
|
|
2877
|
-
console.log(`- invalid registry file path: ${file}`);
|
|
2878
|
-
hasConflict = true;
|
|
2879
|
-
continue;
|
|
2880
|
-
}
|
|
2881
|
-
const targetPath = join(getCwd(), installTarget, fileName);
|
|
2882
|
-
const preparedContent = prepareInstalledFileContent(
|
|
2883
|
-
await readFile(sourcePath, "utf-8"),
|
|
2884
|
-
item
|
|
2885
|
-
);
|
|
2886
|
-
await mkdir(dirname(targetPath), { recursive: true });
|
|
2887
|
-
if (!await fileExists(targetPath)) {
|
|
2888
|
-
await writeFile(targetPath, preparedContent, "utf-8");
|
|
2889
|
-
console.log(`- restored missing file: ${targetPath}`);
|
|
2890
|
-
continue;
|
|
2891
|
-
}
|
|
2892
|
-
const targetContent = await readFile(targetPath, "utf-8");
|
|
2893
|
-
if (hashesAreEqual(preparedContent, targetContent)) {
|
|
2894
|
-
console.log(`- identical: ${targetPath}`);
|
|
2895
|
-
continue;
|
|
2896
|
-
}
|
|
2897
|
-
if (force) {
|
|
2898
|
-
const backupPath = await createBackupFile(targetPath);
|
|
2899
|
-
if (backupPath) {
|
|
2900
|
-
console.log(`- backup created: ${backupPath}`);
|
|
2901
|
-
}
|
|
2902
|
-
await writeFile(targetPath, preparedContent, "utf-8");
|
|
2903
|
-
console.log(`- force updated file: ${targetPath}`);
|
|
2904
|
-
continue;
|
|
2905
|
-
}
|
|
2906
|
-
hasConflict = true;
|
|
2907
|
-
console.log(`- conflict (user modified): ${targetPath}`);
|
|
2908
|
-
}
|
|
2909
|
-
if (hasConflict) {
|
|
2910
|
-
console.log(
|
|
2911
|
-
"Update finished with conflicts. Installed version was not changed."
|
|
2912
|
-
);
|
|
2913
|
-
return false;
|
|
2914
|
-
}
|
|
2915
|
-
console.log(
|
|
2916
|
-
`\u2714 ${item.canonicalName} updated successfully to v${item.version}`
|
|
2917
|
-
);
|
|
2918
|
-
return true;
|
|
2919
|
-
};
|
|
2920
|
-
var checkItemUpdate = async (name, installedVersion, dryRun, force, sync = false) => {
|
|
2921
|
-
const item = await findItem(name);
|
|
2922
|
-
if (!item) {
|
|
2923
|
-
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2924
|
-
return false;
|
|
2925
|
-
}
|
|
2926
|
-
const versionUpdateAvailable = isUpdateAvailable(
|
|
2927
|
-
installedVersion,
|
|
2928
|
-
item.version
|
|
2929
|
-
);
|
|
2930
|
-
if (!sync && !versionUpdateAvailable) {
|
|
2931
|
-
console.log(`${item.canonicalName} is up to date (v${installedVersion}).`);
|
|
2932
|
-
return false;
|
|
2933
|
-
}
|
|
2934
|
-
if (sync && !versionUpdateAvailable) {
|
|
2935
|
-
console.log(
|
|
2936
|
-
`${item.canonicalName} template sync (installed v${installedVersion}, registry v${item.version})`
|
|
2937
|
-
);
|
|
2938
|
-
} else {
|
|
2939
|
-
console.log(
|
|
2940
|
-
`${item.canonicalName} can be updated: v${installedVersion} \u2192 v${item.version}`
|
|
2941
|
-
);
|
|
2942
|
-
}
|
|
2943
|
-
if (dryRun) {
|
|
2944
|
-
console.log("\nChanged file candidates:");
|
|
2945
|
-
for (const file of item.files) {
|
|
2946
|
-
console.log(`~ ${file}`);
|
|
2947
|
-
}
|
|
2948
|
-
console.log("\nDry run: no files will be changed.");
|
|
2949
|
-
console.log("Update plan:");
|
|
2950
|
-
if (force) {
|
|
2951
|
-
console.log(
|
|
2952
|
-
"- Force mode requested: conflicted files require backup before overwrite"
|
|
2953
|
-
);
|
|
2954
|
-
console.log(
|
|
2955
|
-
"- Dry run: backups would be created before forced overwrites"
|
|
2956
|
-
);
|
|
2957
|
-
}
|
|
2958
|
-
console.log(`- Check installed files for ${item.canonicalName}`);
|
|
2959
|
-
console.log("- Compare existing files with registry templates");
|
|
2960
|
-
console.log("- Report conflicts before writing changes");
|
|
2961
|
-
console.log("- Never overwrite user-modified files silently");
|
|
2962
|
-
await checkItemFiles(name);
|
|
2963
|
-
return false;
|
|
2964
|
-
}
|
|
2965
|
-
return await applySafeItemUpdate(name, force);
|
|
2966
|
-
};
|
|
2967
|
-
|
|
2968
3124
|
// src/commands/update.ts
|
|
2969
3125
|
var styleUpdateNames = ["theme"];
|
|
2970
|
-
var
|
|
2971
|
-
|
|
2972
|
-
|
|
3126
|
+
var resolveInstalledKey2 = async (name, installed) => {
|
|
3127
|
+
const direct = findInstalledKey(installed, name);
|
|
3128
|
+
if (direct) {
|
|
3129
|
+
return direct;
|
|
2973
3130
|
}
|
|
2974
3131
|
const item = await findItem(name);
|
|
2975
3132
|
if (!item) {
|
|
2976
3133
|
return void 0;
|
|
2977
3134
|
}
|
|
2978
|
-
return installed
|
|
3135
|
+
return findInstalledKey(installed, item.name);
|
|
2979
3136
|
};
|
|
2980
3137
|
var runStylesUpdate = async (config, dryRun) => {
|
|
2981
3138
|
const styles = resolveRegistryStyles(styleUpdateNames);
|
|
@@ -3002,9 +3159,7 @@ Tailwind CSS entrypoint: ${config.tailwind.css}`);
|
|
|
3002
3159
|
}
|
|
3003
3160
|
};
|
|
3004
3161
|
var runUtilitiesUpdate = async (config, installed, dryRun, force) => {
|
|
3005
|
-
const installedItems = (await Promise.all(
|
|
3006
|
-
Object.keys(installed).map(async (name) => findItem(name))
|
|
3007
|
-
)).filter((item) => Boolean(item));
|
|
3162
|
+
const installedItems = (await Promise.all(installed.map(async (name) => findItem(name)))).filter((item) => Boolean(item));
|
|
3008
3163
|
const utilityNames = collectUtilities(installedItems);
|
|
3009
3164
|
if (!utilityNames.length) {
|
|
3010
3165
|
console.log("No shared utilities are tracked for installed components.");
|
|
@@ -3029,26 +3184,18 @@ var runUtilitiesUpdate = async (config, installed, dryRun, force) => {
|
|
|
3029
3184
|
);
|
|
3030
3185
|
}
|
|
3031
3186
|
};
|
|
3032
|
-
var runComponentUpdates = async (
|
|
3033
|
-
|
|
3034
|
-
if (!Object.keys(installed).length) {
|
|
3187
|
+
var runComponentUpdates = async (installed, targetNames, dryRun, force, sync) => {
|
|
3188
|
+
if (!installed.length) {
|
|
3035
3189
|
console.log("No Lexsys components are currently tracked.");
|
|
3036
|
-
return
|
|
3190
|
+
return;
|
|
3037
3191
|
}
|
|
3038
3192
|
console.log("Checking installed Lexsys components:\n");
|
|
3039
3193
|
for (const name of targetNames) {
|
|
3040
|
-
|
|
3041
|
-
if (!version) {
|
|
3194
|
+
if (!isInstalled(installed, name)) {
|
|
3042
3195
|
continue;
|
|
3043
3196
|
}
|
|
3044
|
-
|
|
3045
|
-
const item = await findItem(name);
|
|
3046
|
-
if (didUpdate && item) {
|
|
3047
|
-
installed[name] = item.version;
|
|
3048
|
-
changed = true;
|
|
3049
|
-
}
|
|
3197
|
+
await checkItemUpdate(name, dryRun, force, sync);
|
|
3050
3198
|
}
|
|
3051
|
-
return changed;
|
|
3052
3199
|
};
|
|
3053
3200
|
var runUpdate = async (args2) => {
|
|
3054
3201
|
const dryRun = hasFlag(args2, "--dry-run", "-d");
|
|
@@ -3076,7 +3223,7 @@ var runUpdate = async (args2) => {
|
|
|
3076
3223
|
"-a"
|
|
3077
3224
|
]);
|
|
3078
3225
|
const config = await loadConfig();
|
|
3079
|
-
const installed =
|
|
3226
|
+
const installed = [...config.installed ?? []];
|
|
3080
3227
|
try {
|
|
3081
3228
|
await getRegistryProviderResult({
|
|
3082
3229
|
fallback: !noFallback
|
|
@@ -3088,19 +3235,18 @@ var runUpdate = async (args2) => {
|
|
|
3088
3235
|
return;
|
|
3089
3236
|
}
|
|
3090
3237
|
if (!updateAll && !stylesFlag && !utilitiesFlag && targetArgs.length === 0) {
|
|
3091
|
-
|
|
3092
|
-
if (installedNames.length === 0) {
|
|
3238
|
+
if (installed.length === 0) {
|
|
3093
3239
|
console.log(
|
|
3094
3240
|
"No components installed. Run `lexsys add <component>` first."
|
|
3095
3241
|
);
|
|
3096
3242
|
return;
|
|
3097
3243
|
}
|
|
3098
3244
|
if (yes) {
|
|
3099
|
-
targetArgs.push(...
|
|
3245
|
+
targetArgs.push(...installed);
|
|
3100
3246
|
} else {
|
|
3101
3247
|
const selected = await promptMultiselect(
|
|
3102
3248
|
"Select components to update",
|
|
3103
|
-
|
|
3249
|
+
installed.map((name) => ({ title: name, value: name })),
|
|
3104
3250
|
{ min: 1 }
|
|
3105
3251
|
);
|
|
3106
3252
|
if (!selected.length) return;
|
|
@@ -3127,42 +3273,18 @@ var runUpdate = async (args2) => {
|
|
|
3127
3273
|
if (!shouldUpdateComponents) {
|
|
3128
3274
|
return;
|
|
3129
3275
|
}
|
|
3130
|
-
let changed = false;
|
|
3131
3276
|
if (updateAll) {
|
|
3132
|
-
|
|
3133
|
-
config,
|
|
3134
|
-
installed,
|
|
3135
|
-
Object.keys(installed),
|
|
3136
|
-
dryRun,
|
|
3137
|
-
force,
|
|
3138
|
-
sync
|
|
3139
|
-
);
|
|
3277
|
+
await runComponentUpdates(installed, installed, dryRun, force, sync);
|
|
3140
3278
|
} else {
|
|
3141
3279
|
for (const name of targetArgs) {
|
|
3142
|
-
const installedKey = await
|
|
3280
|
+
const installedKey = await resolveInstalledKey2(name, installed);
|
|
3143
3281
|
if (!installedKey) {
|
|
3144
3282
|
console.log(`Component "${name}" is not tracked as installed.`);
|
|
3145
3283
|
continue;
|
|
3146
3284
|
}
|
|
3147
|
-
|
|
3148
|
-
config,
|
|
3149
|
-
installed,
|
|
3150
|
-
[installedKey],
|
|
3151
|
-
dryRun,
|
|
3152
|
-
force,
|
|
3153
|
-
sync
|
|
3154
|
-
);
|
|
3155
|
-
if (didUpdateOne) {
|
|
3156
|
-
changed = true;
|
|
3157
|
-
}
|
|
3285
|
+
await runComponentUpdates(installed, [installedKey], dryRun, force, sync);
|
|
3158
3286
|
}
|
|
3159
3287
|
}
|
|
3160
|
-
if (changed) {
|
|
3161
|
-
await saveConfig({
|
|
3162
|
-
...config,
|
|
3163
|
-
installed
|
|
3164
|
-
});
|
|
3165
|
-
}
|
|
3166
3288
|
};
|
|
3167
3289
|
var cliDistDir = dirname(fileURLToPath(import.meta.url));
|
|
3168
3290
|
var packageJsonPath = join(cliDistDir, "..", "package.json");
|
|
@@ -3269,6 +3391,14 @@ try {
|
|
|
3269
3391
|
});
|
|
3270
3392
|
process.exit(0);
|
|
3271
3393
|
}
|
|
3394
|
+
if (command === "reset") {
|
|
3395
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3396
|
+
runHelpFor("reset");
|
|
3397
|
+
process.exit(0);
|
|
3398
|
+
}
|
|
3399
|
+
await runReset(args);
|
|
3400
|
+
process.exit(0);
|
|
3401
|
+
}
|
|
3272
3402
|
if (command === "uninstall" || command === "rm") {
|
|
3273
3403
|
if (hasFlag(args, "--help", "-h")) {
|
|
3274
3404
|
runHelpFor("uninstall");
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RegistryItem } from "@dalexto/lexsys-registry";
|
|
2
|
+
export type ComponentDriftStatus = "missing" | "in-sync" | "drift";
|
|
3
|
+
export declare const getComponentDriftStatus: (name: string) => Promise<ComponentDriftStatus>;
|
|
4
|
+
export declare const itemHasTemplateDrift: (item: RegistryItem) => Promise<boolean>;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export declare const checkItemFiles: (name: string) => Promise<void>;
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const applyItemOverwrite: (name: string, force: boolean) => Promise<boolean>;
|
|
3
|
+
export declare const checkItemUpdate: (name: string, dryRun: boolean, force: boolean, sync?: boolean) => Promise<boolean>;
|
|
4
|
+
export declare const resetItem: (name: string, dryRun: boolean) => Promise<boolean>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { RegistryItem } from "@dalexto/lexsys-registry";
|
|
2
2
|
export declare const computeRegistryClosure: (rootNames: string[], items: RegistryItem[]) => Set<string>;
|
|
3
|
-
export declare const findOrphanInstalledItems: (removedTargetNames: string[], remainingInstalled:
|
|
3
|
+
export declare const findOrphanInstalledItems: (removedTargetNames: string[], remainingInstalled: string[], items: RegistryItem[]) => RegistryItem[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dalexto/lexsys-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Registry-first CLI that installs Lexsys React UI components into your project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"prompts": "^2.4.2",
|
|
35
|
-
"@dalexto/lexsys-registry": "0.0.
|
|
35
|
+
"@dalexto/lexsys-registry": "0.0.4"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^25.9.1",
|
package/dist/utils/version.d.ts
DELETED