create-krispya 0.5.3 → 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 +1261 -273
- package/dist/cli.mjs +1264 -276
- 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
|
}
|
|
@@ -1008,7 +1730,7 @@ Or in \`package.json\`:
|
|
|
1008
1730
|
content: existingContent
|
|
1009
1731
|
};
|
|
1010
1732
|
}
|
|
1011
|
-
async function createPackageInWorkspace(monorepoRoot, packageManager,
|
|
1733
|
+
async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedSettings, scope) {
|
|
1012
1734
|
const workspaceDirectories = await parseWorkspaceDirectories(monorepoRoot);
|
|
1013
1735
|
const defaultDirectories = ["apps", "packages"];
|
|
1014
1736
|
const hasCustomDirectories = workspaceDirectories.length > 0 && !workspaceDirectories.every((dir) => defaultDirectories.includes(dir));
|
|
@@ -1044,7 +1766,7 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
1044
1766
|
const packageOptions = await promptForPackageOptions(
|
|
1045
1767
|
scopedName,
|
|
1046
1768
|
packageType,
|
|
1047
|
-
|
|
1769
|
+
inheritedSettings
|
|
1048
1770
|
);
|
|
1049
1771
|
let targetDir = defaultDir;
|
|
1050
1772
|
if (hasCustomDirectories && workspaceDirectories.length > 0) {
|
|
@@ -1122,7 +1844,7 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedT
|
|
|
1122
1844
|
})
|
|
1123
1845
|
);
|
|
1124
1846
|
}
|
|
1125
|
-
const formatter = packageOptions.formatter ?? "
|
|
1847
|
+
const formatter = packageOptions.formatter ?? "prettier";
|
|
1126
1848
|
if (formatter === "prettier") {
|
|
1127
1849
|
versionPromises.push(
|
|
1128
1850
|
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
@@ -1277,11 +1999,11 @@ async function handleFixCommand(options) {
|
|
|
1277
1999
|
console.log(color__default.dim(` \u2022 ${error}`));
|
|
1278
2000
|
}
|
|
1279
2001
|
console.log();
|
|
1280
|
-
const tooling = await
|
|
2002
|
+
const tooling = await detectWorkspaceSettings(monorepoRoot);
|
|
1281
2003
|
const existingConfigs = await detectExistingConfigs(monorepoRoot);
|
|
1282
2004
|
const detectedLinter = tooling.linter ?? existingConfigs.linter ?? "oxlint";
|
|
1283
|
-
const detectedFormatter = tooling.formatter ?? existingConfigs.formatter ?? "
|
|
1284
|
-
const isNonInteractive = options.linter && options.formatter;
|
|
2005
|
+
const detectedFormatter = tooling.formatter ?? existingConfigs.formatter ?? "prettier";
|
|
2006
|
+
const isNonInteractive = Boolean(options.linter && options.formatter);
|
|
1285
2007
|
let linter;
|
|
1286
2008
|
let formatter;
|
|
1287
2009
|
if (isNonInteractive) {
|
|
@@ -1459,85 +2181,346 @@ async function handleFixCommand(options) {
|
|
|
1459
2181
|
console.log(color__default.dim(" Generated .vscode/extensions.json"));
|
|
1460
2182
|
}
|
|
1461
2183
|
}
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
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
|
+
}
|
|
1472
2206
|
}
|
|
1473
2207
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
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")
|
|
1484
2315
|
);
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
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?",
|
|
1491
2385
|
initialValue: true
|
|
1492
2386
|
});
|
|
1493
|
-
if (!p__namespace.isCancel(
|
|
1494
|
-
|
|
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++;
|
|
1495
2396
|
}
|
|
1496
2397
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
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}`
|
|
1504
2445
|
})),
|
|
2446
|
+
initialValues: newChanges.map((c) => c.path),
|
|
2447
|
+
// Pre-select new files
|
|
1505
2448
|
required: false
|
|
1506
2449
|
});
|
|
1507
|
-
if (
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
message: "Save as default for future?",
|
|
1511
|
-
initialValue: true
|
|
1512
|
-
});
|
|
1513
|
-
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1514
|
-
setAiFiles(selectedAiFiles);
|
|
1515
|
-
}
|
|
2450
|
+
if (p__namespace.isCancel(selectedFiles)) {
|
|
2451
|
+
p__namespace.cancel("Operation cancelled.");
|
|
2452
|
+
process.exit(0);
|
|
1516
2453
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
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
|
|
1527
2468
|
});
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
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;
|
|
1533
2492
|
}
|
|
1534
2493
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
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();
|
|
1540
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);
|
|
1541
2524
|
}
|
|
1542
2525
|
async function handleWorkspaceCommand(name, options) {
|
|
1543
2526
|
const monorepoRoot = await detectMonorepoRoot();
|
|
@@ -1559,7 +2542,7 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
1559
2542
|
process.exit(1);
|
|
1560
2543
|
}
|
|
1561
2544
|
const scope = await getMonorepoScope(monorepoRoot);
|
|
1562
|
-
const
|
|
2545
|
+
const inheritedSettings = await detectWorkspaceSettings(monorepoRoot);
|
|
1563
2546
|
const projectType = options.type ?? "app";
|
|
1564
2547
|
const defaultDir = projectType === "library" ? "packages" : "apps";
|
|
1565
2548
|
const targetDir = options.dir ?? defaultDir;
|
|
@@ -1585,8 +2568,11 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
1585
2568
|
})
|
|
1586
2569
|
);
|
|
1587
2570
|
}
|
|
1588
|
-
const linter =
|
|
1589
|
-
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;
|
|
1590
2576
|
await Promise.all(versionPromises);
|
|
1591
2577
|
const relativePkgPath = path.join(targetDir, name);
|
|
1592
2578
|
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
@@ -1597,6 +2583,9 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
1597
2583
|
template,
|
|
1598
2584
|
linter,
|
|
1599
2585
|
formatter,
|
|
2586
|
+
packageManager,
|
|
2587
|
+
nodeVersion,
|
|
2588
|
+
pnpmManageVersions,
|
|
1600
2589
|
workspaceRoot,
|
|
1601
2590
|
versions,
|
|
1602
2591
|
...baseTemplate === "r3f" && {
|
|
@@ -1630,7 +2619,7 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
1630
2619
|
process.exit(1);
|
|
1631
2620
|
}
|
|
1632
2621
|
}
|
|
1633
|
-
async function handleMonorepoCreation(generateOptions) {
|
|
2622
|
+
async function handleMonorepoCreation(generateOptions, isNonInteractive) {
|
|
1634
2623
|
const { generateMonorepo } = await import('./chunks/index.cjs').then(function (n) { return n.monorepo; });
|
|
1635
2624
|
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1636
2625
|
if (packageManager === "pnpm") {
|
|
@@ -1644,55 +2633,7 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1644
2633
|
if (nodeVersion === "latest") {
|
|
1645
2634
|
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1646
2635
|
}
|
|
1647
|
-
const
|
|
1648
|
-
let selectedAiFiles = [];
|
|
1649
|
-
if (savedAiFiles && savedAiFiles.length > 0) {
|
|
1650
|
-
const aiFileLabels = {
|
|
1651
|
-
"cursor-rules": ".cursor/rules",
|
|
1652
|
-
"agents-md": "AGENTS.md",
|
|
1653
|
-
"claude-md": "CLAUDE.md",
|
|
1654
|
-
"copilot-md": ".github/copilot-instructions.md"
|
|
1655
|
-
};
|
|
1656
|
-
const savedLabels = savedAiFiles.map((f) => aiFileLabels[f]).join(", ");
|
|
1657
|
-
const useDefault = await p__namespace.confirm({
|
|
1658
|
-
message: `Generate AI instruction files? ${color__default.dim(
|
|
1659
|
-
`(${savedLabels})`
|
|
1660
|
-
)}`,
|
|
1661
|
-
initialValue: true
|
|
1662
|
-
});
|
|
1663
|
-
if (!p__namespace.isCancel(useDefault) && useDefault) {
|
|
1664
|
-
selectedAiFiles = savedAiFiles;
|
|
1665
|
-
}
|
|
1666
|
-
} else {
|
|
1667
|
-
const aiFilesChoice = await p__namespace.multiselect({
|
|
1668
|
-
message: "Generate AI instruction files?",
|
|
1669
|
-
options: [
|
|
1670
|
-
{ value: "cursor-rules", label: ".cursor/rules", hint: "Cursor AI" },
|
|
1671
|
-
{
|
|
1672
|
-
value: "agents-md",
|
|
1673
|
-
label: "AGENTS.md",
|
|
1674
|
-
hint: "GitHub Copilot, general"
|
|
1675
|
-
},
|
|
1676
|
-
{ value: "claude-md", label: "CLAUDE.md", hint: "Claude" },
|
|
1677
|
-
{
|
|
1678
|
-
value: "copilot-md",
|
|
1679
|
-
label: ".github/copilot-instructions.md",
|
|
1680
|
-
hint: "GitHub Copilot"
|
|
1681
|
-
}
|
|
1682
|
-
],
|
|
1683
|
-
required: false
|
|
1684
|
-
});
|
|
1685
|
-
if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
|
|
1686
|
-
selectedAiFiles = aiFilesChoice;
|
|
1687
|
-
const saveChoice = await p__namespace.confirm({
|
|
1688
|
-
message: "Save as default for future monorepos?",
|
|
1689
|
-
initialValue: true
|
|
1690
|
-
});
|
|
1691
|
-
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1692
|
-
setAiFiles(selectedAiFiles);
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
2636
|
+
const aiPlatforms = await promptForAiPlatforms(isNonInteractive);
|
|
1696
2637
|
const projectPath = path.join(process$1.cwd(), generateOptions.name);
|
|
1697
2638
|
const spinner = p__namespace.spinner();
|
|
1698
2639
|
spinner.start("Creating monorepo workspace...");
|
|
@@ -1700,12 +2641,12 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1700
2641
|
const { files } = generateMonorepo({
|
|
1701
2642
|
name: generateOptions.name,
|
|
1702
2643
|
linter: generateOptions.linter ?? "oxlint",
|
|
1703
|
-
formatter: generateOptions.formatter ?? "
|
|
2644
|
+
formatter: generateOptions.formatter ?? "prettier",
|
|
1704
2645
|
packageManager,
|
|
1705
2646
|
pnpmVersion: generateOptions.pnpmVersion,
|
|
1706
2647
|
pnpmManageVersions: generateOptions.pnpmManageVersions,
|
|
1707
2648
|
nodeVersion: generateOptions.nodeVersion,
|
|
1708
|
-
|
|
2649
|
+
aiPlatforms: aiPlatforms.length > 0 ? aiPlatforms : void 0
|
|
1709
2650
|
});
|
|
1710
2651
|
const filePaths = Object.keys(files).sort();
|
|
1711
2652
|
for (const filePath of filePaths) {
|
|
@@ -1717,9 +2658,15 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1717
2658
|
}
|
|
1718
2659
|
}
|
|
1719
2660
|
spinner.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
|
|
1720
|
-
|
|
2661
|
+
if (isNonInteractive) {
|
|
2662
|
+
process.exit(0);
|
|
2663
|
+
}
|
|
2664
|
+
const newWorkspaceSettings = {
|
|
1721
2665
|
linter: generateOptions.linter,
|
|
1722
|
-
formatter: generateOptions.formatter
|
|
2666
|
+
formatter: generateOptions.formatter,
|
|
2667
|
+
packageManager,
|
|
2668
|
+
nodeVersion: generateOptions.nodeVersion,
|
|
2669
|
+
pnpmManageVersions: generateOptions.pnpmManageVersions
|
|
1723
2670
|
};
|
|
1724
2671
|
const scope = generateOptions.name;
|
|
1725
2672
|
let addMore = true;
|
|
@@ -1727,7 +2674,7 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1727
2674
|
addMore = await createPackageInWorkspace(
|
|
1728
2675
|
projectPath,
|
|
1729
2676
|
packageManager,
|
|
1730
|
-
|
|
2677
|
+
newWorkspaceSettings,
|
|
1731
2678
|
scope
|
|
1732
2679
|
);
|
|
1733
2680
|
}
|
|
@@ -1746,10 +2693,14 @@ async function handleMonorepoCreation(generateOptions) {
|
|
|
1746
2693
|
process.exit(1);
|
|
1747
2694
|
}
|
|
1748
2695
|
}
|
|
1749
|
-
async function handleStandaloneProjectCreation(generateOptions) {
|
|
2696
|
+
async function handleStandaloneProjectCreation(generateOptions, isNonInteractive) {
|
|
1750
2697
|
const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
|
|
1751
2698
|
const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
|
|
1752
2699
|
generateOptions.name ??= defaultFallbackName;
|
|
2700
|
+
const aiPlatforms = await promptForAiPlatforms(isNonInteractive);
|
|
2701
|
+
if (aiPlatforms.length > 0) {
|
|
2702
|
+
generateOptions.aiPlatforms = aiPlatforms;
|
|
2703
|
+
}
|
|
1753
2704
|
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1754
2705
|
if (packageManager === "pnpm") {
|
|
1755
2706
|
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
@@ -1800,7 +2751,7 @@ async function handleStandaloneProjectCreation(generateOptions) {
|
|
|
1800
2751
|
})
|
|
1801
2752
|
);
|
|
1802
2753
|
}
|
|
1803
|
-
const formatter = generateOptions.formatter ?? "
|
|
2754
|
+
const formatter = generateOptions.formatter ?? "prettier";
|
|
1804
2755
|
if (formatter === "prettier") {
|
|
1805
2756
|
versionPromises.push(
|
|
1806
2757
|
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
@@ -1829,6 +2780,9 @@ async function handleStandaloneProjectCreation(generateOptions) {
|
|
|
1829
2780
|
const files = index.generate(generateOptions);
|
|
1830
2781
|
await writeGeneratedFiles(projectPath, files);
|
|
1831
2782
|
spinner.stop(color__default.green.inverse(" \u2713 Project created! "));
|
|
2783
|
+
if (isNonInteractive) {
|
|
2784
|
+
process.exit(0);
|
|
2785
|
+
}
|
|
1832
2786
|
const nextSteps = isLibrary ? [
|
|
1833
2787
|
`cd ${generateOptions.name}`,
|
|
1834
2788
|
`${packageManager} install`,
|
|
@@ -1861,21 +2815,23 @@ async function handleInteractiveMonorepoMode(monorepoRoot) {
|
|
|
1861
2815
|
process.exit(0);
|
|
1862
2816
|
}
|
|
1863
2817
|
if (choice === "add") {
|
|
1864
|
-
const
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
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}`
|
|
1869
2825
|
].filter(Boolean).join(", ");
|
|
1870
|
-
p__namespace.log.info(`Using workspace
|
|
2826
|
+
p__namespace.log.info(`Using workspace settings (${settingsInfo})`);
|
|
1871
2827
|
}
|
|
1872
2828
|
const scope = await getMonorepoScope(monorepoRoot);
|
|
1873
2829
|
let addMore = true;
|
|
1874
2830
|
while (addMore) {
|
|
1875
2831
|
addMore = await createPackageInWorkspace(
|
|
1876
2832
|
monorepoRoot,
|
|
1877
|
-
"pnpm",
|
|
1878
|
-
|
|
2833
|
+
inheritedSettings.packageManager ?? "pnpm",
|
|
2834
|
+
inheritedSettings,
|
|
1879
2835
|
scope
|
|
1880
2836
|
);
|
|
1881
2837
|
}
|
|
@@ -1902,7 +2858,7 @@ async function main() {
|
|
|
1902
2858
|
"linter: eslint, oxlint, or biome (default: oxlint)"
|
|
1903
2859
|
).option(
|
|
1904
2860
|
"--formatter <type>",
|
|
1905
|
-
"formatter: prettier, oxfmt, or biome (default:
|
|
2861
|
+
"formatter: prettier, oxfmt, or biome (default: prettier)"
|
|
1906
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(
|
|
1907
2863
|
"--package-manager <manager>",
|
|
1908
2864
|
"specify package manager (e.g. npm, yarn, pnpm)"
|
|
@@ -1924,7 +2880,7 @@ async function main() {
|
|
|
1924
2880
|
).option("--clear-config", "Clear saved preferences (e.g. editor choice)").option("--config-path", "Print the path to the config file").option(
|
|
1925
2881
|
"--check",
|
|
1926
2882
|
"Check if current directory is in a valid monorepo workspace"
|
|
1927
|
-
).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(
|
|
1928
2884
|
"--path <directory>",
|
|
1929
2885
|
"Run in specified directory instead of current working directory"
|
|
1930
2886
|
).action(async (name, options) => {
|
|
@@ -1963,6 +2919,12 @@ async function main() {
|
|
|
1963
2919
|
case "--fix":
|
|
1964
2920
|
options.fix = true;
|
|
1965
2921
|
break;
|
|
2922
|
+
case "--update":
|
|
2923
|
+
options.update = true;
|
|
2924
|
+
break;
|
|
2925
|
+
case "--yes":
|
|
2926
|
+
options.yes = true;
|
|
2927
|
+
break;
|
|
1966
2928
|
default:
|
|
1967
2929
|
console.error(color__default.red(`Unknown option: ${name}`));
|
|
1968
2930
|
process.exit(1);
|
|
@@ -1974,6 +2936,9 @@ async function main() {
|
|
|
1974
2936
|
if (options.fix) {
|
|
1975
2937
|
await handleFixCommand(options);
|
|
1976
2938
|
}
|
|
2939
|
+
if (options.update) {
|
|
2940
|
+
await handleUpdateCommand(options);
|
|
2941
|
+
}
|
|
1977
2942
|
if (options.dir && !options.workspace) {
|
|
1978
2943
|
console.error(color__default.red("Error:") + " --dir requires --workspace flag");
|
|
1979
2944
|
console.log(
|
|
@@ -1989,11 +2954,11 @@ async function main() {
|
|
|
1989
2954
|
console.clear();
|
|
1990
2955
|
p__namespace.intro(color__default.bgCyan(color__default.black(` create-krispya v${pkg.version} `)));
|
|
1991
2956
|
const monorepoRoot = await detectMonorepoRoot();
|
|
1992
|
-
if (monorepoRoot &&
|
|
2957
|
+
if (monorepoRoot && !hasConfigOptions(options)) {
|
|
1993
2958
|
await handleInteractiveMonorepoMode(monorepoRoot);
|
|
1994
2959
|
}
|
|
1995
2960
|
let generateOptions;
|
|
1996
|
-
if (
|
|
2961
|
+
if (options.yes) {
|
|
1997
2962
|
const template = options.template ?? "vanilla";
|
|
1998
2963
|
const baseTemplate = index.getBaseTemplate(template);
|
|
1999
2964
|
const defaultName = getDefaultProjectName(template);
|
|
@@ -2004,7 +2969,7 @@ async function main() {
|
|
|
2004
2969
|
libraryBundler: projectType === "library" ? options.bundler ?? "unbuild" : void 0,
|
|
2005
2970
|
template,
|
|
2006
2971
|
linter: options.linter ?? "oxlint",
|
|
2007
|
-
formatter: options.formatter ?? "
|
|
2972
|
+
formatter: options.formatter ?? "prettier",
|
|
2008
2973
|
...baseTemplate === "r3f" && {
|
|
2009
2974
|
drei: options.drei ? {} : void 0,
|
|
2010
2975
|
handle: options.handle ? {} : void 0,
|
|
@@ -2024,12 +2989,35 @@ async function main() {
|
|
|
2024
2989
|
nodeVersion: options.nodeVersion ?? "latest"
|
|
2025
2990
|
};
|
|
2026
2991
|
} else {
|
|
2027
|
-
|
|
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);
|
|
2028
3015
|
}
|
|
3016
|
+
const isNonInteractive = options.yes ?? false;
|
|
2029
3017
|
if (generateOptions.projectType === "monorepo") {
|
|
2030
|
-
await handleMonorepoCreation(generateOptions);
|
|
3018
|
+
await handleMonorepoCreation(generateOptions, isNonInteractive);
|
|
2031
3019
|
} else {
|
|
2032
|
-
await handleStandaloneProjectCreation(generateOptions);
|
|
3020
|
+
await handleStandaloneProjectCreation(generateOptions, isNonInteractive);
|
|
2033
3021
|
}
|
|
2034
3022
|
});
|
|
2035
3023
|
await program.parseAsync();
|