create-krispya 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +115 -11
- package/dist/chunks/index.cjs +487 -277
- package/dist/chunks/index.mjs +484 -278
- package/dist/cli.cjs +1282 -302
- package/dist/cli.mjs +1285 -305
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +21 -5
- package/dist/index.d.mts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.mjs +4 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -58,14 +58,15 @@ function openInEditor(editor, path, reuseWindow) {
|
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
function formatConfigSummary(options) {
|
|
61
|
+
function formatConfigSummary(options, inherited) {
|
|
62
62
|
const lines = [];
|
|
63
63
|
const VALUE_COL = 27;
|
|
64
|
-
const formatRow = (label, value, indent = "") => {
|
|
64
|
+
const formatRow = (label, value, isInherited = false, indent = "") => {
|
|
65
65
|
const fullLabel = indent + label;
|
|
66
66
|
const dotCount = Math.max(1, VALUE_COL - fullLabel.length - 1);
|
|
67
67
|
const dots = color__default.gray(".".repeat(dotCount));
|
|
68
|
-
|
|
68
|
+
const displayValue = isInherited ? `${value} \u{1F512}` : value;
|
|
69
|
+
return `${indent}${label} ${dots} ${displayValue}`;
|
|
69
70
|
};
|
|
70
71
|
const formatLanguage = (lang) => {
|
|
71
72
|
return lang === "typescript" ? "TypeScript" : lang === "javascript" ? "JavaScript" : lang;
|
|
@@ -84,20 +85,29 @@ function formatConfigSummary(options) {
|
|
|
84
85
|
} else {
|
|
85
86
|
lines.push(formatRow("Bundler", "vite"));
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
-
lines.push(formatRow("
|
|
88
|
+
const nodeVersionInherited = inherited?.nodeVersion !== void 0;
|
|
89
|
+
lines.push(formatRow("Node version", options.nodeVersion || "latest", nodeVersionInherited));
|
|
90
|
+
const pmInherited = inherited?.packageManager !== void 0;
|
|
91
|
+
lines.push(formatRow("Package manager", options.packageManager || "pnpm", pmInherited));
|
|
89
92
|
if (options.packageManager === "pnpm") {
|
|
90
93
|
const versionManaged = options.pnpmManageVersions ? "yes" : "no";
|
|
91
|
-
|
|
94
|
+
const pnpmVersionInherited = inherited?.pnpmManageVersions !== void 0;
|
|
95
|
+
lines.push(formatRow("\u21B3 Version managed", versionManaged, pnpmVersionInherited, ""));
|
|
92
96
|
}
|
|
93
97
|
if (options.linter) {
|
|
94
|
-
|
|
98
|
+
const linterInherited = inherited?.linter !== void 0;
|
|
99
|
+
lines.push(formatRow("Linter", options.linter, linterInherited));
|
|
95
100
|
}
|
|
96
101
|
if (options.formatter) {
|
|
97
|
-
|
|
102
|
+
const formatterInherited = inherited?.formatter !== void 0;
|
|
103
|
+
lines.push(formatRow("Formatter", options.formatter, formatterInherited));
|
|
98
104
|
}
|
|
99
105
|
const testing = options.testing ?? (projectType === "library" ? "vitest" : "none");
|
|
100
106
|
lines.push(formatRow("Testing", testing));
|
|
107
|
+
if (!inherited) {
|
|
108
|
+
const configStrategy = options.configStrategy ?? "stealth";
|
|
109
|
+
lines.push(formatRow("Config strategy", configStrategy));
|
|
110
|
+
}
|
|
101
111
|
if (options.template && index.getBaseTemplate(options.template) === "r3f") {
|
|
102
112
|
const integrationNames = [
|
|
103
113
|
options.drei && "drei",
|
|
@@ -159,11 +169,14 @@ function getReuseWindow() {
|
|
|
159
169
|
function setReuseWindow(reuse) {
|
|
160
170
|
config.set("reuseWindow", reuse);
|
|
161
171
|
}
|
|
162
|
-
function
|
|
163
|
-
return config.get("
|
|
172
|
+
function getAiPlatforms() {
|
|
173
|
+
return config.get("aiPlatforms");
|
|
174
|
+
}
|
|
175
|
+
function setAiPlatforms(platforms) {
|
|
176
|
+
config.set("aiPlatforms", platforms);
|
|
164
177
|
}
|
|
165
|
-
function
|
|
166
|
-
config.
|
|
178
|
+
function getConfigStrategy() {
|
|
179
|
+
return config.get("configStrategy") ?? "stealth";
|
|
167
180
|
}
|
|
168
181
|
function clearConfig() {
|
|
169
182
|
config.clear();
|
|
@@ -175,20 +188,21 @@ function getCustomTemplates() {
|
|
|
175
188
|
return config.get("customTemplates") ?? {};
|
|
176
189
|
}
|
|
177
190
|
|
|
178
|
-
function getDefaultOptions(template, name, projectType = "app", libraryBundler, integrations,
|
|
191
|
+
function getDefaultOptions(template, name, projectType = "app", libraryBundler, integrations, inheritedSettings) {
|
|
179
192
|
const baseTemplate = index.getBaseTemplate(template);
|
|
180
193
|
const base = {
|
|
181
194
|
name,
|
|
182
195
|
template,
|
|
183
196
|
projectType,
|
|
184
197
|
libraryBundler: projectType === "library" ? libraryBundler ?? "unbuild" : void 0,
|
|
185
|
-
packageManager: "pnpm",
|
|
186
|
-
pnpmManageVersions: true,
|
|
187
|
-
nodeVersion: "latest",
|
|
188
|
-
linter:
|
|
189
|
-
formatter:
|
|
198
|
+
packageManager: inheritedSettings?.packageManager ?? "pnpm",
|
|
199
|
+
pnpmManageVersions: inheritedSettings?.pnpmManageVersions ?? true,
|
|
200
|
+
nodeVersion: inheritedSettings?.nodeVersion ?? "latest",
|
|
201
|
+
linter: inheritedSettings?.linter ?? "oxlint",
|
|
202
|
+
formatter: inheritedSettings?.formatter ?? "prettier",
|
|
190
203
|
// Libraries get vitest by default, apps don't
|
|
191
|
-
testing: projectType === "library" ? "vitest" : "none"
|
|
204
|
+
testing: projectType === "library" ? "vitest" : "none",
|
|
205
|
+
configStrategy: getConfigStrategy()
|
|
192
206
|
};
|
|
193
207
|
if (baseTemplate === "r3f" && integrations) {
|
|
194
208
|
return {
|
|
@@ -220,7 +234,22 @@ function getDefaultProjectName(template) {
|
|
|
220
234
|
return `react-three-${index.generateRandomName()}`;
|
|
221
235
|
}
|
|
222
236
|
}
|
|
223
|
-
async function promptForR3fIntegrations() {
|
|
237
|
+
async function promptForR3fIntegrations(presets) {
|
|
238
|
+
const initialValues = [];
|
|
239
|
+
if (presets) {
|
|
240
|
+
if (presets.drei) initialValues.push("drei");
|
|
241
|
+
if (presets.handle) initialValues.push("handle");
|
|
242
|
+
if (presets.leva) initialValues.push("leva");
|
|
243
|
+
if (presets.postprocessing) initialValues.push("postprocessing");
|
|
244
|
+
if (presets.rapier) initialValues.push("rapier");
|
|
245
|
+
if (presets.xr) initialValues.push("xr");
|
|
246
|
+
if (presets.uikit) initialValues.push("uikit");
|
|
247
|
+
if (presets.offscreen) initialValues.push("offscreen");
|
|
248
|
+
if (presets.zustand) initialValues.push("zustand");
|
|
249
|
+
if (presets.koota) initialValues.push("koota");
|
|
250
|
+
if (presets.triplex) initialValues.push("triplex");
|
|
251
|
+
if (presets.viverse) initialValues.push("viverse");
|
|
252
|
+
}
|
|
224
253
|
const selected = await p__namespace.multiselect({
|
|
225
254
|
message: "R3F integrations",
|
|
226
255
|
options: [
|
|
@@ -237,7 +266,7 @@ async function promptForR3fIntegrations() {
|
|
|
237
266
|
{ value: "triplex", label: "Triplex" },
|
|
238
267
|
{ value: "viverse", label: "Viverse" }
|
|
239
268
|
],
|
|
240
|
-
initialValues: ["drei"],
|
|
269
|
+
initialValues: initialValues.length > 0 ? initialValues : ["drei"],
|
|
241
270
|
required: false
|
|
242
271
|
});
|
|
243
272
|
if (p__namespace.isCancel(selected)) {
|
|
@@ -246,7 +275,7 @@ async function promptForR3fIntegrations() {
|
|
|
246
275
|
}
|
|
247
276
|
return selected;
|
|
248
277
|
}
|
|
249
|
-
async function promptForCustomization(template, name, projectType, integrations,
|
|
278
|
+
async function promptForCustomization(template, name, projectType, integrations, inheritedSettings, presets) {
|
|
250
279
|
let libraryBundler;
|
|
251
280
|
if (projectType === "library") {
|
|
252
281
|
const bundler = await p__namespace.select({
|
|
@@ -255,7 +284,7 @@ async function promptForCustomization(template, name, projectType, integrations,
|
|
|
255
284
|
{ value: "unbuild", label: "unbuild", hint: "unjs, simple config" },
|
|
256
285
|
{ value: "tsdown", label: "tsdown", hint: "fast, esbuild-based" }
|
|
257
286
|
],
|
|
258
|
-
initialValue: "unbuild"
|
|
287
|
+
initialValue: presets?.bundler ?? "unbuild"
|
|
259
288
|
});
|
|
260
289
|
if (p__namespace.isCancel(bundler)) {
|
|
261
290
|
p__namespace.cancel("Operation cancelled.");
|
|
@@ -263,64 +292,57 @@ async function promptForCustomization(template, name, projectType, integrations,
|
|
|
263
292
|
}
|
|
264
293
|
libraryBundler = bundler;
|
|
265
294
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
if (p__namespace.isCancel(nodeVersion)) {
|
|
278
|
-
p__namespace.cancel("Operation cancelled.");
|
|
279
|
-
process.exit(0);
|
|
280
|
-
}
|
|
281
|
-
const packageManager = await p__namespace.select({
|
|
282
|
-
message: "Package manager",
|
|
283
|
-
options: [
|
|
284
|
-
{ value: "pnpm", label: "pnpm" },
|
|
285
|
-
{ value: "npm", label: "npm" },
|
|
286
|
-
{ value: "yarn", label: "yarn" },
|
|
287
|
-
{ value: "custom", label: "Other (custom)" }
|
|
288
|
-
],
|
|
289
|
-
initialValue: "pnpm"
|
|
290
|
-
});
|
|
291
|
-
if (p__namespace.isCancel(packageManager)) {
|
|
292
|
-
p__namespace.cancel("Operation cancelled.");
|
|
293
|
-
process.exit(0);
|
|
294
|
-
}
|
|
295
|
-
let finalPackageManager = packageManager;
|
|
296
|
-
if (packageManager === "custom") {
|
|
297
|
-
const customPm = await p__namespace.text({
|
|
298
|
-
message: "Enter package manager command",
|
|
295
|
+
let nodeVersion = inheritedSettings?.nodeVersion ?? presets?.nodeVersion ?? "latest";
|
|
296
|
+
let finalPackageManager = inheritedSettings?.packageManager ?? presets?.packageManager ?? "pnpm";
|
|
297
|
+
let pnpmManageVersions = inheritedSettings?.pnpmManageVersions ?? presets?.pnpmManageVersions ?? true;
|
|
298
|
+
if (!inheritedSettings?.nodeVersion) {
|
|
299
|
+
const nodeVersionInput = await p__namespace.text({
|
|
300
|
+
message: "Node.js version",
|
|
301
|
+
placeholder: presets?.nodeVersion ?? "latest",
|
|
302
|
+
defaultValue: presets?.nodeVersion ?? "latest",
|
|
299
303
|
validate: (value) => {
|
|
300
304
|
if (!value.length) return "Required";
|
|
305
|
+
if (value !== "latest" && !/^\d+(\.\d+(\.\d+)?)?$/.test(value)) {
|
|
306
|
+
return 'Must be "latest" or a valid semver (e.g., "22" or "22.13.0")';
|
|
307
|
+
}
|
|
301
308
|
}
|
|
302
309
|
});
|
|
303
|
-
if (p__namespace.isCancel(
|
|
310
|
+
if (p__namespace.isCancel(nodeVersionInput)) {
|
|
304
311
|
p__namespace.cancel("Operation cancelled.");
|
|
305
312
|
process.exit(0);
|
|
306
313
|
}
|
|
307
|
-
|
|
314
|
+
nodeVersion = nodeVersionInput;
|
|
308
315
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
316
|
+
if (!inheritedSettings?.packageManager) {
|
|
317
|
+
const packageManager = await p__namespace.select({
|
|
318
|
+
message: "Package manager",
|
|
319
|
+
options: [
|
|
320
|
+
{ value: "pnpm", label: "pnpm" },
|
|
321
|
+
{ value: "npm", label: "npm" },
|
|
322
|
+
{ value: "yarn", label: "yarn" }
|
|
323
|
+
],
|
|
324
|
+
initialValue: presets?.packageManager ?? "pnpm"
|
|
314
325
|
});
|
|
315
|
-
if (p__namespace.isCancel(
|
|
326
|
+
if (p__namespace.isCancel(packageManager)) {
|
|
316
327
|
p__namespace.cancel("Operation cancelled.");
|
|
317
328
|
process.exit(0);
|
|
318
329
|
}
|
|
319
|
-
|
|
330
|
+
finalPackageManager = packageManager;
|
|
331
|
+
if (packageManager === "pnpm") {
|
|
332
|
+
const managePnpm = await p__namespace.confirm({
|
|
333
|
+
message: "Enable manage-package-manager-versions?",
|
|
334
|
+
initialValue: presets?.pnpmManageVersions ?? true
|
|
335
|
+
});
|
|
336
|
+
if (p__namespace.isCancel(managePnpm)) {
|
|
337
|
+
p__namespace.cancel("Operation cancelled.");
|
|
338
|
+
process.exit(0);
|
|
339
|
+
}
|
|
340
|
+
pnpmManageVersions = managePnpm;
|
|
341
|
+
}
|
|
320
342
|
}
|
|
321
|
-
let linter =
|
|
322
|
-
let formatter =
|
|
323
|
-
if (!
|
|
343
|
+
let linter = inheritedSettings?.linter ?? presets?.linter ?? "oxlint";
|
|
344
|
+
let formatter = inheritedSettings?.formatter ?? presets?.formatter ?? "prettier";
|
|
345
|
+
if (!inheritedSettings?.linter) {
|
|
324
346
|
const linterChoice = await p__namespace.select({
|
|
325
347
|
message: "Linter",
|
|
326
348
|
options: [
|
|
@@ -328,7 +350,7 @@ async function promptForCustomization(template, name, projectType, integrations,
|
|
|
328
350
|
{ value: "eslint", label: "ESLint", hint: "classic" },
|
|
329
351
|
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
330
352
|
],
|
|
331
|
-
initialValue: "oxlint"
|
|
353
|
+
initialValue: presets?.linter ?? "oxlint"
|
|
332
354
|
});
|
|
333
355
|
if (p__namespace.isCancel(linterChoice)) {
|
|
334
356
|
p__namespace.cancel("Operation cancelled.");
|
|
@@ -336,15 +358,15 @@ async function promptForCustomization(template, name, projectType, integrations,
|
|
|
336
358
|
}
|
|
337
359
|
linter = linterChoice;
|
|
338
360
|
}
|
|
339
|
-
if (!
|
|
361
|
+
if (!inheritedSettings?.formatter) {
|
|
340
362
|
const formatterChoice = await p__namespace.select({
|
|
341
363
|
message: "Formatter",
|
|
342
364
|
options: [
|
|
365
|
+
{ value: "prettier", label: "Prettier", hint: "widely adopted" },
|
|
343
366
|
{ value: "oxfmt", label: "Oxfmt", hint: "fast, Prettier-compatible" },
|
|
344
|
-
{ value: "prettier", label: "Prettier", hint: "classic" },
|
|
345
367
|
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
346
368
|
],
|
|
347
|
-
initialValue: "
|
|
369
|
+
initialValue: presets?.formatter ?? "prettier"
|
|
348
370
|
});
|
|
349
371
|
if (p__namespace.isCancel(formatterChoice)) {
|
|
350
372
|
p__namespace.cancel("Operation cancelled.");
|
|
@@ -376,6 +398,18 @@ async function promptForCustomization(template, name, projectType, integrations,
|
|
|
376
398
|
p__namespace.cancel("Operation cancelled.");
|
|
377
399
|
process.exit(0);
|
|
378
400
|
}
|
|
401
|
+
const configStrategyChoice = await p__namespace.select({
|
|
402
|
+
message: "Config strategy",
|
|
403
|
+
options: [
|
|
404
|
+
{ value: "stealth", label: "stealth", hint: "configs in .config/" },
|
|
405
|
+
{ value: "root", label: "root", hint: "configs at project root" }
|
|
406
|
+
],
|
|
407
|
+
initialValue: getConfigStrategy()
|
|
408
|
+
});
|
|
409
|
+
if (p__namespace.isCancel(configStrategyChoice)) {
|
|
410
|
+
p__namespace.cancel("Operation cancelled.");
|
|
411
|
+
process.exit(0);
|
|
412
|
+
}
|
|
379
413
|
const baseTemplate = index.getBaseTemplate(template);
|
|
380
414
|
const finalTemplate = language === "javascript" ? `${baseTemplate}-js` : baseTemplate;
|
|
381
415
|
const base = {
|
|
@@ -388,7 +422,8 @@ async function promptForCustomization(template, name, projectType, integrations,
|
|
|
388
422
|
pnpmManageVersions,
|
|
389
423
|
linter,
|
|
390
424
|
formatter,
|
|
391
|
-
testing
|
|
425
|
+
testing,
|
|
426
|
+
configStrategy: configStrategyChoice
|
|
392
427
|
};
|
|
393
428
|
if (baseTemplate === "r3f" && integrations) {
|
|
394
429
|
return {
|
|
@@ -433,14 +468,14 @@ function getDefaultMonorepoOptions(name) {
|
|
|
433
468
|
pnpmManageVersions: true,
|
|
434
469
|
nodeVersion: "latest",
|
|
435
470
|
linter: "oxlint",
|
|
436
|
-
formatter: "
|
|
471
|
+
formatter: "prettier"
|
|
437
472
|
};
|
|
438
473
|
}
|
|
439
|
-
async function promptForMonorepoCustomization(name) {
|
|
474
|
+
async function promptForMonorepoCustomization(name, presets) {
|
|
440
475
|
const nodeVersion = await p__namespace.text({
|
|
441
476
|
message: "Node.js version",
|
|
442
|
-
placeholder: "latest",
|
|
443
|
-
defaultValue: "latest",
|
|
477
|
+
placeholder: presets?.nodeVersion ?? "latest",
|
|
478
|
+
defaultValue: presets?.nodeVersion ?? "latest",
|
|
444
479
|
validate: (value) => {
|
|
445
480
|
if (!value.length) return "Required";
|
|
446
481
|
if (value !== "latest" && !/^\d+(\.\d+(\.\d+)?)?$/.test(value)) {
|
|
@@ -454,7 +489,7 @@ async function promptForMonorepoCustomization(name) {
|
|
|
454
489
|
}
|
|
455
490
|
const managePnpm = await p__namespace.confirm({
|
|
456
491
|
message: "Enable manage-package-manager-versions?",
|
|
457
|
-
initialValue: true
|
|
492
|
+
initialValue: presets?.pnpmManageVersions ?? true
|
|
458
493
|
});
|
|
459
494
|
if (p__namespace.isCancel(managePnpm)) {
|
|
460
495
|
p__namespace.cancel("Operation cancelled.");
|
|
@@ -467,7 +502,7 @@ async function promptForMonorepoCustomization(name) {
|
|
|
467
502
|
{ value: "eslint", label: "ESLint", hint: "classic" },
|
|
468
503
|
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
469
504
|
],
|
|
470
|
-
initialValue: "oxlint"
|
|
505
|
+
initialValue: presets?.linter ?? "oxlint"
|
|
471
506
|
});
|
|
472
507
|
if (p__namespace.isCancel(linter)) {
|
|
473
508
|
p__namespace.cancel("Operation cancelled.");
|
|
@@ -476,11 +511,11 @@ async function promptForMonorepoCustomization(name) {
|
|
|
476
511
|
const formatter = await p__namespace.select({
|
|
477
512
|
message: "Formatter",
|
|
478
513
|
options: [
|
|
514
|
+
{ value: "prettier", label: "Prettier", hint: "widely adopted" },
|
|
479
515
|
{ value: "oxfmt", label: "Oxfmt", hint: "fast, Prettier-compatible" },
|
|
480
|
-
{ value: "prettier", label: "Prettier", hint: "classic" },
|
|
481
516
|
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
482
517
|
],
|
|
483
|
-
initialValue: "
|
|
518
|
+
initialValue: presets?.formatter ?? "prettier"
|
|
484
519
|
});
|
|
485
520
|
if (p__namespace.isCancel(formatter)) {
|
|
486
521
|
p__namespace.cancel("Operation cancelled.");
|
|
@@ -496,8 +531,15 @@ async function promptForMonorepoCustomization(name) {
|
|
|
496
531
|
formatter
|
|
497
532
|
};
|
|
498
533
|
}
|
|
499
|
-
async function promptForMonorepo(workspaceName) {
|
|
534
|
+
async function promptForMonorepo(workspaceName, presets) {
|
|
500
535
|
const defaultOptions = getDefaultMonorepoOptions(workspaceName);
|
|
536
|
+
if (presets) {
|
|
537
|
+
if (presets.linter) defaultOptions.linter = presets.linter;
|
|
538
|
+
if (presets.formatter) defaultOptions.formatter = presets.formatter;
|
|
539
|
+
if (presets.nodeVersion) defaultOptions.nodeVersion = presets.nodeVersion;
|
|
540
|
+
if (presets.pnpmManageVersions !== void 0)
|
|
541
|
+
defaultOptions.pnpmManageVersions = presets.pnpmManageVersions;
|
|
542
|
+
}
|
|
501
543
|
p__namespace.note(
|
|
502
544
|
formatMonorepoConfigSummary({
|
|
503
545
|
name: defaultOptions.name,
|
|
@@ -505,24 +547,28 @@ async function promptForMonorepo(workspaceName) {
|
|
|
505
547
|
packageManager: defaultOptions.packageManager ?? "pnpm",
|
|
506
548
|
pnpmManageVersions: defaultOptions.pnpmManageVersions,
|
|
507
549
|
linter: defaultOptions.linter ?? "oxlint",
|
|
508
|
-
formatter: defaultOptions.formatter ?? "
|
|
550
|
+
formatter: defaultOptions.formatter ?? "prettier"
|
|
509
551
|
}),
|
|
510
552
|
"Workspace Configuration"
|
|
511
553
|
);
|
|
512
|
-
const proceed = await p__namespace.
|
|
554
|
+
const proceed = await p__namespace.select({
|
|
513
555
|
message: "Proceed with these settings?",
|
|
514
|
-
|
|
556
|
+
options: [
|
|
557
|
+
{ value: "continue", label: "Yes, continue" },
|
|
558
|
+
{ value: "customize", label: "No, customize settings" }
|
|
559
|
+
],
|
|
560
|
+
initialValue: "continue"
|
|
515
561
|
});
|
|
516
562
|
if (p__namespace.isCancel(proceed)) {
|
|
517
563
|
p__namespace.cancel("Operation cancelled.");
|
|
518
564
|
process.exit(0);
|
|
519
565
|
}
|
|
520
|
-
if (proceed) {
|
|
566
|
+
if (proceed === "continue") {
|
|
521
567
|
return defaultOptions;
|
|
522
568
|
}
|
|
523
|
-
return promptForMonorepoCustomization(workspaceName);
|
|
569
|
+
return promptForMonorepoCustomization(workspaceName, presets);
|
|
524
570
|
}
|
|
525
|
-
async function promptForOptions(name) {
|
|
571
|
+
async function promptForOptions(name, presets) {
|
|
526
572
|
let projectName = name;
|
|
527
573
|
if (!projectName) {
|
|
528
574
|
const nameResult = await p__namespace.text({
|
|
@@ -546,30 +592,36 @@ async function promptForOptions(name) {
|
|
|
546
592
|
{ value: "library", label: "Library" },
|
|
547
593
|
{ value: "monorepo", label: "Monorepo" }
|
|
548
594
|
],
|
|
549
|
-
initialValue: "app"
|
|
595
|
+
initialValue: presets?.type ?? "app"
|
|
550
596
|
});
|
|
551
597
|
if (p__namespace.isCancel(projectType)) {
|
|
552
598
|
p__namespace.cancel("Operation cancelled.");
|
|
553
599
|
process.exit(0);
|
|
554
600
|
}
|
|
555
601
|
if (projectType === "monorepo") {
|
|
556
|
-
return promptForMonorepo(projectName);
|
|
602
|
+
return promptForMonorepo(projectName, presets);
|
|
557
603
|
}
|
|
558
|
-
return promptForPackageOptions(
|
|
604
|
+
return promptForPackageOptions(
|
|
605
|
+
projectName,
|
|
606
|
+
projectType,
|
|
607
|
+
void 0,
|
|
608
|
+
presets
|
|
609
|
+
);
|
|
559
610
|
}
|
|
560
|
-
function customTemplateToOptions(customTemplate, name, projectType) {
|
|
611
|
+
function customTemplateToOptions(customTemplate, name, projectType, inheritedSettings) {
|
|
561
612
|
const baseTemplate = customTemplate.baseTemplate;
|
|
562
613
|
const template = baseTemplate;
|
|
563
614
|
const base = {
|
|
564
615
|
name,
|
|
565
616
|
template,
|
|
566
617
|
projectType,
|
|
567
|
-
packageManager: "pnpm",
|
|
568
|
-
pnpmManageVersions: true,
|
|
569
|
-
nodeVersion: "latest",
|
|
570
|
-
linter: customTemplate.linter,
|
|
571
|
-
formatter: customTemplate.formatter,
|
|
572
|
-
testing: customTemplate.testing
|
|
618
|
+
packageManager: inheritedSettings?.packageManager ?? "pnpm",
|
|
619
|
+
pnpmManageVersions: inheritedSettings?.pnpmManageVersions ?? true,
|
|
620
|
+
nodeVersion: inheritedSettings?.nodeVersion ?? "latest",
|
|
621
|
+
linter: inheritedSettings?.linter ?? customTemplate.linter,
|
|
622
|
+
formatter: inheritedSettings?.formatter ?? customTemplate.formatter,
|
|
623
|
+
testing: customTemplate.testing,
|
|
624
|
+
configStrategy: customTemplate.configStrategy ?? getConfigStrategy()
|
|
573
625
|
};
|
|
574
626
|
if (baseTemplate === "r3f" && customTemplate.integrations) {
|
|
575
627
|
const integrations = customTemplate.integrations;
|
|
@@ -591,7 +643,17 @@ function customTemplateToOptions(customTemplate, name, projectType) {
|
|
|
591
643
|
}
|
|
592
644
|
return base;
|
|
593
645
|
}
|
|
594
|
-
|
|
646
|
+
function presetsToInheritedSettings(presets) {
|
|
647
|
+
if (!presets) return void 0;
|
|
648
|
+
return {
|
|
649
|
+
linter: presets.linter,
|
|
650
|
+
formatter: presets.formatter,
|
|
651
|
+
packageManager: presets.packageManager,
|
|
652
|
+
nodeVersion: presets.nodeVersion,
|
|
653
|
+
pnpmManageVersions: presets.pnpmManageVersions
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
async function promptForPackageOptions(projectName, projectType, inheritedSettings, presets) {
|
|
595
657
|
const builtInOptions = [
|
|
596
658
|
{ value: "vanilla", label: "Vanilla" },
|
|
597
659
|
{ value: "react", label: "React" },
|
|
@@ -607,7 +669,7 @@ async function promptForPackageOptions(projectName, projectType, inheritedToolin
|
|
|
607
669
|
const templateSelection = await p__namespace.select({
|
|
608
670
|
message: "Select a template",
|
|
609
671
|
options: allOptions,
|
|
610
|
-
initialValue: "vanilla"
|
|
672
|
+
initialValue: presets?.template ?? "vanilla"
|
|
611
673
|
});
|
|
612
674
|
if (p__namespace.isCancel(templateSelection)) {
|
|
613
675
|
p__namespace.cancel("Operation cancelled.");
|
|
@@ -617,24 +679,27 @@ async function promptForPackageOptions(projectName, projectType, inheritedToolin
|
|
|
617
679
|
if (selection.startsWith("custom:")) {
|
|
618
680
|
const customName = selection.slice(7);
|
|
619
681
|
const customTemplate = customTemplates[customName];
|
|
620
|
-
const defaultOptions2 = customTemplateToOptions(
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
p__namespace.
|
|
629
|
-
const proceed2 = await p__namespace.confirm({
|
|
682
|
+
const defaultOptions2 = customTemplateToOptions(
|
|
683
|
+
customTemplate,
|
|
684
|
+
projectName,
|
|
685
|
+
projectType,
|
|
686
|
+
inheritedSettings
|
|
687
|
+
);
|
|
688
|
+
const configTitle2 = inheritedSettings ? `Template: ${customName} (using workspace settings)` : `Template: ${customName}`;
|
|
689
|
+
p__namespace.note(formatConfigSummary(defaultOptions2, inheritedSettings), configTitle2);
|
|
690
|
+
const proceed2 = await p__namespace.select({
|
|
630
691
|
message: "Proceed with these settings?",
|
|
631
|
-
|
|
692
|
+
options: [
|
|
693
|
+
{ value: "continue", label: "Yes, continue" },
|
|
694
|
+
{ value: "customize", label: "No, customize settings" }
|
|
695
|
+
],
|
|
696
|
+
initialValue: "continue"
|
|
632
697
|
});
|
|
633
698
|
if (p__namespace.isCancel(proceed2)) {
|
|
634
699
|
p__namespace.cancel("Operation cancelled.");
|
|
635
700
|
process.exit(0);
|
|
636
701
|
}
|
|
637
|
-
if (proceed2) {
|
|
702
|
+
if (proceed2 === "continue") {
|
|
638
703
|
return defaultOptions2;
|
|
639
704
|
}
|
|
640
705
|
return promptForCustomization(
|
|
@@ -642,37 +707,48 @@ async function promptForPackageOptions(projectName, projectType, inheritedToolin
|
|
|
642
707
|
projectName,
|
|
643
708
|
projectType,
|
|
644
709
|
customTemplate.integrations,
|
|
645
|
-
|
|
710
|
+
inheritedSettings
|
|
646
711
|
);
|
|
647
712
|
}
|
|
648
713
|
const template = selection;
|
|
649
714
|
const baseTemplate = index.getBaseTemplate(template);
|
|
650
715
|
let integrations;
|
|
651
716
|
if (baseTemplate === "r3f") {
|
|
652
|
-
integrations = await promptForR3fIntegrations();
|
|
717
|
+
integrations = await promptForR3fIntegrations(presets);
|
|
653
718
|
}
|
|
654
719
|
const defaultOptions = getDefaultOptions(
|
|
655
720
|
template,
|
|
656
721
|
projectName,
|
|
657
722
|
projectType,
|
|
658
|
-
|
|
723
|
+
presets?.bundler,
|
|
659
724
|
integrations,
|
|
660
|
-
|
|
725
|
+
inheritedSettings ?? presetsToInheritedSettings(presets)
|
|
661
726
|
);
|
|
662
|
-
const configTitle =
|
|
663
|
-
p__namespace.note(formatConfigSummary(defaultOptions), configTitle);
|
|
664
|
-
const proceed = await p__namespace.
|
|
727
|
+
const configTitle = inheritedSettings ? "Template Configuration (using workspace settings)" : "Template Configuration";
|
|
728
|
+
p__namespace.note(formatConfigSummary(defaultOptions, inheritedSettings), configTitle);
|
|
729
|
+
const proceed = await p__namespace.select({
|
|
665
730
|
message: "Proceed with these settings?",
|
|
666
|
-
|
|
731
|
+
options: [
|
|
732
|
+
{ value: "continue", label: "Yes, continue" },
|
|
733
|
+
{ value: "customize", label: "No, customize settings" }
|
|
734
|
+
],
|
|
735
|
+
initialValue: "continue"
|
|
667
736
|
});
|
|
668
737
|
if (p__namespace.isCancel(proceed)) {
|
|
669
738
|
p__namespace.cancel("Operation cancelled.");
|
|
670
739
|
process.exit(0);
|
|
671
740
|
}
|
|
672
|
-
if (proceed) {
|
|
741
|
+
if (proceed === "continue") {
|
|
673
742
|
return defaultOptions;
|
|
674
743
|
}
|
|
675
|
-
return promptForCustomization(
|
|
744
|
+
return promptForCustomization(
|
|
745
|
+
template,
|
|
746
|
+
projectName,
|
|
747
|
+
projectType,
|
|
748
|
+
integrations,
|
|
749
|
+
inheritedSettings,
|
|
750
|
+
presets
|
|
751
|
+
);
|
|
676
752
|
}
|
|
677
753
|
|
|
678
754
|
async function checkAnyExists(paths) {
|
|
@@ -720,8 +796,586 @@ async function validateWorkspace(monorepoRoot) {
|
|
|
720
796
|
return { valid: errors.length === 0, errors };
|
|
721
797
|
}
|
|
722
798
|
|
|
799
|
+
async function detectCurrentConfig(root) {
|
|
800
|
+
let name = root.split(/[/\\]/).pop() ?? "workspace";
|
|
801
|
+
try {
|
|
802
|
+
const pkgPath = path.join(root, "package.json");
|
|
803
|
+
const content = await promises.readFile(pkgPath, "utf-8");
|
|
804
|
+
const pkgJson = JSON.parse(content);
|
|
805
|
+
if (pkgJson.name) {
|
|
806
|
+
name = pkgJson.name.replace(/^@/, "").replace(/\/.*$/, "");
|
|
807
|
+
}
|
|
808
|
+
} catch {
|
|
809
|
+
}
|
|
810
|
+
const tooling = await index.detectTooling(root);
|
|
811
|
+
return {
|
|
812
|
+
name,
|
|
813
|
+
linter: tooling.linter ?? "oxlint",
|
|
814
|
+
formatter: tooling.formatter ?? "prettier",
|
|
815
|
+
packageManager: "pnpm"
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
function generateExpectedFiles(config) {
|
|
819
|
+
const { name, linter, formatter, packageManager } = config;
|
|
820
|
+
const aiFilesMap = {};
|
|
821
|
+
index.generateAiFiles(aiFilesMap, {
|
|
822
|
+
name,
|
|
823
|
+
packageManager,
|
|
824
|
+
linter,
|
|
825
|
+
formatter,
|
|
826
|
+
isMonorepo: true,
|
|
827
|
+
platforms: index.ALL_AI_PLATFORMS
|
|
828
|
+
});
|
|
829
|
+
const vscodeFiles = {};
|
|
830
|
+
index.generateVscodeFiles(vscodeFiles, linter, formatter);
|
|
831
|
+
const configPackages = {};
|
|
832
|
+
index.generateTypescriptConfigPackage(configPackages);
|
|
833
|
+
if (linter === "oxlint") {
|
|
834
|
+
index.generateOxlintConfigPackage(configPackages);
|
|
835
|
+
} else if (linter === "eslint") {
|
|
836
|
+
index.generateEslintConfigPackage(configPackages);
|
|
837
|
+
}
|
|
838
|
+
if (formatter === "oxfmt") {
|
|
839
|
+
index.generateOxfmtConfigPackage(configPackages);
|
|
840
|
+
} else if (formatter === "prettier") {
|
|
841
|
+
index.generatePrettierConfigPackage(configPackages);
|
|
842
|
+
}
|
|
843
|
+
const workspaceConfig = {};
|
|
844
|
+
const rootConfig = {};
|
|
845
|
+
rootConfig[".gitignore"] = {
|
|
846
|
+
type: "text",
|
|
847
|
+
content: ["node_modules", "dist", "*.tsbuildinfo", ".DS_Store"].join("\n")
|
|
848
|
+
};
|
|
849
|
+
rootConfig[".gitattributes"] = {
|
|
850
|
+
type: "text",
|
|
851
|
+
content: `* text=auto eol=lf
|
|
852
|
+
*.{cmd,[cC][mM][dD]} text eol=crlf
|
|
853
|
+
*.{bat,[bB][aA][tT]} text eol=crlf
|
|
854
|
+
`
|
|
855
|
+
};
|
|
856
|
+
if (linter === "biome" || formatter === "biome") {
|
|
857
|
+
const biomeConfig = {
|
|
858
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
859
|
+
vcs: {
|
|
860
|
+
enabled: true,
|
|
861
|
+
clientKind: "git",
|
|
862
|
+
useIgnoreFile: true
|
|
863
|
+
},
|
|
864
|
+
linter: {
|
|
865
|
+
enabled: linter === "biome",
|
|
866
|
+
rules: {
|
|
867
|
+
recommended: true
|
|
868
|
+
}
|
|
869
|
+
},
|
|
870
|
+
formatter: {
|
|
871
|
+
enabled: formatter === "biome"
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
rootConfig["biome.json"] = {
|
|
875
|
+
type: "text",
|
|
876
|
+
content: JSON.stringify(biomeConfig, null, 2)
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
return {
|
|
880
|
+
"ai-files": aiFilesMap,
|
|
881
|
+
vscode: vscodeFiles,
|
|
882
|
+
"config-packages": configPackages,
|
|
883
|
+
"workspace-config": workspaceConfig,
|
|
884
|
+
"root-config": rootConfig
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
async function fileExists$1(path) {
|
|
888
|
+
try {
|
|
889
|
+
await promises.access(path, fs.constants.F_OK);
|
|
890
|
+
return true;
|
|
891
|
+
} catch {
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
async function compareWithDisk(expected, root) {
|
|
896
|
+
const categoryLabels = {
|
|
897
|
+
"ai-files": "AI Files",
|
|
898
|
+
vscode: "VS Code",
|
|
899
|
+
"config-packages": "Config Packages",
|
|
900
|
+
"workspace-config": "Workspace Config",
|
|
901
|
+
"root-config": "Root Config"
|
|
902
|
+
};
|
|
903
|
+
const categories = [];
|
|
904
|
+
for (const [category, files] of Object.entries(expected)) {
|
|
905
|
+
const changes = [];
|
|
906
|
+
for (const [filePath, file] of Object.entries(files)) {
|
|
907
|
+
if (file.type !== "text") continue;
|
|
908
|
+
const fullPath = path.join(root, filePath);
|
|
909
|
+
const newContent = file.content;
|
|
910
|
+
if (await fileExists$1(fullPath)) {
|
|
911
|
+
const currentContent = await promises.readFile(fullPath, "utf-8");
|
|
912
|
+
if (currentContent === newContent) {
|
|
913
|
+
changes.push({
|
|
914
|
+
path: filePath,
|
|
915
|
+
status: "unchanged",
|
|
916
|
+
currentContent,
|
|
917
|
+
newContent
|
|
918
|
+
});
|
|
919
|
+
} else {
|
|
920
|
+
changes.push({
|
|
921
|
+
path: filePath,
|
|
922
|
+
status: "modified",
|
|
923
|
+
currentContent,
|
|
924
|
+
newContent
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
} else {
|
|
928
|
+
changes.push({
|
|
929
|
+
path: filePath,
|
|
930
|
+
status: "added",
|
|
931
|
+
newContent
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (changes.length === 0) continue;
|
|
936
|
+
const hasUserModifications = changes.some((c) => c.status === "modified");
|
|
937
|
+
categories.push({
|
|
938
|
+
category,
|
|
939
|
+
label: categoryLabels[category],
|
|
940
|
+
changes,
|
|
941
|
+
hasUserModifications
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
return categories;
|
|
945
|
+
}
|
|
946
|
+
async function getWorkspaceConfigUpdates(root) {
|
|
947
|
+
const workspacePath = path.join(root, "pnpm-workspace.yaml");
|
|
948
|
+
const changes = [];
|
|
949
|
+
let currentContent = "";
|
|
950
|
+
let exists = false;
|
|
951
|
+
try {
|
|
952
|
+
currentContent = await promises.readFile(workspacePath, "utf-8");
|
|
953
|
+
exists = true;
|
|
954
|
+
} catch {
|
|
955
|
+
}
|
|
956
|
+
if (!exists) {
|
|
957
|
+
const newContent = `manage-package-manager-versions: true
|
|
958
|
+
|
|
959
|
+
packages:
|
|
960
|
+
- ".config/*"
|
|
961
|
+
- "apps/*"
|
|
962
|
+
- "packages/*"
|
|
963
|
+
|
|
964
|
+
onlyBuiltDependencies:
|
|
965
|
+
- esbuild
|
|
966
|
+
`;
|
|
967
|
+
changes.push({
|
|
968
|
+
path: "pnpm-workspace.yaml",
|
|
969
|
+
status: "added",
|
|
970
|
+
newContent
|
|
971
|
+
});
|
|
972
|
+
return changes;
|
|
973
|
+
}
|
|
974
|
+
let updatedContent = currentContent;
|
|
975
|
+
let needsUpdate = false;
|
|
976
|
+
if (!currentContent.includes("manage-package-manager-versions")) {
|
|
977
|
+
updatedContent = `manage-package-manager-versions: true
|
|
978
|
+
|
|
979
|
+
${updatedContent}`;
|
|
980
|
+
needsUpdate = true;
|
|
981
|
+
}
|
|
982
|
+
if (!currentContent.includes("onlyBuiltDependencies")) {
|
|
983
|
+
updatedContent = `${updatedContent.trimEnd()}
|
|
984
|
+
|
|
985
|
+
onlyBuiltDependencies:
|
|
986
|
+
- esbuild
|
|
987
|
+
`;
|
|
988
|
+
needsUpdate = true;
|
|
989
|
+
}
|
|
990
|
+
if (!currentContent.includes(".config/*") && !currentContent.includes('".config/*"')) {
|
|
991
|
+
const lines = updatedContent.split("\n");
|
|
992
|
+
const packagesIndex = lines.findIndex(
|
|
993
|
+
(line) => line.trim().startsWith("packages:")
|
|
994
|
+
);
|
|
995
|
+
if (packagesIndex !== -1) {
|
|
996
|
+
lines.splice(packagesIndex + 1, 0, ' - ".config/*"');
|
|
997
|
+
updatedContent = lines.join("\n");
|
|
998
|
+
needsUpdate = true;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (needsUpdate) {
|
|
1002
|
+
changes.push({
|
|
1003
|
+
path: "pnpm-workspace.yaml",
|
|
1004
|
+
status: "modified",
|
|
1005
|
+
currentContent,
|
|
1006
|
+
newContent: updatedContent
|
|
1007
|
+
});
|
|
1008
|
+
} else {
|
|
1009
|
+
changes.push({
|
|
1010
|
+
path: "pnpm-workspace.yaml",
|
|
1011
|
+
status: "unchanged",
|
|
1012
|
+
currentContent,
|
|
1013
|
+
newContent: currentContent
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
return changes;
|
|
1017
|
+
}
|
|
1018
|
+
async function applyUpdates(changes, root) {
|
|
1019
|
+
for (const change of changes) {
|
|
1020
|
+
if (change.status === "unchanged") continue;
|
|
1021
|
+
const fullPath = path.join(root, change.path);
|
|
1022
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1023
|
+
await promises.writeFile(fullPath, change.newContent);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
function formatFileChange(change) {
|
|
1027
|
+
const icon = change.status === "added" ? "+" : change.status === "modified" ? "~" : "=";
|
|
1028
|
+
return ` ${icon} ${change.path}`;
|
|
1029
|
+
}
|
|
1030
|
+
const LINTER_DEPS = {
|
|
1031
|
+
oxlint: "oxlint",
|
|
1032
|
+
eslint: "eslint",
|
|
1033
|
+
biome: "@biomejs/biome"
|
|
1034
|
+
};
|
|
1035
|
+
const FORMATTER_DEPS = {
|
|
1036
|
+
oxfmt: "oxfmt",
|
|
1037
|
+
prettier: "prettier",
|
|
1038
|
+
biome: "@biomejs/biome"
|
|
1039
|
+
};
|
|
1040
|
+
const LINTER_CONFIG_PACKAGES = {
|
|
1041
|
+
oxlint: "@config/oxlint",
|
|
1042
|
+
eslint: "@config/eslint",
|
|
1043
|
+
biome: null
|
|
1044
|
+
// biome uses root biome.json
|
|
1045
|
+
};
|
|
1046
|
+
const FORMATTER_CONFIG_PACKAGES = {
|
|
1047
|
+
oxfmt: "@config/oxfmt",
|
|
1048
|
+
prettier: "@config/prettier",
|
|
1049
|
+
biome: null
|
|
1050
|
+
// biome uses root biome.json
|
|
1051
|
+
};
|
|
1052
|
+
function needsMigration(current, target) {
|
|
1053
|
+
const linterChange = target.linter && target.linter !== current.linter;
|
|
1054
|
+
const formatterChange = target.formatter && target.formatter !== current.formatter;
|
|
1055
|
+
return linterChange || formatterChange || false;
|
|
1056
|
+
}
|
|
1057
|
+
async function getMigrationPlan(current, target, root) {
|
|
1058
|
+
const toLinter = target.linter ?? current.linter;
|
|
1059
|
+
const toFormatter = target.formatter ?? current.formatter;
|
|
1060
|
+
const changes = [];
|
|
1061
|
+
if (toLinter !== current.linter) {
|
|
1062
|
+
if (current.linter !== "biome") {
|
|
1063
|
+
changes.push({
|
|
1064
|
+
type: "remove-dir",
|
|
1065
|
+
path: `.config/${current.linter}`,
|
|
1066
|
+
description: `Remove @config/${current.linter} package`
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
if (toLinter !== "biome") {
|
|
1070
|
+
const files = {};
|
|
1071
|
+
if (toLinter === "oxlint") {
|
|
1072
|
+
index.generateOxlintConfigPackage(files);
|
|
1073
|
+
} else if (toLinter === "eslint") {
|
|
1074
|
+
index.generateEslintConfigPackage(files);
|
|
1075
|
+
}
|
|
1076
|
+
for (const [path, file] of Object.entries(files)) {
|
|
1077
|
+
if (file.type === "text") {
|
|
1078
|
+
changes.push({
|
|
1079
|
+
type: "add-file",
|
|
1080
|
+
path,
|
|
1081
|
+
description: `Add ${path}`,
|
|
1082
|
+
content: file.content
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
if (toLinter === "biome" && toFormatter === "biome") {
|
|
1088
|
+
changes.push({
|
|
1089
|
+
type: "add-file",
|
|
1090
|
+
path: "biome.json",
|
|
1091
|
+
description: "Add biome.json config",
|
|
1092
|
+
content: JSON.stringify(
|
|
1093
|
+
{
|
|
1094
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
1095
|
+
vcs: { enabled: true, clientKind: "git", useIgnoreFile: true },
|
|
1096
|
+
linter: { enabled: true, rules: { recommended: true } },
|
|
1097
|
+
formatter: { enabled: true }
|
|
1098
|
+
},
|
|
1099
|
+
null,
|
|
1100
|
+
2
|
|
1101
|
+
)
|
|
1102
|
+
});
|
|
1103
|
+
} else if (toLinter === "biome" && toFormatter !== "biome") {
|
|
1104
|
+
changes.push({
|
|
1105
|
+
type: "add-file",
|
|
1106
|
+
path: "biome.json",
|
|
1107
|
+
description: "Add biome.json config (linter only)",
|
|
1108
|
+
content: JSON.stringify(
|
|
1109
|
+
{
|
|
1110
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
1111
|
+
vcs: { enabled: true, clientKind: "git", useIgnoreFile: true },
|
|
1112
|
+
linter: { enabled: true, rules: { recommended: true } },
|
|
1113
|
+
formatter: { enabled: false }
|
|
1114
|
+
},
|
|
1115
|
+
null,
|
|
1116
|
+
2
|
|
1117
|
+
)
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
if (current.linter === "biome" && toLinter !== "biome" && current.formatter !== "biome" && toFormatter !== "biome") {
|
|
1121
|
+
changes.push({
|
|
1122
|
+
type: "remove-file",
|
|
1123
|
+
path: "biome.json",
|
|
1124
|
+
description: "Remove biome.json"
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
if (toFormatter !== current.formatter) {
|
|
1129
|
+
const formatterSameAsLinter = current.formatter === current.linter;
|
|
1130
|
+
if (current.formatter !== "biome" && !formatterSameAsLinter) {
|
|
1131
|
+
changes.push({
|
|
1132
|
+
type: "remove-dir",
|
|
1133
|
+
path: `.config/${current.formatter}`,
|
|
1134
|
+
description: `Remove @config/${current.formatter} package`
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
const newFormatterSameAsLinter = toFormatter === toLinter;
|
|
1138
|
+
if (toFormatter !== "biome" && !newFormatterSameAsLinter) {
|
|
1139
|
+
const files = {};
|
|
1140
|
+
if (toFormatter === "oxfmt") {
|
|
1141
|
+
index.generateOxfmtConfigPackage(files);
|
|
1142
|
+
} else if (toFormatter === "prettier") {
|
|
1143
|
+
index.generatePrettierConfigPackage(files);
|
|
1144
|
+
}
|
|
1145
|
+
for (const [path, file] of Object.entries(files)) {
|
|
1146
|
+
if (file.type === "text") {
|
|
1147
|
+
changes.push({
|
|
1148
|
+
type: "add-file",
|
|
1149
|
+
path,
|
|
1150
|
+
description: `Add ${path}`,
|
|
1151
|
+
content: file.content
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
if (toFormatter === "biome" && toLinter !== "biome") {
|
|
1157
|
+
changes.push({
|
|
1158
|
+
type: "add-file",
|
|
1159
|
+
path: "biome.json",
|
|
1160
|
+
description: "Add biome.json config (formatter only)",
|
|
1161
|
+
content: JSON.stringify(
|
|
1162
|
+
{
|
|
1163
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
1164
|
+
vcs: { enabled: true, clientKind: "git", useIgnoreFile: true },
|
|
1165
|
+
linter: { enabled: false },
|
|
1166
|
+
formatter: { enabled: true }
|
|
1167
|
+
},
|
|
1168
|
+
null,
|
|
1169
|
+
2
|
|
1170
|
+
)
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
if (current.formatter === "biome" && toFormatter !== "biome" && current.linter !== "biome" && toLinter !== "biome") {
|
|
1174
|
+
changes.push({
|
|
1175
|
+
type: "remove-file",
|
|
1176
|
+
path: "biome.json",
|
|
1177
|
+
description: "Remove biome.json"
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
changes.push({
|
|
1182
|
+
type: "update-package-json",
|
|
1183
|
+
path: "package.json",
|
|
1184
|
+
description: "Update root package.json (devDependencies, scripts)"
|
|
1185
|
+
});
|
|
1186
|
+
const subPackageUpdates = await getSubPackageUpdates(
|
|
1187
|
+
root,
|
|
1188
|
+
current,
|
|
1189
|
+
toLinter,
|
|
1190
|
+
toFormatter
|
|
1191
|
+
);
|
|
1192
|
+
return {
|
|
1193
|
+
fromLinter: current.linter,
|
|
1194
|
+
toLinter,
|
|
1195
|
+
fromFormatter: current.formatter,
|
|
1196
|
+
toFormatter,
|
|
1197
|
+
changes,
|
|
1198
|
+
subPackageUpdates
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
async function getSubPackageUpdates(root, current, toLinter, toFormatter) {
|
|
1202
|
+
const updates = [];
|
|
1203
|
+
const workspacePath = path.join(root, "pnpm-workspace.yaml");
|
|
1204
|
+
let workspaceContent;
|
|
1205
|
+
try {
|
|
1206
|
+
workspaceContent = await promises.readFile(workspacePath, "utf-8");
|
|
1207
|
+
} catch {
|
|
1208
|
+
return updates;
|
|
1209
|
+
}
|
|
1210
|
+
const packageGlobs = index.parseWorkspaceYamlContent(workspaceContent);
|
|
1211
|
+
for (const glob of packageGlobs) {
|
|
1212
|
+
if (glob.includes(".config")) continue;
|
|
1213
|
+
const baseDir = glob.replace(/\/\*$/, "").replace(/^["']|["']$/g, "");
|
|
1214
|
+
const basePath = path.join(root, baseDir);
|
|
1215
|
+
try {
|
|
1216
|
+
const entries = await promises.readdir(basePath, { withFileTypes: true });
|
|
1217
|
+
for (const entry of entries) {
|
|
1218
|
+
if (!entry.isDirectory()) continue;
|
|
1219
|
+
const pkgJsonPath = path.join(basePath, entry.name, "package.json");
|
|
1220
|
+
try {
|
|
1221
|
+
const content = await promises.readFile(pkgJsonPath, "utf-8");
|
|
1222
|
+
const pkg = JSON.parse(content);
|
|
1223
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
1224
|
+
const remove = [];
|
|
1225
|
+
const add = [];
|
|
1226
|
+
const oldLinterPkg = LINTER_CONFIG_PACKAGES[current.linter];
|
|
1227
|
+
const newLinterPkg = LINTER_CONFIG_PACKAGES[toLinter];
|
|
1228
|
+
if (oldLinterPkg && oldLinterPkg !== newLinterPkg && devDeps[oldLinterPkg]) {
|
|
1229
|
+
remove.push(oldLinterPkg);
|
|
1230
|
+
}
|
|
1231
|
+
if (newLinterPkg && newLinterPkg !== oldLinterPkg && oldLinterPkg && devDeps[oldLinterPkg]) {
|
|
1232
|
+
add.push(newLinterPkg);
|
|
1233
|
+
}
|
|
1234
|
+
if (current.formatter !== current.linter) {
|
|
1235
|
+
const oldFormatterPkg = FORMATTER_CONFIG_PACKAGES[current.formatter];
|
|
1236
|
+
const newFormatterPkg = FORMATTER_CONFIG_PACKAGES[toFormatter];
|
|
1237
|
+
if (oldFormatterPkg && oldFormatterPkg !== newFormatterPkg && devDeps[oldFormatterPkg]) {
|
|
1238
|
+
remove.push(oldFormatterPkg);
|
|
1239
|
+
}
|
|
1240
|
+
if (newFormatterPkg && newFormatterPkg !== oldFormatterPkg && oldFormatterPkg && devDeps[oldFormatterPkg]) {
|
|
1241
|
+
add.push(newFormatterPkg);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
if (remove.length > 0 || add.length > 0) {
|
|
1245
|
+
updates.push({
|
|
1246
|
+
path: path.join(baseDir, entry.name, "package.json"),
|
|
1247
|
+
remove,
|
|
1248
|
+
add
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
} catch {
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
} catch {
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
return updates;
|
|
1258
|
+
}
|
|
1259
|
+
async function applyMigration(plan, root) {
|
|
1260
|
+
for (const change of plan.changes) {
|
|
1261
|
+
if (change.type === "remove-dir") {
|
|
1262
|
+
const fullPath = path.join(root, change.path);
|
|
1263
|
+
try {
|
|
1264
|
+
await promises.rm(fullPath, { recursive: true });
|
|
1265
|
+
} catch {
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
for (const change of plan.changes) {
|
|
1270
|
+
if (change.type === "remove-file") {
|
|
1271
|
+
const fullPath = path.join(root, change.path);
|
|
1272
|
+
try {
|
|
1273
|
+
await promises.rm(fullPath);
|
|
1274
|
+
} catch {
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
for (const change of plan.changes) {
|
|
1279
|
+
if (change.type === "add-file" && change.content) {
|
|
1280
|
+
const fullPath = path.join(root, change.path);
|
|
1281
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1282
|
+
await promises.writeFile(fullPath, change.content);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
await updateRootPackageJson(root, plan);
|
|
1286
|
+
for (const update of plan.subPackageUpdates) {
|
|
1287
|
+
await updateSubPackageJson(root, update);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
async function updateRootPackageJson(root, plan) {
|
|
1291
|
+
const pkgPath = path.join(root, "package.json");
|
|
1292
|
+
const content = await promises.readFile(pkgPath, "utf-8");
|
|
1293
|
+
const pkg = JSON.parse(content);
|
|
1294
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
1295
|
+
const oldLinterDep = LINTER_DEPS[plan.fromLinter];
|
|
1296
|
+
delete devDeps[oldLinterDep];
|
|
1297
|
+
if (plan.fromFormatter !== plan.fromLinter) {
|
|
1298
|
+
const oldFormatterDep = FORMATTER_DEPS[plan.fromFormatter];
|
|
1299
|
+
delete devDeps[oldFormatterDep];
|
|
1300
|
+
}
|
|
1301
|
+
const newLinterDep = LINTER_DEPS[plan.toLinter];
|
|
1302
|
+
if (plan.toLinter === "oxlint") {
|
|
1303
|
+
devDeps[newLinterDep] = "^1.36.0";
|
|
1304
|
+
} else if (plan.toLinter === "eslint") {
|
|
1305
|
+
devDeps[newLinterDep] = "^9.17.0";
|
|
1306
|
+
} else if (plan.toLinter === "biome") {
|
|
1307
|
+
devDeps[newLinterDep] = "^1.9.4";
|
|
1308
|
+
}
|
|
1309
|
+
if (plan.toFormatter !== plan.toLinter) {
|
|
1310
|
+
const newFormatterDep = FORMATTER_DEPS[plan.toFormatter];
|
|
1311
|
+
if (plan.toFormatter === "oxfmt") {
|
|
1312
|
+
devDeps[newFormatterDep] = "^0.21.0";
|
|
1313
|
+
} else if (plan.toFormatter === "prettier") {
|
|
1314
|
+
devDeps[newFormatterDep] = "^3.4.2";
|
|
1315
|
+
} else if (plan.toFormatter === "biome") {
|
|
1316
|
+
devDeps[newFormatterDep] = "^1.9.4";
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
pkg.devDependencies = Object.fromEntries(
|
|
1320
|
+
Object.entries(devDeps).sort(([a], [b]) => a.localeCompare(b))
|
|
1321
|
+
);
|
|
1322
|
+
const scripts = pkg.scripts ?? {};
|
|
1323
|
+
if (plan.toLinter === "oxlint") {
|
|
1324
|
+
scripts.lint = "oxlint .";
|
|
1325
|
+
} else if (plan.toLinter === "eslint") {
|
|
1326
|
+
scripts.lint = "eslint .";
|
|
1327
|
+
} else if (plan.toLinter === "biome") {
|
|
1328
|
+
scripts.lint = "biome check .";
|
|
1329
|
+
}
|
|
1330
|
+
if (plan.toFormatter === "oxfmt") {
|
|
1331
|
+
scripts.format = "oxfmt .";
|
|
1332
|
+
} else if (plan.toFormatter === "prettier") {
|
|
1333
|
+
scripts.format = "prettier --write .";
|
|
1334
|
+
} else if (plan.toFormatter === "biome") {
|
|
1335
|
+
scripts.format = "biome format . --write";
|
|
1336
|
+
}
|
|
1337
|
+
pkg.scripts = scripts;
|
|
1338
|
+
await promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1339
|
+
}
|
|
1340
|
+
async function updateSubPackageJson(root, update) {
|
|
1341
|
+
const pkgPath = path.join(root, update.path);
|
|
1342
|
+
const content = await promises.readFile(pkgPath, "utf-8");
|
|
1343
|
+
const pkg = JSON.parse(content);
|
|
1344
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
1345
|
+
for (const dep of update.remove) {
|
|
1346
|
+
delete devDeps[dep];
|
|
1347
|
+
}
|
|
1348
|
+
for (const dep of update.add) {
|
|
1349
|
+
devDeps[dep] = "workspace:*";
|
|
1350
|
+
}
|
|
1351
|
+
pkg.devDependencies = Object.fromEntries(
|
|
1352
|
+
Object.entries(devDeps).sort(([a], [b]) => a.localeCompare(b))
|
|
1353
|
+
);
|
|
1354
|
+
await promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1355
|
+
}
|
|
1356
|
+
function formatMigrationChange(change) {
|
|
1357
|
+
const icon = change.type === "remove-dir" || change.type === "remove-file" ? "-" : change.type === "add-file" ? "+" : "~";
|
|
1358
|
+
return ` ${icon} ${change.description}`;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
723
1361
|
const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
|
|
724
1362
|
const pkg = require$1("../package.json");
|
|
1363
|
+
const META_OPTIONS = [
|
|
1364
|
+
"clearConfig",
|
|
1365
|
+
"configPath",
|
|
1366
|
+
"check",
|
|
1367
|
+
"fix",
|
|
1368
|
+
"update",
|
|
1369
|
+
"yes",
|
|
1370
|
+
"workspace",
|
|
1371
|
+
"path",
|
|
1372
|
+
"dir"
|
|
1373
|
+
];
|
|
1374
|
+
function hasConfigOptions(options) {
|
|
1375
|
+
return Object.keys(options).some(
|
|
1376
|
+
(key) => !META_OPTIONS.includes(key)
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
725
1379
|
async function fileExists(path) {
|
|
726
1380
|
try {
|
|
727
1381
|
await promises.access(path, fs.constants.F_OK);
|
|
@@ -730,6 +1384,50 @@ async function fileExists(path) {
|
|
|
730
1384
|
return false;
|
|
731
1385
|
}
|
|
732
1386
|
}
|
|
1387
|
+
async function promptForAiPlatforms(isNonInteractive) {
|
|
1388
|
+
const savedPlatforms = getAiPlatforms();
|
|
1389
|
+
if (isNonInteractive) {
|
|
1390
|
+
return savedPlatforms ?? index.ALL_AI_PLATFORMS;
|
|
1391
|
+
}
|
|
1392
|
+
if (savedPlatforms && savedPlatforms.length > 0) {
|
|
1393
|
+
const savedLabels = savedPlatforms.map((plat) => index.AI_PLATFORM_LABELS[plat]).join(", ");
|
|
1394
|
+
const useDefault = await p__namespace.confirm({
|
|
1395
|
+
message: `Add AI rules? ${color__default.dim(`(${savedLabels})`)}`,
|
|
1396
|
+
initialValue: true
|
|
1397
|
+
});
|
|
1398
|
+
if (p__namespace.isCancel(useDefault)) {
|
|
1399
|
+
return [];
|
|
1400
|
+
}
|
|
1401
|
+
if (useDefault) {
|
|
1402
|
+
return savedPlatforms;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
const selected = await p__namespace.multiselect({
|
|
1406
|
+
message: "Add AI rules?",
|
|
1407
|
+
options: index.ALL_AI_PLATFORMS.map((platform) => ({
|
|
1408
|
+
value: platform,
|
|
1409
|
+
label: index.AI_PLATFORM_LABELS[platform],
|
|
1410
|
+
hint: index.AI_PLATFORM_HINTS[platform]
|
|
1411
|
+
})),
|
|
1412
|
+
initialValues: [],
|
|
1413
|
+
required: false
|
|
1414
|
+
});
|
|
1415
|
+
if (p__namespace.isCancel(selected)) {
|
|
1416
|
+
return [];
|
|
1417
|
+
}
|
|
1418
|
+
const platforms = selected;
|
|
1419
|
+
if (platforms.length === 0) {
|
|
1420
|
+
return [];
|
|
1421
|
+
}
|
|
1422
|
+
const saveChoice = await p__namespace.confirm({
|
|
1423
|
+
message: "Save selection for future projects?",
|
|
1424
|
+
initialValue: true
|
|
1425
|
+
});
|
|
1426
|
+
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1427
|
+
setAiPlatforms(platforms);
|
|
1428
|
+
}
|
|
1429
|
+
return platforms;
|
|
1430
|
+
}
|
|
733
1431
|
async function writeGeneratedFiles(basePath, files) {
|
|
734
1432
|
const filePaths = Object.keys(files).sort();
|
|
735
1433
|
for (const filePath of filePaths) {
|
|
@@ -774,15 +1472,39 @@ async function parseWorkspaceDirectories(monorepoRoot) {
|
|
|
774
1472
|
return [];
|
|
775
1473
|
}
|
|
776
1474
|
}
|
|
777
|
-
async function
|
|
1475
|
+
async function detectWorkspaceSettings(monorepoRoot) {
|
|
778
1476
|
try {
|
|
1477
|
+
const tooling = await index.detectTooling(monorepoRoot);
|
|
779
1478
|
const pkgPath = path.join(monorepoRoot, "package.json");
|
|
780
1479
|
const content = await promises.readFile(pkgPath, "utf-8");
|
|
781
1480
|
const pkgJson = JSON.parse(content);
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1481
|
+
let packageManager;
|
|
1482
|
+
if (pkgJson.packageManager) {
|
|
1483
|
+
packageManager = pkgJson.packageManager.split("@")[0];
|
|
1484
|
+
}
|
|
1485
|
+
let nodeVersion;
|
|
1486
|
+
if (pkgJson.engines?.node) {
|
|
1487
|
+
const match = pkgJson.engines.node.match(/(\d+)/);
|
|
1488
|
+
if (match) {
|
|
1489
|
+
nodeVersion = match[1];
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
let pnpmManageVersions;
|
|
1493
|
+
try {
|
|
1494
|
+
const workspaceFile = path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
1495
|
+
const workspaceContent = await promises.readFile(workspaceFile, "utf-8");
|
|
1496
|
+
pnpmManageVersions = workspaceContent.includes(
|
|
1497
|
+
"manage-package-manager-versions: true"
|
|
1498
|
+
);
|
|
1499
|
+
} catch {
|
|
1500
|
+
}
|
|
1501
|
+
return {
|
|
1502
|
+
linter: tooling.linter,
|
|
1503
|
+
formatter: tooling.formatter,
|
|
1504
|
+
packageManager,
|
|
1505
|
+
nodeVersion,
|
|
1506
|
+
pnpmManageVersions
|
|
1507
|
+
};
|
|
786
1508
|
} catch {
|
|
787
1509
|
return {};
|
|
788
1510
|
}
|
|
@@ -821,29 +1543,26 @@ async function getMonorepoScope(monorepoRoot) {
|
|
|
821
1543
|
}
|
|
822
1544
|
async function getWorkspacePackages(monorepoRoot) {
|
|
823
1545
|
const packagesDir = path.join(monorepoRoot, "packages");
|
|
824
|
-
const packages = [];
|
|
825
1546
|
try {
|
|
826
1547
|
const { readdir } = await import('fs/promises');
|
|
827
1548
|
const entries = await readdir(packagesDir, { withFileTypes: true });
|
|
1549
|
+
const names = [];
|
|
828
1550
|
for (const entry of entries) {
|
|
829
|
-
if (entry.isDirectory())
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
});
|
|
839
|
-
}
|
|
840
|
-
} catch {
|
|
841
|
-
}
|
|
1551
|
+
if (!entry.isDirectory()) continue;
|
|
1552
|
+
try {
|
|
1553
|
+
const content = await promises.readFile(
|
|
1554
|
+
path.join(packagesDir, entry.name, "package.json"),
|
|
1555
|
+
"utf-8"
|
|
1556
|
+
);
|
|
1557
|
+
const pkg2 = JSON.parse(content);
|
|
1558
|
+
if (pkg2.name) names.push(pkg2.name);
|
|
1559
|
+
} catch {
|
|
842
1560
|
}
|
|
843
1561
|
}
|
|
1562
|
+
return names;
|
|
844
1563
|
} catch {
|
|
1564
|
+
return [];
|
|
845
1565
|
}
|
|
846
|
-
return packages;
|
|
847
1566
|
}
|
|
848
1567
|
async function ensureConfigInWorkspace(monorepoRoot) {
|
|
849
1568
|
const workspacePath = path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
@@ -1011,7 +1730,7 @@ Or in \`package.json\`:
|
|
|
1011
1730
|
content: existingContent
|
|
1012
1731
|
};
|
|
1013
1732
|
}
|
|
1014
|
-
async function createPackageInWorkspace(monorepoRoot, packageManager,
|
|
1733
|
+
async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedSettings, scope) {
|
|
1015
1734
|
const workspaceDirectories = await parseWorkspaceDirectories(monorepoRoot);
|
|
1016
1735
|
const defaultDirectories = ["apps", "packages"];
|
|
1017
1736
|
const hasCustomDirectories = workspaceDirectories.length > 0 && !workspaceDirectories.every((dir) => defaultDirectories.includes(dir));
|
|
@@ -1047,7 +1766,7 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
1047
1766
|
const packageOptions = await promptForPackageOptions(
|
|
1048
1767
|
scopedName,
|
|
1049
1768
|
packageType,
|
|
1050
|
-
|
|
1769
|
+
inheritedSettings
|
|
1051
1770
|
);
|
|
1052
1771
|
let targetDir = defaultDir;
|
|
1053
1772
|
if (hasCustomDirectories && workspaceDirectories.length > 0) {
|
|
@@ -1125,7 +1844,7 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
1125
1844
|
})
|
|
1126
1845
|
);
|
|
1127
1846
|
}
|
|
1128
|
-
const formatter = packageOptions.formatter ?? "
|
|
1847
|
+
const formatter = packageOptions.formatter ?? "prettier";
|
|
1129
1848
|
if (formatter === "prettier") {
|
|
1130
1849
|
versionPromises.push(
|
|
1131
1850
|
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
@@ -1147,20 +1866,15 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
1147
1866
|
}
|
|
1148
1867
|
await Promise.all(versionPromises);
|
|
1149
1868
|
packageOptions.versions = versions;
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
required: false
|
|
1160
|
-
});
|
|
1161
|
-
if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
|
|
1162
|
-
packageOptions.workspaceDependencies = selectedDeps;
|
|
1163
|
-
}
|
|
1869
|
+
const workspacePackages = packageType === "app" ? await getWorkspacePackages(monorepoRoot) : [];
|
|
1870
|
+
if (workspacePackages.length > 0) {
|
|
1871
|
+
const selectedDeps = await p__namespace.multiselect({
|
|
1872
|
+
message: "Add workspace dependencies?",
|
|
1873
|
+
options: workspacePackages.map((name) => ({ value: name, label: name })),
|
|
1874
|
+
required: false
|
|
1875
|
+
});
|
|
1876
|
+
if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
|
|
1877
|
+
packageOptions.workspaceDependencies = selectedDeps;
|
|
1164
1878
|
}
|
|
1165
1879
|
}
|
|
1166
1880
|
const outputPath = path.join(monorepoRoot, relativePkgPath);
|
|
@@ -1285,11 +1999,11 @@ async function handleFixCommand(options) {
|
|
|
1285
1999
|
console.log(color__default.dim(` \u2022 ${error}`));
|
|
1286
2000
|
}
|
|
1287
2001
|
console.log();
|
|
1288
|
-
const tooling = await
|
|
2002
|
+
const tooling = await detectWorkspaceSettings(monorepoRoot);
|
|
1289
2003
|
const existingConfigs = await detectExistingConfigs(monorepoRoot);
|
|
1290
2004
|
const detectedLinter = tooling.linter ?? existingConfigs.linter ?? "oxlint";
|
|
1291
|
-
const detectedFormatter = tooling.formatter ?? existingConfigs.formatter ?? "
|
|
1292
|
-
const isNonInteractive = options.linter && options.formatter;
|
|
2005
|
+
const detectedFormatter = tooling.formatter ?? existingConfigs.formatter ?? "prettier";
|
|
2006
|
+
const isNonInteractive = Boolean(options.linter && options.formatter);
|
|
1293
2007
|
let linter;
|
|
1294
2008
|
let formatter;
|
|
1295
2009
|
if (isNonInteractive) {
|
|
@@ -1467,85 +2181,346 @@ async function handleFixCommand(options) {
|
|
|
1467
2181
|
console.log(color__default.dim(" Generated .vscode/extensions.json"));
|
|
1468
2182
|
}
|
|
1469
2183
|
}
|
|
1470
|
-
const
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
2184
|
+
const aiRulesExist = await fileExists(
|
|
2185
|
+
path.join(monorepoRoot, ".ai/workspace.md")
|
|
2186
|
+
);
|
|
2187
|
+
if (!aiRulesExist) {
|
|
2188
|
+
const platforms = await promptForAiPlatforms(isNonInteractive);
|
|
2189
|
+
if (platforms.length > 0) {
|
|
2190
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
2191
|
+
const aiFilesOutput = {};
|
|
2192
|
+
index.generateAiFiles(aiFilesOutput, {
|
|
2193
|
+
name: scope,
|
|
2194
|
+
packageManager: "pnpm",
|
|
2195
|
+
linter,
|
|
2196
|
+
formatter,
|
|
2197
|
+
isMonorepo: true,
|
|
2198
|
+
platforms
|
|
2199
|
+
});
|
|
2200
|
+
for (const [filePath, file] of Object.entries(aiFilesOutput)) {
|
|
2201
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
2202
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
2203
|
+
await promises.writeFile(fullPath, file.content);
|
|
2204
|
+
console.log(color__default.dim(` Generated ${filePath}`));
|
|
2205
|
+
}
|
|
1480
2206
|
}
|
|
1481
2207
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
2208
|
+
process.exit(0);
|
|
2209
|
+
} catch (error) {
|
|
2210
|
+
spinner.stop(color__default.red("\u2717") + " Failed to fix workspace");
|
|
2211
|
+
console.error(error);
|
|
2212
|
+
process.exit(1);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
async function handleMigration(config, target, root, options) {
|
|
2216
|
+
const plan = await getMigrationPlan(config, target, root);
|
|
2217
|
+
console.log(color__default.cyan("Migration:"));
|
|
2218
|
+
if (plan.fromLinter !== plan.toLinter) {
|
|
2219
|
+
console.log(
|
|
2220
|
+
` Linter: ${color__default.dim(plan.fromLinter)} \u2192 ${color__default.green(plan.toLinter)}`
|
|
2221
|
+
);
|
|
2222
|
+
}
|
|
2223
|
+
if (plan.fromFormatter !== plan.toFormatter) {
|
|
2224
|
+
console.log(
|
|
2225
|
+
` Formatter: ${color__default.dim(plan.fromFormatter)} \u2192 ${color__default.green(
|
|
2226
|
+
plan.toFormatter
|
|
2227
|
+
)}`
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
console.log();
|
|
2231
|
+
console.log(color__default.cyan("Changes:"));
|
|
2232
|
+
for (const change of plan.changes) {
|
|
2233
|
+
console.log(formatMigrationChange(change));
|
|
2234
|
+
}
|
|
2235
|
+
if (plan.subPackageUpdates.length > 0) {
|
|
2236
|
+
console.log();
|
|
2237
|
+
console.log(color__default.cyan(`Sub-packages (${plan.subPackageUpdates.length}):`));
|
|
2238
|
+
for (const update of plan.subPackageUpdates) {
|
|
2239
|
+
const changes = [
|
|
2240
|
+
...update.remove.map((d) => `-${d}`),
|
|
2241
|
+
...update.add.map((d) => `+${d}`)
|
|
2242
|
+
].join(", ");
|
|
2243
|
+
console.log(` ~ ${update.path} (${changes})`);
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
console.log();
|
|
2247
|
+
if (!options.yes) {
|
|
2248
|
+
const confirm = await p__namespace.confirm({
|
|
2249
|
+
message: "Apply migration?",
|
|
2250
|
+
initialValue: true
|
|
2251
|
+
});
|
|
2252
|
+
if (p__namespace.isCancel(confirm) || !confirm) {
|
|
2253
|
+
console.log(color__default.dim(" Migration cancelled"));
|
|
2254
|
+
process.exit(0);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
await applyMigration(plan, root);
|
|
2258
|
+
const aiWorkspacePath = path.join(root, ".ai/workspace.md");
|
|
2259
|
+
const aiRulesExist = await fileExists(aiWorkspacePath);
|
|
2260
|
+
if (aiRulesExist) {
|
|
2261
|
+
console.log();
|
|
2262
|
+
console.log(color__default.cyan("Updating AI rules..."));
|
|
2263
|
+
const scope = await getMonorepoScope(root);
|
|
2264
|
+
const existingPlatforms = [];
|
|
2265
|
+
if (await fileExists(path.join(root, "AGENTS.md"))) {
|
|
2266
|
+
existingPlatforms.push("agents");
|
|
2267
|
+
}
|
|
2268
|
+
if (await fileExists(path.join(root, "CLAUDE.md"))) {
|
|
2269
|
+
existingPlatforms.push("claude");
|
|
2270
|
+
}
|
|
2271
|
+
const aiFilesOutput = {};
|
|
2272
|
+
index.generateAiFiles(aiFilesOutput, {
|
|
2273
|
+
name: scope,
|
|
2274
|
+
packageManager: "pnpm",
|
|
2275
|
+
linter: plan.toLinter,
|
|
2276
|
+
formatter: plan.toFormatter,
|
|
2277
|
+
isMonorepo: true,
|
|
2278
|
+
platforms: existingPlatforms.length > 0 ? existingPlatforms : ["agents"]
|
|
2279
|
+
});
|
|
2280
|
+
for (const [filePath, file] of Object.entries(aiFilesOutput)) {
|
|
2281
|
+
const fullPath = path.join(root, filePath);
|
|
2282
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
2283
|
+
await promises.writeFile(fullPath, file.content);
|
|
2284
|
+
console.log(color__default.dim(` ${filePath}`));
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
console.log();
|
|
2288
|
+
console.log(
|
|
2289
|
+
color__default.green("\u2713") + ` Migrated to ${plan.toLinter}/${plan.toFormatter}`
|
|
2290
|
+
);
|
|
2291
|
+
console.log(color__default.dim(" Run `pnpm install` to update dependencies"));
|
|
2292
|
+
process.exit(0);
|
|
2293
|
+
}
|
|
2294
|
+
async function handleUpdateCommand(options) {
|
|
2295
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
2296
|
+
if (!monorepoRoot) {
|
|
2297
|
+
console.log(color__default.red("\u2717") + " Not a monorepo workspace");
|
|
2298
|
+
console.log(color__default.dim(" --update only supports pnpm monorepos"));
|
|
2299
|
+
process.exit(1);
|
|
2300
|
+
}
|
|
2301
|
+
const { valid, errors } = await validateWorkspace(monorepoRoot);
|
|
2302
|
+
if (!valid) {
|
|
2303
|
+
console.log(color__default.yellow("!") + " Workspace has issues:");
|
|
2304
|
+
for (const error of errors) {
|
|
2305
|
+
console.log(color__default.dim(` \u2022 ${error}`));
|
|
2306
|
+
}
|
|
2307
|
+
console.log();
|
|
2308
|
+
const shouldFix = options.yes || await p__namespace.confirm({
|
|
2309
|
+
message: "Run fix first to resolve these issues?",
|
|
2310
|
+
initialValue: true
|
|
2311
|
+
});
|
|
2312
|
+
if (p__namespace.isCancel(shouldFix) || !shouldFix) {
|
|
2313
|
+
console.log(
|
|
2314
|
+
color__default.dim(" Run `pnpm create krispya --fix` to fix manually")
|
|
1492
2315
|
);
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
2316
|
+
process.exit(1);
|
|
2317
|
+
}
|
|
2318
|
+
const preFixConfig = await detectCurrentConfig(monorepoRoot);
|
|
2319
|
+
const fixOptions = {
|
|
2320
|
+
...options,
|
|
2321
|
+
linter: options.linter ?? preFixConfig.linter,
|
|
2322
|
+
formatter: options.formatter ?? preFixConfig.formatter
|
|
2323
|
+
};
|
|
2324
|
+
await handleFixCommand(fixOptions);
|
|
2325
|
+
}
|
|
2326
|
+
const config = await detectCurrentConfig(monorepoRoot);
|
|
2327
|
+
const targetLinter = options.linter;
|
|
2328
|
+
const targetFormatter = options.formatter;
|
|
2329
|
+
const migrationTarget = { linter: targetLinter, formatter: targetFormatter };
|
|
2330
|
+
if (needsMigration(config, migrationTarget)) {
|
|
2331
|
+
await handleMigration(config, migrationTarget, monorepoRoot, options);
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
console.log(
|
|
2335
|
+
color__default.cyan("Checking for updates...") + color__default.dim(` (${config.linter}/${config.formatter})`)
|
|
2336
|
+
);
|
|
2337
|
+
console.log();
|
|
2338
|
+
const expected = generateExpectedFiles(config);
|
|
2339
|
+
const categories = await compareWithDisk(expected, monorepoRoot);
|
|
2340
|
+
const workspaceConfigChanges = await getWorkspaceConfigUpdates(monorepoRoot);
|
|
2341
|
+
const workspaceCategory = {
|
|
2342
|
+
category: "workspace-config",
|
|
2343
|
+
label: "Workspace Config",
|
|
2344
|
+
changes: workspaceConfigChanges,
|
|
2345
|
+
hasUserModifications: workspaceConfigChanges.some(
|
|
2346
|
+
(c) => c.status === "modified"
|
|
2347
|
+
)
|
|
2348
|
+
};
|
|
2349
|
+
const allCategories = categories.filter(
|
|
2350
|
+
(c) => c.category !== "workspace-config"
|
|
2351
|
+
);
|
|
2352
|
+
if (workspaceConfigChanges.length > 0) {
|
|
2353
|
+
const configPkgIndex = allCategories.findIndex(
|
|
2354
|
+
(c) => c.category === "config-packages"
|
|
2355
|
+
);
|
|
2356
|
+
if (configPkgIndex !== -1) {
|
|
2357
|
+
allCategories.splice(configPkgIndex + 1, 0, workspaceCategory);
|
|
2358
|
+
} else {
|
|
2359
|
+
allCategories.push(workspaceCategory);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
let updatedCount = 0;
|
|
2363
|
+
let skippedCount = 0;
|
|
2364
|
+
for (const category of allCategories) {
|
|
2365
|
+
const newChanges = category.changes.filter((c) => c.status === "added");
|
|
2366
|
+
const modifiedChanges = category.changes.filter(
|
|
2367
|
+
(c) => c.status === "modified"
|
|
2368
|
+
);
|
|
2369
|
+
const hasNew = newChanges.length > 0;
|
|
2370
|
+
const hasModified = modifiedChanges.length > 0;
|
|
2371
|
+
const hasChanges = hasNew || hasModified;
|
|
2372
|
+
if (!hasChanges) {
|
|
2373
|
+
console.log(color__default.green("\u2713") + ` ${category.label}: Up to date`);
|
|
2374
|
+
continue;
|
|
2375
|
+
}
|
|
2376
|
+
if (category.category === "ai-files") {
|
|
2377
|
+
if (hasNew) {
|
|
2378
|
+
console.log(color__default.cyan(category.label + ":"));
|
|
2379
|
+
console.log(
|
|
2380
|
+
color__default.dim(` ${newChanges.length} AI file(s) can be added`)
|
|
2381
|
+
);
|
|
2382
|
+
console.log();
|
|
2383
|
+
const applyAi = options.yes ? true : await p__namespace.confirm({
|
|
2384
|
+
message: "Add AI rules?",
|
|
1499
2385
|
initialValue: true
|
|
1500
2386
|
});
|
|
1501
|
-
if (!p__namespace.isCancel(
|
|
1502
|
-
|
|
2387
|
+
if (!p__namespace.isCancel(applyAi) && applyAi) {
|
|
2388
|
+
await applyUpdates(newChanges, monorepoRoot);
|
|
2389
|
+
console.log(
|
|
2390
|
+
color__default.green("\u2713") + ` Added ${newChanges.length} AI file(s)`
|
|
2391
|
+
);
|
|
2392
|
+
updatedCount++;
|
|
2393
|
+
} else {
|
|
2394
|
+
console.log(color__default.dim(` Skipped ${category.label}`));
|
|
2395
|
+
skippedCount++;
|
|
1503
2396
|
}
|
|
1504
2397
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
2398
|
+
if (hasModified) {
|
|
2399
|
+
console.log(color__default.cyan("AI Files (existing):"));
|
|
2400
|
+
for (const change of modifiedChanges) {
|
|
2401
|
+
console.log(formatFileChange(change));
|
|
2402
|
+
}
|
|
2403
|
+
console.log();
|
|
2404
|
+
if (options.yes) {
|
|
2405
|
+
console.log(color__default.dim(" (--yes mode: keeping existing AI files)"));
|
|
2406
|
+
} else {
|
|
2407
|
+
const updateExisting = await p__namespace.confirm({
|
|
2408
|
+
message: "Update existing AI files to latest template?",
|
|
2409
|
+
initialValue: false
|
|
2410
|
+
});
|
|
2411
|
+
if (!p__namespace.isCancel(updateExisting) && updateExisting) {
|
|
2412
|
+
await applyUpdates(modifiedChanges, monorepoRoot);
|
|
2413
|
+
console.log(color__default.green("\u2713") + " Updated existing AI files");
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
console.log();
|
|
2418
|
+
continue;
|
|
2419
|
+
}
|
|
2420
|
+
let changesToApply = [];
|
|
2421
|
+
if (options.yes) {
|
|
2422
|
+
console.log(color__default.cyan(category.label + ":"));
|
|
2423
|
+
for (const change of [...newChanges, ...modifiedChanges]) {
|
|
2424
|
+
console.log(formatFileChange(change));
|
|
2425
|
+
}
|
|
2426
|
+
console.log();
|
|
2427
|
+
if (category.category === "workspace-config") {
|
|
2428
|
+
changesToApply = [...newChanges, ...modifiedChanges];
|
|
2429
|
+
if (changesToApply.length > 0) {
|
|
2430
|
+
console.log(color__default.dim(" (--yes mode: applying merge updates)"));
|
|
2431
|
+
}
|
|
2432
|
+
} else {
|
|
2433
|
+
changesToApply = newChanges;
|
|
2434
|
+
if (newChanges.length > 0) {
|
|
2435
|
+
console.log(color__default.dim(" (--yes mode: adding new files only)"));
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
} else if (hasNew && hasModified) {
|
|
2439
|
+
const allChanges = [...newChanges, ...modifiedChanges];
|
|
2440
|
+
const selectedFiles = await p__namespace.multiselect({
|
|
2441
|
+
message: `${category.label} (+ new, ~ changed)`,
|
|
2442
|
+
options: allChanges.map((change) => ({
|
|
2443
|
+
value: change.path,
|
|
2444
|
+
label: change.status === "added" ? `+ ${change.path}` : `~ ${change.path}`
|
|
1512
2445
|
})),
|
|
2446
|
+
initialValues: newChanges.map((c) => c.path),
|
|
2447
|
+
// Pre-select new files
|
|
1513
2448
|
required: false
|
|
1514
2449
|
});
|
|
1515
|
-
if (
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
message: "Save as default for future?",
|
|
1519
|
-
initialValue: true
|
|
1520
|
-
});
|
|
1521
|
-
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1522
|
-
setAiFiles(selectedAiFiles);
|
|
1523
|
-
}
|
|
2450
|
+
if (p__namespace.isCancel(selectedFiles)) {
|
|
2451
|
+
p__namespace.cancel("Operation cancelled.");
|
|
2452
|
+
process.exit(0);
|
|
1524
2453
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
2454
|
+
if (selectedFiles.length > 0) {
|
|
2455
|
+
changesToApply = allChanges.filter(
|
|
2456
|
+
(c) => selectedFiles.includes(c.path)
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
} else if (hasNew) {
|
|
2460
|
+
console.log(color__default.cyan(category.label + ":"));
|
|
2461
|
+
for (const change of newChanges) {
|
|
2462
|
+
console.log(formatFileChange(change));
|
|
2463
|
+
}
|
|
2464
|
+
console.log();
|
|
2465
|
+
const shouldAdd = await p__namespace.confirm({
|
|
2466
|
+
message: `Add ${newChanges.length} new file(s)?`,
|
|
2467
|
+
initialValue: true
|
|
1535
2468
|
});
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
2469
|
+
if (p__namespace.isCancel(shouldAdd)) {
|
|
2470
|
+
p__namespace.cancel("Operation cancelled.");
|
|
2471
|
+
process.exit(0);
|
|
2472
|
+
}
|
|
2473
|
+
if (shouldAdd) {
|
|
2474
|
+
changesToApply = newChanges;
|
|
2475
|
+
}
|
|
2476
|
+
} else if (hasModified) {
|
|
2477
|
+
console.log(color__default.cyan(category.label + ":"));
|
|
2478
|
+
for (const change of modifiedChanges) {
|
|
2479
|
+
console.log(formatFileChange(change));
|
|
2480
|
+
}
|
|
2481
|
+
console.log();
|
|
2482
|
+
const shouldUpdate = await p__namespace.confirm({
|
|
2483
|
+
message: `Update ${modifiedChanges.length} file(s)? (will overwrite)`,
|
|
2484
|
+
initialValue: false
|
|
2485
|
+
});
|
|
2486
|
+
if (p__namespace.isCancel(shouldUpdate)) {
|
|
2487
|
+
p__namespace.cancel("Operation cancelled.");
|
|
2488
|
+
process.exit(0);
|
|
2489
|
+
}
|
|
2490
|
+
if (shouldUpdate) {
|
|
2491
|
+
changesToApply = modifiedChanges;
|
|
1541
2492
|
}
|
|
1542
2493
|
}
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
2494
|
+
if (changesToApply.length > 0) {
|
|
2495
|
+
await applyUpdates(changesToApply, monorepoRoot);
|
|
2496
|
+
const addedCount = changesToApply.filter(
|
|
2497
|
+
(c) => c.status === "added"
|
|
2498
|
+
).length;
|
|
2499
|
+
const updatedFilesCount = changesToApply.filter(
|
|
2500
|
+
(c) => c.status === "modified"
|
|
2501
|
+
).length;
|
|
2502
|
+
const parts = [];
|
|
2503
|
+
if (addedCount > 0) parts.push(`added ${addedCount}`);
|
|
2504
|
+
if (updatedFilesCount > 0) parts.push(`updated ${updatedFilesCount}`);
|
|
2505
|
+
console.log(color__default.green("\u2713") + ` ${category.label}: ${parts.join(", ")}`);
|
|
2506
|
+
updatedCount++;
|
|
2507
|
+
} else {
|
|
2508
|
+
console.log(color__default.dim(` Skipped ${category.label}`));
|
|
2509
|
+
skippedCount++;
|
|
2510
|
+
}
|
|
2511
|
+
console.log();
|
|
1548
2512
|
}
|
|
2513
|
+
if (updatedCount === 0 && skippedCount === 0) {
|
|
2514
|
+
console.log(color__default.green("\u2713") + " Everything is up to date!");
|
|
2515
|
+
} else if (updatedCount > 0) {
|
|
2516
|
+
console.log(
|
|
2517
|
+
color__default.green("\u2713") + ` Updated ${updatedCount} ${updatedCount === 1 ? "category" : "categories"}`
|
|
2518
|
+
);
|
|
2519
|
+
if (skippedCount > 0) {
|
|
2520
|
+
console.log(color__default.dim(` Skipped ${skippedCount}`));
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
process.exit(0);
|
|
1549
2524
|
}
|
|
1550
2525
|
async function handleWorkspaceCommand(name, options) {
|
|
1551
2526
|
const monorepoRoot = await detectMonorepoRoot();
|
|
@@ -1567,7 +2542,7 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
1567
2542
|
process.exit(1);
|
|
1568
2543
|
}
|
|
1569
2544
|
const scope = await getMonorepoScope(monorepoRoot);
|
|
1570
|
-
const
|
|
2545
|
+
const inheritedSettings = await detectWorkspaceSettings(monorepoRoot);
|
|
1571
2546
|
const projectType = options.type ?? "app";
|
|
1572
2547
|
const defaultDir = projectType === "library" ? "packages" : "apps";
|
|
1573
2548
|
const targetDir = options.dir ?? defaultDir;
|
|
@@ -1593,8 +2568,11 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
1593
2568
|
})
|
|
1594
2569
|
);
|
|
1595
2570
|
}
|
|
1596
|
-
const linter =
|
|
1597
|
-
const formatter =
|
|
2571
|
+
const linter = inheritedSettings.linter ?? options.linter ?? "oxlint";
|
|
2572
|
+
const formatter = inheritedSettings.formatter ?? options.formatter ?? "prettier";
|
|
2573
|
+
const packageManager = inheritedSettings.packageManager ?? "pnpm";
|
|
2574
|
+
const nodeVersion = inheritedSettings.nodeVersion ?? "latest";
|
|
2575
|
+
const pnpmManageVersions = inheritedSettings.pnpmManageVersions ?? true;
|
|
1598
2576
|
await Promise.all(versionPromises);
|
|
1599
2577
|
const relativePkgPath = path.join(targetDir, name);
|
|
1600
2578
|
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
@@ -1605,6 +2583,9 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
1605
2583
|
template,
|
|
1606
2584
|
linter,
|
|
1607
2585
|
formatter,
|
|
2586
|
+
packageManager,
|
|
2587
|
+
nodeVersion,
|
|
2588
|
+
pnpmManageVersions,
|
|
1608
2589
|
workspaceRoot,
|
|
1609
2590
|
versions,
|
|
1610
2591
|
...baseTemplate === "r3f" && {
|
|
@@ -1638,7 +2619,7 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
1638
2619
|
process.exit(1);
|
|
1639
2620
|
}
|
|
1640
2621
|
}
|
|
1641
|
-
async function handleMonorepoCreation(generateOptions) {
|
|
2622
|
+
async function handleMonorepoCreation(generateOptions, isNonInteractive) {
|
|
1642
2623
|
const { generateMonorepo } = await import('./chunks/index.cjs').then(function (n) { return n.monorepo; });
|
|
1643
2624
|
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1644
2625
|
if (packageManager === "pnpm") {
|
|
@@ -1652,55 +2633,7 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1652
2633
|
if (nodeVersion === "latest") {
|
|
1653
2634
|
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1654
2635
|
}
|
|
1655
|
-
const
|
|
1656
|
-
let selectedAiFiles = [];
|
|
1657
|
-
if (savedAiFiles && savedAiFiles.length > 0) {
|
|
1658
|
-
const aiFileLabels = {
|
|
1659
|
-
"cursor-rules": ".cursor/rules",
|
|
1660
|
-
"agents-md": "AGENTS.md",
|
|
1661
|
-
"claude-md": "CLAUDE.md",
|
|
1662
|
-
"copilot-md": ".github/copilot-instructions.md"
|
|
1663
|
-
};
|
|
1664
|
-
const savedLabels = savedAiFiles.map((f) => aiFileLabels[f]).join(", ");
|
|
1665
|
-
const useDefault = await p__namespace.confirm({
|
|
1666
|
-
message: `Generate AI instruction files? ${color__default.dim(
|
|
1667
|
-
`(${savedLabels})`
|
|
1668
|
-
)}`,
|
|
1669
|
-
initialValue: true
|
|
1670
|
-
});
|
|
1671
|
-
if (!p__namespace.isCancel(useDefault) && useDefault) {
|
|
1672
|
-
selectedAiFiles = savedAiFiles;
|
|
1673
|
-
}
|
|
1674
|
-
} else {
|
|
1675
|
-
const aiFilesChoice = await p__namespace.multiselect({
|
|
1676
|
-
message: "Generate AI instruction files?",
|
|
1677
|
-
options: [
|
|
1678
|
-
{ value: "cursor-rules", label: ".cursor/rules", hint: "Cursor AI" },
|
|
1679
|
-
{
|
|
1680
|
-
value: "agents-md",
|
|
1681
|
-
label: "AGENTS.md",
|
|
1682
|
-
hint: "GitHub Copilot, general"
|
|
1683
|
-
},
|
|
1684
|
-
{ value: "claude-md", label: "CLAUDE.md", hint: "Claude" },
|
|
1685
|
-
{
|
|
1686
|
-
value: "copilot-md",
|
|
1687
|
-
label: ".github/copilot-instructions.md",
|
|
1688
|
-
hint: "GitHub Copilot"
|
|
1689
|
-
}
|
|
1690
|
-
],
|
|
1691
|
-
required: false
|
|
1692
|
-
});
|
|
1693
|
-
if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
|
|
1694
|
-
selectedAiFiles = aiFilesChoice;
|
|
1695
|
-
const saveChoice = await p__namespace.confirm({
|
|
1696
|
-
message: "Save as default for future monorepos?",
|
|
1697
|
-
initialValue: true
|
|
1698
|
-
});
|
|
1699
|
-
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1700
|
-
setAiFiles(selectedAiFiles);
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
2636
|
+
const aiPlatforms = await promptForAiPlatforms(isNonInteractive);
|
|
1704
2637
|
const projectPath = path.join(process$1.cwd(), generateOptions.name);
|
|
1705
2638
|
const spinner = p__namespace.spinner();
|
|
1706
2639
|
spinner.start("Creating monorepo workspace...");
|
|
@@ -1708,12 +2641,12 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1708
2641
|
const { files } = generateMonorepo({
|
|
1709
2642
|
name: generateOptions.name,
|
|
1710
2643
|
linter: generateOptions.linter ?? "oxlint",
|
|
1711
|
-
formatter: generateOptions.formatter ?? "
|
|
2644
|
+
formatter: generateOptions.formatter ?? "prettier",
|
|
1712
2645
|
packageManager,
|
|
1713
2646
|
pnpmVersion: generateOptions.pnpmVersion,
|
|
1714
2647
|
pnpmManageVersions: generateOptions.pnpmManageVersions,
|
|
1715
2648
|
nodeVersion: generateOptions.nodeVersion,
|
|
1716
|
-
|
|
2649
|
+
aiPlatforms: aiPlatforms.length > 0 ? aiPlatforms : void 0
|
|
1717
2650
|
});
|
|
1718
2651
|
const filePaths = Object.keys(files).sort();
|
|
1719
2652
|
for (const filePath of filePaths) {
|
|
@@ -1725,9 +2658,15 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1725
2658
|
}
|
|
1726
2659
|
}
|
|
1727
2660
|
spinner.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
|
|
1728
|
-
|
|
2661
|
+
if (isNonInteractive) {
|
|
2662
|
+
process.exit(0);
|
|
2663
|
+
}
|
|
2664
|
+
const newWorkspaceSettings = {
|
|
1729
2665
|
linter: generateOptions.linter,
|
|
1730
|
-
formatter: generateOptions.formatter
|
|
2666
|
+
formatter: generateOptions.formatter,
|
|
2667
|
+
packageManager,
|
|
2668
|
+
nodeVersion: generateOptions.nodeVersion,
|
|
2669
|
+
pnpmManageVersions: generateOptions.pnpmManageVersions
|
|
1731
2670
|
};
|
|
1732
2671
|
const scope = generateOptions.name;
|
|
1733
2672
|
let addMore = true;
|
|
@@ -1735,7 +2674,7 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1735
2674
|
addMore = await createPackageInWorkspace(
|
|
1736
2675
|
projectPath,
|
|
1737
2676
|
packageManager,
|
|
1738
|
-
|
|
2677
|
+
newWorkspaceSettings,
|
|
1739
2678
|
scope
|
|
1740
2679
|
);
|
|
1741
2680
|
}
|
|
@@ -1754,10 +2693,14 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1754
2693
|
process.exit(1);
|
|
1755
2694
|
}
|
|
1756
2695
|
}
|
|
1757
|
-
async function handleStandaloneProjectCreation(generateOptions) {
|
|
2696
|
+
async function handleStandaloneProjectCreation(generateOptions, isNonInteractive) {
|
|
1758
2697
|
const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
|
|
1759
2698
|
const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
|
|
1760
2699
|
generateOptions.name ??= defaultFallbackName;
|
|
2700
|
+
const aiPlatforms = await promptForAiPlatforms(isNonInteractive);
|
|
2701
|
+
if (aiPlatforms.length > 0) {
|
|
2702
|
+
generateOptions.aiPlatforms = aiPlatforms;
|
|
2703
|
+
}
|
|
1761
2704
|
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1762
2705
|
if (packageManager === "pnpm") {
|
|
1763
2706
|
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
@@ -1808,7 +2751,7 @@ async function handleStandaloneProjectCreation(generateOptions) {
|
|
|
1808
2751
|
})
|
|
1809
2752
|
);
|
|
1810
2753
|
}
|
|
1811
|
-
const formatter = generateOptions.formatter ?? "
|
|
2754
|
+
const formatter = generateOptions.formatter ?? "prettier";
|
|
1812
2755
|
if (formatter === "prettier") {
|
|
1813
2756
|
versionPromises.push(
|
|
1814
2757
|
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
@@ -1837,6 +2780,9 @@ async function handleStandaloneProjectCreation(generateOptions) {
|
|
|
1837
2780
|
const files = index.generate(generateOptions);
|
|
1838
2781
|
await writeGeneratedFiles(projectPath, files);
|
|
1839
2782
|
spinner.stop(color__default.green.inverse(" \u2713 Project created! "));
|
|
2783
|
+
if (isNonInteractive) {
|
|
2784
|
+
process.exit(0);
|
|
2785
|
+
}
|
|
1840
2786
|
const nextSteps = isLibrary ? [
|
|
1841
2787
|
`cd ${generateOptions.name}`,
|
|
1842
2788
|
`${packageManager} install`,
|
|
@@ -1869,21 +2815,23 @@ async function handleInteractiveMonorepoMode(monorepoRoot) {
|
|
|
1869
2815
|
process.exit(0);
|
|
1870
2816
|
}
|
|
1871
2817
|
if (choice === "add") {
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
2818
|
+
const inheritedSettings = await detectWorkspaceSettings(monorepoRoot);
|
|
2819
|
+
const hasSettings = Object.values(inheritedSettings).some(Boolean);
|
|
2820
|
+
if (hasSettings) {
|
|
2821
|
+
const settingsInfo = [
|
|
2822
|
+
inheritedSettings.linter && `linter: ${inheritedSettings.linter}`,
|
|
2823
|
+
inheritedSettings.formatter && `formatter: ${inheritedSettings.formatter}`,
|
|
2824
|
+
inheritedSettings.packageManager && `pm: ${inheritedSettings.packageManager}`
|
|
1877
2825
|
].filter(Boolean).join(", ");
|
|
1878
|
-
p__namespace.log.info(`Using workspace
|
|
2826
|
+
p__namespace.log.info(`Using workspace settings (${settingsInfo})`);
|
|
1879
2827
|
}
|
|
1880
2828
|
const scope = await getMonorepoScope(monorepoRoot);
|
|
1881
2829
|
let addMore = true;
|
|
1882
2830
|
while (addMore) {
|
|
1883
2831
|
addMore = await createPackageInWorkspace(
|
|
1884
2832
|
monorepoRoot,
|
|
1885
|
-
"pnpm",
|
|
1886
|
-
|
|
2833
|
+
inheritedSettings.packageManager ?? "pnpm",
|
|
2834
|
+
inheritedSettings,
|
|
1887
2835
|
scope
|
|
1888
2836
|
);
|
|
1889
2837
|
}
|
|
@@ -1910,7 +2858,7 @@ async function main() {
|
|
|
1910
2858
|
"linter: eslint, oxlint, or biome (default: oxlint)"
|
|
1911
2859
|
).option(
|
|
1912
2860
|
"--formatter <type>",
|
|
1913
|
-
"formatter: prettier, oxfmt, or biome (default:
|
|
2861
|
+
"formatter: prettier, oxfmt, or biome (default: prettier)"
|
|
1914
2862
|
).option("--drei", "add @react-three/drei (r3f only)").option("--handle", "add @react-three/handle (r3f only)").option("--leva", "add leva (r3f only)").option("--postprocessing", "add @react-three/postprocessing (r3f only)").option("--rapier", "add @react-three/rapier (r3f only)").option("--xr", "add @react-three/xr (r3f only)").option("--uikit", "add @react-three/uikit (r3f only)").option("--offscreen", "add @react-three/offscreen (r3f only)").option("--zustand", "add zustand (r3f only)").option("--koota", "add koota (r3f only)").option("--triplex", "set up triplex development environment (r3f only)").option("--viverse", "set up viverse deployment (r3f only)").option(
|
|
1915
2863
|
"--package-manager <manager>",
|
|
1916
2864
|
"specify package manager (e.g. npm, yarn, pnpm)"
|
|
@@ -1932,7 +2880,7 @@ async function main() {
|
|
|
1932
2880
|
).option("--clear-config", "Clear saved preferences (e.g. editor choice)").option("--config-path", "Print the path to the config file").option(
|
|
1933
2881
|
"--check",
|
|
1934
2882
|
"Check if current directory is in a valid monorepo workspace"
|
|
1935
|
-
).option("--fix", "Fix monorepo by generating missing .config packages").option(
|
|
2883
|
+
).option("--fix", "Fix monorepo by generating missing .config packages").option("--update", "Update monorepo workspace to latest configuration").option("-y, --yes", "Non-interactive mode - accept all prompts").option(
|
|
1936
2884
|
"--path <directory>",
|
|
1937
2885
|
"Run in specified directory instead of current working directory"
|
|
1938
2886
|
).action(async (name, options) => {
|
|
@@ -1971,6 +2919,12 @@ async function main() {
|
|
|
1971
2919
|
case "--fix":
|
|
1972
2920
|
options.fix = true;
|
|
1973
2921
|
break;
|
|
2922
|
+
case "--update":
|
|
2923
|
+
options.update = true;
|
|
2924
|
+
break;
|
|
2925
|
+
case "--yes":
|
|
2926
|
+
options.yes = true;
|
|
2927
|
+
break;
|
|
1974
2928
|
default:
|
|
1975
2929
|
console.error(color__default.red(`Unknown option: ${name}`));
|
|
1976
2930
|
process.exit(1);
|
|
@@ -1982,6 +2936,9 @@ async function main() {
|
|
|
1982
2936
|
if (options.fix) {
|
|
1983
2937
|
await handleFixCommand(options);
|
|
1984
2938
|
}
|
|
2939
|
+
if (options.update) {
|
|
2940
|
+
await handleUpdateCommand(options);
|
|
2941
|
+
}
|
|
1985
2942
|
if (options.dir && !options.workspace) {
|
|
1986
2943
|
console.error(color__default.red("Error:") + " --dir requires --workspace flag");
|
|
1987
2944
|
console.log(
|
|
@@ -1997,11 +2954,11 @@ async function main() {
|
|
|
1997
2954
|
console.clear();
|
|
1998
2955
|
p__namespace.intro(color__default.bgCyan(color__default.black(` create-krispya v${pkg.version} `)));
|
|
1999
2956
|
const monorepoRoot = await detectMonorepoRoot();
|
|
2000
|
-
if (monorepoRoot &&
|
|
2957
|
+
if (monorepoRoot && !hasConfigOptions(options)) {
|
|
2001
2958
|
await handleInteractiveMonorepoMode(monorepoRoot);
|
|
2002
2959
|
}
|
|
2003
2960
|
let generateOptions;
|
|
2004
|
-
if (
|
|
2961
|
+
if (options.yes) {
|
|
2005
2962
|
const template = options.template ?? "vanilla";
|
|
2006
2963
|
const baseTemplate = index.getBaseTemplate(template);
|
|
2007
2964
|
const defaultName = getDefaultProjectName(template);
|
|
@@ -2012,7 +2969,7 @@ async function main() {
|
|
|
2012
2969
|
libraryBundler: projectType === "library" ? options.bundler ?? "unbuild" : void 0,
|
|
2013
2970
|
template,
|
|
2014
2971
|
linter: options.linter ?? "oxlint",
|
|
2015
|
-
formatter: options.formatter ?? "
|
|
2972
|
+
formatter: options.formatter ?? "prettier",
|
|
2016
2973
|
...baseTemplate === "r3f" && {
|
|
2017
2974
|
drei: options.drei ? {} : void 0,
|
|
2018
2975
|
handle: options.handle ? {} : void 0,
|
|
@@ -2032,12 +2989,35 @@ async function main() {
|
|
|
2032
2989
|
nodeVersion: options.nodeVersion ?? "latest"
|
|
2033
2990
|
};
|
|
2034
2991
|
} else {
|
|
2035
|
-
|
|
2992
|
+
const presets = hasConfigOptions(options) ? {
|
|
2993
|
+
type: options.type,
|
|
2994
|
+
template: options.template,
|
|
2995
|
+
bundler: options.bundler,
|
|
2996
|
+
linter: options.linter,
|
|
2997
|
+
formatter: options.formatter,
|
|
2998
|
+
packageManager: options.packageManager,
|
|
2999
|
+
nodeVersion: options.nodeVersion,
|
|
3000
|
+
pnpmManageVersions: options.pnpmManageVersions,
|
|
3001
|
+
drei: options.drei,
|
|
3002
|
+
handle: options.handle,
|
|
3003
|
+
leva: options.leva,
|
|
3004
|
+
postprocessing: options.postprocessing,
|
|
3005
|
+
rapier: options.rapier,
|
|
3006
|
+
xr: options.xr,
|
|
3007
|
+
uikit: options.uikit,
|
|
3008
|
+
offscreen: options.offscreen,
|
|
3009
|
+
zustand: options.zustand,
|
|
3010
|
+
koota: options.koota,
|
|
3011
|
+
triplex: options.triplex,
|
|
3012
|
+
viverse: options.viverse
|
|
3013
|
+
} : void 0;
|
|
3014
|
+
generateOptions = await promptForOptions(name, presets);
|
|
2036
3015
|
}
|
|
3016
|
+
const isNonInteractive = options.yes ?? false;
|
|
2037
3017
|
if (generateOptions.projectType === "monorepo") {
|
|
2038
|
-
await handleMonorepoCreation(generateOptions);
|
|
3018
|
+
await handleMonorepoCreation(generateOptions, isNonInteractive);
|
|
2039
3019
|
} else {
|
|
2040
|
-
await handleStandaloneProjectCreation(generateOptions);
|
|
3020
|
+
await handleStandaloneProjectCreation(generateOptions, isNonInteractive);
|
|
2041
3021
|
}
|
|
2042
3022
|
});
|
|
2043
3023
|
await program.parseAsync();
|