@dalexto/lexsys-cli 0.0.2
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/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/commands/add.d.ts +1 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/help.d.ts +2 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/registry.d.ts +9 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/version.d.ts +1 -0
- package/dist/config/config.d.ts +28 -0
- package/dist/core/backup.d.ts +1 -0
- package/dist/core/cli-error.d.ts +5 -0
- package/dist/core/context.d.ts +2 -0
- package/dist/core/flags.d.ts +4 -0
- package/dist/core/fs.d.ts +3 -0
- package/dist/core/hash.d.ts +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3333 -0
- package/dist/install/import-rewriter.d.ts +3 -0
- package/dist/install/installer.d.ts +14 -0
- package/dist/install/results.d.ts +10 -0
- package/dist/install/target.d.ts +4 -0
- package/dist/install/uninstall-results.d.ts +10 -0
- package/dist/install/update-engine.d.ts +2 -0
- package/dist/registry/closure.d.ts +3 -0
- package/dist/registry/provider.d.ts +13 -0
- package/dist/registry/remote-files.d.ts +1 -0
- package/dist/registry/remote.d.ts +15 -0
- package/dist/registry/resolver.d.ts +13 -0
- package/dist/registry/source.d.ts +1 -0
- package/dist/registry/types.d.ts +14 -0
- package/dist/scaffold/next.d.ts +2 -0
- package/dist/scaffold/tailwind.d.ts +6 -0
- package/dist/scaffold/template-validator.d.ts +2 -0
- package/dist/scaffold/vite.d.ts +1 -0
- package/dist/utils/package-manager.d.ts +12 -0
- package/dist/utils/suggestions.d.ts +2 -0
- package/dist/utils/version.d.ts +2 -0
- package/package.json +49 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3333 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import prompts from 'prompts';
|
|
3
|
+
import { registryManifest, getInstallLayer, registryItems, validateRegistry, registryStyles, registryVersion, validateRegistryItem, registryUtilities as registryUtilities$1 } from '@dalexto/lexsys-registry';
|
|
4
|
+
import { readFile, access, mkdir, copyFile, writeFile, unlink, readdir, rm } from 'fs/promises';
|
|
5
|
+
import { dirname, join, resolve, relative, basename } from 'path';
|
|
6
|
+
import { createHash } from 'crypto';
|
|
7
|
+
import { constants } from 'fs';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { execFileSync } from 'child_process';
|
|
10
|
+
|
|
11
|
+
// src/core/cli-error.ts
|
|
12
|
+
var CliError = class extends Error {
|
|
13
|
+
suggestion;
|
|
14
|
+
constructor(message, suggestion) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "CliError";
|
|
17
|
+
this.suggestion = suggestion;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var handleCliError = (error) => {
|
|
21
|
+
if (error instanceof CliError) {
|
|
22
|
+
console.error(`
|
|
23
|
+
\u2717 ${error.message}`);
|
|
24
|
+
if (error.suggestion) {
|
|
25
|
+
console.error(` ${error.suggestion}`);
|
|
26
|
+
}
|
|
27
|
+
console.error(` Run \`lexsys --help\` to see available commands.
|
|
28
|
+
`);
|
|
29
|
+
} else if (error instanceof Error) {
|
|
30
|
+
console.error(`
|
|
31
|
+
\u2717 ${error.message}
|
|
32
|
+
`);
|
|
33
|
+
} else {
|
|
34
|
+
console.error(`
|
|
35
|
+
\u2717 An unexpected error occurred.
|
|
36
|
+
`);
|
|
37
|
+
}
|
|
38
|
+
process.exit(1);
|
|
39
|
+
};
|
|
40
|
+
var hashContent = (content) => {
|
|
41
|
+
return createHash("sha256").update(content, "utf-8").digest("hex");
|
|
42
|
+
};
|
|
43
|
+
var hashFile = async (path) => {
|
|
44
|
+
const content = await readFile(path, "utf-8");
|
|
45
|
+
return hashContent(content);
|
|
46
|
+
};
|
|
47
|
+
var hashesAreEqual = (firstContent, secondContent) => {
|
|
48
|
+
return hashContent(firstContent) === hashContent(secondContent);
|
|
49
|
+
};
|
|
50
|
+
var isNotFoundError = (error) => {
|
|
51
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
52
|
+
};
|
|
53
|
+
var fileExists = async (path) => {
|
|
54
|
+
try {
|
|
55
|
+
await access(path, constants.F_OK);
|
|
56
|
+
return true;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (isNotFoundError(error)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var filesAreEqual = async (sourcePath, targetPath) => {
|
|
65
|
+
const sourceHash = await hashFile(sourcePath);
|
|
66
|
+
const targetHash = await hashFile(targetPath);
|
|
67
|
+
return sourceHash === targetHash;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/core/context.ts
|
|
71
|
+
var currentWorkingDirectory = process.cwd();
|
|
72
|
+
var setCwd = (cwd2) => {
|
|
73
|
+
currentWorkingDirectory = cwd2;
|
|
74
|
+
};
|
|
75
|
+
var getCwd = () => {
|
|
76
|
+
return currentWorkingDirectory;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// src/config/config.ts
|
|
80
|
+
var defaultPathsConfig = {
|
|
81
|
+
components: "src/components/ui",
|
|
82
|
+
utilities: "src/lib",
|
|
83
|
+
styles: "styles"
|
|
84
|
+
};
|
|
85
|
+
var defaultAliasesConfig = {
|
|
86
|
+
components: "@/components/ui",
|
|
87
|
+
ui: "@/components/ui",
|
|
88
|
+
utils: "@/lib/utils",
|
|
89
|
+
lib: "@/lib",
|
|
90
|
+
hooks: "@/hooks"
|
|
91
|
+
};
|
|
92
|
+
var defaultTailwindConfig = {
|
|
93
|
+
version: "v4",
|
|
94
|
+
css: "src/style.css"
|
|
95
|
+
};
|
|
96
|
+
var defaultConfig = {
|
|
97
|
+
style: "default",
|
|
98
|
+
paths: defaultPathsConfig,
|
|
99
|
+
aliases: defaultAliasesConfig,
|
|
100
|
+
tailwind: defaultTailwindConfig,
|
|
101
|
+
installed: {},
|
|
102
|
+
registryUrl: null
|
|
103
|
+
};
|
|
104
|
+
var getConfigPath = () => {
|
|
105
|
+
return join(getCwd(), "lexsys.config.json");
|
|
106
|
+
};
|
|
107
|
+
var loadConfig = async () => {
|
|
108
|
+
const configPath = getConfigPath();
|
|
109
|
+
if (!await fileExists(configPath)) {
|
|
110
|
+
return defaultConfig;
|
|
111
|
+
}
|
|
112
|
+
const content = await readFile(configPath, "utf-8");
|
|
113
|
+
const parsed = JSON.parse(content);
|
|
114
|
+
return {
|
|
115
|
+
...defaultConfig,
|
|
116
|
+
...parsed,
|
|
117
|
+
paths: {
|
|
118
|
+
...defaultPathsConfig,
|
|
119
|
+
...parsed.paths
|
|
120
|
+
},
|
|
121
|
+
aliases: {
|
|
122
|
+
...defaultAliasesConfig,
|
|
123
|
+
...parsed.aliases
|
|
124
|
+
},
|
|
125
|
+
tailwind: {
|
|
126
|
+
...defaultTailwindConfig,
|
|
127
|
+
...parsed.tailwind
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
var saveConfig = async (config) => {
|
|
132
|
+
const configPath = getConfigPath();
|
|
133
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
134
|
+
};
|
|
135
|
+
var crossLayerImportPatterns = [
|
|
136
|
+
/\.\.\/\.\.\/primitives\//g,
|
|
137
|
+
/\.\.\/\.\.\/blocks\//g,
|
|
138
|
+
/\.\.\/\.\.\/templates\//g
|
|
139
|
+
];
|
|
140
|
+
var rewriteCrossLayerImports = (content) => {
|
|
141
|
+
let rewritten = content;
|
|
142
|
+
for (const pattern of crossLayerImportPatterns) {
|
|
143
|
+
rewritten = rewritten.replace(pattern, "../");
|
|
144
|
+
}
|
|
145
|
+
return rewritten;
|
|
146
|
+
};
|
|
147
|
+
var prepareInstalledFileContent = (content, item) => {
|
|
148
|
+
const layer = getInstallLayer(item);
|
|
149
|
+
if (layer !== "block" && layer !== "template") {
|
|
150
|
+
return content;
|
|
151
|
+
}
|
|
152
|
+
return rewriteCrossLayerImports(content);
|
|
153
|
+
};
|
|
154
|
+
var toPosixPath = (value) => {
|
|
155
|
+
return value.replace(/\\/g, "/");
|
|
156
|
+
};
|
|
157
|
+
var resolveItemInstallTarget = (config, item) => {
|
|
158
|
+
return toPosixPath(join(config.paths.components, item.canonicalName));
|
|
159
|
+
};
|
|
160
|
+
var createTimestamp = () => {
|
|
161
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
162
|
+
};
|
|
163
|
+
var createBackupFile = async (path) => {
|
|
164
|
+
if (!await fileExists(path)) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const backupPath = `${path}.${createTimestamp()}.bak`;
|
|
168
|
+
await mkdir(dirname(backupPath), { recursive: true });
|
|
169
|
+
await copyFile(path, backupPath);
|
|
170
|
+
return backupPath;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/registry/remote-files.ts
|
|
174
|
+
var fetchRemoteFile = async (url) => {
|
|
175
|
+
const response = await fetch(url);
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
178
|
+
}
|
|
179
|
+
return response.text();
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/scaffold/template-validator.ts
|
|
183
|
+
var validateTemplateFiles = async (item) => {
|
|
184
|
+
for (const file of item.files) {
|
|
185
|
+
const fullPath = getRegistryTemplatePath(file);
|
|
186
|
+
if (!await fileExists(fullPath)) {
|
|
187
|
+
throw new Error(`Template file missing for "${item.name}": ${file}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// src/install/results.ts
|
|
193
|
+
var createInstallResourceResult = () => {
|
|
194
|
+
return {
|
|
195
|
+
created: [],
|
|
196
|
+
updated: [],
|
|
197
|
+
skipped: [],
|
|
198
|
+
conflicted: []
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
var mergeInstallResults = (results) => {
|
|
202
|
+
return results.reduce(
|
|
203
|
+
(merged, result) => ({
|
|
204
|
+
created: [...merged.created, ...result.created],
|
|
205
|
+
updated: [...merged.updated, ...result.updated],
|
|
206
|
+
skipped: [...merged.skipped, ...result.skipped],
|
|
207
|
+
conflicted: [...merged.conflicted, ...result.conflicted]
|
|
208
|
+
}),
|
|
209
|
+
createInstallResourceResult()
|
|
210
|
+
);
|
|
211
|
+
};
|
|
212
|
+
var hasInstallConflicts = (result) => {
|
|
213
|
+
return result.conflicted.length > 0;
|
|
214
|
+
};
|
|
215
|
+
var printResourceSummary = (label, result) => {
|
|
216
|
+
const total = result.created.length + result.updated.length + result.skipped.length + result.conflicted.length;
|
|
217
|
+
if (!total) {
|
|
218
|
+
console.log(`- ${label}: no changes`);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const parts = [
|
|
222
|
+
result.created.length ? `${result.created.length} created` : void 0,
|
|
223
|
+
result.updated.length ? `${result.updated.length} updated` : void 0,
|
|
224
|
+
result.skipped.length ? `${result.skipped.length} skipped` : void 0,
|
|
225
|
+
result.conflicted.length ? `${result.conflicted.length} conflicted` : void 0
|
|
226
|
+
].filter((part) => typeof part === "string");
|
|
227
|
+
console.log(`- ${label}: ${parts.join(", ")}`);
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// src/install/uninstall-results.ts
|
|
231
|
+
var createUninstallResourceResult = () => {
|
|
232
|
+
return {
|
|
233
|
+
removed: [],
|
|
234
|
+
skipped: [],
|
|
235
|
+
conflicted: [],
|
|
236
|
+
missing: []
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
var mergeUninstallResults = (results) => {
|
|
240
|
+
return results.reduce(
|
|
241
|
+
(merged, result) => ({
|
|
242
|
+
removed: [...merged.removed, ...result.removed],
|
|
243
|
+
skipped: [...merged.skipped, ...result.skipped],
|
|
244
|
+
conflicted: [...merged.conflicted, ...result.conflicted],
|
|
245
|
+
missing: [...merged.missing, ...result.missing]
|
|
246
|
+
}),
|
|
247
|
+
createUninstallResourceResult()
|
|
248
|
+
);
|
|
249
|
+
};
|
|
250
|
+
var hasUninstallConflicts = (result) => {
|
|
251
|
+
return result.conflicted.length > 0;
|
|
252
|
+
};
|
|
253
|
+
var printUninstallSummary = (label, result) => {
|
|
254
|
+
const total = result.removed.length + result.skipped.length + result.conflicted.length + result.missing.length;
|
|
255
|
+
if (!total) {
|
|
256
|
+
console.log(`- ${label}: no changes`);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const parts = [
|
|
260
|
+
result.removed.length ? `${result.removed.length} removed` : void 0,
|
|
261
|
+
result.skipped.length ? `${result.skipped.length} skipped` : void 0,
|
|
262
|
+
result.missing.length ? `${result.missing.length} missing` : void 0,
|
|
263
|
+
result.conflicted.length ? `${result.conflicted.length} conflicted` : void 0
|
|
264
|
+
].filter((part) => typeof part === "string");
|
|
265
|
+
console.log(`- ${label}: ${parts.join(", ")}`);
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// src/install/installer.ts
|
|
269
|
+
var getRegistryTemplatePath = (templatePath) => {
|
|
270
|
+
const templateUrl = import.meta.resolve(
|
|
271
|
+
`@dalexto/lexsys-registry/templates/${templatePath}`
|
|
272
|
+
);
|
|
273
|
+
return fileURLToPath(templateUrl);
|
|
274
|
+
};
|
|
275
|
+
var generatedStyleHeader = "/* Generated by @dalexto/lexsys-tokens. Do not edit directly. */";
|
|
276
|
+
var isGeneratedLexsysStyle = (content) => {
|
|
277
|
+
return content.startsWith(generatedStyleHeader);
|
|
278
|
+
};
|
|
279
|
+
var ensureProjectStructure = async (config) => {
|
|
280
|
+
await mkdir(join(getCwd(), config.paths.components), { recursive: true });
|
|
281
|
+
await mkdir(join(getCwd(), config.paths.utilities), { recursive: true });
|
|
282
|
+
await mkdir(join(getCwd(), config.paths.styles), { recursive: true });
|
|
283
|
+
};
|
|
284
|
+
var installUtilities = async (utilities, config) => {
|
|
285
|
+
const result = createInstallResourceResult();
|
|
286
|
+
for (const utility of utilities) {
|
|
287
|
+
const sourcePath = getRegistryTemplatePath(utility.path);
|
|
288
|
+
const targetPath = join(getCwd(), config.paths.utilities, utility.target);
|
|
289
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
290
|
+
if (await fileExists(targetPath)) {
|
|
291
|
+
const isSameFile = await filesAreEqual(sourcePath, targetPath);
|
|
292
|
+
if (isSameFile) {
|
|
293
|
+
console.log(`Skipped identical utility: ${targetPath}`);
|
|
294
|
+
result.skipped.push(targetPath);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
console.log(`Conflict: utility already exists and differs: ${targetPath}`);
|
|
298
|
+
result.conflicted.push(targetPath);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
await copyFile(sourcePath, targetPath);
|
|
302
|
+
console.log(`Created utility: ${targetPath}`);
|
|
303
|
+
result.created.push(targetPath);
|
|
304
|
+
}
|
|
305
|
+
return result;
|
|
306
|
+
};
|
|
307
|
+
var updateUtilities = async (utilities, config, force) => {
|
|
308
|
+
const result = createInstallResourceResult();
|
|
309
|
+
for (const utility of utilities) {
|
|
310
|
+
const sourcePath = getRegistryTemplatePath(utility.path);
|
|
311
|
+
const targetPath = join(getCwd(), config.paths.utilities, utility.target);
|
|
312
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
313
|
+
if (!await fileExists(targetPath)) {
|
|
314
|
+
await copyFile(sourcePath, targetPath);
|
|
315
|
+
console.log(`Created utility: ${targetPath}`);
|
|
316
|
+
result.created.push(targetPath);
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const isSameFile = await filesAreEqual(sourcePath, targetPath);
|
|
320
|
+
if (isSameFile) {
|
|
321
|
+
console.log(`Skipped identical utility: ${targetPath}`);
|
|
322
|
+
result.skipped.push(targetPath);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (force) {
|
|
326
|
+
const backupPath = await createBackupFile(targetPath);
|
|
327
|
+
if (backupPath) {
|
|
328
|
+
console.log(`- backup created: ${backupPath}`);
|
|
329
|
+
}
|
|
330
|
+
await copyFile(sourcePath, targetPath);
|
|
331
|
+
console.log(`Updated utility: ${targetPath}`);
|
|
332
|
+
result.updated.push(targetPath);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
console.log(`Conflict: utility already exists and differs: ${targetPath}`);
|
|
336
|
+
result.conflicted.push(targetPath);
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
339
|
+
};
|
|
340
|
+
var installStyles = async (styles, config) => {
|
|
341
|
+
const result = createInstallResourceResult();
|
|
342
|
+
const installedStyleTargets = [];
|
|
343
|
+
for (const style of styles) {
|
|
344
|
+
console.log(`Installing style ${style.name}...`);
|
|
345
|
+
for (const file of style.files) {
|
|
346
|
+
const sourcePath = getRegistryTemplatePath(file.path);
|
|
347
|
+
const targetPath = join(getCwd(), config.paths.styles, file.target);
|
|
348
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
349
|
+
if (await fileExists(targetPath)) {
|
|
350
|
+
const isSameFile = await filesAreEqual(sourcePath, targetPath);
|
|
351
|
+
if (isSameFile) {
|
|
352
|
+
console.log(`Skipped identical style: ${targetPath}`);
|
|
353
|
+
result.skipped.push(targetPath);
|
|
354
|
+
installedStyleTargets.push(file.target);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const sourceContent = await readFile(sourcePath, "utf-8");
|
|
358
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
359
|
+
if (isGeneratedLexsysStyle(sourceContent) && isGeneratedLexsysStyle(targetContent)) {
|
|
360
|
+
await writeFile(targetPath, sourceContent, "utf-8");
|
|
361
|
+
console.log(`Updated generated style: ${targetPath}`);
|
|
362
|
+
result.updated.push(targetPath);
|
|
363
|
+
installedStyleTargets.push(file.target);
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
console.log(`Conflict: style already exists and differs: ${targetPath}`);
|
|
367
|
+
result.conflicted.push(targetPath);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
await copyFile(sourcePath, targetPath);
|
|
371
|
+
console.log(`Created style: ${targetPath}`);
|
|
372
|
+
result.created.push(targetPath);
|
|
373
|
+
installedStyleTargets.push(file.target);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (!hasInstallConflicts(result) && installedStyleTargets.length) {
|
|
377
|
+
const entrypointResult = await installStyleEntrypointImports(
|
|
378
|
+
installedStyleTargets,
|
|
379
|
+
config
|
|
380
|
+
);
|
|
381
|
+
result.updated.push(...entrypointResult.updated);
|
|
382
|
+
result.skipped.push(...entrypointResult.skipped);
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
385
|
+
};
|
|
386
|
+
var toCssImportPath = (cssPath, styleTarget, config) => {
|
|
387
|
+
const importPath = relative(
|
|
388
|
+
dirname(cssPath),
|
|
389
|
+
join(config.paths.styles, styleTarget)
|
|
390
|
+
).replaceAll("\\", "/");
|
|
391
|
+
return importPath.startsWith(".") ? importPath : `./${importPath}`;
|
|
392
|
+
};
|
|
393
|
+
var getStyleImportInsertionIndex = (content) => {
|
|
394
|
+
const importHeaderMatch = content.match(
|
|
395
|
+
/^(?:\s*@charset\s+["'][^"']+["'];\s*)?(?:\s*@import\s+[^;]+;\s*)*/u
|
|
396
|
+
);
|
|
397
|
+
return importHeaderMatch?.[0].length ?? 0;
|
|
398
|
+
};
|
|
399
|
+
var installStyleEntrypointImports = async (styleTargets, config) => {
|
|
400
|
+
const cssPath = join(getCwd(), config.tailwind.css);
|
|
401
|
+
if (!await fileExists(cssPath)) {
|
|
402
|
+
console.log(
|
|
403
|
+
`Skipped style wiring: Tailwind CSS entrypoint not found at ${cssPath}`
|
|
404
|
+
);
|
|
405
|
+
return {
|
|
406
|
+
skipped: [cssPath],
|
|
407
|
+
updated: []
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const content = await readFile(cssPath, "utf-8");
|
|
411
|
+
const missingImports = styleTargets.map(
|
|
412
|
+
(styleTarget) => toCssImportPath(config.tailwind.css, styleTarget, config)
|
|
413
|
+
).filter(
|
|
414
|
+
(importPath) => !content.includes(`"${importPath}"`) && !content.includes(`'${importPath}'`)
|
|
415
|
+
).map((importPath) => `@import "${importPath}";`);
|
|
416
|
+
if (!missingImports.length) {
|
|
417
|
+
console.log(
|
|
418
|
+
`Skipped style wiring: ${cssPath} already imports Lexsys styles`
|
|
419
|
+
);
|
|
420
|
+
return {
|
|
421
|
+
skipped: [cssPath],
|
|
422
|
+
updated: []
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const insertionIndex = getStyleImportInsertionIndex(content);
|
|
426
|
+
const before = content.slice(0, insertionIndex);
|
|
427
|
+
const after = content.slice(insertionIndex);
|
|
428
|
+
const separator = before.endsWith("\n") || before.length === 0 ? "" : "\n";
|
|
429
|
+
const updatedContent = `${before}${separator}${missingImports.join("\n")}
|
|
430
|
+
${after}`;
|
|
431
|
+
await writeFile(cssPath, updatedContent, "utf-8");
|
|
432
|
+
console.log(`Updated style entrypoint: ${cssPath}`);
|
|
433
|
+
return {
|
|
434
|
+
skipped: [],
|
|
435
|
+
updated: [cssPath]
|
|
436
|
+
};
|
|
437
|
+
};
|
|
438
|
+
var readPreparedTemplateContent = async (item, file, remoteContent) => {
|
|
439
|
+
const rawContent = remoteContent ?? await readFile(getRegistryTemplatePath(file), "utf-8");
|
|
440
|
+
return prepareInstalledFileContent(rawContent, item);
|
|
441
|
+
};
|
|
442
|
+
var installItemFiles = async (item, config) => {
|
|
443
|
+
const result = createInstallResourceResult();
|
|
444
|
+
const installTarget = resolveItemInstallTarget(config, item);
|
|
445
|
+
console.log(`Installing ${item.canonicalName}...
|
|
446
|
+
`);
|
|
447
|
+
await validateTemplateFiles(item);
|
|
448
|
+
for (const file of item.files) {
|
|
449
|
+
const remoteFile = item.remoteFiles?.find(
|
|
450
|
+
(registryFile) => registryFile.path === file && registryFile.remoteUrl
|
|
451
|
+
);
|
|
452
|
+
const fileName = file.split("/").at(-1);
|
|
453
|
+
if (!fileName) {
|
|
454
|
+
throw new Error(`Invalid registry file path: ${file}`);
|
|
455
|
+
}
|
|
456
|
+
const targetPath = join(getCwd(), installTarget, fileName);
|
|
457
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
458
|
+
if (remoteFile?.remoteUrl) {
|
|
459
|
+
console.log(`Fetching remote file: ${remoteFile.remoteUrl}`);
|
|
460
|
+
const remoteContent = await fetchRemoteFile(remoteFile.remoteUrl);
|
|
461
|
+
const preparedContent2 = await readPreparedTemplateContent(
|
|
462
|
+
item,
|
|
463
|
+
file,
|
|
464
|
+
remoteContent
|
|
465
|
+
);
|
|
466
|
+
if (await fileExists(targetPath)) {
|
|
467
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
468
|
+
if (hashesAreEqual(preparedContent2, targetContent)) {
|
|
469
|
+
console.log(`Skipped identical file: ${targetPath}`);
|
|
470
|
+
result.skipped.push(targetPath);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
console.log(`Conflict: file already exists and differs: ${targetPath}`);
|
|
474
|
+
result.conflicted.push(targetPath);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
await writeFile(targetPath, preparedContent2, "utf-8");
|
|
478
|
+
console.log(`Created: ${targetPath}`);
|
|
479
|
+
result.created.push(targetPath);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const preparedContent = await readPreparedTemplateContent(item, file);
|
|
483
|
+
if (await fileExists(targetPath)) {
|
|
484
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
485
|
+
if (hashesAreEqual(preparedContent, targetContent)) {
|
|
486
|
+
console.log(`Skipped identical file: ${targetPath}`);
|
|
487
|
+
result.skipped.push(targetPath);
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
console.log(`Conflict: file already exists and differs: ${targetPath}`);
|
|
491
|
+
result.conflicted.push(targetPath);
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
await writeFile(targetPath, preparedContent, "utf-8");
|
|
495
|
+
console.log(`Created: ${targetPath}`);
|
|
496
|
+
result.created.push(targetPath);
|
|
497
|
+
}
|
|
498
|
+
console.log("\nDone.");
|
|
499
|
+
return result;
|
|
500
|
+
};
|
|
501
|
+
var removeFileIfMatchesTemplate = async (sourcePath, targetPath, result) => {
|
|
502
|
+
if (!await fileExists(targetPath)) {
|
|
503
|
+
console.log(`Skipped missing file: ${targetPath}`);
|
|
504
|
+
result.missing.push(targetPath);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const isSameFile = await filesAreEqual(sourcePath, targetPath);
|
|
508
|
+
if (!isSameFile) {
|
|
509
|
+
console.log(`Conflict: file differs from registry template: ${targetPath}`);
|
|
510
|
+
result.conflicted.push(targetPath);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
await unlink(targetPath);
|
|
514
|
+
console.log(`Removed: ${targetPath}`);
|
|
515
|
+
result.removed.push(targetPath);
|
|
516
|
+
};
|
|
517
|
+
var removeGeneratedStyleIfMatchesTemplate = async (sourcePath, targetPath, result) => {
|
|
518
|
+
if (!await fileExists(targetPath)) {
|
|
519
|
+
console.log(`Skipped missing style: ${targetPath}`);
|
|
520
|
+
result.missing.push(targetPath);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const sourceContent = await readFile(sourcePath, "utf-8");
|
|
524
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
525
|
+
if (isGeneratedLexsysStyle(sourceContent) && isGeneratedLexsysStyle(targetContent) && sourceContent === targetContent) {
|
|
526
|
+
await unlink(targetPath);
|
|
527
|
+
console.log(`Removed generated style: ${targetPath}`);
|
|
528
|
+
result.removed.push(targetPath);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const isSameFile = await filesAreEqual(sourcePath, targetPath);
|
|
532
|
+
if (isSameFile) {
|
|
533
|
+
await unlink(targetPath);
|
|
534
|
+
console.log(`Removed style: ${targetPath}`);
|
|
535
|
+
result.removed.push(targetPath);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
console.log(`Conflict: style differs from registry template: ${targetPath}`);
|
|
539
|
+
result.conflicted.push(targetPath);
|
|
540
|
+
};
|
|
541
|
+
var tryRemoveEmptyDirectory = async (directoryPath) => {
|
|
542
|
+
try {
|
|
543
|
+
const entries = await readdir(directoryPath);
|
|
544
|
+
if (entries.length === 0) {
|
|
545
|
+
await rm(directoryPath, { recursive: true });
|
|
546
|
+
console.log(`Removed empty directory: ${directoryPath}`);
|
|
547
|
+
}
|
|
548
|
+
} catch {
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
var uninstallItemFiles = async (item, config) => {
|
|
553
|
+
const result = createUninstallResourceResult();
|
|
554
|
+
console.log(`Uninstalling ${item.canonicalName}...
|
|
555
|
+
`);
|
|
556
|
+
await validateTemplateFiles(item);
|
|
557
|
+
const componentDirectory = join(
|
|
558
|
+
getCwd(),
|
|
559
|
+
resolveItemInstallTarget(config, item)
|
|
560
|
+
);
|
|
561
|
+
const plannedRemovals = [];
|
|
562
|
+
for (const file of item.files) {
|
|
563
|
+
const fileName = file.split("/").at(-1);
|
|
564
|
+
if (!fileName) {
|
|
565
|
+
throw new Error(`Invalid registry file path: ${file}`);
|
|
566
|
+
}
|
|
567
|
+
const targetPath = join(componentDirectory, fileName);
|
|
568
|
+
if (!await fileExists(targetPath)) {
|
|
569
|
+
console.log(`Skipped missing file: ${targetPath}`);
|
|
570
|
+
result.missing.push(targetPath);
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const preparedContent = await readPreparedTemplateContent(item, file);
|
|
574
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
575
|
+
if (!hashesAreEqual(preparedContent, targetContent)) {
|
|
576
|
+
console.log(
|
|
577
|
+
`Conflict: file differs from registry template: ${targetPath}`
|
|
578
|
+
);
|
|
579
|
+
result.conflicted.push(targetPath);
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
plannedRemovals.push(targetPath);
|
|
583
|
+
}
|
|
584
|
+
if (result.conflicted.length > 0) {
|
|
585
|
+
console.log(
|
|
586
|
+
"Skipped component file removal because one or more files differ from registry templates."
|
|
587
|
+
);
|
|
588
|
+
console.log("\nDone.");
|
|
589
|
+
return result;
|
|
590
|
+
}
|
|
591
|
+
for (const targetPath of plannedRemovals) {
|
|
592
|
+
await unlink(targetPath);
|
|
593
|
+
console.log(`Removed: ${targetPath}`);
|
|
594
|
+
result.removed.push(targetPath);
|
|
595
|
+
}
|
|
596
|
+
await tryRemoveEmptyDirectory(componentDirectory);
|
|
597
|
+
console.log("\nDone.");
|
|
598
|
+
return result;
|
|
599
|
+
};
|
|
600
|
+
var uninstallUtilities = async (utilities, config) => {
|
|
601
|
+
const result = createUninstallResourceResult();
|
|
602
|
+
for (const utility of utilities) {
|
|
603
|
+
const sourcePath = getRegistryTemplatePath(utility.path);
|
|
604
|
+
const targetPath = join(getCwd(), config.paths.utilities, utility.target);
|
|
605
|
+
await removeFileIfMatchesTemplate(sourcePath, targetPath, result);
|
|
606
|
+
}
|
|
607
|
+
return result;
|
|
608
|
+
};
|
|
609
|
+
var uninstallStyles = async (styles, config) => {
|
|
610
|
+
const result = createUninstallResourceResult();
|
|
611
|
+
for (const style of styles) {
|
|
612
|
+
console.log(`Uninstalling style ${style.name}...`);
|
|
613
|
+
for (const file of style.files) {
|
|
614
|
+
const sourcePath = getRegistryTemplatePath(file.path);
|
|
615
|
+
const targetPath = join(getCwd(), config.paths.styles, file.target);
|
|
616
|
+
await removeGeneratedStyleIfMatchesTemplate(
|
|
617
|
+
sourcePath,
|
|
618
|
+
targetPath,
|
|
619
|
+
result
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return result;
|
|
624
|
+
};
|
|
625
|
+
var dependencyNamePattern = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/iu;
|
|
626
|
+
var parseDependencySpec = (spec) => {
|
|
627
|
+
if (spec.startsWith("@")) {
|
|
628
|
+
const versionSeparator2 = spec.indexOf("@", 1);
|
|
629
|
+
if (versionSeparator2 === -1) {
|
|
630
|
+
return { name: spec };
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
name: spec.slice(0, versionSeparator2),
|
|
634
|
+
version: spec.slice(versionSeparator2 + 1)
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
const versionSeparator = spec.indexOf("@");
|
|
638
|
+
if (versionSeparator === -1) {
|
|
639
|
+
return { name: spec };
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
name: spec.slice(0, versionSeparator),
|
|
643
|
+
version: spec.slice(versionSeparator + 1)
|
|
644
|
+
};
|
|
645
|
+
};
|
|
646
|
+
var isSafeDependencySpec = (spec) => {
|
|
647
|
+
const { name, version } = parseDependencySpec(spec);
|
|
648
|
+
if (!dependencyNamePattern.test(name)) {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
if (version === void 0) {
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
return version.length > 0;
|
|
655
|
+
};
|
|
656
|
+
var assertSafeDependencyNames = (deps) => {
|
|
657
|
+
const unsafeDependency = deps.find(
|
|
658
|
+
(dependency) => !isSafeDependencySpec(dependency)
|
|
659
|
+
);
|
|
660
|
+
if (unsafeDependency) {
|
|
661
|
+
throw new Error(`Invalid dependency name: ${unsafeDependency}`);
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
var getPackageManagerInvocation = (packageManager, args2) => {
|
|
665
|
+
if (process.platform === "win32") {
|
|
666
|
+
return {
|
|
667
|
+
command: "cmd.exe",
|
|
668
|
+
args: ["/d", "/s", "/c", packageManager, ...args2]
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
command: packageManager,
|
|
673
|
+
args: args2
|
|
674
|
+
};
|
|
675
|
+
};
|
|
676
|
+
var detectPackageManager = async (packageJson) => {
|
|
677
|
+
const declaredPackageManager = typeof packageJson.packageManager === "string" ? packageJson.packageManager : void 0;
|
|
678
|
+
if (declaredPackageManager?.startsWith("pnpm@")) return "pnpm";
|
|
679
|
+
if (declaredPackageManager?.startsWith("yarn@")) return "yarn";
|
|
680
|
+
if (declaredPackageManager?.startsWith("npm@")) return "npm";
|
|
681
|
+
const hasPackageLock = await fileExists(join(getCwd(), "package-lock.json"));
|
|
682
|
+
const hasPnpmLock = await fileExists(join(getCwd(), "pnpm-lock.yaml"));
|
|
683
|
+
const hasYarnLock = await fileExists(join(getCwd(), "yarn.lock"));
|
|
684
|
+
if (hasPackageLock) return "npm";
|
|
685
|
+
if (hasPnpmLock) return "pnpm";
|
|
686
|
+
if (hasYarnLock) return "yarn";
|
|
687
|
+
return "npm";
|
|
688
|
+
};
|
|
689
|
+
var installDependencies = async (deps, options = {}) => {
|
|
690
|
+
if (!deps.length) return;
|
|
691
|
+
let packageJson;
|
|
692
|
+
try {
|
|
693
|
+
const content = await readFile(join(getCwd(), "package.json"), "utf-8");
|
|
694
|
+
packageJson = JSON.parse(content);
|
|
695
|
+
} catch (error) {
|
|
696
|
+
if (typeof error !== "object" || error === null || !("code" in error) || error.code !== "ENOENT") {
|
|
697
|
+
throw error;
|
|
698
|
+
}
|
|
699
|
+
console.log("No package.json found, skipping dependency install.");
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const dependencies = typeof packageJson.dependencies === "object" && packageJson.dependencies !== null ? packageJson.dependencies : {};
|
|
703
|
+
const devDependencies = typeof packageJson.devDependencies === "object" && packageJson.devDependencies !== null ? packageJson.devDependencies : {};
|
|
704
|
+
const existingDeps = {
|
|
705
|
+
...dependencies,
|
|
706
|
+
...devDependencies
|
|
707
|
+
};
|
|
708
|
+
const packageManager = await detectPackageManager(packageJson);
|
|
709
|
+
const missing = deps.filter((dep) => {
|
|
710
|
+
const { name } = parseDependencySpec(dep);
|
|
711
|
+
return !existingDeps[name];
|
|
712
|
+
});
|
|
713
|
+
assertSafeDependencyNames(missing);
|
|
714
|
+
if (!missing.length) {
|
|
715
|
+
console.log("All dependencies already installed.\n");
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
console.log("Installing dependencies:");
|
|
719
|
+
missing.forEach((dependency) => console.log(`- ${dependency}`));
|
|
720
|
+
console.log(`Using package manager: ${packageManager}`);
|
|
721
|
+
console.log("");
|
|
722
|
+
const installCommand = packageManager === "pnpm" ? ["pnpm", ["add", ...options.dev ? ["-D"] : [], ...missing]] : packageManager === "yarn" ? ["yarn", ["add", ...options.dev ? ["--dev"] : [], ...missing]] : [
|
|
723
|
+
"npm",
|
|
724
|
+
["install", ...options.dev ? ["--save-dev"] : [], ...missing]
|
|
725
|
+
];
|
|
726
|
+
const [command2, commandArgs] = installCommand;
|
|
727
|
+
const invocation = getPackageManagerInvocation(command2, commandArgs);
|
|
728
|
+
execFileSync(invocation.command, invocation.args, {
|
|
729
|
+
cwd: getCwd(),
|
|
730
|
+
stdio: "inherit"
|
|
731
|
+
});
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
// src/registry/remote.ts
|
|
735
|
+
var isStringArray = (value) => {
|
|
736
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
737
|
+
};
|
|
738
|
+
var isRegistryStyle = (value) => {
|
|
739
|
+
if (typeof value !== "object" || value === null) {
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
const style = value;
|
|
743
|
+
return typeof style.name === "string" && typeof style.version === "string" && Array.isArray(style.files) && style.files.every((file) => {
|
|
744
|
+
return typeof file === "object" && file !== null && typeof file.path === "string" && typeof file.target === "string";
|
|
745
|
+
});
|
|
746
|
+
};
|
|
747
|
+
var isRegistryItem = (value) => {
|
|
748
|
+
if (typeof value !== "object" || value === null) {
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
const item = value;
|
|
752
|
+
return typeof item.name === "string" && typeof item.canonicalName === "string" && typeof item.version === "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";
|
|
753
|
+
};
|
|
754
|
+
var findInvalidRegistryItemIndex = (items) => {
|
|
755
|
+
return items.findIndex((item) => {
|
|
756
|
+
return !isRegistryItem(item);
|
|
757
|
+
});
|
|
758
|
+
};
|
|
759
|
+
var parseRegistryItems = (items) => {
|
|
760
|
+
const invalidIndex = findInvalidRegistryItemIndex(items);
|
|
761
|
+
if (invalidIndex !== -1) {
|
|
762
|
+
throw new Error(
|
|
763
|
+
`Remote registry contains invalid registry item at index ${invalidIndex}.`
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
return items;
|
|
767
|
+
};
|
|
768
|
+
var parseRegistryStyles = (styles) => {
|
|
769
|
+
const invalidIndex = styles.findIndex((style) => {
|
|
770
|
+
return !isRegistryStyle(style);
|
|
771
|
+
});
|
|
772
|
+
if (invalidIndex !== -1) {
|
|
773
|
+
throw new Error(
|
|
774
|
+
`Remote registry contains invalid style entry at index ${invalidIndex}.`
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
return styles;
|
|
778
|
+
};
|
|
779
|
+
var parseRemoteRegistry = (value) => {
|
|
780
|
+
if (Array.isArray(value)) {
|
|
781
|
+
return {
|
|
782
|
+
version: "unknown",
|
|
783
|
+
items: parseRegistryItems(value)
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
if (typeof value !== "object" || value === null) {
|
|
787
|
+
throw new Error("Remote registry must be a JSON array or manifest object.");
|
|
788
|
+
}
|
|
789
|
+
const manifest = value;
|
|
790
|
+
if (typeof manifest.version !== "string" || !Array.isArray(manifest.items)) {
|
|
791
|
+
throw new Error("Remote registry manifest must contain version and items.");
|
|
792
|
+
}
|
|
793
|
+
const parsed = {
|
|
794
|
+
version: manifest.version,
|
|
795
|
+
items: parseRegistryItems(manifest.items)
|
|
796
|
+
};
|
|
797
|
+
if (manifest.styles !== void 0) {
|
|
798
|
+
if (!Array.isArray(manifest.styles)) {
|
|
799
|
+
throw new Error("Remote registry manifest styles must be an array.");
|
|
800
|
+
}
|
|
801
|
+
parsed.styles = parseRegistryStyles(manifest.styles);
|
|
802
|
+
}
|
|
803
|
+
return parsed;
|
|
804
|
+
};
|
|
805
|
+
var fetchRemoteRegistry = async (url) => {
|
|
806
|
+
const response = await fetch(url);
|
|
807
|
+
if (!response.ok) {
|
|
808
|
+
throw new Error(`Remote registry responded with HTTP ${response.status}`);
|
|
809
|
+
}
|
|
810
|
+
const data = await response.json();
|
|
811
|
+
return parseRemoteRegistry(data);
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
// src/registry/source.ts
|
|
815
|
+
var getRegistrySource = async () => {
|
|
816
|
+
const config = await loadConfig();
|
|
817
|
+
return config.registryUrl ?? "local";
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// src/registry/provider.ts
|
|
821
|
+
var cachedRegistry = null;
|
|
822
|
+
var cachedSource = null;
|
|
823
|
+
var cachedManifestVersion = null;
|
|
824
|
+
var fallbackWarningShown = false;
|
|
825
|
+
var getRegistryItems = async (options = {}) => {
|
|
826
|
+
const fallback = options.fallback ?? true;
|
|
827
|
+
const source = await getRegistrySource();
|
|
828
|
+
if (cachedRegistry && cachedSource === source) {
|
|
829
|
+
return cachedRegistry;
|
|
830
|
+
}
|
|
831
|
+
if (source === "local") {
|
|
832
|
+
validateRegistry(registryItems, {
|
|
833
|
+
styles: registryStyles
|
|
834
|
+
});
|
|
835
|
+
cachedRegistry = registryItems;
|
|
836
|
+
cachedSource = source;
|
|
837
|
+
cachedManifestVersion = registryVersion;
|
|
838
|
+
return registryItems;
|
|
839
|
+
}
|
|
840
|
+
try {
|
|
841
|
+
const remote = await fetchRemoteRegistry(source);
|
|
842
|
+
validateRegistry(
|
|
843
|
+
remote.items,
|
|
844
|
+
remote.styles !== void 0 ? {
|
|
845
|
+
styles: remote.styles
|
|
846
|
+
} : {}
|
|
847
|
+
);
|
|
848
|
+
cachedRegistry = remote.items;
|
|
849
|
+
cachedSource = source;
|
|
850
|
+
cachedManifestVersion = remote.version;
|
|
851
|
+
return remote.items;
|
|
852
|
+
} catch (error) {
|
|
853
|
+
if (!fallback) {
|
|
854
|
+
throw error;
|
|
855
|
+
}
|
|
856
|
+
if (!fallbackWarningShown) {
|
|
857
|
+
console.log("Remote registry failed. Falling back to local registry.");
|
|
858
|
+
fallbackWarningShown = true;
|
|
859
|
+
}
|
|
860
|
+
cachedRegistry = registryItems;
|
|
861
|
+
cachedSource = source;
|
|
862
|
+
cachedManifestVersion = registryVersion;
|
|
863
|
+
return registryItems;
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
var getRegistryProviderResult = async (options = {}) => {
|
|
867
|
+
const source = await getRegistrySource();
|
|
868
|
+
const items = await getRegistryItems(options);
|
|
869
|
+
validateRegistry(
|
|
870
|
+
items,
|
|
871
|
+
items === registryItems ? {
|
|
872
|
+
styles: registryStyles
|
|
873
|
+
} : {}
|
|
874
|
+
);
|
|
875
|
+
return {
|
|
876
|
+
items,
|
|
877
|
+
source,
|
|
878
|
+
fallbackUsed: source !== "local" && items === registryItems,
|
|
879
|
+
manifestVersion: cachedManifestVersion ?? registryVersion
|
|
880
|
+
};
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
// src/utils/suggestions.ts
|
|
884
|
+
var calculateLevenshteinDistance = (firstValue, secondValue) => {
|
|
885
|
+
const firstLength = firstValue.length;
|
|
886
|
+
const secondLength = secondValue.length;
|
|
887
|
+
const matrix = Array.from(
|
|
888
|
+
{ length: firstLength + 1 },
|
|
889
|
+
() => Array.from({ length: secondLength + 1 }, () => 0)
|
|
890
|
+
);
|
|
891
|
+
for (let firstIndex = 0; firstIndex <= firstLength; firstIndex += 1) {
|
|
892
|
+
matrix[firstIndex][0] = firstIndex;
|
|
893
|
+
}
|
|
894
|
+
for (let secondIndex = 0; secondIndex <= secondLength; secondIndex += 1) {
|
|
895
|
+
matrix[0][secondIndex] = secondIndex;
|
|
896
|
+
}
|
|
897
|
+
for (let firstIndex = 1; firstIndex <= firstLength; firstIndex += 1) {
|
|
898
|
+
for (let secondIndex = 1; secondIndex <= secondLength; secondIndex += 1) {
|
|
899
|
+
const cost = firstValue[firstIndex - 1] === secondValue[secondIndex - 1] ? 0 : 1;
|
|
900
|
+
matrix[firstIndex][secondIndex] = Math.min(
|
|
901
|
+
matrix[firstIndex - 1][secondIndex] + 1,
|
|
902
|
+
matrix[firstIndex][secondIndex - 1] + 1,
|
|
903
|
+
matrix[firstIndex - 1][secondIndex - 1] + cost
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return matrix[firstLength][secondLength];
|
|
908
|
+
};
|
|
909
|
+
var findClosestValue = (input, values) => {
|
|
910
|
+
const normalizedInput = input.toLowerCase();
|
|
911
|
+
const ranked = values.map((value) => ({
|
|
912
|
+
value,
|
|
913
|
+
distance: calculateLevenshteinDistance(
|
|
914
|
+
normalizedInput,
|
|
915
|
+
value.toLowerCase()
|
|
916
|
+
)
|
|
917
|
+
})).sort((first, second) => first.distance - second.distance);
|
|
918
|
+
const closest = ranked[0];
|
|
919
|
+
if (!closest || closest.distance > 3) {
|
|
920
|
+
return void 0;
|
|
921
|
+
}
|
|
922
|
+
return closest.value;
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
// src/registry/resolver.ts
|
|
926
|
+
var registryStyles2 = registryStyles;
|
|
927
|
+
var registryUtilities = registryUtilities$1;
|
|
928
|
+
var normalizeName = (name) => {
|
|
929
|
+
return name.toLowerCase();
|
|
930
|
+
};
|
|
931
|
+
var findItem = async (name, options = {}) => {
|
|
932
|
+
const items = await getRegistryItems(options);
|
|
933
|
+
const normalizedName = normalizeName(name);
|
|
934
|
+
const item = items.find(
|
|
935
|
+
(registryItem) => normalizeName(registryItem.name) === normalizedName || normalizeName(registryItem.canonicalName) === normalizedName || registryItem.aliases.some(
|
|
936
|
+
(alias) => normalizeName(alias) === normalizedName
|
|
937
|
+
)
|
|
938
|
+
);
|
|
939
|
+
if (item) {
|
|
940
|
+
validateRegistryItem(item);
|
|
941
|
+
}
|
|
942
|
+
return item;
|
|
943
|
+
};
|
|
944
|
+
var resolveRegistryItems = async (names, options = {}) => {
|
|
945
|
+
const items = await getRegistryItems(options);
|
|
946
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
947
|
+
const findLocalItem = (name) => {
|
|
948
|
+
const normalizedName = normalizeName(name);
|
|
949
|
+
return items.find(
|
|
950
|
+
(item) => normalizeName(item.name) === normalizedName || normalizeName(item.canonicalName) === normalizedName || item.aliases.some((alias) => normalizeName(alias) === normalizedName)
|
|
951
|
+
);
|
|
952
|
+
};
|
|
953
|
+
const visit = (name) => {
|
|
954
|
+
const item = findLocalItem(name);
|
|
955
|
+
if (!item) {
|
|
956
|
+
const availableNames = items.flatMap((registryItem) => [
|
|
957
|
+
registryItem.name,
|
|
958
|
+
registryItem.canonicalName,
|
|
959
|
+
...registryItem.aliases
|
|
960
|
+
]);
|
|
961
|
+
const suggestion = findClosestValue(name, availableNames);
|
|
962
|
+
throw new CliError(
|
|
963
|
+
suggestion ? `Component "${name}" not found. Did you mean "${suggestion}"?` : `Component "${name}" not found.`
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
validateRegistryItem(item);
|
|
967
|
+
const key = normalizeName(item.canonicalName);
|
|
968
|
+
if (resolved.has(key)) {
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
resolved.set(key, item);
|
|
972
|
+
for (const dependency of item.registryDependencies) {
|
|
973
|
+
visit(dependency);
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
for (const name of names) {
|
|
977
|
+
visit(name);
|
|
978
|
+
}
|
|
979
|
+
return Array.from(resolved.values());
|
|
980
|
+
};
|
|
981
|
+
var collectDependencies = (items) => {
|
|
982
|
+
return Array.from(new Set(items.flatMap((item) => item.dependencies)));
|
|
983
|
+
};
|
|
984
|
+
var collectUtilities = (items) => {
|
|
985
|
+
return Array.from(new Set(items.flatMap((item) => item.utilities)));
|
|
986
|
+
};
|
|
987
|
+
var collectStyles = (items) => {
|
|
988
|
+
return Array.from(new Set(items.flatMap((item) => item.styles)));
|
|
989
|
+
};
|
|
990
|
+
var resolveRegistryStyles = (names) => {
|
|
991
|
+
return names.map((name) => {
|
|
992
|
+
const style = registryStyles2.find((registryStyle) => {
|
|
993
|
+
return normalizeName(registryStyle.name) === normalizeName(name);
|
|
994
|
+
});
|
|
995
|
+
if (!style) {
|
|
996
|
+
throw new CliError(`Style "${name}" not found in registry.`);
|
|
997
|
+
}
|
|
998
|
+
return style;
|
|
999
|
+
});
|
|
1000
|
+
};
|
|
1001
|
+
var resolveRegistryUtilities = (names) => {
|
|
1002
|
+
return names.map((name) => {
|
|
1003
|
+
const utility = registryUtilities.find((registryUtility) => {
|
|
1004
|
+
return normalizeName(registryUtility.name) === normalizeName(name);
|
|
1005
|
+
});
|
|
1006
|
+
if (!utility) {
|
|
1007
|
+
throw new CliError(`Utility "${name}" not found in registry.`);
|
|
1008
|
+
}
|
|
1009
|
+
return utility;
|
|
1010
|
+
});
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
// src/core/flags.ts
|
|
1014
|
+
var hasFlag = (args2, ...flags) => {
|
|
1015
|
+
return flags.some((f) => args2.includes(f));
|
|
1016
|
+
};
|
|
1017
|
+
var removeFlags = (args2, flags) => {
|
|
1018
|
+
return args2.filter((arg) => !flags.includes(arg));
|
|
1019
|
+
};
|
|
1020
|
+
var getFlagValue = (args2, ...flags) => {
|
|
1021
|
+
for (const flag of flags) {
|
|
1022
|
+
const index = args2.indexOf(flag);
|
|
1023
|
+
if (index !== -1) return args2[index + 1];
|
|
1024
|
+
}
|
|
1025
|
+
return void 0;
|
|
1026
|
+
};
|
|
1027
|
+
var removeFlagsWithValues = (args2, flags) => {
|
|
1028
|
+
const result = [];
|
|
1029
|
+
for (let index = 0; index < args2.length; index += 1) {
|
|
1030
|
+
const arg = args2[index];
|
|
1031
|
+
if (flags.includes(arg)) {
|
|
1032
|
+
index += 1;
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
result.push(arg);
|
|
1036
|
+
}
|
|
1037
|
+
return result;
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
// src/commands/add.ts
|
|
1041
|
+
var promptSelectItems = async () => {
|
|
1042
|
+
const response = await prompts({
|
|
1043
|
+
type: "multiselect",
|
|
1044
|
+
name: "items",
|
|
1045
|
+
message: "Select components to add",
|
|
1046
|
+
choices: registryItems.map((item) => ({
|
|
1047
|
+
title: `${item.canonicalName} (${item.category})`,
|
|
1048
|
+
value: item.name
|
|
1049
|
+
}))
|
|
1050
|
+
});
|
|
1051
|
+
if (typeof response !== "object" || response === null) {
|
|
1052
|
+
return [];
|
|
1053
|
+
}
|
|
1054
|
+
const items = response.items;
|
|
1055
|
+
if (!Array.isArray(items)) {
|
|
1056
|
+
return [];
|
|
1057
|
+
}
|
|
1058
|
+
return items.filter((item) => typeof item === "string");
|
|
1059
|
+
};
|
|
1060
|
+
var runAdd = async (args2) => {
|
|
1061
|
+
const dryRun = hasFlag(args2, "--dry-run", "-d");
|
|
1062
|
+
hasFlag(args2, "--yes", "-y");
|
|
1063
|
+
const noFallback = hasFlag(args2, "--no-fallback");
|
|
1064
|
+
let items = removeFlagsWithValues(args2, ["--cwd", "-C"]);
|
|
1065
|
+
items = removeFlags(items, [
|
|
1066
|
+
"--dry-run",
|
|
1067
|
+
"-d",
|
|
1068
|
+
"--yes",
|
|
1069
|
+
"-y",
|
|
1070
|
+
"--no-fallback"
|
|
1071
|
+
]);
|
|
1072
|
+
if (!items.length) {
|
|
1073
|
+
items = await promptSelectItems();
|
|
1074
|
+
if (!items.length) {
|
|
1075
|
+
console.log("No components selected.");
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
let resolvedItems;
|
|
1080
|
+
try {
|
|
1081
|
+
resolvedItems = await resolveRegistryItems(items, {
|
|
1082
|
+
fallback: !noFallback
|
|
1083
|
+
});
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
console.log("Failed to resolve registry.");
|
|
1086
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
1087
|
+
process.exitCode = 1;
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const dependencies = collectDependencies(resolvedItems);
|
|
1091
|
+
const utilities = collectUtilities(resolvedItems);
|
|
1092
|
+
const styleNames = collectStyles(resolvedItems);
|
|
1093
|
+
let resolvedUtilities;
|
|
1094
|
+
let styles;
|
|
1095
|
+
try {
|
|
1096
|
+
resolvedUtilities = resolveRegistryUtilities(utilities);
|
|
1097
|
+
styles = resolveRegistryStyles(styleNames);
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
console.log("Failed to resolve registry shared resources.");
|
|
1100
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
1101
|
+
process.exitCode = 1;
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
const config = await loadConfig();
|
|
1105
|
+
if (dryRun) {
|
|
1106
|
+
console.log("Dry run: no files or dependencies will be changed.\n");
|
|
1107
|
+
console.log("Components:");
|
|
1108
|
+
for (const item of resolvedItems) {
|
|
1109
|
+
console.log(`- ${item.canonicalName} v${item.version}`);
|
|
1110
|
+
}
|
|
1111
|
+
console.log("\nDependencies:");
|
|
1112
|
+
for (const dependency of dependencies) {
|
|
1113
|
+
console.log(`- ${dependency}`);
|
|
1114
|
+
}
|
|
1115
|
+
console.log("\nUtilities:");
|
|
1116
|
+
for (const utility of utilities) {
|
|
1117
|
+
console.log(`- ${utility}`);
|
|
1118
|
+
}
|
|
1119
|
+
console.log("\nStyles:");
|
|
1120
|
+
for (const styleName of styleNames) {
|
|
1121
|
+
console.log(`- ${styleName}`);
|
|
1122
|
+
}
|
|
1123
|
+
console.log("\nInstall paths:");
|
|
1124
|
+
console.log(`- components: ${config.paths.components}`);
|
|
1125
|
+
console.log(`- utilities: ${config.paths.utilities}`);
|
|
1126
|
+
console.log(`- styles: ${config.paths.styles}`);
|
|
1127
|
+
console.log(`- tailwind css: ${config.tailwind.css}`);
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
await ensureProjectStructure(config);
|
|
1131
|
+
await installDependencies(dependencies);
|
|
1132
|
+
const utilitiesResult = await installUtilities(resolvedUtilities, config);
|
|
1133
|
+
const stylesResult = await installStyles(styles, config);
|
|
1134
|
+
const successfullyInstalled = [];
|
|
1135
|
+
const itemResults = [];
|
|
1136
|
+
for (const item of resolvedItems) {
|
|
1137
|
+
const itemResult = await installItemFiles(item, config);
|
|
1138
|
+
itemResults.push(itemResult);
|
|
1139
|
+
if (hasInstallConflicts(itemResult)) {
|
|
1140
|
+
console.log(
|
|
1141
|
+
`${item.canonicalName} was not marked as installed because conflicts were found.`
|
|
1142
|
+
);
|
|
1143
|
+
} else {
|
|
1144
|
+
successfullyInstalled.push(item);
|
|
1145
|
+
}
|
|
1146
|
+
console.log("");
|
|
1147
|
+
}
|
|
1148
|
+
const installed = {
|
|
1149
|
+
...config.installed ?? {}
|
|
1150
|
+
};
|
|
1151
|
+
for (const item of successfullyInstalled) {
|
|
1152
|
+
installed[item.name] = item.version;
|
|
1153
|
+
}
|
|
1154
|
+
await saveConfig({
|
|
1155
|
+
...config,
|
|
1156
|
+
installed
|
|
1157
|
+
});
|
|
1158
|
+
const itemSummary = mergeInstallResults(itemResults);
|
|
1159
|
+
const sharedSummary = mergeInstallResults([utilitiesResult, stylesResult]);
|
|
1160
|
+
console.log("Install summary:");
|
|
1161
|
+
printResourceSummary("components", itemSummary);
|
|
1162
|
+
printResourceSummary("shared resources", sharedSummary);
|
|
1163
|
+
console.log(
|
|
1164
|
+
`- tracked components: ${successfullyInstalled.length}/${resolvedItems.length}`
|
|
1165
|
+
);
|
|
1166
|
+
if (hasInstallConflicts(sharedSummary)) {
|
|
1167
|
+
console.log(
|
|
1168
|
+
"Shared resource conflicts were left untouched. Review them before relying on the installed components."
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
|
|
1173
|
+
// src/commands/config.ts
|
|
1174
|
+
var runConfig = async (options = {}) => {
|
|
1175
|
+
const configPath = getConfigPath();
|
|
1176
|
+
if (options.path) {
|
|
1177
|
+
console.log(configPath);
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
if (options.exists) {
|
|
1181
|
+
const exists = await fileExists(configPath);
|
|
1182
|
+
console.log(exists ? "Config exists." : "Config does not exist.");
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
if (options.clearRegistryUrl) {
|
|
1186
|
+
const config2 = await loadConfig();
|
|
1187
|
+
await saveConfig({
|
|
1188
|
+
...config2,
|
|
1189
|
+
registryUrl: null
|
|
1190
|
+
});
|
|
1191
|
+
console.log("Registry URL cleared.");
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
if (options.setRegistryUrl !== void 0) {
|
|
1195
|
+
const config2 = await loadConfig();
|
|
1196
|
+
await saveConfig({
|
|
1197
|
+
...config2,
|
|
1198
|
+
registryUrl: options.setRegistryUrl || null
|
|
1199
|
+
});
|
|
1200
|
+
console.log(
|
|
1201
|
+
options.setRegistryUrl ? `Registry URL set to: ${options.setRegistryUrl}` : "Registry URL cleared."
|
|
1202
|
+
);
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
const config = await loadConfig();
|
|
1206
|
+
console.log(JSON.stringify(config, null, 2));
|
|
1207
|
+
};
|
|
1208
|
+
var runDoctor = async (options = {}) => {
|
|
1209
|
+
console.log("Lexsys doctor\n");
|
|
1210
|
+
const config = await loadConfig();
|
|
1211
|
+
let registryFailed = false;
|
|
1212
|
+
const checks = [
|
|
1213
|
+
{
|
|
1214
|
+
label: "package.json",
|
|
1215
|
+
path: join(getCwd(), "package.json")
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
label: config.paths.components,
|
|
1219
|
+
path: join(getCwd(), config.paths.components)
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
label: config.paths.utilities,
|
|
1223
|
+
path: join(getCwd(), config.paths.utilities)
|
|
1224
|
+
},
|
|
1225
|
+
{
|
|
1226
|
+
label: config.paths.styles,
|
|
1227
|
+
path: join(getCwd(), config.paths.styles)
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
label: config.tailwind.css,
|
|
1231
|
+
path: join(getCwd(), config.tailwind.css)
|
|
1232
|
+
}
|
|
1233
|
+
];
|
|
1234
|
+
for (const check of checks) {
|
|
1235
|
+
const exists = await fileExists(check.path);
|
|
1236
|
+
console.log(`${exists ? "\u2713" : "\xD7"} ${check.label}`);
|
|
1237
|
+
}
|
|
1238
|
+
try {
|
|
1239
|
+
const registryResult = await getRegistryProviderResult({
|
|
1240
|
+
fallback: !options.noFallback
|
|
1241
|
+
});
|
|
1242
|
+
console.log("\nRegistry:");
|
|
1243
|
+
console.log(`\u2713 source: ${registryResult.source}`);
|
|
1244
|
+
console.log(`\u2713 fallback: ${registryResult.fallbackUsed ? "yes" : "no"}`);
|
|
1245
|
+
console.log(`\u2713 items: ${registryResult.items.length}`);
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
registryFailed = true;
|
|
1248
|
+
console.log("\nRegistry:");
|
|
1249
|
+
console.log("\xD7 failed to resolve registry");
|
|
1250
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
1251
|
+
process.exitCode = 1;
|
|
1252
|
+
}
|
|
1253
|
+
if (registryFailed && options.noFallback) {
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
const installed = config.installed ?? {};
|
|
1257
|
+
if (Object.keys(installed).length) {
|
|
1258
|
+
console.log("\nTracked components:");
|
|
1259
|
+
for (const [name, version] of Object.entries(installed)) {
|
|
1260
|
+
const item = await findItem(name);
|
|
1261
|
+
if (!item) {
|
|
1262
|
+
console.log(`\xD7 ${name} v${version} (missing from registry)`);
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
const componentPath = join(
|
|
1266
|
+
getCwd(),
|
|
1267
|
+
resolveItemInstallTarget(config, item)
|
|
1268
|
+
);
|
|
1269
|
+
const exists = await fileExists(componentPath);
|
|
1270
|
+
const layer = getInstallLayer(item) ?? "unknown";
|
|
1271
|
+
console.log(
|
|
1272
|
+
`${exists ? "\u2713" : "\xD7"} ${item.canonicalName} v${version} (${layer})`
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
// src/commands/help.ts
|
|
1279
|
+
var COMMAND_HELP = {
|
|
1280
|
+
init: `
|
|
1281
|
+
Usage
|
|
1282
|
+
lexsys init [framework] [directory]
|
|
1283
|
+
lexsys create [framework] [directory]
|
|
1284
|
+
|
|
1285
|
+
Frameworks
|
|
1286
|
+
vite [directory] Scaffold a Vite React app and initialize Lexsys
|
|
1287
|
+
next [directory] Scaffold a Next.js App Router app and initialize Lexsys
|
|
1288
|
+
|
|
1289
|
+
Run without arguments for guided setup.
|
|
1290
|
+
|
|
1291
|
+
Options
|
|
1292
|
+
--help, -h Show this help
|
|
1293
|
+
|
|
1294
|
+
Examples
|
|
1295
|
+
lexsys create
|
|
1296
|
+
lexsys create vite
|
|
1297
|
+
lexsys create vite my-app
|
|
1298
|
+
lexsys create next my-app
|
|
1299
|
+
`,
|
|
1300
|
+
add: `
|
|
1301
|
+
Usage
|
|
1302
|
+
lexsys add <component...>
|
|
1303
|
+
lexsys a <component...>
|
|
1304
|
+
|
|
1305
|
+
Options
|
|
1306
|
+
--dry-run, -d Preview files, dependencies, and install paths
|
|
1307
|
+
--yes, -y Auto-confirm safe prompts
|
|
1308
|
+
--no-fallback Fail instead of falling back to local registry
|
|
1309
|
+
--cwd, -C <path> Run from a different project directory
|
|
1310
|
+
--help, -h Show this help
|
|
1311
|
+
|
|
1312
|
+
Examples
|
|
1313
|
+
lexsys add button
|
|
1314
|
+
lexsys add button dialog toast
|
|
1315
|
+
lexsys add button --dry-run
|
|
1316
|
+
lexsys add button --cwd ./apps/web
|
|
1317
|
+
`,
|
|
1318
|
+
update: `
|
|
1319
|
+
Usage
|
|
1320
|
+
lexsys update [component...]
|
|
1321
|
+
lexsys up [component...]
|
|
1322
|
+
|
|
1323
|
+
Options
|
|
1324
|
+
--all, -a Update all tracked components
|
|
1325
|
+
--styles, -S Update generated token/theme CSS files
|
|
1326
|
+
--utilities, -u Update shared utility files
|
|
1327
|
+
--sync Refresh tracked components even when versions match
|
|
1328
|
+
--dry-run, -d Preview update without writing files
|
|
1329
|
+
--force, -f Write conflicted updates after creating backups
|
|
1330
|
+
--yes, -y Auto-confirm safe prompts
|
|
1331
|
+
--no-fallback Fail instead of falling back to local registry
|
|
1332
|
+
--cwd, -C <path> Run from a different project directory
|
|
1333
|
+
--help, -h Show this help
|
|
1334
|
+
|
|
1335
|
+
Run without arguments for guided update picker.
|
|
1336
|
+
|
|
1337
|
+
Examples
|
|
1338
|
+
lexsys up
|
|
1339
|
+
lexsys up button
|
|
1340
|
+
lexsys up --all
|
|
1341
|
+
lexsys up --styles
|
|
1342
|
+
lexsys up --all --sync --utilities --styles
|
|
1343
|
+
`,
|
|
1344
|
+
list: `
|
|
1345
|
+
Usage
|
|
1346
|
+
lexsys list
|
|
1347
|
+
lexsys ls
|
|
1348
|
+
|
|
1349
|
+
Options
|
|
1350
|
+
--json, -j Print registry items as JSON
|
|
1351
|
+
--no-fallback Fail instead of falling back to local registry
|
|
1352
|
+
--help, -h Show this help
|
|
1353
|
+
|
|
1354
|
+
Examples
|
|
1355
|
+
lexsys ls
|
|
1356
|
+
lexsys ls --json
|
|
1357
|
+
`,
|
|
1358
|
+
status: `
|
|
1359
|
+
Usage
|
|
1360
|
+
lexsys status
|
|
1361
|
+
lexsys st
|
|
1362
|
+
|
|
1363
|
+
Options
|
|
1364
|
+
--no-fallback Fail instead of falling back to local registry
|
|
1365
|
+
--help, -h Show this help
|
|
1366
|
+
`,
|
|
1367
|
+
uninstall: `
|
|
1368
|
+
Usage
|
|
1369
|
+
lexsys uninstall [component...]
|
|
1370
|
+
lexsys rm [component...]
|
|
1371
|
+
|
|
1372
|
+
Options
|
|
1373
|
+
--dry-run, -d Preview uninstall without removing files
|
|
1374
|
+
--with-deps, -w Also remove registry-owned shared dependencies
|
|
1375
|
+
--help, -h Show this help
|
|
1376
|
+
|
|
1377
|
+
Run without arguments for guided uninstall picker.
|
|
1378
|
+
|
|
1379
|
+
Examples
|
|
1380
|
+
lexsys rm button
|
|
1381
|
+
lexsys rm button --dry-run
|
|
1382
|
+
`,
|
|
1383
|
+
doctor: `
|
|
1384
|
+
Usage
|
|
1385
|
+
lexsys doctor
|
|
1386
|
+
lexsys dr
|
|
1387
|
+
|
|
1388
|
+
Options
|
|
1389
|
+
--no-fallback Fail instead of falling back to local registry
|
|
1390
|
+
--help, -h Show this help
|
|
1391
|
+
`,
|
|
1392
|
+
registry: `
|
|
1393
|
+
Usage
|
|
1394
|
+
lexsys registry
|
|
1395
|
+
lexsys reg
|
|
1396
|
+
|
|
1397
|
+
Options
|
|
1398
|
+
--summary, -s Print a human-readable registry summary
|
|
1399
|
+
--source Print effective registry source
|
|
1400
|
+
--local, -l Read only the bundled local registry
|
|
1401
|
+
--remote, -r Read only the configured remote registry
|
|
1402
|
+
--no-fallback Disable local fallback for default resolution
|
|
1403
|
+
--help, -h Show this help
|
|
1404
|
+
|
|
1405
|
+
Examples
|
|
1406
|
+
lexsys reg --summary
|
|
1407
|
+
lexsys reg --remote --source
|
|
1408
|
+
`,
|
|
1409
|
+
config: `
|
|
1410
|
+
Usage
|
|
1411
|
+
lexsys config
|
|
1412
|
+
lexsys cfg
|
|
1413
|
+
|
|
1414
|
+
Options
|
|
1415
|
+
--path, -p Print config file path
|
|
1416
|
+
--exists, -e Check whether config exists
|
|
1417
|
+
--set-registry-url <url> Set remote registry URL
|
|
1418
|
+
--clear-registry-url Clear remote registry URL
|
|
1419
|
+
--help, -h Show this help
|
|
1420
|
+
|
|
1421
|
+
Examples
|
|
1422
|
+
lexsys cfg --path
|
|
1423
|
+
lexsys cfg --set-registry-url https://example.com/registry.json
|
|
1424
|
+
`
|
|
1425
|
+
};
|
|
1426
|
+
var runHelpFor = (command2) => {
|
|
1427
|
+
const help = COMMAND_HELP[command2];
|
|
1428
|
+
if (help) {
|
|
1429
|
+
console.log(help);
|
|
1430
|
+
} else {
|
|
1431
|
+
runHelp();
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
var runHelp = () => {
|
|
1435
|
+
console.log(`
|
|
1436
|
+
Lexsys CLI
|
|
1437
|
+
|
|
1438
|
+
Usage
|
|
1439
|
+
lexsys <command> [options]
|
|
1440
|
+
|
|
1441
|
+
Scaffold
|
|
1442
|
+
init, create Initialize Lexsys or scaffold a framework starter
|
|
1443
|
+
Alias: create | Run without args for guided setup
|
|
1444
|
+
|
|
1445
|
+
Components
|
|
1446
|
+
add <component...> Install components into your project [alias: a]
|
|
1447
|
+
update [component...] Update installed components [alias: up]
|
|
1448
|
+
uninstall [component...] Remove installed components [alias: rm]
|
|
1449
|
+
|
|
1450
|
+
Inspect
|
|
1451
|
+
list List available registry items [alias: ls]
|
|
1452
|
+
status Show installed component status [alias: st]
|
|
1453
|
+
doctor Check local project setup [alias: dr]
|
|
1454
|
+
registry Inspect registry source and manifest [alias: reg]
|
|
1455
|
+
config Print or update Lexsys config [alias: cfg]
|
|
1456
|
+
|
|
1457
|
+
Meta
|
|
1458
|
+
version Print CLI version [-v]
|
|
1459
|
+
help Show this help [-h]
|
|
1460
|
+
|
|
1461
|
+
Global Options
|
|
1462
|
+
--cwd, -C <path> Run from a different project directory
|
|
1463
|
+
--yes, -y Auto-confirm safe prompts where supported
|
|
1464
|
+
--no-fallback Disable local registry fallback
|
|
1465
|
+
--help, -h Show help
|
|
1466
|
+
--version, -v Show CLI version
|
|
1467
|
+
|
|
1468
|
+
Run \`lexsys <command> --help\` for command-specific options.
|
|
1469
|
+
`);
|
|
1470
|
+
};
|
|
1471
|
+
var NEXT_VERSION = "15.3.3";
|
|
1472
|
+
var gitIgnore = `node_modules
|
|
1473
|
+
.next
|
|
1474
|
+
out
|
|
1475
|
+
dist
|
|
1476
|
+
*.local
|
|
1477
|
+
.env
|
|
1478
|
+
.env.*
|
|
1479
|
+
!.env.example
|
|
1480
|
+
`;
|
|
1481
|
+
var prettierIgnore = `node_modules
|
|
1482
|
+
.next
|
|
1483
|
+
out
|
|
1484
|
+
dist
|
|
1485
|
+
coverage
|
|
1486
|
+
pnpm-lock.yaml
|
|
1487
|
+
package-lock.json
|
|
1488
|
+
yarn.lock
|
|
1489
|
+
`;
|
|
1490
|
+
var prettierConfig = `{
|
|
1491
|
+
"semi": false,
|
|
1492
|
+
"trailingComma": "all"
|
|
1493
|
+
}
|
|
1494
|
+
`;
|
|
1495
|
+
var nextConfig = `import type { NextConfig } from "next"
|
|
1496
|
+
|
|
1497
|
+
const nextConfig: NextConfig = {}
|
|
1498
|
+
|
|
1499
|
+
export default nextConfig
|
|
1500
|
+
`;
|
|
1501
|
+
var postcssConfig = `const config = {
|
|
1502
|
+
plugins: {
|
|
1503
|
+
"@tailwindcss/postcss": {},
|
|
1504
|
+
},
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
export default config
|
|
1508
|
+
`;
|
|
1509
|
+
var tsConfig = `{
|
|
1510
|
+
"compilerOptions": {
|
|
1511
|
+
"target": "ES2017",
|
|
1512
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
1513
|
+
"allowJs": true,
|
|
1514
|
+
"skipLibCheck": true,
|
|
1515
|
+
"strict": true,
|
|
1516
|
+
"noEmit": true,
|
|
1517
|
+
"esModuleInterop": true,
|
|
1518
|
+
"module": "esnext",
|
|
1519
|
+
"moduleResolution": "bundler",
|
|
1520
|
+
"resolveJsonModule": true,
|
|
1521
|
+
"isolatedModules": true,
|
|
1522
|
+
"jsx": "preserve",
|
|
1523
|
+
"incremental": true,
|
|
1524
|
+
"plugins": [{ "name": "next" }],
|
|
1525
|
+
"paths": {
|
|
1526
|
+
"@/*": ["./src/*"]
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
"include": ["next-env.d.ts", "global.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
1530
|
+
"exclude": ["node_modules"]
|
|
1531
|
+
}
|
|
1532
|
+
`;
|
|
1533
|
+
var nextEnvDts = `/// <reference types="next" />
|
|
1534
|
+
/// <reference types="next/image-types/global" />
|
|
1535
|
+
`;
|
|
1536
|
+
var globalDts = `declare module "*.css" {
|
|
1537
|
+
const content: Record<string, string>
|
|
1538
|
+
export default content
|
|
1539
|
+
}
|
|
1540
|
+
`;
|
|
1541
|
+
var layoutTsx = `import type { Metadata } from "next"
|
|
1542
|
+
import "./globals.css"
|
|
1543
|
+
|
|
1544
|
+
export const metadata: Metadata = {
|
|
1545
|
+
title: "Lexsys Next App",
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
export default function RootLayout({
|
|
1549
|
+
children,
|
|
1550
|
+
}: Readonly<{
|
|
1551
|
+
children: React.ReactNode
|
|
1552
|
+
}>) {
|
|
1553
|
+
return (
|
|
1554
|
+
<html lang="en">
|
|
1555
|
+
<body>{children}</body>
|
|
1556
|
+
</html>
|
|
1557
|
+
)
|
|
1558
|
+
}
|
|
1559
|
+
`;
|
|
1560
|
+
var pageTsx = `export default function Home() {
|
|
1561
|
+
return (
|
|
1562
|
+
<main>
|
|
1563
|
+
<h1>Lexsys + Next.js</h1>
|
|
1564
|
+
</main>
|
|
1565
|
+
)
|
|
1566
|
+
}
|
|
1567
|
+
`;
|
|
1568
|
+
var globalsCss = `@import "tailwindcss";
|
|
1569
|
+
|
|
1570
|
+
:root {
|
|
1571
|
+
font-family:
|
|
1572
|
+
Inter,
|
|
1573
|
+
ui-sans-serif,
|
|
1574
|
+
system-ui,
|
|
1575
|
+
-apple-system,
|
|
1576
|
+
BlinkMacSystemFont,
|
|
1577
|
+
"Segoe UI",
|
|
1578
|
+
sans-serif;
|
|
1579
|
+
color: #111827;
|
|
1580
|
+
background: #ffffff;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
body {
|
|
1584
|
+
margin: 0;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
main {
|
|
1588
|
+
min-height: 100vh;
|
|
1589
|
+
display: grid;
|
|
1590
|
+
place-items: center;
|
|
1591
|
+
}
|
|
1592
|
+
`;
|
|
1593
|
+
var sanitizePackageName = (name) => {
|
|
1594
|
+
const normalized = name.toLowerCase().replace(/[^a-z0-9._-]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
1595
|
+
return normalized || "lexsys-next-app";
|
|
1596
|
+
};
|
|
1597
|
+
var getPackageManagerFromUserAgent = () => {
|
|
1598
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
1599
|
+
const match = userAgent?.match(/^(npm|pnpm|yarn)\/([^\s]+)/u);
|
|
1600
|
+
if (!match) {
|
|
1601
|
+
return void 0;
|
|
1602
|
+
}
|
|
1603
|
+
return `${match[1]}@${match[2]}`;
|
|
1604
|
+
};
|
|
1605
|
+
var getPackageJson = (targetDirectory) => {
|
|
1606
|
+
const packageManager = getPackageManagerFromUserAgent();
|
|
1607
|
+
const packageJson = {
|
|
1608
|
+
name: sanitizePackageName(basename(targetDirectory)),
|
|
1609
|
+
private: true,
|
|
1610
|
+
version: "0.0.0",
|
|
1611
|
+
scripts: {
|
|
1612
|
+
dev: "next dev",
|
|
1613
|
+
build: "next build",
|
|
1614
|
+
start: "next start",
|
|
1615
|
+
typecheck: "tsc --noEmit",
|
|
1616
|
+
format: "prettier --write .",
|
|
1617
|
+
"format:check": "prettier --check ."
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
if (packageManager) {
|
|
1621
|
+
packageJson.packageManager = packageManager;
|
|
1622
|
+
}
|
|
1623
|
+
return JSON.stringify(packageJson, null, 2) + "\n";
|
|
1624
|
+
};
|
|
1625
|
+
var getRecordValue = (value) => {
|
|
1626
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
1627
|
+
};
|
|
1628
|
+
var mergePackageJson = (targetDirectory, existingPackageJson) => {
|
|
1629
|
+
const packageManager = getPackageManagerFromUserAgent();
|
|
1630
|
+
const existingScripts = getRecordValue(existingPackageJson.scripts);
|
|
1631
|
+
const mergedPackageJson = {
|
|
1632
|
+
...existingPackageJson,
|
|
1633
|
+
name: typeof existingPackageJson.name === "string" ? existingPackageJson.name : sanitizePackageName(basename(targetDirectory)),
|
|
1634
|
+
private: typeof existingPackageJson.private === "boolean" ? existingPackageJson.private : true,
|
|
1635
|
+
version: typeof existingPackageJson.version === "string" ? existingPackageJson.version : "0.0.0",
|
|
1636
|
+
scripts: {
|
|
1637
|
+
dev: "next dev",
|
|
1638
|
+
build: "next build",
|
|
1639
|
+
start: "next start",
|
|
1640
|
+
typecheck: "tsc --noEmit",
|
|
1641
|
+
format: "prettier --write .",
|
|
1642
|
+
"format:check": "prettier --check .",
|
|
1643
|
+
...existingScripts
|
|
1644
|
+
}
|
|
1645
|
+
};
|
|
1646
|
+
if (packageManager && typeof mergedPackageJson.packageManager !== "string") {
|
|
1647
|
+
mergedPackageJson.packageManager = packageManager;
|
|
1648
|
+
}
|
|
1649
|
+
return JSON.stringify(mergedPackageJson, null, 2) + "\n";
|
|
1650
|
+
};
|
|
1651
|
+
var writePackageJson = async (targetDirectory) => {
|
|
1652
|
+
const targetPath = join(targetDirectory, "package.json");
|
|
1653
|
+
if (!await fileExists(targetPath)) {
|
|
1654
|
+
await writeFile(targetPath, getPackageJson(targetDirectory), "utf-8");
|
|
1655
|
+
console.log(`Created scaffold file: ${targetPath}`);
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
let parsed;
|
|
1659
|
+
try {
|
|
1660
|
+
parsed = JSON.parse(await readFile(targetPath, "utf-8"));
|
|
1661
|
+
} catch {
|
|
1662
|
+
throw new Error(`Invalid existing package.json: ${targetPath}`);
|
|
1663
|
+
}
|
|
1664
|
+
const content = await readFile(targetPath, "utf-8");
|
|
1665
|
+
const mergedContent = mergePackageJson(targetDirectory, parsed);
|
|
1666
|
+
if (content === mergedContent) {
|
|
1667
|
+
console.log(`Skipped package.json: ${targetPath} already configured`);
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
await writeFile(targetPath, mergedContent, "utf-8");
|
|
1671
|
+
console.log(`Updated package.json: ${targetPath}`);
|
|
1672
|
+
};
|
|
1673
|
+
var writeScaffoldFile = async (targetPath, content, options = {}) => {
|
|
1674
|
+
if (await fileExists(targetPath)) {
|
|
1675
|
+
const existingContent = await readFile(targetPath, "utf-8");
|
|
1676
|
+
if (existingContent === content) {
|
|
1677
|
+
console.log(`Skipped identical scaffold file: ${targetPath}`);
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
if (options.allowExisting) {
|
|
1681
|
+
console.log(`Skipped existing scaffold file: ${targetPath}`);
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
throw new Error(
|
|
1685
|
+
`Refusing to overwrite existing scaffold file: ${targetPath}`
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
1689
|
+
await writeFile(targetPath, content, "utf-8");
|
|
1690
|
+
console.log(`Created scaffold file: ${targetPath}`);
|
|
1691
|
+
};
|
|
1692
|
+
var scaffoldNextProject = async (targetDirectory) => {
|
|
1693
|
+
await mkdir(targetDirectory, { recursive: true });
|
|
1694
|
+
await writePackageJson(targetDirectory);
|
|
1695
|
+
await writeScaffoldFile(join(targetDirectory, ".gitignore"), gitIgnore, {
|
|
1696
|
+
allowExisting: true
|
|
1697
|
+
});
|
|
1698
|
+
await writeScaffoldFile(
|
|
1699
|
+
join(targetDirectory, ".prettierignore"),
|
|
1700
|
+
prettierIgnore,
|
|
1701
|
+
{
|
|
1702
|
+
allowExisting: true
|
|
1703
|
+
}
|
|
1704
|
+
);
|
|
1705
|
+
await writeScaffoldFile(
|
|
1706
|
+
join(targetDirectory, ".prettierrc"),
|
|
1707
|
+
prettierConfig,
|
|
1708
|
+
{
|
|
1709
|
+
allowExisting: true
|
|
1710
|
+
}
|
|
1711
|
+
);
|
|
1712
|
+
await writeScaffoldFile(join(targetDirectory, "next.config.ts"), nextConfig, {
|
|
1713
|
+
allowExisting: true
|
|
1714
|
+
});
|
|
1715
|
+
await writeScaffoldFile(
|
|
1716
|
+
join(targetDirectory, "postcss.config.mjs"),
|
|
1717
|
+
postcssConfig,
|
|
1718
|
+
{
|
|
1719
|
+
allowExisting: true
|
|
1720
|
+
}
|
|
1721
|
+
);
|
|
1722
|
+
await writeScaffoldFile(join(targetDirectory, "tsconfig.json"), tsConfig, {
|
|
1723
|
+
allowExisting: true
|
|
1724
|
+
});
|
|
1725
|
+
await writeScaffoldFile(join(targetDirectory, "next-env.d.ts"), nextEnvDts, {
|
|
1726
|
+
allowExisting: true
|
|
1727
|
+
});
|
|
1728
|
+
await writeScaffoldFile(join(targetDirectory, "global.d.ts"), globalDts, {
|
|
1729
|
+
allowExisting: true
|
|
1730
|
+
});
|
|
1731
|
+
await writeScaffoldFile(join(targetDirectory, "app", "layout.tsx"), layoutTsx);
|
|
1732
|
+
await writeScaffoldFile(join(targetDirectory, "app", "page.tsx"), pageTsx);
|
|
1733
|
+
await writeScaffoldFile(
|
|
1734
|
+
join(targetDirectory, "app", "globals.css"),
|
|
1735
|
+
globalsCss,
|
|
1736
|
+
{
|
|
1737
|
+
allowExisting: true
|
|
1738
|
+
}
|
|
1739
|
+
);
|
|
1740
|
+
};
|
|
1741
|
+
var tailwindImport = '@import "tailwindcss";';
|
|
1742
|
+
var viteConfigFiles = [
|
|
1743
|
+
"vite.config.ts",
|
|
1744
|
+
"vite.config.mts",
|
|
1745
|
+
"vite.config.js",
|
|
1746
|
+
"vite.config.mjs"
|
|
1747
|
+
];
|
|
1748
|
+
var tsConfigFiles = ["tsconfig.app.json", "tsconfig.json"];
|
|
1749
|
+
var postcssConfigFiles = ["postcss.config.mjs", "postcss.config.js"];
|
|
1750
|
+
var viteSrcAlias = `"@": fileURLToPath(new URL("./src", import.meta.url))`;
|
|
1751
|
+
var tsSrcAlias = `"@/*": ["./src/*"]`;
|
|
1752
|
+
var ensureTailwindCssImport = async (config) => {
|
|
1753
|
+
const cssPath = join(getCwd(), config.tailwind.css);
|
|
1754
|
+
if (!await fileExists(cssPath)) {
|
|
1755
|
+
await mkdir(dirname(cssPath), { recursive: true });
|
|
1756
|
+
await writeFile(cssPath, `${tailwindImport}
|
|
1757
|
+
`, "utf-8");
|
|
1758
|
+
console.log(`Created Tailwind CSS entrypoint: ${cssPath}`);
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
const content = await readFile(cssPath, "utf-8");
|
|
1762
|
+
if (content.includes(tailwindImport)) {
|
|
1763
|
+
console.log(
|
|
1764
|
+
`Skipped Tailwind CSS import: ${cssPath} already imports Tailwind`
|
|
1765
|
+
);
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
await writeFile(cssPath, `${tailwindImport}
|
|
1769
|
+
${content}`, "utf-8");
|
|
1770
|
+
console.log(`Updated Tailwind CSS entrypoint: ${cssPath}`);
|
|
1771
|
+
};
|
|
1772
|
+
var ensureViteTailwindPlugin = async () => {
|
|
1773
|
+
const viteConfigPath = await findViteConfigPath();
|
|
1774
|
+
if (!viteConfigPath) {
|
|
1775
|
+
console.log("Skipped Vite Tailwind plugin: no Vite config found.");
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
const content = await readFile(viteConfigPath, "utf-8");
|
|
1779
|
+
const withImport = ensureTailwindViteImport(content);
|
|
1780
|
+
const updatedContent = ensureTailwindVitePluginUsage(withImport);
|
|
1781
|
+
if (updatedContent === content) {
|
|
1782
|
+
console.log(
|
|
1783
|
+
`Skipped Vite Tailwind plugin: ${viteConfigPath} already configured`
|
|
1784
|
+
);
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
await writeFile(viteConfigPath, updatedContent, "utf-8");
|
|
1788
|
+
console.log(`Updated Vite config: ${viteConfigPath}`);
|
|
1789
|
+
};
|
|
1790
|
+
var ensureViteSrcAlias = async () => {
|
|
1791
|
+
const viteConfigPath = await findViteConfigPath();
|
|
1792
|
+
if (!viteConfigPath) {
|
|
1793
|
+
console.log("Skipped Vite alias: no Vite config found.");
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
const content = await readFile(viteConfigPath, "utf-8");
|
|
1797
|
+
const withImport = ensureNodeUrlImport(content);
|
|
1798
|
+
const updatedContent = ensureViteResolveAliasUsage(withImport);
|
|
1799
|
+
if (updatedContent === content) {
|
|
1800
|
+
console.log(`Skipped Vite alias: ${viteConfigPath} already configured`);
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
await writeFile(viteConfigPath, updatedContent, "utf-8");
|
|
1804
|
+
console.log(`Updated Vite alias: ${viteConfigPath}`);
|
|
1805
|
+
};
|
|
1806
|
+
var ensureTypeScriptSrcAlias = async () => {
|
|
1807
|
+
const tsConfigPath = await findTypeScriptConfigPath();
|
|
1808
|
+
if (!tsConfigPath) {
|
|
1809
|
+
const fallbackPath = join(getCwd(), "tsconfig.json");
|
|
1810
|
+
await writeFile(
|
|
1811
|
+
fallbackPath,
|
|
1812
|
+
`{
|
|
1813
|
+
"compilerOptions": {
|
|
1814
|
+
"paths": {
|
|
1815
|
+
"@/*": ["./src/*"]
|
|
1816
|
+
}
|
|
1817
|
+
},
|
|
1818
|
+
"include": ["src"]
|
|
1819
|
+
}
|
|
1820
|
+
`,
|
|
1821
|
+
"utf-8"
|
|
1822
|
+
);
|
|
1823
|
+
console.log(`Created TypeScript alias config: ${fallbackPath}`);
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
const content = await readFile(tsConfigPath, "utf-8");
|
|
1827
|
+
const updatedContent = ensureTypeScriptPathsAlias(content);
|
|
1828
|
+
if (updatedContent === content) {
|
|
1829
|
+
console.log(`Skipped TypeScript alias: ${tsConfigPath} already configured`);
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
await writeFile(tsConfigPath, updatedContent, "utf-8");
|
|
1833
|
+
console.log(`Updated TypeScript alias: ${tsConfigPath}`);
|
|
1834
|
+
};
|
|
1835
|
+
var ensureNextPostCssConfig = async () => {
|
|
1836
|
+
for (const file of postcssConfigFiles) {
|
|
1837
|
+
const configPath = join(getCwd(), file);
|
|
1838
|
+
if (!await fileExists(configPath)) {
|
|
1839
|
+
continue;
|
|
1840
|
+
}
|
|
1841
|
+
const content = await readFile(configPath, "utf-8");
|
|
1842
|
+
if (content.includes("@tailwindcss/postcss")) {
|
|
1843
|
+
console.log(
|
|
1844
|
+
`Skipped PostCSS config: ${configPath} already configures Tailwind`
|
|
1845
|
+
);
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
const targetPath = join(getCwd(), "postcss.config.mjs");
|
|
1850
|
+
await writeFile(
|
|
1851
|
+
targetPath,
|
|
1852
|
+
`const config = {
|
|
1853
|
+
plugins: {
|
|
1854
|
+
"@tailwindcss/postcss": {},
|
|
1855
|
+
},
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
export default config
|
|
1859
|
+
`,
|
|
1860
|
+
"utf-8"
|
|
1861
|
+
);
|
|
1862
|
+
console.log(`Created PostCSS config: ${targetPath}`);
|
|
1863
|
+
};
|
|
1864
|
+
var findViteConfigPath = async () => {
|
|
1865
|
+
for (const file of viteConfigFiles) {
|
|
1866
|
+
const path = join(getCwd(), file);
|
|
1867
|
+
if (await fileExists(path)) {
|
|
1868
|
+
return path;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
return void 0;
|
|
1872
|
+
};
|
|
1873
|
+
var findTypeScriptConfigPath = async () => {
|
|
1874
|
+
for (const file of tsConfigFiles) {
|
|
1875
|
+
const path = join(getCwd(), file);
|
|
1876
|
+
if (await fileExists(path)) {
|
|
1877
|
+
return path;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
return void 0;
|
|
1881
|
+
};
|
|
1882
|
+
var ensureTailwindViteImport = (content) => {
|
|
1883
|
+
if (content.includes("@tailwindcss/vite")) {
|
|
1884
|
+
return content;
|
|
1885
|
+
}
|
|
1886
|
+
const importMatch = content.match(/^(?:import[^\n]*\n)+/u);
|
|
1887
|
+
if (!importMatch) {
|
|
1888
|
+
return `import tailwindcss from "@tailwindcss/vite"
|
|
1889
|
+
${content}`;
|
|
1890
|
+
}
|
|
1891
|
+
return `${content.slice(0, importMatch[0].length)}import tailwindcss from "@tailwindcss/vite"
|
|
1892
|
+
${content.slice(importMatch[0].length)}`;
|
|
1893
|
+
};
|
|
1894
|
+
var ensureTailwindVitePluginUsage = (content) => {
|
|
1895
|
+
if (content.includes("tailwindcss()")) {
|
|
1896
|
+
return content;
|
|
1897
|
+
}
|
|
1898
|
+
const pluginsMatch = content.match(/plugins\s*:\s*\[/u);
|
|
1899
|
+
if (pluginsMatch?.index !== void 0) {
|
|
1900
|
+
const insertionIndex = pluginsMatch.index + pluginsMatch[0].length;
|
|
1901
|
+
return `${content.slice(0, insertionIndex)}tailwindcss(), ${content.slice(insertionIndex)}`;
|
|
1902
|
+
}
|
|
1903
|
+
return content.replace(
|
|
1904
|
+
/defineConfig\(\s*\{/u,
|
|
1905
|
+
"defineConfig({\n plugins: [tailwindcss()],"
|
|
1906
|
+
);
|
|
1907
|
+
};
|
|
1908
|
+
var ensureNodeUrlImport = (content) => {
|
|
1909
|
+
if (content.includes('from "node:url"')) {
|
|
1910
|
+
return content;
|
|
1911
|
+
}
|
|
1912
|
+
return `import { fileURLToPath, URL } from "node:url"
|
|
1913
|
+
${content}`;
|
|
1914
|
+
};
|
|
1915
|
+
var ensureViteResolveAliasUsage = (content) => {
|
|
1916
|
+
if (content.includes(viteSrcAlias)) {
|
|
1917
|
+
return content;
|
|
1918
|
+
}
|
|
1919
|
+
if (/"@":\s*fileURLToPath/u.test(content) || content.includes("resolve:")) {
|
|
1920
|
+
return content;
|
|
1921
|
+
}
|
|
1922
|
+
return content.replace(
|
|
1923
|
+
/defineConfig\(\s*\{/u,
|
|
1924
|
+
`defineConfig({
|
|
1925
|
+
resolve: {
|
|
1926
|
+
alias: {
|
|
1927
|
+
${viteSrcAlias},
|
|
1928
|
+
},
|
|
1929
|
+
},`
|
|
1930
|
+
);
|
|
1931
|
+
};
|
|
1932
|
+
var ensureTypeScriptPathsAlias = (content) => {
|
|
1933
|
+
if (content.includes(tsSrcAlias)) {
|
|
1934
|
+
return content;
|
|
1935
|
+
}
|
|
1936
|
+
if (/"paths"\s*:\s*\{\s*\}/u.test(content)) {
|
|
1937
|
+
return content.replace(
|
|
1938
|
+
/"paths"\s*:\s*\{\s*\}/u,
|
|
1939
|
+
`"paths": {
|
|
1940
|
+
${tsSrcAlias}
|
|
1941
|
+
}`
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
if (/"paths"\s*:\s*\{/u.test(content)) {
|
|
1945
|
+
return content.replace(
|
|
1946
|
+
/"paths"\s*:\s*\{/u,
|
|
1947
|
+
`"paths": {
|
|
1948
|
+
${tsSrcAlias},`
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
if (/"compilerOptions"\s*:\s*\{\s*\}/u.test(content)) {
|
|
1952
|
+
return content.replace(
|
|
1953
|
+
/"compilerOptions"\s*:\s*\{\s*\}/u,
|
|
1954
|
+
`"compilerOptions": {
|
|
1955
|
+
"paths": {
|
|
1956
|
+
${tsSrcAlias}
|
|
1957
|
+
}
|
|
1958
|
+
}`
|
|
1959
|
+
);
|
|
1960
|
+
}
|
|
1961
|
+
if (/"compilerOptions"\s*:\s*\{/u.test(content)) {
|
|
1962
|
+
return content.replace(
|
|
1963
|
+
/"compilerOptions"\s*:\s*\{/u,
|
|
1964
|
+
`"compilerOptions": {
|
|
1965
|
+
"paths": {
|
|
1966
|
+
${tsSrcAlias}
|
|
1967
|
+
},`
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1970
|
+
return content;
|
|
1971
|
+
};
|
|
1972
|
+
var viteConfig = `import { fileURLToPath, URL } from "node:url"
|
|
1973
|
+
import { defineConfig } from "vite"
|
|
1974
|
+
import react from "@vitejs/plugin-react"
|
|
1975
|
+
|
|
1976
|
+
export default defineConfig({
|
|
1977
|
+
plugins: [react()],
|
|
1978
|
+
resolve: {
|
|
1979
|
+
alias: {
|
|
1980
|
+
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
|
1981
|
+
},
|
|
1982
|
+
},
|
|
1983
|
+
})
|
|
1984
|
+
`;
|
|
1985
|
+
var tsConfig2 = `{
|
|
1986
|
+
"files": [],
|
|
1987
|
+
"references": [
|
|
1988
|
+
{ "path": "./tsconfig.app.json" },
|
|
1989
|
+
{ "path": "./tsconfig.node.json" }
|
|
1990
|
+
]
|
|
1991
|
+
}
|
|
1992
|
+
`;
|
|
1993
|
+
var tsConfigApp = `{
|
|
1994
|
+
"compilerOptions": {
|
|
1995
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
1996
|
+
"target": "ES2020",
|
|
1997
|
+
"useDefineForClassFields": true,
|
|
1998
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
1999
|
+
"module": "ESNext",
|
|
2000
|
+
"skipLibCheck": true,
|
|
2001
|
+
"moduleResolution": "bundler",
|
|
2002
|
+
"allowImportingTsExtensions": true,
|
|
2003
|
+
"isolatedModules": true,
|
|
2004
|
+
"moduleDetection": "force",
|
|
2005
|
+
"noEmit": true,
|
|
2006
|
+
"jsx": "react-jsx",
|
|
2007
|
+
"strict": true,
|
|
2008
|
+
"noUnusedLocals": true,
|
|
2009
|
+
"noUnusedParameters": true,
|
|
2010
|
+
"noFallthroughCasesInSwitch": true,
|
|
2011
|
+
"noUncheckedSideEffectImports": true,
|
|
2012
|
+
"types": ["vite/client"],
|
|
2013
|
+
"paths": {
|
|
2014
|
+
"@/*": ["./src/*"]
|
|
2015
|
+
}
|
|
2016
|
+
},
|
|
2017
|
+
"include": ["src"]
|
|
2018
|
+
}
|
|
2019
|
+
`;
|
|
2020
|
+
var tsConfigNode = `{
|
|
2021
|
+
"compilerOptions": {
|
|
2022
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
2023
|
+
"target": "ES2022",
|
|
2024
|
+
"lib": ["ES2023"],
|
|
2025
|
+
"module": "ESNext",
|
|
2026
|
+
"skipLibCheck": true,
|
|
2027
|
+
"moduleResolution": "bundler",
|
|
2028
|
+
"allowImportingTsExtensions": true,
|
|
2029
|
+
"isolatedModules": true,
|
|
2030
|
+
"moduleDetection": "force",
|
|
2031
|
+
"noEmit": true,
|
|
2032
|
+
"strict": true,
|
|
2033
|
+
"noUnusedLocals": true,
|
|
2034
|
+
"noUnusedParameters": true,
|
|
2035
|
+
"noFallthroughCasesInSwitch": true,
|
|
2036
|
+
"noUncheckedSideEffectImports": true
|
|
2037
|
+
},
|
|
2038
|
+
"include": ["vite.config.ts"]
|
|
2039
|
+
}
|
|
2040
|
+
`;
|
|
2041
|
+
var gitIgnore2 = `node_modules
|
|
2042
|
+
dist
|
|
2043
|
+
dist-ssr
|
|
2044
|
+
*.local
|
|
2045
|
+
.env
|
|
2046
|
+
.env.*
|
|
2047
|
+
!.env.example
|
|
2048
|
+
`;
|
|
2049
|
+
var prettierIgnore2 = `node_modules
|
|
2050
|
+
dist
|
|
2051
|
+
dist-ssr
|
|
2052
|
+
coverage
|
|
2053
|
+
pnpm-lock.yaml
|
|
2054
|
+
package-lock.json
|
|
2055
|
+
yarn.lock
|
|
2056
|
+
`;
|
|
2057
|
+
var prettierConfig2 = `{
|
|
2058
|
+
"semi": false,
|
|
2059
|
+
"trailingComma": "all"
|
|
2060
|
+
}
|
|
2061
|
+
`;
|
|
2062
|
+
var indexHtml = `<!doctype html>
|
|
2063
|
+
<html lang="en">
|
|
2064
|
+
<head>
|
|
2065
|
+
<meta charset="UTF-8" />
|
|
2066
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
2067
|
+
<title>Lexsys Vite App</title>
|
|
2068
|
+
</head>
|
|
2069
|
+
<body>
|
|
2070
|
+
<div id="root"></div>
|
|
2071
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
2072
|
+
</body>
|
|
2073
|
+
</html>
|
|
2074
|
+
`;
|
|
2075
|
+
var mainTsx = `import React from "react"
|
|
2076
|
+
import ReactDOM from "react-dom/client"
|
|
2077
|
+
import "./style.css"
|
|
2078
|
+
import { App } from "./App"
|
|
2079
|
+
|
|
2080
|
+
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
2081
|
+
<React.StrictMode>
|
|
2082
|
+
<App />
|
|
2083
|
+
</React.StrictMode>,
|
|
2084
|
+
)
|
|
2085
|
+
`;
|
|
2086
|
+
var appTsx = `export const App = () => {
|
|
2087
|
+
return (
|
|
2088
|
+
<main>
|
|
2089
|
+
<h1>Lexsys + Vite</h1>
|
|
2090
|
+
</main>
|
|
2091
|
+
)
|
|
2092
|
+
}
|
|
2093
|
+
`;
|
|
2094
|
+
var styleCss = `@import "tailwindcss";
|
|
2095
|
+
|
|
2096
|
+
:root {
|
|
2097
|
+
font-family:
|
|
2098
|
+
Inter,
|
|
2099
|
+
ui-sans-serif,
|
|
2100
|
+
system-ui,
|
|
2101
|
+
-apple-system,
|
|
2102
|
+
BlinkMacSystemFont,
|
|
2103
|
+
"Segoe UI",
|
|
2104
|
+
sans-serif;
|
|
2105
|
+
color: #111827;
|
|
2106
|
+
background: #ffffff;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
body {
|
|
2110
|
+
margin: 0;
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
main {
|
|
2114
|
+
min-height: 100vh;
|
|
2115
|
+
display: grid;
|
|
2116
|
+
place-items: center;
|
|
2117
|
+
}
|
|
2118
|
+
`;
|
|
2119
|
+
var sanitizePackageName2 = (name) => {
|
|
2120
|
+
const normalized = name.toLowerCase().replace(/[^a-z0-9._-]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
2121
|
+
return normalized || "lexsys-vite-app";
|
|
2122
|
+
};
|
|
2123
|
+
var getPackageManagerFromUserAgent2 = () => {
|
|
2124
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
2125
|
+
const match = userAgent?.match(/^(npm|pnpm|yarn)\/([^\s]+)/u);
|
|
2126
|
+
if (!match) {
|
|
2127
|
+
return void 0;
|
|
2128
|
+
}
|
|
2129
|
+
return `${match[1]}@${match[2]}`;
|
|
2130
|
+
};
|
|
2131
|
+
var getPackageJson2 = (targetDirectory) => {
|
|
2132
|
+
const packageManager = getPackageManagerFromUserAgent2();
|
|
2133
|
+
const packageJson = {
|
|
2134
|
+
name: sanitizePackageName2(basename(targetDirectory)),
|
|
2135
|
+
private: true,
|
|
2136
|
+
version: "0.0.0",
|
|
2137
|
+
type: "module",
|
|
2138
|
+
scripts: {
|
|
2139
|
+
dev: "vite",
|
|
2140
|
+
build: "tsc -b && vite build",
|
|
2141
|
+
typecheck: "tsc -b",
|
|
2142
|
+
format: "prettier --write .",
|
|
2143
|
+
"format:check": "prettier --check .",
|
|
2144
|
+
preview: "vite preview"
|
|
2145
|
+
}
|
|
2146
|
+
};
|
|
2147
|
+
if (packageManager) {
|
|
2148
|
+
packageJson.packageManager = packageManager;
|
|
2149
|
+
}
|
|
2150
|
+
return JSON.stringify(packageJson, null, 2) + "\n";
|
|
2151
|
+
};
|
|
2152
|
+
var getRecordValue2 = (value) => {
|
|
2153
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
2154
|
+
};
|
|
2155
|
+
var mergePackageJson2 = (targetDirectory, existingPackageJson) => {
|
|
2156
|
+
const packageManager = getPackageManagerFromUserAgent2();
|
|
2157
|
+
const existingScripts = getRecordValue2(existingPackageJson.scripts);
|
|
2158
|
+
const mergedPackageJson = {
|
|
2159
|
+
...existingPackageJson,
|
|
2160
|
+
name: typeof existingPackageJson.name === "string" ? existingPackageJson.name : sanitizePackageName2(basename(targetDirectory)),
|
|
2161
|
+
private: typeof existingPackageJson.private === "boolean" ? existingPackageJson.private : true,
|
|
2162
|
+
version: typeof existingPackageJson.version === "string" ? existingPackageJson.version : "0.0.0",
|
|
2163
|
+
type: typeof existingPackageJson.type === "string" ? existingPackageJson.type : "module",
|
|
2164
|
+
scripts: {
|
|
2165
|
+
dev: "vite",
|
|
2166
|
+
build: "tsc -b && vite build",
|
|
2167
|
+
typecheck: "tsc -b",
|
|
2168
|
+
format: "prettier --write .",
|
|
2169
|
+
"format:check": "prettier --check .",
|
|
2170
|
+
preview: "vite preview",
|
|
2171
|
+
...existingScripts
|
|
2172
|
+
}
|
|
2173
|
+
};
|
|
2174
|
+
if (packageManager && typeof mergedPackageJson.packageManager !== "string") {
|
|
2175
|
+
mergedPackageJson.packageManager = packageManager;
|
|
2176
|
+
}
|
|
2177
|
+
return JSON.stringify(mergedPackageJson, null, 2) + "\n";
|
|
2178
|
+
};
|
|
2179
|
+
var writePackageJson2 = async (targetDirectory) => {
|
|
2180
|
+
const targetPath = join(targetDirectory, "package.json");
|
|
2181
|
+
if (!await fileExists(targetPath)) {
|
|
2182
|
+
await writeFile(targetPath, getPackageJson2(targetDirectory), "utf-8");
|
|
2183
|
+
console.log(`Created scaffold file: ${targetPath}`);
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
let parsed;
|
|
2187
|
+
try {
|
|
2188
|
+
parsed = JSON.parse(await readFile(targetPath, "utf-8"));
|
|
2189
|
+
} catch {
|
|
2190
|
+
throw new Error(`Invalid existing package.json: ${targetPath}`);
|
|
2191
|
+
}
|
|
2192
|
+
const content = await readFile(targetPath, "utf-8");
|
|
2193
|
+
const mergedContent = mergePackageJson2(targetDirectory, parsed);
|
|
2194
|
+
if (content === mergedContent) {
|
|
2195
|
+
console.log(`Skipped package.json: ${targetPath} already configured`);
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
await writeFile(targetPath, mergedContent, "utf-8");
|
|
2199
|
+
console.log(`Updated package.json: ${targetPath}`);
|
|
2200
|
+
};
|
|
2201
|
+
var writeScaffoldFile2 = async (targetPath, content, options = {}) => {
|
|
2202
|
+
if (await fileExists(targetPath)) {
|
|
2203
|
+
const existingContent = await readFile(targetPath, "utf-8");
|
|
2204
|
+
if (existingContent === content) {
|
|
2205
|
+
console.log(`Skipped identical scaffold file: ${targetPath}`);
|
|
2206
|
+
return;
|
|
2207
|
+
}
|
|
2208
|
+
if (options.allowExisting) {
|
|
2209
|
+
console.log(`Skipped existing scaffold file: ${targetPath}`);
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
throw new Error(
|
|
2213
|
+
`Refusing to overwrite existing scaffold file: ${targetPath}`
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
2217
|
+
await writeFile(targetPath, content, "utf-8");
|
|
2218
|
+
console.log(`Created scaffold file: ${targetPath}`);
|
|
2219
|
+
};
|
|
2220
|
+
var scaffoldViteProject = async (targetDirectory) => {
|
|
2221
|
+
await mkdir(targetDirectory, { recursive: true });
|
|
2222
|
+
await writePackageJson2(targetDirectory);
|
|
2223
|
+
await writeScaffoldFile2(join(targetDirectory, ".gitignore"), gitIgnore2, {
|
|
2224
|
+
allowExisting: true
|
|
2225
|
+
});
|
|
2226
|
+
await writeScaffoldFile2(
|
|
2227
|
+
join(targetDirectory, ".prettierignore"),
|
|
2228
|
+
prettierIgnore2,
|
|
2229
|
+
{
|
|
2230
|
+
allowExisting: true
|
|
2231
|
+
}
|
|
2232
|
+
);
|
|
2233
|
+
await writeScaffoldFile2(
|
|
2234
|
+
join(targetDirectory, ".prettierrc"),
|
|
2235
|
+
prettierConfig2,
|
|
2236
|
+
{
|
|
2237
|
+
allowExisting: true
|
|
2238
|
+
}
|
|
2239
|
+
);
|
|
2240
|
+
await writeScaffoldFile2(join(targetDirectory, "index.html"), indexHtml);
|
|
2241
|
+
await writeScaffoldFile2(join(targetDirectory, "tsconfig.json"), tsConfig2);
|
|
2242
|
+
await writeScaffoldFile2(
|
|
2243
|
+
join(targetDirectory, "tsconfig.app.json"),
|
|
2244
|
+
tsConfigApp
|
|
2245
|
+
);
|
|
2246
|
+
await writeScaffoldFile2(
|
|
2247
|
+
join(targetDirectory, "tsconfig.node.json"),
|
|
2248
|
+
tsConfigNode
|
|
2249
|
+
);
|
|
2250
|
+
await writeScaffoldFile2(join(targetDirectory, "vite.config.ts"), viteConfig, {
|
|
2251
|
+
allowExisting: true
|
|
2252
|
+
});
|
|
2253
|
+
await writeScaffoldFile2(join(targetDirectory, "src", "main.tsx"), mainTsx);
|
|
2254
|
+
await writeScaffoldFile2(join(targetDirectory, "src", "App.tsx"), appTsx);
|
|
2255
|
+
await writeScaffoldFile2(join(targetDirectory, "src", "style.css"), styleCss, {
|
|
2256
|
+
allowExisting: true
|
|
2257
|
+
});
|
|
2258
|
+
};
|
|
2259
|
+
|
|
2260
|
+
// src/commands/init.ts
|
|
2261
|
+
var tailwindViteDependencies = ["tailwindcss", "@tailwindcss/vite"];
|
|
2262
|
+
var tailwindNextDependencies = ["tailwindcss", "@tailwindcss/postcss"];
|
|
2263
|
+
var viteDependencies = ["react", "react-dom"];
|
|
2264
|
+
var viteDevDependencies = [
|
|
2265
|
+
"@types/react",
|
|
2266
|
+
"@types/react-dom",
|
|
2267
|
+
"@types/node",
|
|
2268
|
+
"@vitejs/plugin-react",
|
|
2269
|
+
"prettier",
|
|
2270
|
+
"typescript",
|
|
2271
|
+
"vite"
|
|
2272
|
+
];
|
|
2273
|
+
var nextDependencies = ["react", "react-dom", `next@${NEXT_VERSION}`];
|
|
2274
|
+
var nextDevDependencies = [
|
|
2275
|
+
"@types/node",
|
|
2276
|
+
"@types/react",
|
|
2277
|
+
"@types/react-dom",
|
|
2278
|
+
"prettier",
|
|
2279
|
+
"typescript"
|
|
2280
|
+
];
|
|
2281
|
+
var nextConfigFiles = ["next.config.ts", "next.config.mjs", "next.config.js"];
|
|
2282
|
+
var packageJsonHasDependency = (packageJson, dependencyName) => {
|
|
2283
|
+
const dependencies = typeof packageJson.dependencies === "object" && packageJson.dependencies !== null ? packageJson.dependencies : {};
|
|
2284
|
+
const devDependencies = typeof packageJson.devDependencies === "object" && packageJson.devDependencies !== null ? packageJson.devDependencies : {};
|
|
2285
|
+
return dependencyName in dependencies || dependencyName in devDependencies;
|
|
2286
|
+
};
|
|
2287
|
+
var hasNextScaffold = async () => {
|
|
2288
|
+
for (const configFile of nextConfigFiles) {
|
|
2289
|
+
if (await fileExists(resolve(getCwd(), configFile))) {
|
|
2290
|
+
return true;
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
const packageJsonPath2 = resolve(getCwd(), "package.json");
|
|
2294
|
+
if (!await fileExists(packageJsonPath2)) {
|
|
2295
|
+
return false;
|
|
2296
|
+
}
|
|
2297
|
+
const packageJson = JSON.parse(
|
|
2298
|
+
await readFile(packageJsonPath2, "utf-8")
|
|
2299
|
+
);
|
|
2300
|
+
return packageJsonHasDependency(packageJson, "next");
|
|
2301
|
+
};
|
|
2302
|
+
var runLexsysInit = async () => {
|
|
2303
|
+
console.log("Initializing Lexsys...\n");
|
|
2304
|
+
const config = await loadConfig();
|
|
2305
|
+
const usesNext = await hasNextScaffold();
|
|
2306
|
+
if (usesNext && config.tailwind.css === "src/style.css") {
|
|
2307
|
+
config.tailwind.css = "app/globals.css";
|
|
2308
|
+
}
|
|
2309
|
+
await ensureProjectStructure(config);
|
|
2310
|
+
await installDependencies(
|
|
2311
|
+
usesNext ? tailwindNextDependencies : tailwindViteDependencies,
|
|
2312
|
+
{ dev: true }
|
|
2313
|
+
);
|
|
2314
|
+
await ensureTailwindCssImport(config);
|
|
2315
|
+
if (usesNext) {
|
|
2316
|
+
await ensureNextPostCssConfig();
|
|
2317
|
+
} else {
|
|
2318
|
+
await ensureViteTailwindPlugin();
|
|
2319
|
+
await ensureViteSrcAlias();
|
|
2320
|
+
}
|
|
2321
|
+
await ensureTypeScriptSrcAlias();
|
|
2322
|
+
await saveConfig(config);
|
|
2323
|
+
console.log("\nDone.");
|
|
2324
|
+
};
|
|
2325
|
+
var runViteInit = async (args2) => {
|
|
2326
|
+
if (args2.length > 1) {
|
|
2327
|
+
throw new CliError("Usage: lexsys init vite [directory]");
|
|
2328
|
+
}
|
|
2329
|
+
const targetDirectory = resolve(getCwd(), args2[0] ?? ".");
|
|
2330
|
+
setCwd(targetDirectory);
|
|
2331
|
+
console.log(`Initializing Vite project at ${targetDirectory}...
|
|
2332
|
+
`);
|
|
2333
|
+
await scaffoldViteProject(targetDirectory);
|
|
2334
|
+
await installDependencies(viteDependencies);
|
|
2335
|
+
await installDependencies(viteDevDependencies, { dev: true });
|
|
2336
|
+
await runLexsysInit();
|
|
2337
|
+
};
|
|
2338
|
+
var runNextInit = async (args2) => {
|
|
2339
|
+
if (args2.length > 1) {
|
|
2340
|
+
throw new CliError("Usage: lexsys init next [directory]");
|
|
2341
|
+
}
|
|
2342
|
+
const targetDirectory = resolve(getCwd(), args2[0] ?? ".");
|
|
2343
|
+
setCwd(targetDirectory);
|
|
2344
|
+
console.log(`Initializing Next.js project at ${targetDirectory}...
|
|
2345
|
+
`);
|
|
2346
|
+
await scaffoldNextProject(targetDirectory);
|
|
2347
|
+
await installDependencies(nextDependencies);
|
|
2348
|
+
await installDependencies(nextDevDependencies, { dev: true });
|
|
2349
|
+
await runLexsysInit();
|
|
2350
|
+
};
|
|
2351
|
+
var promptFrameworkInit = async () => {
|
|
2352
|
+
const response = await prompts([
|
|
2353
|
+
{
|
|
2354
|
+
type: "select",
|
|
2355
|
+
name: "framework",
|
|
2356
|
+
message: "What would you like to create?",
|
|
2357
|
+
choices: [
|
|
2358
|
+
{ title: "Vite React app", value: "vite" },
|
|
2359
|
+
{ title: "Next.js App Router app", value: "next" }
|
|
2360
|
+
]
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
type: "text",
|
|
2364
|
+
name: "dir",
|
|
2365
|
+
message: "Project name",
|
|
2366
|
+
initial: "my-app"
|
|
2367
|
+
}
|
|
2368
|
+
]);
|
|
2369
|
+
if (typeof response !== "object" || response === null) {
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
const framework = "framework" in response && typeof response.framework === "string" ? response.framework : void 0;
|
|
2373
|
+
if (!framework) return;
|
|
2374
|
+
const dir = "dir" in response && typeof response.dir === "string" && response.dir ? [response.dir] : [];
|
|
2375
|
+
if (framework === "vite") {
|
|
2376
|
+
await runViteInit(dir);
|
|
2377
|
+
return;
|
|
2378
|
+
}
|
|
2379
|
+
if (framework === "next") {
|
|
2380
|
+
await runNextInit(dir);
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2383
|
+
};
|
|
2384
|
+
var hasViteScaffold = async () => {
|
|
2385
|
+
for (const configFile of [
|
|
2386
|
+
"vite.config.ts",
|
|
2387
|
+
"vite.config.js",
|
|
2388
|
+
"vite.config.mts"
|
|
2389
|
+
]) {
|
|
2390
|
+
if (await fileExists(resolve(getCwd(), configFile))) return true;
|
|
2391
|
+
}
|
|
2392
|
+
return false;
|
|
2393
|
+
};
|
|
2394
|
+
var runInit = async (args2 = []) => {
|
|
2395
|
+
const positionalArgs = removeFlagsWithValues(args2, ["--cwd"]);
|
|
2396
|
+
const [framework, ...frameworkArgs] = positionalArgs;
|
|
2397
|
+
if (!framework) {
|
|
2398
|
+
if (await hasNextScaffold() || await hasViteScaffold()) {
|
|
2399
|
+
await runLexsysInit();
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
await promptFrameworkInit();
|
|
2403
|
+
return;
|
|
2404
|
+
}
|
|
2405
|
+
if (framework === "vite") {
|
|
2406
|
+
await runViteInit(frameworkArgs);
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
if (framework === "next") {
|
|
2410
|
+
await runNextInit(frameworkArgs);
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
throw new CliError(`Unsupported init target: ${framework}`);
|
|
2414
|
+
};
|
|
2415
|
+
var layerLabels = {
|
|
2416
|
+
block: "Blocks",
|
|
2417
|
+
primitive: "Primitives",
|
|
2418
|
+
template: "Templates"
|
|
2419
|
+
};
|
|
2420
|
+
var runList = async (options = {}) => {
|
|
2421
|
+
try {
|
|
2422
|
+
const registryItems3 = await getRegistryItems({
|
|
2423
|
+
fallback: !options.noFallback
|
|
2424
|
+
});
|
|
2425
|
+
if (options.json) {
|
|
2426
|
+
const simplified = registryItems3.map((item) => ({
|
|
2427
|
+
name: item.name,
|
|
2428
|
+
canonicalName: item.canonicalName,
|
|
2429
|
+
version: item.version,
|
|
2430
|
+
category: item.category,
|
|
2431
|
+
layer: getInstallLayer(item)
|
|
2432
|
+
}));
|
|
2433
|
+
console.log(JSON.stringify(simplified, null, 2));
|
|
2434
|
+
return;
|
|
2435
|
+
}
|
|
2436
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
2437
|
+
for (const item of registryItems3) {
|
|
2438
|
+
const layer = getInstallLayer(item) ?? "other";
|
|
2439
|
+
const current = grouped.get(layer) ?? [];
|
|
2440
|
+
current.push(item);
|
|
2441
|
+
grouped.set(layer, current);
|
|
2442
|
+
}
|
|
2443
|
+
console.log("Available Lexsys registry items:\n");
|
|
2444
|
+
for (const layer of ["primitive", "block", "template"]) {
|
|
2445
|
+
const items = grouped.get(layer);
|
|
2446
|
+
if (!items?.length) {
|
|
2447
|
+
continue;
|
|
2448
|
+
}
|
|
2449
|
+
console.log(`${layerLabels[layer] ?? layer}:`);
|
|
2450
|
+
for (const item of items) {
|
|
2451
|
+
console.log(
|
|
2452
|
+
`- ${item.canonicalName} v${item.version} (${item.category})`
|
|
2453
|
+
);
|
|
2454
|
+
}
|
|
2455
|
+
console.log("");
|
|
2456
|
+
}
|
|
2457
|
+
} catch (error) {
|
|
2458
|
+
console.log("Failed to resolve registry.");
|
|
2459
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
2460
|
+
process.exitCode = 1;
|
|
2461
|
+
}
|
|
2462
|
+
};
|
|
2463
|
+
var localRegistryResult = {
|
|
2464
|
+
items: registryItems,
|
|
2465
|
+
source: "local",
|
|
2466
|
+
fallbackUsed: false,
|
|
2467
|
+
manifestVersion: registryManifest.version
|
|
2468
|
+
};
|
|
2469
|
+
var printRegistrySummary = (result) => {
|
|
2470
|
+
console.log("Lexsys registry summary\n");
|
|
2471
|
+
console.log(`Registry source: ${result.source}`);
|
|
2472
|
+
console.log(`Fallback used: ${result.fallbackUsed ? "yes" : "no"}`);
|
|
2473
|
+
console.log(`Items: ${result.items.length}`);
|
|
2474
|
+
for (const item of result.items) {
|
|
2475
|
+
const remoteFileCount = item.remoteFiles?.length ?? 0;
|
|
2476
|
+
console.log(
|
|
2477
|
+
`- ${item.canonicalName} v${item.version} (${item.type}/${item.category}, remote files: ${remoteFileCount})`
|
|
2478
|
+
);
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
var printRegistryJson = (result) => {
|
|
2482
|
+
console.log(
|
|
2483
|
+
JSON.stringify(
|
|
2484
|
+
{
|
|
2485
|
+
version: result.manifestVersion,
|
|
2486
|
+
source: result.source,
|
|
2487
|
+
fallbackUsed: result.fallbackUsed,
|
|
2488
|
+
items: result.items
|
|
2489
|
+
},
|
|
2490
|
+
null,
|
|
2491
|
+
2
|
|
2492
|
+
)
|
|
2493
|
+
);
|
|
2494
|
+
};
|
|
2495
|
+
var getRemoteRegistryResult = async (source) => {
|
|
2496
|
+
const remote = await fetchRemoteRegistry(source);
|
|
2497
|
+
validateRegistry(
|
|
2498
|
+
remote.items,
|
|
2499
|
+
remote.styles !== void 0 ? {
|
|
2500
|
+
styles: remote.styles
|
|
2501
|
+
} : {}
|
|
2502
|
+
);
|
|
2503
|
+
return {
|
|
2504
|
+
items: remote.items,
|
|
2505
|
+
source,
|
|
2506
|
+
fallbackUsed: false,
|
|
2507
|
+
manifestVersion: remote.version
|
|
2508
|
+
};
|
|
2509
|
+
};
|
|
2510
|
+
var runRegistry = async (options = {}) => {
|
|
2511
|
+
const fallback = !options.noFallback;
|
|
2512
|
+
try {
|
|
2513
|
+
if (options.local) {
|
|
2514
|
+
if (options.source) {
|
|
2515
|
+
console.log(localRegistryResult.source);
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
if (options.summary) {
|
|
2519
|
+
printRegistrySummary(localRegistryResult);
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
console.log(JSON.stringify(registryManifest, null, 2));
|
|
2523
|
+
return;
|
|
2524
|
+
}
|
|
2525
|
+
if (options.remote) {
|
|
2526
|
+
const source = await getRegistrySource();
|
|
2527
|
+
if (source === "local") {
|
|
2528
|
+
console.log("No remote registry URL configured.");
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
const result2 = await getRemoteRegistryResult(source);
|
|
2532
|
+
if (options.source) {
|
|
2533
|
+
console.log(result2.source);
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
if (options.summary) {
|
|
2537
|
+
printRegistrySummary(result2);
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
printRegistryJson(result2);
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
const result = await getRegistryProviderResult({ fallback });
|
|
2544
|
+
if (options.source) {
|
|
2545
|
+
if (result.fallbackUsed) {
|
|
2546
|
+
console.log(`${result.source} (fallback: local)`);
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
console.log(result.source);
|
|
2550
|
+
return;
|
|
2551
|
+
}
|
|
2552
|
+
if (options.summary) {
|
|
2553
|
+
printRegistrySummary(result);
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
printRegistryJson(result);
|
|
2557
|
+
} catch (error) {
|
|
2558
|
+
console.log("Failed to resolve registry.");
|
|
2559
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
2560
|
+
process.exitCode = 1;
|
|
2561
|
+
}
|
|
2562
|
+
};
|
|
2563
|
+
|
|
2564
|
+
// src/commands/status.ts
|
|
2565
|
+
var runStatus = async (options = {}) => {
|
|
2566
|
+
const config = await loadConfig();
|
|
2567
|
+
const installed = config.installed ?? {};
|
|
2568
|
+
if (!Object.keys(installed).length) {
|
|
2569
|
+
console.log("No Lexsys components are currently tracked.");
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2572
|
+
try {
|
|
2573
|
+
await getRegistryProviderResult({
|
|
2574
|
+
fallback: !options.noFallback
|
|
2575
|
+
});
|
|
2576
|
+
} catch (error) {
|
|
2577
|
+
console.log("Failed to resolve registry.");
|
|
2578
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
2579
|
+
process.exitCode = 1;
|
|
2580
|
+
return;
|
|
2581
|
+
}
|
|
2582
|
+
console.log("Installed Lexsys components:\n");
|
|
2583
|
+
for (const [name, installedVersion] of Object.entries(installed)) {
|
|
2584
|
+
const item = await findItem(name);
|
|
2585
|
+
if (!item) {
|
|
2586
|
+
console.log(`- ${name} v${installedVersion} (missing from registry)`);
|
|
2587
|
+
continue;
|
|
2588
|
+
}
|
|
2589
|
+
const status = item.version === installedVersion ? "up to date" : `update available: v${installedVersion} \u2192 v${item.version}`;
|
|
2590
|
+
console.log(`- ${item.canonicalName} v${installedVersion} (${status})`);
|
|
2591
|
+
}
|
|
2592
|
+
};
|
|
2593
|
+
|
|
2594
|
+
// src/registry/closure.ts
|
|
2595
|
+
var computeRegistryClosure = (rootNames, items) => {
|
|
2596
|
+
const closure = /* @__PURE__ */ new Set();
|
|
2597
|
+
const visit = (name) => {
|
|
2598
|
+
const item = items.find(
|
|
2599
|
+
(registryItem) => registryItem.name.toLowerCase() === name.toLowerCase()
|
|
2600
|
+
);
|
|
2601
|
+
if (!item || closure.has(item.name)) {
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
closure.add(item.name);
|
|
2605
|
+
for (const dependency of item.registryDependencies) {
|
|
2606
|
+
visit(dependency);
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
for (const name of rootNames) {
|
|
2610
|
+
visit(name);
|
|
2611
|
+
}
|
|
2612
|
+
return closure;
|
|
2613
|
+
};
|
|
2614
|
+
var findOrphanInstalledItems = (removedTargetNames, remainingInstalled, items) => {
|
|
2615
|
+
const remainingNames = Object.keys(remainingInstalled);
|
|
2616
|
+
const removedDependencyNames = /* @__PURE__ */ new Set();
|
|
2617
|
+
for (const removedName of removedTargetNames) {
|
|
2618
|
+
const closure = computeRegistryClosure([removedName], items);
|
|
2619
|
+
for (const name of closure) {
|
|
2620
|
+
const isRemovedTarget = removedTargetNames.some((targetName) => {
|
|
2621
|
+
return targetName.toLowerCase() === name.toLowerCase();
|
|
2622
|
+
});
|
|
2623
|
+
if (!isRemovedTarget) {
|
|
2624
|
+
removedDependencyNames.add(name);
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
return remainingNames.map(
|
|
2629
|
+
(name) => items.find(
|
|
2630
|
+
(registryItem) => registryItem.name.toLowerCase() === name.toLowerCase()
|
|
2631
|
+
)
|
|
2632
|
+
).filter((item) => Boolean(item)).filter((item) => removedDependencyNames.has(item.name));
|
|
2633
|
+
};
|
|
2634
|
+
|
|
2635
|
+
// src/commands/uninstall.ts
|
|
2636
|
+
var normalizeInstalledKey = (name) => {
|
|
2637
|
+
return name.toLowerCase();
|
|
2638
|
+
};
|
|
2639
|
+
var resolveInstalledItems = async (installed) => {
|
|
2640
|
+
const names = Object.keys(installed);
|
|
2641
|
+
if (!names.length) {
|
|
2642
|
+
return [];
|
|
2643
|
+
}
|
|
2644
|
+
return resolveRegistryItems(names);
|
|
2645
|
+
};
|
|
2646
|
+
var collectOrphanedSharedResources = (removedItems, remainingItems) => {
|
|
2647
|
+
const removedUtilities = collectUtilities(removedItems);
|
|
2648
|
+
const removedStyles = collectStyles(removedItems);
|
|
2649
|
+
const remainingUtilities = new Set(collectUtilities(remainingItems));
|
|
2650
|
+
const remainingStyles = new Set(collectStyles(remainingItems));
|
|
2651
|
+
return {
|
|
2652
|
+
utilities: removedUtilities.filter((utility) => {
|
|
2653
|
+
return !remainingUtilities.has(utility);
|
|
2654
|
+
}),
|
|
2655
|
+
styles: removedStyles.filter((style) => {
|
|
2656
|
+
return !remainingStyles.has(style);
|
|
2657
|
+
})
|
|
2658
|
+
};
|
|
2659
|
+
};
|
|
2660
|
+
var removeInstalledKey = (installed, itemName) => {
|
|
2661
|
+
const installedKey = Object.keys(installed).find((key) => {
|
|
2662
|
+
return normalizeInstalledKey(key) === normalizeInstalledKey(itemName);
|
|
2663
|
+
});
|
|
2664
|
+
if (installedKey) {
|
|
2665
|
+
delete installed[installedKey];
|
|
2666
|
+
}
|
|
2667
|
+
};
|
|
2668
|
+
var runUninstall = async (args2) => {
|
|
2669
|
+
const dryRun = hasFlag(args2, "--dry-run", "-d");
|
|
2670
|
+
const withDeps = hasFlag(args2, "--with-deps", "-w");
|
|
2671
|
+
const noFallback = hasFlag(args2, "--no-fallback");
|
|
2672
|
+
const targetArgs = removeFlagsWithValues(
|
|
2673
|
+
removeFlags(args2, [
|
|
2674
|
+
"--dry-run",
|
|
2675
|
+
"-d",
|
|
2676
|
+
"--with-deps",
|
|
2677
|
+
"-w",
|
|
2678
|
+
"--no-fallback"
|
|
2679
|
+
]),
|
|
2680
|
+
["--cwd", "-C"]
|
|
2681
|
+
);
|
|
2682
|
+
if (!targetArgs.length) {
|
|
2683
|
+
const preConfig = await loadConfig();
|
|
2684
|
+
const installedNames = Object.keys(preConfig.installed ?? {});
|
|
2685
|
+
if (installedNames.length === 0) {
|
|
2686
|
+
console.log("No components installed.");
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
const response = await prompts({
|
|
2690
|
+
type: "multiselect",
|
|
2691
|
+
name: "components",
|
|
2692
|
+
message: "Select components to uninstall",
|
|
2693
|
+
choices: installedNames.map((name) => ({ title: name, value: name })),
|
|
2694
|
+
min: 1
|
|
2695
|
+
});
|
|
2696
|
+
const selected = response.components;
|
|
2697
|
+
if (!Array.isArray(selected) || !selected.length) return;
|
|
2698
|
+
targetArgs.push(
|
|
2699
|
+
...selected.filter((c) => typeof c === "string")
|
|
2700
|
+
);
|
|
2701
|
+
}
|
|
2702
|
+
const config = await loadConfig();
|
|
2703
|
+
const installed = config.installed ?? {};
|
|
2704
|
+
const resolvedTargets = [];
|
|
2705
|
+
const notTracked = [];
|
|
2706
|
+
for (const name of targetArgs) {
|
|
2707
|
+
const item = await findItem(name, { fallback: !noFallback });
|
|
2708
|
+
if (!item) {
|
|
2709
|
+
console.log(`Component "${name}" not found in registry.`);
|
|
2710
|
+
continue;
|
|
2711
|
+
}
|
|
2712
|
+
const installedKey = Object.keys(installed).find((key) => {
|
|
2713
|
+
return normalizeInstalledKey(key) === normalizeInstalledKey(item.name);
|
|
2714
|
+
});
|
|
2715
|
+
if (!installedKey) {
|
|
2716
|
+
notTracked.push(name);
|
|
2717
|
+
console.log(`Component "${name}" is not tracked as installed.`);
|
|
2718
|
+
continue;
|
|
2719
|
+
}
|
|
2720
|
+
resolvedTargets.push(item);
|
|
2721
|
+
}
|
|
2722
|
+
if (!resolvedTargets.length) {
|
|
2723
|
+
if (!notTracked.length) {
|
|
2724
|
+
console.log("No installed components matched the request.");
|
|
2725
|
+
}
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
const remainingInstalled = { ...installed };
|
|
2729
|
+
for (const item of resolvedTargets) {
|
|
2730
|
+
removeInstalledKey(remainingInstalled, item.name);
|
|
2731
|
+
}
|
|
2732
|
+
const allItems = await getRegistryItems({ fallback: !noFallback });
|
|
2733
|
+
const orphanItems = withDeps ? findOrphanInstalledItems(
|
|
2734
|
+
resolvedTargets.map((item) => item.name),
|
|
2735
|
+
remainingInstalled,
|
|
2736
|
+
allItems
|
|
2737
|
+
) : findOrphanInstalledItems(
|
|
2738
|
+
resolvedTargets.map((item) => item.name),
|
|
2739
|
+
remainingInstalled,
|
|
2740
|
+
allItems
|
|
2741
|
+
);
|
|
2742
|
+
const orphanHints = withDeps ? [] : orphanItems;
|
|
2743
|
+
const allRemovalTargets = [
|
|
2744
|
+
...resolvedTargets,
|
|
2745
|
+
...withDeps ? orphanItems : []
|
|
2746
|
+
].filter((item, index, array) => {
|
|
2747
|
+
return array.findIndex((entry) => entry.name === item.name) === index;
|
|
2748
|
+
});
|
|
2749
|
+
if (dryRun) {
|
|
2750
|
+
console.log("Dry run: no files will be removed.\n");
|
|
2751
|
+
console.log("Components:");
|
|
2752
|
+
for (const item of resolvedTargets) {
|
|
2753
|
+
console.log(
|
|
2754
|
+
`- ${item.canonicalName} v${installed[item.name] ?? "unknown"}`
|
|
2755
|
+
);
|
|
2756
|
+
}
|
|
2757
|
+
if (withDeps && orphanItems.length) {
|
|
2758
|
+
console.log("\nOrphan registry items (--with-deps):");
|
|
2759
|
+
for (const item of orphanItems) {
|
|
2760
|
+
console.log(`- ${item.canonicalName}`);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
if (!withDeps && orphanHints.length) {
|
|
2764
|
+
console.log(
|
|
2765
|
+
"\nPossible orphan registry items (use --with-deps to remove):"
|
|
2766
|
+
);
|
|
2767
|
+
for (const item of orphanHints) {
|
|
2768
|
+
console.log(`- ${item.canonicalName}`);
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
const postOrphanRemaining = { ...remainingInstalled };
|
|
2772
|
+
for (const item of withDeps ? orphanItems : []) {
|
|
2773
|
+
removeInstalledKey(postOrphanRemaining, item.name);
|
|
2774
|
+
}
|
|
2775
|
+
const dryRunOrphans = collectOrphanedSharedResources(
|
|
2776
|
+
allRemovalTargets,
|
|
2777
|
+
await resolveInstalledItems(postOrphanRemaining)
|
|
2778
|
+
);
|
|
2779
|
+
console.log(
|
|
2780
|
+
"\nDependencies (npm packages are never removed automatically):"
|
|
2781
|
+
);
|
|
2782
|
+
for (const dependency of collectDependencies(resolvedTargets)) {
|
|
2783
|
+
console.log(`- ${dependency}`);
|
|
2784
|
+
}
|
|
2785
|
+
console.log("\nShared resources eligible for removal:");
|
|
2786
|
+
for (const utility of dryRunOrphans.utilities) {
|
|
2787
|
+
console.log(`- utility: ${utility}`);
|
|
2788
|
+
}
|
|
2789
|
+
for (const style of dryRunOrphans.styles) {
|
|
2790
|
+
console.log(`- style: ${style}`);
|
|
2791
|
+
}
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
const itemResults = [];
|
|
2795
|
+
const successfullyUninstalled = [];
|
|
2796
|
+
for (const item of allRemovalTargets) {
|
|
2797
|
+
const itemResult = await uninstallItemFiles(item, config);
|
|
2798
|
+
itemResults.push(itemResult);
|
|
2799
|
+
if (hasUninstallConflicts(itemResult)) {
|
|
2800
|
+
console.log(
|
|
2801
|
+
`${item.canonicalName} remains tracked because modified files were left in place.`
|
|
2802
|
+
);
|
|
2803
|
+
} else {
|
|
2804
|
+
successfullyUninstalled.push(item);
|
|
2805
|
+
}
|
|
2806
|
+
console.log("");
|
|
2807
|
+
}
|
|
2808
|
+
const postUninstallInstalled = { ...installed };
|
|
2809
|
+
for (const item of successfullyUninstalled) {
|
|
2810
|
+
removeInstalledKey(postUninstallInstalled, item.name);
|
|
2811
|
+
}
|
|
2812
|
+
const remainingItems = await resolveInstalledItems(postUninstallInstalled);
|
|
2813
|
+
const orphanedResources = collectOrphanedSharedResources(
|
|
2814
|
+
successfullyUninstalled,
|
|
2815
|
+
remainingItems
|
|
2816
|
+
);
|
|
2817
|
+
let resolvedUtilities = [];
|
|
2818
|
+
let resolvedStyles = [];
|
|
2819
|
+
if (orphanedResources.utilities.length || orphanedResources.styles.length) {
|
|
2820
|
+
try {
|
|
2821
|
+
resolvedUtilities = resolveRegistryUtilities(orphanedResources.utilities);
|
|
2822
|
+
resolvedStyles = resolveRegistryStyles(orphanedResources.styles);
|
|
2823
|
+
} catch (error) {
|
|
2824
|
+
console.log("Failed to resolve registry shared resources.");
|
|
2825
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
2826
|
+
process.exitCode = 1;
|
|
2827
|
+
return;
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
const utilitiesResult = await uninstallUtilities(resolvedUtilities, config);
|
|
2831
|
+
const stylesResult = await uninstallStyles(resolvedStyles, config);
|
|
2832
|
+
const updatedInstalled = {
|
|
2833
|
+
...installed
|
|
2834
|
+
};
|
|
2835
|
+
for (const item of successfullyUninstalled) {
|
|
2836
|
+
removeInstalledKey(updatedInstalled, item.name);
|
|
2837
|
+
}
|
|
2838
|
+
await saveConfig({
|
|
2839
|
+
...config,
|
|
2840
|
+
installed: updatedInstalled
|
|
2841
|
+
});
|
|
2842
|
+
const itemSummary = mergeUninstallResults(itemResults);
|
|
2843
|
+
const sharedSummary = mergeUninstallResults([utilitiesResult, stylesResult]);
|
|
2844
|
+
console.log("Uninstall summary:");
|
|
2845
|
+
printUninstallSummary("components", itemSummary);
|
|
2846
|
+
printUninstallSummary("shared resources", sharedSummary);
|
|
2847
|
+
console.log(
|
|
2848
|
+
`- untracked components: ${successfullyUninstalled.length}/${allRemovalTargets.length}`
|
|
2849
|
+
);
|
|
2850
|
+
if (hasUninstallConflicts(mergeUninstallResults([itemSummary, sharedSummary]))) {
|
|
2851
|
+
console.log(
|
|
2852
|
+
"Some files were left in place because they differ from registry templates."
|
|
2853
|
+
);
|
|
2854
|
+
}
|
|
2855
|
+
};
|
|
2856
|
+
|
|
2857
|
+
// src/utils/version.ts
|
|
2858
|
+
var compareVersions = (a, b) => {
|
|
2859
|
+
const pa = a.split(".").map(Number);
|
|
2860
|
+
const pb = b.split(".").map(Number);
|
|
2861
|
+
const length = Math.max(pa.length, pb.length);
|
|
2862
|
+
for (let i = 0; i < length; i++) {
|
|
2863
|
+
const va = pa[i] ?? 0;
|
|
2864
|
+
const vb = pb[i] ?? 0;
|
|
2865
|
+
if (va > vb) return 1;
|
|
2866
|
+
if (va < vb) return -1;
|
|
2867
|
+
}
|
|
2868
|
+
return 0;
|
|
2869
|
+
};
|
|
2870
|
+
var isUpdateAvailable = (installed, latest) => {
|
|
2871
|
+
return compareVersions(latest, installed) === 1;
|
|
2872
|
+
};
|
|
2873
|
+
|
|
2874
|
+
// src/install/update-engine.ts
|
|
2875
|
+
var checkItemFiles = async (name) => {
|
|
2876
|
+
const item = await findItem(name);
|
|
2877
|
+
const config = await loadConfig();
|
|
2878
|
+
if (!item) {
|
|
2879
|
+
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2880
|
+
return;
|
|
2881
|
+
}
|
|
2882
|
+
const installTarget = resolveItemInstallTarget(config, item);
|
|
2883
|
+
console.log(`File check for ${item.canonicalName}:`);
|
|
2884
|
+
for (const file of item.files) {
|
|
2885
|
+
const fileName = file.split("/").at(-1);
|
|
2886
|
+
if (!fileName) {
|
|
2887
|
+
console.log(`- invalid registry file path: ${file}`);
|
|
2888
|
+
continue;
|
|
2889
|
+
}
|
|
2890
|
+
const targetPath = join(getCwd(), installTarget, fileName);
|
|
2891
|
+
if (!await fileExists(targetPath)) {
|
|
2892
|
+
console.log(`- missing: ${targetPath}`);
|
|
2893
|
+
continue;
|
|
2894
|
+
}
|
|
2895
|
+
const preparedContent = prepareInstalledFileContent(
|
|
2896
|
+
await readFile(getRegistryTemplatePath(file), "utf-8"),
|
|
2897
|
+
item
|
|
2898
|
+
);
|
|
2899
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
2900
|
+
if (hashesAreEqual(preparedContent, targetContent)) {
|
|
2901
|
+
console.log(`- identical: ${targetPath}`);
|
|
2902
|
+
continue;
|
|
2903
|
+
}
|
|
2904
|
+
console.log(`- conflict: ${targetPath}`);
|
|
2905
|
+
}
|
|
2906
|
+
};
|
|
2907
|
+
var applySafeItemUpdate = async (name, force) => {
|
|
2908
|
+
const item = await findItem(name);
|
|
2909
|
+
const config = await loadConfig();
|
|
2910
|
+
if (!item) {
|
|
2911
|
+
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2912
|
+
return false;
|
|
2913
|
+
}
|
|
2914
|
+
const installTarget = resolveItemInstallTarget(config, item);
|
|
2915
|
+
let hasConflict = false;
|
|
2916
|
+
console.log(`Applying update for ${item.canonicalName}:`);
|
|
2917
|
+
for (const file of item.files) {
|
|
2918
|
+
const sourcePath = getRegistryTemplatePath(file);
|
|
2919
|
+
const fileName = file.split("/").at(-1);
|
|
2920
|
+
if (!fileName) {
|
|
2921
|
+
console.log(`- invalid registry file path: ${file}`);
|
|
2922
|
+
hasConflict = true;
|
|
2923
|
+
continue;
|
|
2924
|
+
}
|
|
2925
|
+
const targetPath = join(getCwd(), installTarget, fileName);
|
|
2926
|
+
const preparedContent = prepareInstalledFileContent(
|
|
2927
|
+
await readFile(sourcePath, "utf-8"),
|
|
2928
|
+
item
|
|
2929
|
+
);
|
|
2930
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
2931
|
+
if (!await fileExists(targetPath)) {
|
|
2932
|
+
await writeFile(targetPath, preparedContent, "utf-8");
|
|
2933
|
+
console.log(`- restored missing file: ${targetPath}`);
|
|
2934
|
+
continue;
|
|
2935
|
+
}
|
|
2936
|
+
const targetContent = await readFile(targetPath, "utf-8");
|
|
2937
|
+
if (hashesAreEqual(preparedContent, targetContent)) {
|
|
2938
|
+
console.log(`- identical: ${targetPath}`);
|
|
2939
|
+
continue;
|
|
2940
|
+
}
|
|
2941
|
+
if (force) {
|
|
2942
|
+
const backupPath = await createBackupFile(targetPath);
|
|
2943
|
+
if (backupPath) {
|
|
2944
|
+
console.log(`- backup created: ${backupPath}`);
|
|
2945
|
+
}
|
|
2946
|
+
await writeFile(targetPath, preparedContent, "utf-8");
|
|
2947
|
+
console.log(`- force updated file: ${targetPath}`);
|
|
2948
|
+
continue;
|
|
2949
|
+
}
|
|
2950
|
+
hasConflict = true;
|
|
2951
|
+
console.log(`- conflict (user modified): ${targetPath}`);
|
|
2952
|
+
}
|
|
2953
|
+
if (hasConflict) {
|
|
2954
|
+
console.log(
|
|
2955
|
+
"Update finished with conflicts. Installed version was not changed."
|
|
2956
|
+
);
|
|
2957
|
+
return false;
|
|
2958
|
+
}
|
|
2959
|
+
console.log(
|
|
2960
|
+
`\u2714 ${item.canonicalName} updated successfully to v${item.version}`
|
|
2961
|
+
);
|
|
2962
|
+
return true;
|
|
2963
|
+
};
|
|
2964
|
+
var checkItemUpdate = async (name, installedVersion, dryRun, force, sync = false) => {
|
|
2965
|
+
const item = await findItem(name);
|
|
2966
|
+
if (!item) {
|
|
2967
|
+
console.log(`Component "${name}" no longer exists in the registry.`);
|
|
2968
|
+
return false;
|
|
2969
|
+
}
|
|
2970
|
+
const versionUpdateAvailable = isUpdateAvailable(
|
|
2971
|
+
installedVersion,
|
|
2972
|
+
item.version
|
|
2973
|
+
);
|
|
2974
|
+
if (!sync && !versionUpdateAvailable) {
|
|
2975
|
+
console.log(`${item.canonicalName} is up to date (v${installedVersion}).`);
|
|
2976
|
+
return false;
|
|
2977
|
+
}
|
|
2978
|
+
if (sync && !versionUpdateAvailable) {
|
|
2979
|
+
console.log(
|
|
2980
|
+
`${item.canonicalName} template sync (installed v${installedVersion}, registry v${item.version})`
|
|
2981
|
+
);
|
|
2982
|
+
} else {
|
|
2983
|
+
console.log(
|
|
2984
|
+
`${item.canonicalName} can be updated: v${installedVersion} \u2192 v${item.version}`
|
|
2985
|
+
);
|
|
2986
|
+
}
|
|
2987
|
+
if (dryRun) {
|
|
2988
|
+
console.log("\nChanged file candidates:");
|
|
2989
|
+
for (const file of item.files) {
|
|
2990
|
+
console.log(`~ ${file}`);
|
|
2991
|
+
}
|
|
2992
|
+
console.log("\nDry run: no files will be changed.");
|
|
2993
|
+
console.log("Update plan:");
|
|
2994
|
+
if (force) {
|
|
2995
|
+
console.log(
|
|
2996
|
+
"- Force mode requested: conflicted files require backup before overwrite"
|
|
2997
|
+
);
|
|
2998
|
+
console.log(
|
|
2999
|
+
"- Dry run: backups would be created before forced overwrites"
|
|
3000
|
+
);
|
|
3001
|
+
}
|
|
3002
|
+
console.log(`- Check installed files for ${item.canonicalName}`);
|
|
3003
|
+
console.log("- Compare existing files with registry templates");
|
|
3004
|
+
console.log("- Report conflicts before writing changes");
|
|
3005
|
+
console.log("- Never overwrite user-modified files silently");
|
|
3006
|
+
await checkItemFiles(name);
|
|
3007
|
+
return false;
|
|
3008
|
+
}
|
|
3009
|
+
return await applySafeItemUpdate(name, force);
|
|
3010
|
+
};
|
|
3011
|
+
|
|
3012
|
+
// src/commands/update.ts
|
|
3013
|
+
var styleUpdateNames = ["theme"];
|
|
3014
|
+
var resolveInstalledKey = async (name, installed) => {
|
|
3015
|
+
if (installed[name]) {
|
|
3016
|
+
return name;
|
|
3017
|
+
}
|
|
3018
|
+
const item = await findItem(name);
|
|
3019
|
+
if (!item) {
|
|
3020
|
+
return void 0;
|
|
3021
|
+
}
|
|
3022
|
+
return installed[item.name] ? item.name : void 0;
|
|
3023
|
+
};
|
|
3024
|
+
var runStylesUpdate = async (config, dryRun) => {
|
|
3025
|
+
const styles = resolveRegistryStyles(styleUpdateNames);
|
|
3026
|
+
if (dryRun) {
|
|
3027
|
+
console.log("Dry run: no style files will be changed.\n");
|
|
3028
|
+
console.log("Styles:");
|
|
3029
|
+
for (const style of styles) {
|
|
3030
|
+
console.log(`- ${style.name} v${style.version}`);
|
|
3031
|
+
for (const file of style.files) {
|
|
3032
|
+
console.log(` ~ ${config.paths.styles}/${file.target}`);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
console.log(`
|
|
3036
|
+
Tailwind CSS entrypoint: ${config.tailwind.css}`);
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
const result = await installStyles(styles, config);
|
|
3040
|
+
console.log("Style update summary:");
|
|
3041
|
+
printResourceSummary("styles", result);
|
|
3042
|
+
if (hasInstallConflicts(result)) {
|
|
3043
|
+
console.log(
|
|
3044
|
+
"Style conflicts were left untouched. Review them before relying on the generated theme output."
|
|
3045
|
+
);
|
|
3046
|
+
}
|
|
3047
|
+
};
|
|
3048
|
+
var runUtilitiesUpdate = async (config, installed, dryRun, force) => {
|
|
3049
|
+
const installedItems = (await Promise.all(
|
|
3050
|
+
Object.keys(installed).map(async (name) => findItem(name))
|
|
3051
|
+
)).filter((item) => Boolean(item));
|
|
3052
|
+
const utilityNames = collectUtilities(installedItems);
|
|
3053
|
+
if (!utilityNames.length) {
|
|
3054
|
+
console.log("No shared utilities are tracked for installed components.");
|
|
3055
|
+
return;
|
|
3056
|
+
}
|
|
3057
|
+
const utilities = resolveRegistryUtilities(utilityNames);
|
|
3058
|
+
if (dryRun) {
|
|
3059
|
+
console.log("Dry run: no utility files will be changed.\n");
|
|
3060
|
+
console.log("Utilities:");
|
|
3061
|
+
for (const utility of utilities) {
|
|
3062
|
+
console.log(`- ${utility.name}`);
|
|
3063
|
+
console.log(` ~ ${config.paths.utilities}/${utility.target}`);
|
|
3064
|
+
}
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
const result = await updateUtilities(utilities, config, force);
|
|
3068
|
+
console.log("Utility update summary:");
|
|
3069
|
+
printResourceSummary("utilities", result);
|
|
3070
|
+
if (hasInstallConflicts(result)) {
|
|
3071
|
+
console.log(
|
|
3072
|
+
"Utility conflicts were left untouched. Re-run with --force to overwrite after creating backups."
|
|
3073
|
+
);
|
|
3074
|
+
}
|
|
3075
|
+
};
|
|
3076
|
+
var runComponentUpdates = async (config, installed, targetNames, dryRun, force, sync) => {
|
|
3077
|
+
let changed = false;
|
|
3078
|
+
if (!Object.keys(installed).length) {
|
|
3079
|
+
console.log("No Lexsys components are currently tracked.");
|
|
3080
|
+
return false;
|
|
3081
|
+
}
|
|
3082
|
+
console.log("Checking installed Lexsys components:\n");
|
|
3083
|
+
for (const name of targetNames) {
|
|
3084
|
+
const version = installed[name];
|
|
3085
|
+
if (!version) {
|
|
3086
|
+
continue;
|
|
3087
|
+
}
|
|
3088
|
+
const didUpdate = await checkItemUpdate(name, version, dryRun, force, sync);
|
|
3089
|
+
const item = await findItem(name);
|
|
3090
|
+
if (didUpdate && item) {
|
|
3091
|
+
installed[name] = item.version;
|
|
3092
|
+
changed = true;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
return changed;
|
|
3096
|
+
};
|
|
3097
|
+
var runUpdate = async (args2) => {
|
|
3098
|
+
const dryRun = hasFlag(args2, "--dry-run", "-d");
|
|
3099
|
+
const force = hasFlag(args2, "--force", "-f");
|
|
3100
|
+
const yes = hasFlag(args2, "--yes", "-y");
|
|
3101
|
+
const noFallback = hasFlag(args2, "--no-fallback");
|
|
3102
|
+
const sync = hasFlag(args2, "--sync");
|
|
3103
|
+
const stylesFlag = hasFlag(args2, "--styles", "-S");
|
|
3104
|
+
const utilitiesFlag = hasFlag(args2, "--utilities", "-u");
|
|
3105
|
+
const updateAll = hasFlag(args2, "--all", "-a");
|
|
3106
|
+
const targetArgs = removeFlags(removeFlagsWithValues(args2, ["--cwd", "-C"]), [
|
|
3107
|
+
"--dry-run",
|
|
3108
|
+
"-d",
|
|
3109
|
+
"--force",
|
|
3110
|
+
"-f",
|
|
3111
|
+
"--yes",
|
|
3112
|
+
"-y",
|
|
3113
|
+
"--no-fallback",
|
|
3114
|
+
"--styles",
|
|
3115
|
+
"-S",
|
|
3116
|
+
"--sync",
|
|
3117
|
+
"--utilities",
|
|
3118
|
+
"-u",
|
|
3119
|
+
"--all",
|
|
3120
|
+
"-a"
|
|
3121
|
+
]);
|
|
3122
|
+
const config = await loadConfig();
|
|
3123
|
+
const installed = { ...config.installed ?? {} };
|
|
3124
|
+
try {
|
|
3125
|
+
await getRegistryProviderResult({
|
|
3126
|
+
fallback: !noFallback
|
|
3127
|
+
});
|
|
3128
|
+
} catch (error) {
|
|
3129
|
+
console.log("Failed to resolve registry.");
|
|
3130
|
+
console.log(error instanceof Error ? error.message : String(error));
|
|
3131
|
+
process.exitCode = 1;
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
if (yes) {
|
|
3135
|
+
console.log("Auto-confirm mode is enabled.");
|
|
3136
|
+
}
|
|
3137
|
+
if (!updateAll && !stylesFlag && !utilitiesFlag && targetArgs.length === 0) {
|
|
3138
|
+
const installedNames = Object.keys(installed);
|
|
3139
|
+
if (installedNames.length === 0) {
|
|
3140
|
+
console.log(
|
|
3141
|
+
"No components installed. Run `lexsys add <component>` first."
|
|
3142
|
+
);
|
|
3143
|
+
return;
|
|
3144
|
+
}
|
|
3145
|
+
const response = await prompts({
|
|
3146
|
+
type: "multiselect",
|
|
3147
|
+
name: "components",
|
|
3148
|
+
message: "Select components to update",
|
|
3149
|
+
choices: installedNames.map((name) => ({ title: name, value: name })),
|
|
3150
|
+
min: 1
|
|
3151
|
+
});
|
|
3152
|
+
const selected = response.components;
|
|
3153
|
+
if (!Array.isArray(selected) || !selected.length) return;
|
|
3154
|
+
targetArgs.push(
|
|
3155
|
+
...selected.filter((c) => typeof c === "string")
|
|
3156
|
+
);
|
|
3157
|
+
}
|
|
3158
|
+
const shouldUpdateComponents = updateAll || targetArgs.length > 0;
|
|
3159
|
+
const resourcesOnly = (stylesFlag || utilitiesFlag) && !shouldUpdateComponents;
|
|
3160
|
+
if (resourcesOnly) {
|
|
3161
|
+
if (stylesFlag) {
|
|
3162
|
+
await runStylesUpdate(config, dryRun);
|
|
3163
|
+
}
|
|
3164
|
+
if (utilitiesFlag) {
|
|
3165
|
+
await runUtilitiesUpdate(config, installed, dryRun, force);
|
|
3166
|
+
}
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
if (stylesFlag) {
|
|
3170
|
+
await runStylesUpdate(config, dryRun);
|
|
3171
|
+
}
|
|
3172
|
+
if (utilitiesFlag) {
|
|
3173
|
+
await runUtilitiesUpdate(config, installed, dryRun, force);
|
|
3174
|
+
}
|
|
3175
|
+
if (!shouldUpdateComponents) {
|
|
3176
|
+
return;
|
|
3177
|
+
}
|
|
3178
|
+
let changed = false;
|
|
3179
|
+
if (updateAll) {
|
|
3180
|
+
changed = await runComponentUpdates(
|
|
3181
|
+
config,
|
|
3182
|
+
installed,
|
|
3183
|
+
Object.keys(installed),
|
|
3184
|
+
dryRun,
|
|
3185
|
+
force,
|
|
3186
|
+
sync
|
|
3187
|
+
);
|
|
3188
|
+
} else {
|
|
3189
|
+
for (const name of targetArgs) {
|
|
3190
|
+
const installedKey = await resolveInstalledKey(name, installed);
|
|
3191
|
+
if (!installedKey) {
|
|
3192
|
+
console.log(`Component "${name}" is not tracked as installed.`);
|
|
3193
|
+
continue;
|
|
3194
|
+
}
|
|
3195
|
+
const didUpdateOne = await runComponentUpdates(
|
|
3196
|
+
config,
|
|
3197
|
+
installed,
|
|
3198
|
+
[installedKey],
|
|
3199
|
+
dryRun,
|
|
3200
|
+
force,
|
|
3201
|
+
sync
|
|
3202
|
+
);
|
|
3203
|
+
if (didUpdateOne) {
|
|
3204
|
+
changed = true;
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
if (changed) {
|
|
3209
|
+
await saveConfig({
|
|
3210
|
+
...config,
|
|
3211
|
+
installed
|
|
3212
|
+
});
|
|
3213
|
+
}
|
|
3214
|
+
};
|
|
3215
|
+
var cliDistDir = dirname(fileURLToPath(import.meta.url));
|
|
3216
|
+
var packageJsonPath = join(cliDistDir, "..", "package.json");
|
|
3217
|
+
var runVersion = async () => {
|
|
3218
|
+
const content = await readFile(packageJsonPath, "utf-8");
|
|
3219
|
+
const packageJson = JSON.parse(content);
|
|
3220
|
+
console.log(`lexsys ${packageJson.version ?? "unknown"}`);
|
|
3221
|
+
};
|
|
3222
|
+
|
|
3223
|
+
// src/index.ts
|
|
3224
|
+
var [, , command, ...args] = process.argv;
|
|
3225
|
+
var cwd = getFlagValue(args, "--cwd", "-C");
|
|
3226
|
+
if (cwd) {
|
|
3227
|
+
setCwd(cwd);
|
|
3228
|
+
}
|
|
3229
|
+
try {
|
|
3230
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
3231
|
+
runHelp();
|
|
3232
|
+
process.exit(0);
|
|
3233
|
+
}
|
|
3234
|
+
if (command === "version" || command === "--version" || command === "-v") {
|
|
3235
|
+
await runVersion();
|
|
3236
|
+
process.exit(0);
|
|
3237
|
+
}
|
|
3238
|
+
if (command === "list" || command === "ls") {
|
|
3239
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3240
|
+
runHelpFor("list");
|
|
3241
|
+
process.exit(0);
|
|
3242
|
+
}
|
|
3243
|
+
await runList({
|
|
3244
|
+
json: hasFlag(args, "--json", "-j"),
|
|
3245
|
+
noFallback: args.includes("--no-fallback")
|
|
3246
|
+
});
|
|
3247
|
+
process.exit(0);
|
|
3248
|
+
}
|
|
3249
|
+
if (command === "doctor" || command === "dr") {
|
|
3250
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3251
|
+
runHelpFor("doctor");
|
|
3252
|
+
process.exit(0);
|
|
3253
|
+
}
|
|
3254
|
+
await runDoctor({
|
|
3255
|
+
noFallback: args.includes("--no-fallback")
|
|
3256
|
+
});
|
|
3257
|
+
process.exit(0);
|
|
3258
|
+
}
|
|
3259
|
+
if (command === "init" || command === "create") {
|
|
3260
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3261
|
+
runHelpFor("init");
|
|
3262
|
+
process.exit(0);
|
|
3263
|
+
}
|
|
3264
|
+
await runInit(args);
|
|
3265
|
+
process.exit(0);
|
|
3266
|
+
}
|
|
3267
|
+
if (command === "add" || command === "a") {
|
|
3268
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3269
|
+
runHelpFor("add");
|
|
3270
|
+
process.exit(0);
|
|
3271
|
+
}
|
|
3272
|
+
await runAdd(args);
|
|
3273
|
+
process.exit(0);
|
|
3274
|
+
}
|
|
3275
|
+
if (command === "update" || command === "up") {
|
|
3276
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3277
|
+
runHelpFor("update");
|
|
3278
|
+
process.exit(0);
|
|
3279
|
+
}
|
|
3280
|
+
await runUpdate(args);
|
|
3281
|
+
process.exit(0);
|
|
3282
|
+
}
|
|
3283
|
+
if (command === "status" || command === "st") {
|
|
3284
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3285
|
+
runHelpFor("status");
|
|
3286
|
+
process.exit(0);
|
|
3287
|
+
}
|
|
3288
|
+
await runStatus({
|
|
3289
|
+
noFallback: args.includes("--no-fallback")
|
|
3290
|
+
});
|
|
3291
|
+
process.exit(0);
|
|
3292
|
+
}
|
|
3293
|
+
if (command === "registry" || command === "reg") {
|
|
3294
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3295
|
+
runHelpFor("registry");
|
|
3296
|
+
process.exit(0);
|
|
3297
|
+
}
|
|
3298
|
+
await runRegistry({
|
|
3299
|
+
summary: hasFlag(args, "--summary", "-s"),
|
|
3300
|
+
source: args.includes("--source"),
|
|
3301
|
+
local: hasFlag(args, "--local", "-l"),
|
|
3302
|
+
remote: hasFlag(args, "--remote", "-r"),
|
|
3303
|
+
noFallback: args.includes("--no-fallback")
|
|
3304
|
+
});
|
|
3305
|
+
process.exit(0);
|
|
3306
|
+
}
|
|
3307
|
+
if (command === "config" || command === "cfg") {
|
|
3308
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3309
|
+
runHelpFor("config");
|
|
3310
|
+
process.exit(0);
|
|
3311
|
+
}
|
|
3312
|
+
await runConfig({
|
|
3313
|
+
path: args.includes("--path") || args.includes("-p"),
|
|
3314
|
+
exists: args.includes("--exists") || args.includes("-e"),
|
|
3315
|
+
setRegistryUrl: getFlagValue(args, "--set-registry-url"),
|
|
3316
|
+
clearRegistryUrl: args.includes("--clear-registry-url")
|
|
3317
|
+
});
|
|
3318
|
+
process.exit(0);
|
|
3319
|
+
}
|
|
3320
|
+
if (command === "uninstall" || command === "rm") {
|
|
3321
|
+
if (hasFlag(args, "--help", "-h")) {
|
|
3322
|
+
runHelpFor("uninstall");
|
|
3323
|
+
process.exit(0);
|
|
3324
|
+
}
|
|
3325
|
+
await runUninstall(args);
|
|
3326
|
+
process.exit(0);
|
|
3327
|
+
}
|
|
3328
|
+
throw new CliError(`Unknown command: ${command}`);
|
|
3329
|
+
} catch (error) {
|
|
3330
|
+
handleCliError(error);
|
|
3331
|
+
}
|
|
3332
|
+
runHelp();
|
|
3333
|
+
process.exit(1);
|