create-krispya 0.9.0 → 0.11.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 +28 -6
- package/dist/cli.cjs +1319 -1490
- package/dist/cli.d.cts +36 -0
- package/dist/cli.d.mts +36 -0
- package/dist/cli.d.ts +36 -0
- package/dist/cli.mjs +1353 -1524
- package/dist/index.cjs +52 -16
- package/dist/index.d.cts +81 -180
- package/dist/index.d.mts +81 -180
- package/dist/index.d.ts +81 -180
- package/dist/index.mjs +34 -1
- package/dist/{chunks/index.cjs → shared/create-krispya.8KaGuRpu.cjs} +1361 -793
- package/dist/{chunks/index.mjs → shared/create-krispya.DblF9gKc.mjs} +1338 -780
- package/dist/shared/create-krispya.to8NBxeJ.d.cts +237 -0
- package/dist/shared/create-krispya.to8NBxeJ.d.mts +237 -0
- package/dist/shared/create-krispya.to8NBxeJ.d.ts +237 -0
- package/package.json +6 -3
package/dist/cli.cjs
CHANGED
|
@@ -4,31 +4,31 @@
|
|
|
4
4
|
const p = require('@clack/prompts');
|
|
5
5
|
const color = require('chalk');
|
|
6
6
|
const commander = require('commander');
|
|
7
|
-
const node_fs = require('node:fs');
|
|
8
7
|
const promises$1 = require('node:fs/promises');
|
|
9
8
|
const node_module = require('node:module');
|
|
10
9
|
const node_path = require('node:path');
|
|
11
10
|
const node_process = require('node:process');
|
|
12
11
|
const undici = require('undici');
|
|
13
|
-
const
|
|
12
|
+
const workspace = require('./shared/create-krispya.8KaGuRpu.cjs');
|
|
14
13
|
const Conf = require('conf');
|
|
15
14
|
const promises = require('fs/promises');
|
|
16
15
|
const fs = require('fs');
|
|
17
16
|
const path = require('path');
|
|
17
|
+
const node_fs = require('node:fs');
|
|
18
18
|
|
|
19
19
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
20
20
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
21
21
|
|
|
22
22
|
function _interopNamespaceCompat(e) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
23
|
+
if (e && typeof e === 'object' && 'default' in e) return e;
|
|
24
|
+
const n = Object.create(null);
|
|
25
|
+
if (e) {
|
|
26
|
+
for (const k in e) {
|
|
27
|
+
n[k] = e[k];
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
}
|
|
30
|
+
n.default = e;
|
|
31
|
+
return n;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const p__namespace = /*#__PURE__*/_interopNamespaceCompat(p);
|
|
@@ -49,13 +49,13 @@ function formatConfigSummary(options, inherited) {
|
|
|
49
49
|
return lang === "typescript" ? "TypeScript" : lang === "javascript" ? "JavaScript" : lang;
|
|
50
50
|
};
|
|
51
51
|
const projectType = options.projectType ?? "app";
|
|
52
|
-
const baseTemplate = options.template ?
|
|
52
|
+
const baseTemplate = options.template ? workspace.getBaseTemplate(options.template) : "vanilla";
|
|
53
53
|
if (baseTemplate === "react") {
|
|
54
54
|
lines.push(formatRow("Framework", "React"));
|
|
55
55
|
} else if (baseTemplate === "r3f") {
|
|
56
56
|
lines.push(formatRow("Framework", "React Three Fiber"));
|
|
57
57
|
}
|
|
58
|
-
const language = options.template ?
|
|
58
|
+
const language = options.template ? workspace.getLanguageFromTemplate(options.template) : "typescript";
|
|
59
59
|
lines.push(formatRow("Language", formatLanguage(language)));
|
|
60
60
|
if (projectType === "library") {
|
|
61
61
|
lines.push(formatRow("Bundler", options.libraryBundler ?? "unbuild"));
|
|
@@ -66,15 +66,15 @@ function formatConfigSummary(options, inherited) {
|
|
|
66
66
|
lines.push(
|
|
67
67
|
formatRow(
|
|
68
68
|
"Engine",
|
|
69
|
-
`${
|
|
69
|
+
`${workspace.getEngineName(options.engine)}@${options.engine?.version ?? "latest"}`,
|
|
70
70
|
engineInherited
|
|
71
71
|
)
|
|
72
72
|
);
|
|
73
73
|
const pmInherited = inherited?.packageManager !== void 0;
|
|
74
74
|
lines.push(
|
|
75
|
-
formatRow("Package manager",
|
|
75
|
+
formatRow("Package manager", workspace.getPackageManagerName(options.packageManager), pmInherited)
|
|
76
76
|
);
|
|
77
|
-
if (
|
|
77
|
+
if (workspace.getPackageManagerName(options.packageManager) === "pnpm") {
|
|
78
78
|
const versionManaged = options.pnpmManageVersions ? "yes" : "no";
|
|
79
79
|
const pnpmVersionInherited = inherited?.pnpmManageVersions !== void 0;
|
|
80
80
|
lines.push(formatRow("\u21B3 Version managed", versionManaged, pnpmVersionInherited, ""));
|
|
@@ -90,10 +90,11 @@ function formatConfigSummary(options, inherited) {
|
|
|
90
90
|
const testing = options.testing ?? (projectType === "library" ? "vitest" : "none");
|
|
91
91
|
lines.push(formatRow("Testing", testing));
|
|
92
92
|
if (!inherited) {
|
|
93
|
+
lines.push(formatRow("Editor config", options.ide === "none" ? "generic" : "generic + vscode"));
|
|
93
94
|
const configStrategy = options.configStrategy ?? "stealth";
|
|
94
95
|
lines.push(formatRow("Config strategy", configStrategy));
|
|
95
96
|
}
|
|
96
|
-
if (options.template &&
|
|
97
|
+
if (options.template && workspace.getBaseTemplate(options.template) === "r3f") {
|
|
97
98
|
const integrationNames = [
|
|
98
99
|
options.drei && "drei",
|
|
99
100
|
options.handle && "handle",
|
|
@@ -109,7 +110,7 @@ function formatConfigSummary(options, inherited) {
|
|
|
109
110
|
options.viverse && "viverse"
|
|
110
111
|
].filter(Boolean);
|
|
111
112
|
lines.push("");
|
|
112
|
-
lines.push(color__default.dim("
|
|
113
|
+
lines.push(color__default.dim("Features"));
|
|
113
114
|
for (let i = 0; i < integrationNames.length; i += 2) {
|
|
114
115
|
const left = `${color__default.green("\u25CF")} ${integrationNames[i]}`;
|
|
115
116
|
const right = integrationNames[i + 1] ? `${color__default.green("\u25CF")} ${integrationNames[i + 1]}` : "";
|
|
@@ -129,7 +130,7 @@ function formatMonorepoConfigSummary(options) {
|
|
|
129
130
|
return `${indent}${label} ${dots} ${value}`;
|
|
130
131
|
};
|
|
131
132
|
lines.push(
|
|
132
|
-
formatRow("Engine", `${
|
|
133
|
+
formatRow("Engine", `${workspace.getEngineName(options.engine)}@${options.engine.version ?? "latest"}`)
|
|
133
134
|
);
|
|
134
135
|
lines.push(formatRow("Package manager", options.packageManager));
|
|
135
136
|
if (options.packageManager === "pnpm") {
|
|
@@ -138,6 +139,7 @@ function formatMonorepoConfigSummary(options) {
|
|
|
138
139
|
}
|
|
139
140
|
lines.push(formatRow("Linter", options.linter));
|
|
140
141
|
lines.push(formatRow("Formatter", options.formatter));
|
|
142
|
+
lines.push(formatRow("Editor config", options.ide === "none" ? "generic" : "generic + vscode"));
|
|
141
143
|
return lines.join("\n");
|
|
142
144
|
}
|
|
143
145
|
|
|
@@ -156,12 +158,58 @@ function clearConfig() {
|
|
|
156
158
|
function getConfigPath() {
|
|
157
159
|
return config.path;
|
|
158
160
|
}
|
|
159
|
-
function getCustomTemplates() {
|
|
160
|
-
return config.get("customTemplates") ?? {};
|
|
161
|
-
}
|
|
162
161
|
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
const R3F_INTEGRATION_OPTIONS = [
|
|
163
|
+
{ value: "drei", label: "Drei" },
|
|
164
|
+
{ value: "handle", label: "Handle" },
|
|
165
|
+
{ value: "leva", label: "Leva" },
|
|
166
|
+
{ value: "postprocessing", label: "Postprocessing" },
|
|
167
|
+
{ value: "rapier", label: "Rapier" },
|
|
168
|
+
{ value: "xr", label: "XR" },
|
|
169
|
+
{ value: "uikit", label: "UIKit" },
|
|
170
|
+
{ value: "offscreen", label: "Offscreen" },
|
|
171
|
+
{ value: "zustand", label: "Zustand" },
|
|
172
|
+
{ value: "koota", label: "Koota" },
|
|
173
|
+
{ value: "triplex", label: "Triplex" },
|
|
174
|
+
{ value: "viverse", label: "Viverse" }
|
|
175
|
+
];
|
|
176
|
+
function getR3fIntegrationFlags(features) {
|
|
177
|
+
if (!features) return {};
|
|
178
|
+
return {
|
|
179
|
+
drei: features.includes("drei") ? {} : void 0,
|
|
180
|
+
handle: features.includes("handle") ? {} : void 0,
|
|
181
|
+
leva: features.includes("leva") ? {} : void 0,
|
|
182
|
+
postprocessing: features.includes("postprocessing") ? {} : void 0,
|
|
183
|
+
rapier: features.includes("rapier") ? {} : void 0,
|
|
184
|
+
xr: features.includes("xr") ? {} : void 0,
|
|
185
|
+
uikit: features.includes("uikit") ? {} : void 0,
|
|
186
|
+
offscreen: features.includes("offscreen") ? {} : void 0,
|
|
187
|
+
zustand: features.includes("zustand") ? {} : void 0,
|
|
188
|
+
koota: features.includes("koota") ? {} : void 0,
|
|
189
|
+
triplex: features.includes("triplex") ? {} : void 0,
|
|
190
|
+
viverse: features.includes("viverse") ? {} : void 0
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function getInitialR3fIntegrations(presets) {
|
|
194
|
+
if (!presets) return ["drei"];
|
|
195
|
+
const initialValues = R3F_INTEGRATION_OPTIONS.filter(({ value }) => presets[value]).map(
|
|
196
|
+
({ value }) => value
|
|
197
|
+
);
|
|
198
|
+
return initialValues.length > 0 ? initialValues : ["drei"];
|
|
199
|
+
}
|
|
200
|
+
async function promptForProceed() {
|
|
201
|
+
const proceed = await p__namespace.confirm({
|
|
202
|
+
message: "Proceed with these settings?",
|
|
203
|
+
initialValue: true
|
|
204
|
+
});
|
|
205
|
+
if (p__namespace.isCancel(proceed)) {
|
|
206
|
+
p__namespace.cancel("Operation cancelled.");
|
|
207
|
+
process.exit(0);
|
|
208
|
+
}
|
|
209
|
+
return proceed;
|
|
210
|
+
}
|
|
211
|
+
function getDefaultOptions(template, name, projectType = "app", libraryBundler, features, inheritedSettings) {
|
|
212
|
+
const baseTemplate = workspace.getBaseTemplate(template);
|
|
165
213
|
const base = {
|
|
166
214
|
name,
|
|
167
215
|
template,
|
|
@@ -174,71 +222,30 @@ function getDefaultOptions(template, name, projectType = "app", libraryBundler,
|
|
|
174
222
|
formatter: inheritedSettings?.formatter ?? "prettier",
|
|
175
223
|
// Libraries get vitest by default, apps don't
|
|
176
224
|
testing: projectType === "library" ? "vitest" : "none",
|
|
177
|
-
configStrategy: getConfigStrategy()
|
|
225
|
+
configStrategy: getConfigStrategy(),
|
|
226
|
+
ide: "vscode"
|
|
227
|
+
};
|
|
228
|
+
return {
|
|
229
|
+
...base,
|
|
230
|
+
...baseTemplate === "r3f" ? getR3fIntegrationFlags(features) : {}
|
|
178
231
|
};
|
|
179
|
-
if (baseTemplate === "r3f" && integrations) {
|
|
180
|
-
return {
|
|
181
|
-
...base,
|
|
182
|
-
drei: integrations.includes("drei") ? {} : void 0,
|
|
183
|
-
handle: integrations.includes("handle") ? {} : void 0,
|
|
184
|
-
leva: integrations.includes("leva") ? {} : void 0,
|
|
185
|
-
postprocessing: integrations.includes("postprocessing") ? {} : void 0,
|
|
186
|
-
rapier: integrations.includes("rapier") ? {} : void 0,
|
|
187
|
-
xr: integrations.includes("xr") ? {} : void 0,
|
|
188
|
-
uikit: integrations.includes("uikit") ? {} : void 0,
|
|
189
|
-
offscreen: integrations.includes("offscreen") ? {} : void 0,
|
|
190
|
-
zustand: integrations.includes("zustand") ? {} : void 0,
|
|
191
|
-
koota: integrations.includes("koota") ? {} : void 0,
|
|
192
|
-
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
193
|
-
viverse: integrations.includes("viverse") ? {} : void 0
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
return base;
|
|
197
232
|
}
|
|
198
233
|
function getDefaultProjectName(template) {
|
|
199
|
-
const base =
|
|
234
|
+
const base = workspace.getBaseTemplate(template);
|
|
200
235
|
switch (base) {
|
|
201
236
|
case "vanilla":
|
|
202
|
-
return `vanilla-${
|
|
237
|
+
return `vanilla-${workspace.generateRandomName()}`;
|
|
203
238
|
case "react":
|
|
204
|
-
return `react-${
|
|
239
|
+
return `react-${workspace.generateRandomName()}`;
|
|
205
240
|
case "r3f":
|
|
206
|
-
return `react-three-${
|
|
241
|
+
return `react-three-${workspace.generateRandomName()}`;
|
|
207
242
|
}
|
|
208
243
|
}
|
|
209
244
|
async function promptForR3fIntegrations(presets) {
|
|
210
|
-
const initialValues = [];
|
|
211
|
-
if (presets) {
|
|
212
|
-
if (presets.drei) initialValues.push("drei");
|
|
213
|
-
if (presets.handle) initialValues.push("handle");
|
|
214
|
-
if (presets.leva) initialValues.push("leva");
|
|
215
|
-
if (presets.postprocessing) initialValues.push("postprocessing");
|
|
216
|
-
if (presets.rapier) initialValues.push("rapier");
|
|
217
|
-
if (presets.xr) initialValues.push("xr");
|
|
218
|
-
if (presets.uikit) initialValues.push("uikit");
|
|
219
|
-
if (presets.offscreen) initialValues.push("offscreen");
|
|
220
|
-
if (presets.zustand) initialValues.push("zustand");
|
|
221
|
-
if (presets.koota) initialValues.push("koota");
|
|
222
|
-
if (presets.triplex) initialValues.push("triplex");
|
|
223
|
-
if (presets.viverse) initialValues.push("viverse");
|
|
224
|
-
}
|
|
225
245
|
const selected = await p__namespace.multiselect({
|
|
226
|
-
message: "R3F
|
|
227
|
-
options: [
|
|
228
|
-
|
|
229
|
-
{ value: "handle", label: "Handle" },
|
|
230
|
-
{ value: "leva", label: "Leva" },
|
|
231
|
-
{ value: "postprocessing", label: "Postprocessing" },
|
|
232
|
-
{ value: "rapier", label: "Rapier" },
|
|
233
|
-
{ value: "xr", label: "XR" },
|
|
234
|
-
{ value: "uikit", label: "UIKit" },
|
|
235
|
-
{ value: "offscreen", label: "Offscreen" },
|
|
236
|
-
{ value: "zustand", label: "Zustand" },
|
|
237
|
-
{ value: "koota", label: "Koota" },
|
|
238
|
-
{ value: "triplex", label: "Triplex" },
|
|
239
|
-
{ value: "viverse", label: "Viverse" }
|
|
240
|
-
],
|
|
241
|
-
initialValues: initialValues.length > 0 ? initialValues : ["drei"],
|
|
246
|
+
message: "R3F features",
|
|
247
|
+
options: [...R3F_INTEGRATION_OPTIONS],
|
|
248
|
+
initialValues: getInitialR3fIntegrations(presets),
|
|
242
249
|
required: false
|
|
243
250
|
});
|
|
244
251
|
if (p__namespace.isCancel(selected)) {
|
|
@@ -247,7 +254,7 @@ async function promptForR3fIntegrations(presets) {
|
|
|
247
254
|
}
|
|
248
255
|
return selected;
|
|
249
256
|
}
|
|
250
|
-
async function promptForCustomization(template, name, projectType,
|
|
257
|
+
async function promptForCustomization(template, name, projectType, features, inheritedSettings, presets) {
|
|
251
258
|
let libraryBundler;
|
|
252
259
|
if (projectType === "library") {
|
|
253
260
|
const bundler = await p__namespace.select({
|
|
@@ -382,7 +389,19 @@ async function promptForCustomization(template, name, projectType, integrations,
|
|
|
382
389
|
p__namespace.cancel("Operation cancelled.");
|
|
383
390
|
process.exit(0);
|
|
384
391
|
}
|
|
385
|
-
const
|
|
392
|
+
const ideChoice = await p__namespace.select({
|
|
393
|
+
message: "IDE config",
|
|
394
|
+
options: [
|
|
395
|
+
{ value: "vscode", label: "vscode" },
|
|
396
|
+
{ value: "none", label: "None" }
|
|
397
|
+
],
|
|
398
|
+
initialValue: presets?.ide ?? "vscode"
|
|
399
|
+
});
|
|
400
|
+
if (p__namespace.isCancel(ideChoice)) {
|
|
401
|
+
p__namespace.cancel("Operation cancelled.");
|
|
402
|
+
process.exit(0);
|
|
403
|
+
}
|
|
404
|
+
const baseTemplate = workspace.getBaseTemplate(template);
|
|
386
405
|
const finalTemplate = language === "javascript" ? `${baseTemplate}-js` : baseTemplate;
|
|
387
406
|
const base = {
|
|
388
407
|
name,
|
|
@@ -395,26 +414,13 @@ async function promptForCustomization(template, name, projectType, integrations,
|
|
|
395
414
|
linter,
|
|
396
415
|
formatter,
|
|
397
416
|
testing,
|
|
398
|
-
configStrategy: configStrategyChoice
|
|
417
|
+
configStrategy: configStrategyChoice,
|
|
418
|
+
ide: ideChoice
|
|
419
|
+
};
|
|
420
|
+
return {
|
|
421
|
+
...base,
|
|
422
|
+
...baseTemplate === "r3f" ? getR3fIntegrationFlags(features) : {}
|
|
399
423
|
};
|
|
400
|
-
if (baseTemplate === "r3f" && integrations) {
|
|
401
|
-
return {
|
|
402
|
-
...base,
|
|
403
|
-
drei: integrations.includes("drei") ? {} : void 0,
|
|
404
|
-
handle: integrations.includes("handle") ? {} : void 0,
|
|
405
|
-
leva: integrations.includes("leva") ? {} : void 0,
|
|
406
|
-
postprocessing: integrations.includes("postprocessing") ? {} : void 0,
|
|
407
|
-
rapier: integrations.includes("rapier") ? {} : void 0,
|
|
408
|
-
xr: integrations.includes("xr") ? {} : void 0,
|
|
409
|
-
uikit: integrations.includes("uikit") ? {} : void 0,
|
|
410
|
-
offscreen: integrations.includes("offscreen") ? {} : void 0,
|
|
411
|
-
zustand: integrations.includes("zustand") ? {} : void 0,
|
|
412
|
-
koota: integrations.includes("koota") ? {} : void 0,
|
|
413
|
-
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
414
|
-
viverse: integrations.includes("viverse") ? {} : void 0
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
return base;
|
|
418
424
|
}
|
|
419
425
|
async function promptForInitialPackage() {
|
|
420
426
|
const choice = await p__namespace.select({
|
|
@@ -440,7 +446,8 @@ function getDefaultMonorepoOptions(name) {
|
|
|
440
446
|
pnpmManageVersions: true,
|
|
441
447
|
engine: { name: "node", version: "latest" },
|
|
442
448
|
linter: "oxlint",
|
|
443
|
-
formatter: "prettier"
|
|
449
|
+
formatter: "prettier",
|
|
450
|
+
ide: "vscode"
|
|
444
451
|
};
|
|
445
452
|
}
|
|
446
453
|
async function promptForMonorepoCustomization(name, presets) {
|
|
@@ -493,6 +500,18 @@ async function promptForMonorepoCustomization(name, presets) {
|
|
|
493
500
|
p__namespace.cancel("Operation cancelled.");
|
|
494
501
|
process.exit(0);
|
|
495
502
|
}
|
|
503
|
+
const ide = await p__namespace.select({
|
|
504
|
+
message: "IDE config",
|
|
505
|
+
options: [
|
|
506
|
+
{ value: "vscode", label: "vscode" },
|
|
507
|
+
{ value: "none", label: "None" }
|
|
508
|
+
],
|
|
509
|
+
initialValue: presets?.ide ?? "vscode"
|
|
510
|
+
});
|
|
511
|
+
if (p__namespace.isCancel(ide)) {
|
|
512
|
+
p__namespace.cancel("Operation cancelled.");
|
|
513
|
+
process.exit(0);
|
|
514
|
+
}
|
|
496
515
|
return {
|
|
497
516
|
name,
|
|
498
517
|
projectType: "monorepo",
|
|
@@ -500,7 +519,8 @@ async function promptForMonorepoCustomization(name, presets) {
|
|
|
500
519
|
packageManager: { name: "pnpm" },
|
|
501
520
|
pnpmManageVersions: managePnpm,
|
|
502
521
|
linter,
|
|
503
|
-
formatter
|
|
522
|
+
formatter,
|
|
523
|
+
ide
|
|
504
524
|
};
|
|
505
525
|
}
|
|
506
526
|
async function promptForMonorepo(workspaceName, presets) {
|
|
@@ -508,6 +528,7 @@ async function promptForMonorepo(workspaceName, presets) {
|
|
|
508
528
|
if (presets) {
|
|
509
529
|
if (presets.linter) defaultOptions.linter = presets.linter;
|
|
510
530
|
if (presets.formatter) defaultOptions.formatter = presets.formatter;
|
|
531
|
+
if (presets.ide) defaultOptions.ide = presets.ide;
|
|
511
532
|
if (presets.engine) defaultOptions.engine = presets.engine;
|
|
512
533
|
if (presets.pnpmManageVersions !== void 0)
|
|
513
534
|
defaultOptions.pnpmManageVersions = presets.pnpmManageVersions;
|
|
@@ -516,26 +537,15 @@ async function promptForMonorepo(workspaceName, presets) {
|
|
|
516
537
|
formatMonorepoConfigSummary({
|
|
517
538
|
name: defaultOptions.name,
|
|
518
539
|
engine: defaultOptions.engine ?? { name: "node", version: "latest" },
|
|
519
|
-
packageManager:
|
|
540
|
+
packageManager: workspace.getPackageManagerName(defaultOptions.packageManager),
|
|
520
541
|
pnpmManageVersions: defaultOptions.pnpmManageVersions,
|
|
521
542
|
linter: defaultOptions.linter ?? "oxlint",
|
|
522
|
-
formatter: defaultOptions.formatter ?? "prettier"
|
|
543
|
+
formatter: defaultOptions.formatter ?? "prettier",
|
|
544
|
+
ide: defaultOptions.ide ?? "vscode"
|
|
523
545
|
}),
|
|
524
546
|
"Workspace Configuration"
|
|
525
547
|
);
|
|
526
|
-
|
|
527
|
-
message: "Proceed with these settings?",
|
|
528
|
-
options: [
|
|
529
|
-
{ value: "continue", label: "Yes, continue" },
|
|
530
|
-
{ value: "customize", label: "No, customize settings" }
|
|
531
|
-
],
|
|
532
|
-
initialValue: "continue"
|
|
533
|
-
});
|
|
534
|
-
if (p__namespace.isCancel(proceed)) {
|
|
535
|
-
p__namespace.cancel("Operation cancelled.");
|
|
536
|
-
process.exit(0);
|
|
537
|
-
}
|
|
538
|
-
if (proceed === "continue") {
|
|
548
|
+
if (await promptForProceed()) {
|
|
539
549
|
return defaultOptions;
|
|
540
550
|
}
|
|
541
551
|
return promptForMonorepoCustomization(workspaceName, presets);
|
|
@@ -544,9 +554,9 @@ async function promptForOptions(name, presets) {
|
|
|
544
554
|
let projectName = name;
|
|
545
555
|
if (!projectName) {
|
|
546
556
|
const nameResult = await p__namespace.text({
|
|
547
|
-
message: "
|
|
548
|
-
placeholder:
|
|
549
|
-
defaultValue:
|
|
557
|
+
message: "Project name:",
|
|
558
|
+
placeholder: workspace.generateRandomName(),
|
|
559
|
+
defaultValue: workspace.generateRandomName(),
|
|
550
560
|
validate: (value) => {
|
|
551
561
|
if (!value.length) return "Project name is required";
|
|
552
562
|
}
|
|
@@ -558,7 +568,7 @@ async function promptForOptions(name, presets) {
|
|
|
558
568
|
projectName = nameResult;
|
|
559
569
|
}
|
|
560
570
|
const projectType = await p__namespace.select({
|
|
561
|
-
message: "Project type",
|
|
571
|
+
message: "Project type:",
|
|
562
572
|
options: [
|
|
563
573
|
{ value: "app", label: "Application" },
|
|
564
574
|
{ value: "library", label: "Library" },
|
|
@@ -575,41 +585,6 @@ async function promptForOptions(name, presets) {
|
|
|
575
585
|
}
|
|
576
586
|
return promptForPackageOptions(projectName, projectType, void 0, presets);
|
|
577
587
|
}
|
|
578
|
-
function customTemplateToOptions(customTemplate, name, projectType, inheritedSettings) {
|
|
579
|
-
const baseTemplate = customTemplate.baseTemplate;
|
|
580
|
-
const template = baseTemplate;
|
|
581
|
-
const base = {
|
|
582
|
-
name,
|
|
583
|
-
template,
|
|
584
|
-
projectType,
|
|
585
|
-
packageManager: inheritedSettings?.packageManager ?? { name: "pnpm" },
|
|
586
|
-
pnpmManageVersions: inheritedSettings?.pnpmManageVersions ?? true,
|
|
587
|
-
engine: inheritedSettings?.engine ?? { name: "node", version: "latest" },
|
|
588
|
-
linter: inheritedSettings?.linter ?? customTemplate.linter,
|
|
589
|
-
formatter: inheritedSettings?.formatter ?? customTemplate.formatter,
|
|
590
|
-
testing: customTemplate.testing,
|
|
591
|
-
configStrategy: customTemplate.configStrategy ?? getConfigStrategy()
|
|
592
|
-
};
|
|
593
|
-
if (baseTemplate === "r3f" && customTemplate.integrations) {
|
|
594
|
-
const integrations = customTemplate.integrations;
|
|
595
|
-
return {
|
|
596
|
-
...base,
|
|
597
|
-
drei: integrations.includes("drei") ? {} : void 0,
|
|
598
|
-
handle: integrations.includes("handle") ? {} : void 0,
|
|
599
|
-
leva: integrations.includes("leva") ? {} : void 0,
|
|
600
|
-
postprocessing: integrations.includes("postprocessing") ? {} : void 0,
|
|
601
|
-
rapier: integrations.includes("rapier") ? {} : void 0,
|
|
602
|
-
xr: integrations.includes("xr") ? {} : void 0,
|
|
603
|
-
uikit: integrations.includes("uikit") ? {} : void 0,
|
|
604
|
-
offscreen: integrations.includes("offscreen") ? {} : void 0,
|
|
605
|
-
zustand: integrations.includes("zustand") ? {} : void 0,
|
|
606
|
-
koota: integrations.includes("koota") ? {} : void 0,
|
|
607
|
-
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
608
|
-
viverse: integrations.includes("viverse") ? {} : void 0
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
return base;
|
|
612
|
-
}
|
|
613
588
|
function presetsToInheritedSettings(presets) {
|
|
614
589
|
if (!presets) return void 0;
|
|
615
590
|
return {
|
|
@@ -621,798 +596,138 @@ function presetsToInheritedSettings(presets) {
|
|
|
621
596
|
};
|
|
622
597
|
}
|
|
623
598
|
async function promptForPackageOptions(projectName, projectType, inheritedSettings, presets) {
|
|
624
|
-
const builtInOptions = [
|
|
625
|
-
{ value: "vanilla", label: "Vanilla" },
|
|
626
|
-
{ value: "react", label: "React", hint: "experimental" },
|
|
627
|
-
{ value: "r3f", label: "React Three Fiber", hint: "experimental" }
|
|
628
|
-
];
|
|
629
|
-
const customTemplates = getCustomTemplates();
|
|
630
|
-
const customOptions = Object.keys(customTemplates).map((name) => ({
|
|
631
|
-
value: `custom:${name}`,
|
|
632
|
-
label: name,
|
|
633
|
-
hint: "saved template"
|
|
634
|
-
}));
|
|
635
|
-
const allOptions = [...builtInOptions, ...customOptions];
|
|
636
599
|
const templateSelection = await p__namespace.select({
|
|
637
|
-
message: "Select a template",
|
|
638
|
-
options:
|
|
639
|
-
|
|
600
|
+
message: "Select a template:",
|
|
601
|
+
options: [
|
|
602
|
+
{ value: "vanilla", label: color__default.yellow("Vanilla") },
|
|
603
|
+
{ value: "react", label: color__default.cyan("React"), hint: "experimental" },
|
|
604
|
+
{ value: "r3f", label: color__default.magenta("React Three Fiber"), hint: "experimental" }
|
|
605
|
+
],
|
|
606
|
+
initialValue: presets?.template ? workspace.getBaseTemplate(presets.template) : "vanilla"
|
|
640
607
|
});
|
|
641
608
|
if (p__namespace.isCancel(templateSelection)) {
|
|
642
609
|
p__namespace.cancel("Operation cancelled.");
|
|
643
610
|
process.exit(0);
|
|
644
611
|
}
|
|
645
|
-
const
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
const customTemplate = customTemplates[customName];
|
|
649
|
-
const defaultOptions2 = customTemplateToOptions(
|
|
650
|
-
customTemplate,
|
|
651
|
-
projectName,
|
|
652
|
-
projectType,
|
|
653
|
-
inheritedSettings
|
|
654
|
-
);
|
|
655
|
-
const configTitle2 = inheritedSettings ? `Template: ${customName} (using workspace settings)` : `Template: ${customName}`;
|
|
656
|
-
p__namespace.note(formatConfigSummary(defaultOptions2, inheritedSettings), configTitle2);
|
|
657
|
-
const proceed2 = await p__namespace.select({
|
|
658
|
-
message: "Proceed with these settings?",
|
|
659
|
-
options: [
|
|
660
|
-
{ value: "continue", label: "Yes, continue" },
|
|
661
|
-
{ value: "customize", label: "No, customize settings" }
|
|
662
|
-
],
|
|
663
|
-
initialValue: "continue"
|
|
664
|
-
});
|
|
665
|
-
if (p__namespace.isCancel(proceed2)) {
|
|
666
|
-
p__namespace.cancel("Operation cancelled.");
|
|
667
|
-
process.exit(0);
|
|
668
|
-
}
|
|
669
|
-
if (proceed2 === "continue") {
|
|
670
|
-
return defaultOptions2;
|
|
671
|
-
}
|
|
672
|
-
return promptForCustomization(
|
|
673
|
-
customTemplate.baseTemplate,
|
|
674
|
-
projectName,
|
|
675
|
-
projectType,
|
|
676
|
-
customTemplate.integrations,
|
|
677
|
-
inheritedSettings
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
const template = selection;
|
|
681
|
-
const baseTemplate = index.getBaseTemplate(template);
|
|
682
|
-
let integrations;
|
|
612
|
+
const template = templateSelection;
|
|
613
|
+
const baseTemplate = workspace.getBaseTemplate(template);
|
|
614
|
+
let features;
|
|
683
615
|
if (baseTemplate === "r3f") {
|
|
684
|
-
|
|
616
|
+
features = await promptForR3fIntegrations(presets);
|
|
685
617
|
}
|
|
686
618
|
const defaultOptions = getDefaultOptions(
|
|
687
619
|
template,
|
|
688
620
|
projectName,
|
|
689
621
|
projectType,
|
|
690
622
|
presets?.bundler,
|
|
691
|
-
|
|
623
|
+
features,
|
|
692
624
|
inheritedSettings ?? presetsToInheritedSettings(presets)
|
|
693
625
|
);
|
|
626
|
+
if (presets?.ide && !inheritedSettings) {
|
|
627
|
+
defaultOptions.ide = presets.ide;
|
|
628
|
+
}
|
|
694
629
|
const configTitle = inheritedSettings ? "Template Configuration (using workspace settings)" : "Template Configuration";
|
|
695
630
|
p__namespace.note(formatConfigSummary(defaultOptions, inheritedSettings), configTitle);
|
|
696
|
-
|
|
697
|
-
message: "Proceed with these settings?",
|
|
698
|
-
options: [
|
|
699
|
-
{ value: "continue", label: "Yes, continue" },
|
|
700
|
-
{ value: "customize", label: "No, customize settings" }
|
|
701
|
-
],
|
|
702
|
-
initialValue: "continue"
|
|
703
|
-
});
|
|
704
|
-
if (p__namespace.isCancel(proceed)) {
|
|
705
|
-
p__namespace.cancel("Operation cancelled.");
|
|
706
|
-
process.exit(0);
|
|
707
|
-
}
|
|
708
|
-
if (proceed === "continue") {
|
|
631
|
+
if (await promptForProceed()) {
|
|
709
632
|
return defaultOptions;
|
|
710
633
|
}
|
|
711
634
|
return promptForCustomization(
|
|
712
635
|
template,
|
|
713
636
|
projectName,
|
|
714
637
|
projectType,
|
|
715
|
-
|
|
638
|
+
features,
|
|
716
639
|
inheritedSettings,
|
|
717
640
|
presets
|
|
718
641
|
);
|
|
719
642
|
}
|
|
720
643
|
|
|
721
|
-
async function
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
644
|
+
async function promptForAiAgentPlatforms(isNonInteractive) {
|
|
645
|
+
const savedPlatforms = getAiPlatforms();
|
|
646
|
+
if (isNonInteractive) {
|
|
647
|
+
return savedPlatforms ?? workspace.ALL_AI_PLATFORMS;
|
|
648
|
+
}
|
|
649
|
+
if (savedPlatforms && savedPlatforms.length > 0) {
|
|
650
|
+
const savedLabels = savedPlatforms.map((platform) => workspace.AI_PLATFORM_LABELS[platform]).join(", ");
|
|
651
|
+
const useDefault = await p__namespace.confirm({
|
|
652
|
+
message: `Add AI rules? ${color__default.dim(`(${savedLabels})`)}`,
|
|
653
|
+
initialValue: true
|
|
654
|
+
});
|
|
655
|
+
if (p__namespace.isCancel(useDefault)) {
|
|
656
|
+
return [];
|
|
730
657
|
}
|
|
731
|
-
if (
|
|
732
|
-
|
|
658
|
+
if (useDefault) {
|
|
659
|
+
return savedPlatforms;
|
|
733
660
|
}
|
|
734
|
-
} catch {
|
|
735
661
|
}
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
async function detectStandaloneConfigStrategy(root) {
|
|
748
|
-
const hasStealthConfig = await Promise.all([
|
|
749
|
-
fileExists$1(path.join(root, ".config/tsconfig.app.json")),
|
|
750
|
-
fileExists$1(path.join(root, ".config/tsconfig.node.json")),
|
|
751
|
-
fileExists$1(path.join(root, ".config/prettier.json")),
|
|
752
|
-
fileExists$1(path.join(root, ".config/oxlint.json"))
|
|
753
|
-
]).then((matches) => matches.some(Boolean));
|
|
754
|
-
return hasStealthConfig ? "stealth" : "root";
|
|
755
|
-
}
|
|
756
|
-
async function generateExpectedFiles(config) {
|
|
757
|
-
const { name, linter, formatter, packageManager, isMonorepo, configStrategy } = config;
|
|
758
|
-
const versions = linter === "biome" || formatter === "biome" ? await index.resolveMonorepoRootPackageVersions({ linter, formatter }) : {};
|
|
759
|
-
const aiFilesMap = {};
|
|
760
|
-
index.generateAiFiles(aiFilesMap, {
|
|
761
|
-
name,
|
|
762
|
-
packageManager,
|
|
763
|
-
linter,
|
|
764
|
-
formatter,
|
|
765
|
-
isMonorepo,
|
|
766
|
-
configStrategy,
|
|
767
|
-
platforms: index.ALL_AI_PLATFORMS
|
|
662
|
+
const selected = await p__namespace.multiselect({
|
|
663
|
+
message: "Add AI rules?",
|
|
664
|
+
options: workspace.ALL_AI_PLATFORMS.map((platform) => ({
|
|
665
|
+
value: platform,
|
|
666
|
+
label: workspace.AI_PLATFORM_LABELS[platform],
|
|
667
|
+
hint: workspace.AI_PLATFORM_HINTS[platform]
|
|
668
|
+
})),
|
|
669
|
+
initialValues: ["agents"],
|
|
670
|
+
required: false
|
|
768
671
|
});
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
} else if (formatter === "prettier") {
|
|
782
|
-
index.generatePrettierConfigPackage(configPackages);
|
|
672
|
+
if (p__namespace.isCancel(selected)) {
|
|
673
|
+
return [];
|
|
674
|
+
}
|
|
675
|
+
return selected;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
async function checkAnyExists(paths) {
|
|
679
|
+
for (const path of paths) {
|
|
680
|
+
try {
|
|
681
|
+
await promises.access(path, promises.constants.F_OK);
|
|
682
|
+
return true;
|
|
683
|
+
} catch {
|
|
783
684
|
}
|
|
784
685
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
};
|
|
795
|
-
if (linter === "biome" || formatter === "biome") {
|
|
796
|
-
const biomeVersion = index.getResolvedPackageVersion(versions, "@biomejs/biome");
|
|
797
|
-
const biomeConfig = {
|
|
798
|
-
$schema: `https://biomejs.dev/schemas/${biomeVersion}/schema.json`,
|
|
799
|
-
vcs: {
|
|
800
|
-
enabled: true,
|
|
801
|
-
clientKind: "git",
|
|
802
|
-
useIgnoreFile: true
|
|
803
|
-
},
|
|
804
|
-
linter: {
|
|
805
|
-
enabled: linter === "biome",
|
|
806
|
-
rules: {
|
|
807
|
-
recommended: true
|
|
808
|
-
}
|
|
809
|
-
},
|
|
810
|
-
formatter: {
|
|
811
|
-
enabled: formatter === "biome"
|
|
812
|
-
}
|
|
813
|
-
};
|
|
814
|
-
rootConfig["biome.json"] = {
|
|
815
|
-
type: "text",
|
|
816
|
-
content: JSON.stringify(biomeConfig, null, 2)
|
|
817
|
-
};
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
async function validateWorkspace(monorepoRoot) {
|
|
689
|
+
const errors = [];
|
|
690
|
+
const tsConfigPath = path.join(monorepoRoot, ".config/typescript/package.json");
|
|
691
|
+
try {
|
|
692
|
+
await promises.access(tsConfigPath, promises.constants.F_OK);
|
|
693
|
+
} catch {
|
|
694
|
+
errors.push("Missing .config/typescript package");
|
|
818
695
|
}
|
|
819
|
-
|
|
820
|
-
"
|
|
821
|
-
|
|
822
|
-
"config
|
|
823
|
-
"
|
|
824
|
-
|
|
825
|
-
|
|
696
|
+
const linterPaths = [
|
|
697
|
+
path.join(monorepoRoot, ".config/oxlint/package.json"),
|
|
698
|
+
path.join(monorepoRoot, ".config/eslint/package.json"),
|
|
699
|
+
path.join(monorepoRoot, "eslint.config.js"),
|
|
700
|
+
path.join(monorepoRoot, "biome.json")
|
|
701
|
+
];
|
|
702
|
+
const hasLinter = await checkAnyExists(linterPaths);
|
|
703
|
+
if (!hasLinter) {
|
|
704
|
+
errors.push(
|
|
705
|
+
"Missing linter config (.config/oxlint, .config/eslint, eslint.config.js, or biome.json)"
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
const formatterPaths = [
|
|
709
|
+
path.join(monorepoRoot, ".config/oxfmt/package.json"),
|
|
710
|
+
path.join(monorepoRoot, ".config/prettier/package.json"),
|
|
711
|
+
path.join(monorepoRoot, ".prettierrc.json"),
|
|
712
|
+
path.join(monorepoRoot, "biome.json")
|
|
713
|
+
];
|
|
714
|
+
const hasFormatter = await checkAnyExists(formatterPaths);
|
|
715
|
+
if (!hasFormatter) {
|
|
716
|
+
errors.push(
|
|
717
|
+
"Missing formatter config (.config/oxfmt, .config/prettier, .prettierrc.json, or biome.json)"
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
return { valid: errors.length === 0, errors };
|
|
826
721
|
}
|
|
722
|
+
|
|
827
723
|
async function fileExists$1(path) {
|
|
828
724
|
try {
|
|
829
|
-
await promises.access(path,
|
|
725
|
+
await promises$1.access(path, node_fs.constants.F_OK);
|
|
830
726
|
return true;
|
|
831
727
|
} catch {
|
|
832
728
|
return false;
|
|
833
729
|
}
|
|
834
730
|
}
|
|
835
|
-
async function compareWithDisk(expected, root) {
|
|
836
|
-
const categoryLabels = {
|
|
837
|
-
"ai-files": "AI Files",
|
|
838
|
-
vscode: "VS Code",
|
|
839
|
-
"config-packages": "Config Packages",
|
|
840
|
-
"workspace-config": "Workspace Config",
|
|
841
|
-
"root-config": "Root Config"
|
|
842
|
-
};
|
|
843
|
-
const categories = [];
|
|
844
|
-
for (const [category, files] of Object.entries(expected)) {
|
|
845
|
-
const changes = [];
|
|
846
|
-
for (const [filePath, file] of Object.entries(files)) {
|
|
847
|
-
if (file.type !== "text") continue;
|
|
848
|
-
const fullPath = path.join(root, filePath);
|
|
849
|
-
const newContent = file.content;
|
|
850
|
-
if (await fileExists$1(fullPath)) {
|
|
851
|
-
const currentContent = await promises.readFile(fullPath, "utf-8");
|
|
852
|
-
if (currentContent === newContent) {
|
|
853
|
-
changes.push({
|
|
854
|
-
path: filePath,
|
|
855
|
-
status: "unchanged",
|
|
856
|
-
currentContent,
|
|
857
|
-
newContent
|
|
858
|
-
});
|
|
859
|
-
} else {
|
|
860
|
-
changes.push({
|
|
861
|
-
path: filePath,
|
|
862
|
-
status: "modified",
|
|
863
|
-
currentContent,
|
|
864
|
-
newContent
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
} else {
|
|
868
|
-
changes.push({
|
|
869
|
-
path: filePath,
|
|
870
|
-
status: "added",
|
|
871
|
-
newContent
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
if (changes.length === 0) continue;
|
|
876
|
-
const hasUserModifications = changes.some((c) => c.status === "modified");
|
|
877
|
-
categories.push({
|
|
878
|
-
category,
|
|
879
|
-
label: categoryLabels[category],
|
|
880
|
-
changes,
|
|
881
|
-
hasUserModifications
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
return categories;
|
|
885
|
-
}
|
|
886
|
-
async function getWorkspaceConfigUpdates(root) {
|
|
887
|
-
const workspacePath = path.join(root, "pnpm-workspace.yaml");
|
|
888
|
-
const changes = [];
|
|
889
|
-
let currentContent = "";
|
|
890
|
-
let exists = false;
|
|
891
|
-
try {
|
|
892
|
-
currentContent = await promises.readFile(workspacePath, "utf-8");
|
|
893
|
-
exists = true;
|
|
894
|
-
} catch {
|
|
895
|
-
}
|
|
896
|
-
if (!exists) {
|
|
897
|
-
const newContent = `manage-package-manager-versions: true
|
|
898
|
-
|
|
899
|
-
packages:
|
|
900
|
-
- ".config/*"
|
|
901
|
-
- "apps/*"
|
|
902
|
-
- "packages/*"
|
|
903
|
-
|
|
904
|
-
onlyBuiltDependencies:
|
|
905
|
-
- esbuild
|
|
906
|
-
`;
|
|
907
|
-
changes.push({
|
|
908
|
-
path: "pnpm-workspace.yaml",
|
|
909
|
-
status: "added",
|
|
910
|
-
newContent
|
|
911
|
-
});
|
|
912
|
-
return changes;
|
|
913
|
-
}
|
|
914
|
-
let updatedContent = currentContent;
|
|
915
|
-
let needsUpdate = false;
|
|
916
|
-
if (!currentContent.includes("manage-package-manager-versions")) {
|
|
917
|
-
updatedContent = `manage-package-manager-versions: true
|
|
918
|
-
|
|
919
|
-
${updatedContent}`;
|
|
920
|
-
needsUpdate = true;
|
|
921
|
-
}
|
|
922
|
-
if (!currentContent.includes("onlyBuiltDependencies")) {
|
|
923
|
-
updatedContent = `${updatedContent.trimEnd()}
|
|
924
|
-
|
|
925
|
-
onlyBuiltDependencies:
|
|
926
|
-
- esbuild
|
|
927
|
-
`;
|
|
928
|
-
needsUpdate = true;
|
|
929
|
-
}
|
|
930
|
-
if (!currentContent.includes(".config/*") && !currentContent.includes('".config/*"')) {
|
|
931
|
-
const lines = updatedContent.split("\n");
|
|
932
|
-
const packagesIndex = lines.findIndex((line) => line.trim().startsWith("packages:"));
|
|
933
|
-
if (packagesIndex !== -1) {
|
|
934
|
-
lines.splice(packagesIndex + 1, 0, ' - ".config/*"');
|
|
935
|
-
updatedContent = lines.join("\n");
|
|
936
|
-
needsUpdate = true;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
if (needsUpdate) {
|
|
940
|
-
changes.push({
|
|
941
|
-
path: "pnpm-workspace.yaml",
|
|
942
|
-
status: "modified",
|
|
943
|
-
currentContent,
|
|
944
|
-
newContent: updatedContent
|
|
945
|
-
});
|
|
946
|
-
} else {
|
|
947
|
-
changes.push({
|
|
948
|
-
path: "pnpm-workspace.yaml",
|
|
949
|
-
status: "unchanged",
|
|
950
|
-
currentContent,
|
|
951
|
-
newContent: currentContent
|
|
952
|
-
});
|
|
953
|
-
}
|
|
954
|
-
return changes;
|
|
955
|
-
}
|
|
956
|
-
async function applyUpdates(changes, root) {
|
|
957
|
-
for (const change of changes) {
|
|
958
|
-
if (change.status === "unchanged") continue;
|
|
959
|
-
const fullPath = path.join(root, change.path);
|
|
960
|
-
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
961
|
-
await promises.writeFile(fullPath, change.newContent);
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
function formatFileChange(change) {
|
|
965
|
-
const icon = change.status === "added" ? "+" : change.status === "modified" ? "~" : "=";
|
|
966
|
-
return ` ${icon} ${change.path}`;
|
|
967
|
-
}
|
|
968
|
-
const LINTER_DEPS = {
|
|
969
|
-
oxlint: "oxlint",
|
|
970
|
-
eslint: "eslint",
|
|
971
|
-
biome: "@biomejs/biome"
|
|
972
|
-
};
|
|
973
|
-
const FORMATTER_DEPS = {
|
|
974
|
-
oxfmt: "oxfmt",
|
|
975
|
-
prettier: "prettier",
|
|
976
|
-
biome: "@biomejs/biome"
|
|
977
|
-
};
|
|
978
|
-
const LINTER_CONFIG_PACKAGES = {
|
|
979
|
-
oxlint: "@config/oxlint",
|
|
980
|
-
eslint: "@config/eslint",
|
|
981
|
-
biome: null
|
|
982
|
-
// biome uses root biome.json
|
|
983
|
-
};
|
|
984
|
-
const FORMATTER_CONFIG_PACKAGES = {
|
|
985
|
-
oxfmt: "@config/oxfmt",
|
|
986
|
-
prettier: "@config/prettier",
|
|
987
|
-
biome: null
|
|
988
|
-
// biome uses root biome.json
|
|
989
|
-
};
|
|
990
|
-
function needsMigration(current, target) {
|
|
991
|
-
const linterChange = target.linter && target.linter !== current.linter;
|
|
992
|
-
const formatterChange = target.formatter && target.formatter !== current.formatter;
|
|
993
|
-
return linterChange || formatterChange || false;
|
|
994
|
-
}
|
|
995
|
-
async function getMigrationPlan(current, target, root) {
|
|
996
|
-
const toLinter = target.linter ?? current.linter;
|
|
997
|
-
const toFormatter = target.formatter ?? current.formatter;
|
|
998
|
-
const targetVersions = toLinter === "biome" || toFormatter === "biome" ? await index.resolveMonorepoRootPackageVersions({
|
|
999
|
-
linter: toLinter,
|
|
1000
|
-
formatter: toFormatter
|
|
1001
|
-
}) : {};
|
|
1002
|
-
const biomeSchemaUrl = toLinter === "biome" || toFormatter === "biome" ? `https://biomejs.dev/schemas/${index.getResolvedPackageVersion(
|
|
1003
|
-
targetVersions,
|
|
1004
|
-
"@biomejs/biome"
|
|
1005
|
-
)}/schema.json` : "";
|
|
1006
|
-
const changes = [];
|
|
1007
|
-
if (toLinter !== current.linter) {
|
|
1008
|
-
if (current.linter !== "biome") {
|
|
1009
|
-
changes.push({
|
|
1010
|
-
type: "remove-dir",
|
|
1011
|
-
path: `.config/${current.linter}`,
|
|
1012
|
-
description: `Remove @config/${current.linter} package`
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
if (toLinter !== "biome") {
|
|
1016
|
-
const files = {};
|
|
1017
|
-
if (toLinter === "oxlint") {
|
|
1018
|
-
index.generateOxlintConfigPackage(files);
|
|
1019
|
-
} else if (toLinter === "eslint") {
|
|
1020
|
-
index.generateEslintConfigPackage(files);
|
|
1021
|
-
}
|
|
1022
|
-
for (const [path, file] of Object.entries(files)) {
|
|
1023
|
-
if (file.type === "text") {
|
|
1024
|
-
changes.push({
|
|
1025
|
-
type: "add-file",
|
|
1026
|
-
path,
|
|
1027
|
-
description: `Add ${path}`,
|
|
1028
|
-
content: file.content
|
|
1029
|
-
});
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
if (toLinter === "biome" && toFormatter === "biome") {
|
|
1034
|
-
changes.push({
|
|
1035
|
-
type: "add-file",
|
|
1036
|
-
path: "biome.json",
|
|
1037
|
-
description: "Add biome.json config",
|
|
1038
|
-
content: JSON.stringify(
|
|
1039
|
-
{
|
|
1040
|
-
$schema: biomeSchemaUrl,
|
|
1041
|
-
vcs: { enabled: true, clientKind: "git", useIgnoreFile: true },
|
|
1042
|
-
linter: { enabled: true, rules: { recommended: true } },
|
|
1043
|
-
formatter: { enabled: true }
|
|
1044
|
-
},
|
|
1045
|
-
null,
|
|
1046
|
-
2
|
|
1047
|
-
)
|
|
1048
|
-
});
|
|
1049
|
-
} else if (toLinter === "biome" && toFormatter !== "biome") {
|
|
1050
|
-
changes.push({
|
|
1051
|
-
type: "add-file",
|
|
1052
|
-
path: "biome.json",
|
|
1053
|
-
description: "Add biome.json config (linter only)",
|
|
1054
|
-
content: JSON.stringify(
|
|
1055
|
-
{
|
|
1056
|
-
$schema: biomeSchemaUrl,
|
|
1057
|
-
vcs: { enabled: true, clientKind: "git", useIgnoreFile: true },
|
|
1058
|
-
linter: { enabled: true, rules: { recommended: true } },
|
|
1059
|
-
formatter: { enabled: false }
|
|
1060
|
-
},
|
|
1061
|
-
null,
|
|
1062
|
-
2
|
|
1063
|
-
)
|
|
1064
|
-
});
|
|
1065
|
-
}
|
|
1066
|
-
if (current.linter === "biome" && toLinter !== "biome" && current.formatter !== "biome" && toFormatter !== "biome") {
|
|
1067
|
-
changes.push({
|
|
1068
|
-
type: "remove-file",
|
|
1069
|
-
path: "biome.json",
|
|
1070
|
-
description: "Remove biome.json"
|
|
1071
|
-
});
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
if (toFormatter !== current.formatter) {
|
|
1075
|
-
const formatterSameAsLinter = current.formatter === current.linter;
|
|
1076
|
-
if (current.formatter !== "biome" && !formatterSameAsLinter) {
|
|
1077
|
-
changes.push({
|
|
1078
|
-
type: "remove-dir",
|
|
1079
|
-
path: `.config/${current.formatter}`,
|
|
1080
|
-
description: `Remove @config/${current.formatter} package`
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1083
|
-
const newFormatterSameAsLinter = toFormatter === toLinter;
|
|
1084
|
-
if (toFormatter !== "biome" && !newFormatterSameAsLinter) {
|
|
1085
|
-
const files = {};
|
|
1086
|
-
if (toFormatter === "oxfmt") {
|
|
1087
|
-
index.generateOxfmtConfigPackage(files);
|
|
1088
|
-
} else if (toFormatter === "prettier") {
|
|
1089
|
-
index.generatePrettierConfigPackage(files);
|
|
1090
|
-
}
|
|
1091
|
-
for (const [path, file] of Object.entries(files)) {
|
|
1092
|
-
if (file.type === "text") {
|
|
1093
|
-
changes.push({
|
|
1094
|
-
type: "add-file",
|
|
1095
|
-
path,
|
|
1096
|
-
description: `Add ${path}`,
|
|
1097
|
-
content: file.content
|
|
1098
|
-
});
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
if (toFormatter === "biome" && toLinter !== "biome") {
|
|
1103
|
-
changes.push({
|
|
1104
|
-
type: "add-file",
|
|
1105
|
-
path: "biome.json",
|
|
1106
|
-
description: "Add biome.json config (formatter only)",
|
|
1107
|
-
content: JSON.stringify(
|
|
1108
|
-
{
|
|
1109
|
-
$schema: biomeSchemaUrl,
|
|
1110
|
-
vcs: { enabled: true, clientKind: "git", useIgnoreFile: true },
|
|
1111
|
-
linter: { enabled: false },
|
|
1112
|
-
formatter: { enabled: true }
|
|
1113
|
-
},
|
|
1114
|
-
null,
|
|
1115
|
-
2
|
|
1116
|
-
)
|
|
1117
|
-
});
|
|
1118
|
-
}
|
|
1119
|
-
if (current.formatter === "biome" && toFormatter !== "biome" && current.linter !== "biome" && toLinter !== "biome") {
|
|
1120
|
-
changes.push({
|
|
1121
|
-
type: "remove-file",
|
|
1122
|
-
path: "biome.json",
|
|
1123
|
-
description: "Remove biome.json"
|
|
1124
|
-
});
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
changes.push({
|
|
1128
|
-
type: "update-package-json",
|
|
1129
|
-
path: "package.json",
|
|
1130
|
-
description: "Update root package.json (devDependencies, scripts)"
|
|
1131
|
-
});
|
|
1132
|
-
const subPackageUpdates = await getSubPackageUpdates(root, current, toLinter, toFormatter);
|
|
1133
|
-
return {
|
|
1134
|
-
fromLinter: current.linter,
|
|
1135
|
-
toLinter,
|
|
1136
|
-
fromFormatter: current.formatter,
|
|
1137
|
-
toFormatter,
|
|
1138
|
-
changes,
|
|
1139
|
-
subPackageUpdates
|
|
1140
|
-
};
|
|
1141
|
-
}
|
|
1142
|
-
async function getSubPackageUpdates(root, current, toLinter, toFormatter) {
|
|
1143
|
-
const updates = [];
|
|
1144
|
-
const workspacePath = path.join(root, "pnpm-workspace.yaml");
|
|
1145
|
-
let workspaceContent;
|
|
1146
|
-
try {
|
|
1147
|
-
workspaceContent = await promises.readFile(workspacePath, "utf-8");
|
|
1148
|
-
} catch {
|
|
1149
|
-
return updates;
|
|
1150
|
-
}
|
|
1151
|
-
const packageGlobs = index.parseWorkspaceYamlContent(workspaceContent);
|
|
1152
|
-
for (const glob of packageGlobs) {
|
|
1153
|
-
if (glob.includes(".config")) continue;
|
|
1154
|
-
const baseDir = glob.replace(/\/\*$/, "").replace(/^["']|["']$/g, "");
|
|
1155
|
-
const basePath = path.join(root, baseDir);
|
|
1156
|
-
try {
|
|
1157
|
-
const entries = await promises.readdir(basePath, { withFileTypes: true });
|
|
1158
|
-
for (const entry of entries) {
|
|
1159
|
-
if (!entry.isDirectory()) continue;
|
|
1160
|
-
const pkgJsonPath = path.join(basePath, entry.name, "package.json");
|
|
1161
|
-
try {
|
|
1162
|
-
const content = await promises.readFile(pkgJsonPath, "utf-8");
|
|
1163
|
-
const pkg = JSON.parse(content);
|
|
1164
|
-
const devDeps = pkg.devDependencies ?? {};
|
|
1165
|
-
const remove = [];
|
|
1166
|
-
const add = [];
|
|
1167
|
-
const oldLinterPkg = LINTER_CONFIG_PACKAGES[current.linter];
|
|
1168
|
-
const newLinterPkg = LINTER_CONFIG_PACKAGES[toLinter];
|
|
1169
|
-
if (oldLinterPkg && oldLinterPkg !== newLinterPkg && devDeps[oldLinterPkg]) {
|
|
1170
|
-
remove.push(oldLinterPkg);
|
|
1171
|
-
}
|
|
1172
|
-
if (newLinterPkg && newLinterPkg !== oldLinterPkg && oldLinterPkg && devDeps[oldLinterPkg]) {
|
|
1173
|
-
add.push(newLinterPkg);
|
|
1174
|
-
}
|
|
1175
|
-
if (current.formatter !== current.linter) {
|
|
1176
|
-
const oldFormatterPkg = FORMATTER_CONFIG_PACKAGES[current.formatter];
|
|
1177
|
-
const newFormatterPkg = FORMATTER_CONFIG_PACKAGES[toFormatter];
|
|
1178
|
-
if (oldFormatterPkg && oldFormatterPkg !== newFormatterPkg && devDeps[oldFormatterPkg]) {
|
|
1179
|
-
remove.push(oldFormatterPkg);
|
|
1180
|
-
}
|
|
1181
|
-
if (newFormatterPkg && newFormatterPkg !== oldFormatterPkg && oldFormatterPkg && devDeps[oldFormatterPkg]) {
|
|
1182
|
-
add.push(newFormatterPkg);
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
if (remove.length > 0 || add.length > 0) {
|
|
1186
|
-
updates.push({
|
|
1187
|
-
path: path.join(baseDir, entry.name, "package.json"),
|
|
1188
|
-
remove,
|
|
1189
|
-
add
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
} catch {
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
} catch {
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
return updates;
|
|
1199
|
-
}
|
|
1200
|
-
async function applyMigration(plan, root) {
|
|
1201
|
-
for (const change of plan.changes) {
|
|
1202
|
-
if (change.type === "remove-dir") {
|
|
1203
|
-
const fullPath = path.join(root, change.path);
|
|
1204
|
-
try {
|
|
1205
|
-
await promises.rm(fullPath, { recursive: true });
|
|
1206
|
-
} catch {
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
for (const change of plan.changes) {
|
|
1211
|
-
if (change.type === "remove-file") {
|
|
1212
|
-
const fullPath = path.join(root, change.path);
|
|
1213
|
-
try {
|
|
1214
|
-
await promises.rm(fullPath);
|
|
1215
|
-
} catch {
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
for (const change of plan.changes) {
|
|
1220
|
-
if (change.type === "add-file" && change.content) {
|
|
1221
|
-
const fullPath = path.join(root, change.path);
|
|
1222
|
-
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1223
|
-
await promises.writeFile(fullPath, change.content);
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
await updateRootPackageJson(root, plan);
|
|
1227
|
-
for (const update of plan.subPackageUpdates) {
|
|
1228
|
-
await updateSubPackageJson(root, update);
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
async function updateRootPackageJson(root, plan) {
|
|
1232
|
-
const pkgPath = path.join(root, "package.json");
|
|
1233
|
-
const content = await promises.readFile(pkgPath, "utf-8");
|
|
1234
|
-
const pkg = JSON.parse(content);
|
|
1235
|
-
const devDeps = pkg.devDependencies ?? {};
|
|
1236
|
-
const oldLinterDep = LINTER_DEPS[plan.fromLinter];
|
|
1237
|
-
delete devDeps[oldLinterDep];
|
|
1238
|
-
if (plan.fromFormatter !== plan.fromLinter) {
|
|
1239
|
-
const oldFormatterDep = FORMATTER_DEPS[plan.fromFormatter];
|
|
1240
|
-
delete devDeps[oldFormatterDep];
|
|
1241
|
-
}
|
|
1242
|
-
const resolvedVersions = await index.resolveMonorepoRootPackageVersions({
|
|
1243
|
-
linter: plan.toLinter,
|
|
1244
|
-
formatter: plan.toFormatter
|
|
1245
|
-
});
|
|
1246
|
-
const newLinterDep = LINTER_DEPS[plan.toLinter];
|
|
1247
|
-
devDeps[newLinterDep] = index.formatResolvedPackageVersion(resolvedVersions, newLinterDep);
|
|
1248
|
-
if (plan.toFormatter !== plan.toLinter) {
|
|
1249
|
-
const newFormatterDep = FORMATTER_DEPS[plan.toFormatter];
|
|
1250
|
-
devDeps[newFormatterDep] = index.formatResolvedPackageVersion(resolvedVersions, newFormatterDep);
|
|
1251
|
-
}
|
|
1252
|
-
pkg.devDependencies = Object.fromEntries(
|
|
1253
|
-
Object.entries(devDeps).sort(([a], [b]) => a.localeCompare(b))
|
|
1254
|
-
);
|
|
1255
|
-
const scripts = pkg.scripts ?? {};
|
|
1256
|
-
if (plan.toLinter === "oxlint") {
|
|
1257
|
-
scripts.lint = "oxlint .";
|
|
1258
|
-
} else if (plan.toLinter === "eslint") {
|
|
1259
|
-
scripts.lint = "eslint .";
|
|
1260
|
-
} else if (plan.toLinter === "biome") {
|
|
1261
|
-
scripts.lint = "biome check .";
|
|
1262
|
-
}
|
|
1263
|
-
if (plan.toFormatter === "oxfmt") {
|
|
1264
|
-
scripts.format = "oxfmt .";
|
|
1265
|
-
} else if (plan.toFormatter === "prettier") {
|
|
1266
|
-
scripts.format = "prettier --write .";
|
|
1267
|
-
} else if (plan.toFormatter === "biome") {
|
|
1268
|
-
scripts.format = "biome format . --write";
|
|
1269
|
-
}
|
|
1270
|
-
pkg.scripts = scripts;
|
|
1271
|
-
await promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1272
|
-
}
|
|
1273
|
-
async function updateSubPackageJson(root, update) {
|
|
1274
|
-
const pkgPath = path.join(root, update.path);
|
|
1275
|
-
const content = await promises.readFile(pkgPath, "utf-8");
|
|
1276
|
-
const pkg = JSON.parse(content);
|
|
1277
|
-
const devDeps = pkg.devDependencies ?? {};
|
|
1278
|
-
for (const dep of update.remove) {
|
|
1279
|
-
delete devDeps[dep];
|
|
1280
|
-
}
|
|
1281
|
-
for (const dep of update.add) {
|
|
1282
|
-
devDeps[dep] = "workspace:*";
|
|
1283
|
-
}
|
|
1284
|
-
pkg.devDependencies = Object.fromEntries(
|
|
1285
|
-
Object.entries(devDeps).sort(([a], [b]) => a.localeCompare(b))
|
|
1286
|
-
);
|
|
1287
|
-
await promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1288
|
-
}
|
|
1289
|
-
function formatMigrationChange(change) {
|
|
1290
|
-
const icon = change.type === "remove-dir" || change.type === "remove-file" ? "-" : change.type === "add-file" ? "+" : "~";
|
|
1291
|
-
return ` ${icon} ${change.description}`;
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
async function checkAnyExists(paths) {
|
|
1295
|
-
for (const path of paths) {
|
|
1296
|
-
try {
|
|
1297
|
-
await promises.access(path, promises.constants.F_OK);
|
|
1298
|
-
return true;
|
|
1299
|
-
} catch {
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
return false;
|
|
1303
|
-
}
|
|
1304
|
-
async function validateWorkspace(monorepoRoot) {
|
|
1305
|
-
const errors = [];
|
|
1306
|
-
const tsConfigPath = path.join(monorepoRoot, ".config/typescript/package.json");
|
|
1307
|
-
try {
|
|
1308
|
-
await promises.access(tsConfigPath, promises.constants.F_OK);
|
|
1309
|
-
} catch {
|
|
1310
|
-
errors.push("Missing .config/typescript package");
|
|
1311
|
-
}
|
|
1312
|
-
const linterPaths = [
|
|
1313
|
-
path.join(monorepoRoot, ".config/oxlint/package.json"),
|
|
1314
|
-
path.join(monorepoRoot, ".config/eslint/package.json"),
|
|
1315
|
-
path.join(monorepoRoot, "eslint.config.js"),
|
|
1316
|
-
path.join(monorepoRoot, "biome.json")
|
|
1317
|
-
];
|
|
1318
|
-
const hasLinter = await checkAnyExists(linterPaths);
|
|
1319
|
-
if (!hasLinter) {
|
|
1320
|
-
errors.push(
|
|
1321
|
-
"Missing linter config (.config/oxlint, .config/eslint, eslint.config.js, or biome.json)"
|
|
1322
|
-
);
|
|
1323
|
-
}
|
|
1324
|
-
const formatterPaths = [
|
|
1325
|
-
path.join(monorepoRoot, ".config/oxfmt/package.json"),
|
|
1326
|
-
path.join(monorepoRoot, ".config/prettier/package.json"),
|
|
1327
|
-
path.join(monorepoRoot, ".prettierrc.json"),
|
|
1328
|
-
path.join(monorepoRoot, "biome.json")
|
|
1329
|
-
];
|
|
1330
|
-
const hasFormatter = await checkAnyExists(formatterPaths);
|
|
1331
|
-
if (!hasFormatter) {
|
|
1332
|
-
errors.push(
|
|
1333
|
-
"Missing formatter config (.config/oxfmt, .config/prettier, .prettierrc.json, or biome.json)"
|
|
1334
|
-
);
|
|
1335
|
-
}
|
|
1336
|
-
return { valid: errors.length === 0, errors };
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
|
|
1340
|
-
const pkg = require$1("../package.json");
|
|
1341
|
-
const META_OPTIONS = [
|
|
1342
|
-
"clearConfig",
|
|
1343
|
-
"configPath",
|
|
1344
|
-
"check",
|
|
1345
|
-
"fix",
|
|
1346
|
-
"update",
|
|
1347
|
-
"yes",
|
|
1348
|
-
"workspace",
|
|
1349
|
-
"path",
|
|
1350
|
-
"dir"
|
|
1351
|
-
];
|
|
1352
|
-
function hasConfigOptions(options) {
|
|
1353
|
-
return Object.keys(options).some(
|
|
1354
|
-
(key) => !META_OPTIONS.includes(key)
|
|
1355
|
-
);
|
|
1356
|
-
}
|
|
1357
|
-
async function fileExists(path) {
|
|
1358
|
-
try {
|
|
1359
|
-
await promises$1.access(path, node_fs.constants.F_OK);
|
|
1360
|
-
return true;
|
|
1361
|
-
} catch {
|
|
1362
|
-
return false;
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
async function promptForAiPlatforms(isNonInteractive) {
|
|
1366
|
-
const savedPlatforms = getAiPlatforms();
|
|
1367
|
-
if (isNonInteractive) {
|
|
1368
|
-
return savedPlatforms ?? index.ALL_AI_PLATFORMS;
|
|
1369
|
-
}
|
|
1370
|
-
if (savedPlatforms && savedPlatforms.length > 0) {
|
|
1371
|
-
const savedLabels = savedPlatforms.map((plat) => index.AI_PLATFORM_LABELS[plat]).join(", ");
|
|
1372
|
-
const useDefault = await p__namespace.confirm({
|
|
1373
|
-
message: `Add AI rules? ${color__default.dim(`(${savedLabels})`)}`,
|
|
1374
|
-
initialValue: true
|
|
1375
|
-
});
|
|
1376
|
-
if (p__namespace.isCancel(useDefault)) {
|
|
1377
|
-
return [];
|
|
1378
|
-
}
|
|
1379
|
-
if (useDefault) {
|
|
1380
|
-
return savedPlatforms;
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
const selected = await p__namespace.multiselect({
|
|
1384
|
-
message: "Add AI rules?",
|
|
1385
|
-
options: index.ALL_AI_PLATFORMS.map((platform) => ({
|
|
1386
|
-
value: platform,
|
|
1387
|
-
label: index.AI_PLATFORM_LABELS[platform],
|
|
1388
|
-
hint: index.AI_PLATFORM_HINTS[platform]
|
|
1389
|
-
})),
|
|
1390
|
-
initialValues: ["agents"],
|
|
1391
|
-
required: false
|
|
1392
|
-
});
|
|
1393
|
-
if (p__namespace.isCancel(selected)) {
|
|
1394
|
-
return [];
|
|
1395
|
-
}
|
|
1396
|
-
const platforms = selected;
|
|
1397
|
-
if (platforms.length === 0) {
|
|
1398
|
-
return [];
|
|
1399
|
-
}
|
|
1400
|
-
return platforms;
|
|
1401
|
-
}
|
|
1402
|
-
async function writeGeneratedFiles(basePath, files) {
|
|
1403
|
-
const filePaths = Object.keys(files).sort();
|
|
1404
|
-
for (const filePath of filePaths) {
|
|
1405
|
-
const fullFilePath = node_path.join(basePath, filePath);
|
|
1406
|
-
await promises$1.mkdir(node_path.dirname(fullFilePath), { recursive: true });
|
|
1407
|
-
const file = files[filePath];
|
|
1408
|
-
if (file.type === "text") {
|
|
1409
|
-
await promises$1.writeFile(fullFilePath, file.content);
|
|
1410
|
-
} else {
|
|
1411
|
-
const response = await undici.fetch(file.url);
|
|
1412
|
-
await promises$1.writeFile(fullFilePath, response.body);
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
731
|
function calculateWorkspaceRoot(packagePath) {
|
|
1417
732
|
const segments = packagePath.split(/[/\\]/).filter(Boolean);
|
|
1418
733
|
return segments.map(() => "..").join("/");
|
|
@@ -1438,30 +753,30 @@ async function detectPackageRoot() {
|
|
|
1438
753
|
let currentDir = node_process.cwd();
|
|
1439
754
|
const root = node_path.resolve("/");
|
|
1440
755
|
while (currentDir !== root) {
|
|
1441
|
-
if (await fileExists(node_path.join(currentDir, "package.json"))) {
|
|
756
|
+
if (await fileExists$1(node_path.join(currentDir, "package.json"))) {
|
|
1442
757
|
return currentDir;
|
|
1443
758
|
}
|
|
1444
759
|
currentDir = node_path.dirname(currentDir);
|
|
1445
760
|
}
|
|
1446
|
-
return await fileExists(node_path.join(root, "package.json")) ? root : null;
|
|
761
|
+
return await fileExists$1(node_path.join(root, "package.json")) ? root : null;
|
|
1447
762
|
}
|
|
1448
763
|
async function parseWorkspaceDirectories(monorepoRoot) {
|
|
1449
764
|
try {
|
|
1450
765
|
const workspaceFile = node_path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
1451
766
|
const content = await promises$1.readFile(workspaceFile, "utf-8");
|
|
1452
|
-
return
|
|
767
|
+
return workspace.parseWorkspaceYamlContent(content);
|
|
1453
768
|
} catch {
|
|
1454
769
|
return [];
|
|
1455
770
|
}
|
|
1456
771
|
}
|
|
1457
772
|
async function detectWorkspaceSettings(monorepoRoot) {
|
|
1458
773
|
try {
|
|
1459
|
-
const tooling = await
|
|
774
|
+
const tooling = await workspace.detectTooling(monorepoRoot);
|
|
1460
775
|
const pkgPath = node_path.join(monorepoRoot, "package.json");
|
|
1461
776
|
const content = await promises$1.readFile(pkgPath, "utf-8");
|
|
1462
777
|
const pkgJson = JSON.parse(content);
|
|
1463
|
-
const packageManager =
|
|
1464
|
-
const engine =
|
|
778
|
+
const packageManager = workspace.parsePackageManager(pkgJson.packageManager);
|
|
779
|
+
const engine = workspace.parseEngine(pkgJson.engines);
|
|
1465
780
|
let pnpmManageVersions;
|
|
1466
781
|
try {
|
|
1467
782
|
const workspaceFile = node_path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
@@ -1483,17 +798,17 @@ async function detectWorkspaceSettings(monorepoRoot) {
|
|
|
1483
798
|
async function detectExistingConfigs(monorepoRoot) {
|
|
1484
799
|
const configs = {};
|
|
1485
800
|
const eslintPath = node_path.join(monorepoRoot, "eslint.config.js");
|
|
1486
|
-
if (await fileExists(eslintPath)) {
|
|
801
|
+
if (await fileExists$1(eslintPath)) {
|
|
1487
802
|
configs.linter = "eslint";
|
|
1488
803
|
configs.eslintConfigPath = eslintPath;
|
|
1489
804
|
}
|
|
1490
805
|
const prettierPath = node_path.join(monorepoRoot, ".prettierrc.json");
|
|
1491
|
-
if (await fileExists(prettierPath)) {
|
|
806
|
+
if (await fileExists$1(prettierPath)) {
|
|
1492
807
|
configs.formatter = "prettier";
|
|
1493
808
|
configs.prettierConfigPath = prettierPath;
|
|
1494
809
|
}
|
|
1495
810
|
const biomePath = node_path.join(monorepoRoot, "biome.json");
|
|
1496
|
-
if (await fileExists(biomePath)) {
|
|
811
|
+
if (await fileExists$1(biomePath)) {
|
|
1497
812
|
configs.biomeConfigPath = biomePath;
|
|
1498
813
|
if (!configs.linter) configs.linter = "biome";
|
|
1499
814
|
if (!configs.formatter) configs.formatter = "biome";
|
|
@@ -1515,18 +830,14 @@ async function getMonorepoScope(monorepoRoot) {
|
|
|
1515
830
|
async function getWorkspacePackages(monorepoRoot) {
|
|
1516
831
|
const packagesDir = node_path.join(monorepoRoot, "packages");
|
|
1517
832
|
try {
|
|
1518
|
-
const
|
|
1519
|
-
const entries = await readdir(packagesDir, { withFileTypes: true });
|
|
833
|
+
const entries = await promises$1.readdir(packagesDir, { withFileTypes: true });
|
|
1520
834
|
const names = [];
|
|
1521
835
|
for (const entry of entries) {
|
|
1522
836
|
if (!entry.isDirectory()) continue;
|
|
1523
837
|
try {
|
|
1524
|
-
const content = await promises$1.readFile(
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
);
|
|
1528
|
-
const pkg2 = JSON.parse(content);
|
|
1529
|
-
if (pkg2.name) names.push(pkg2.name);
|
|
838
|
+
const content = await promises$1.readFile(node_path.join(packagesDir, entry.name, "package.json"), "utf-8");
|
|
839
|
+
const pkg = JSON.parse(content);
|
|
840
|
+
if (pkg.name) names.push(pkg.name);
|
|
1530
841
|
} catch {
|
|
1531
842
|
}
|
|
1532
843
|
}
|
|
@@ -1542,27 +853,48 @@ async function ensureConfigInWorkspace(monorepoRoot) {
|
|
|
1542
853
|
content = await promises$1.readFile(workspacePath, "utf-8");
|
|
1543
854
|
} catch {
|
|
1544
855
|
content = `packages:
|
|
1545
|
-
-
|
|
1546
|
-
-
|
|
856
|
+
- '.config/*'
|
|
857
|
+
- 'packages/*'
|
|
1547
858
|
`;
|
|
1548
859
|
await promises$1.writeFile(workspacePath, content);
|
|
1549
860
|
return;
|
|
1550
861
|
}
|
|
1551
|
-
if (content.includes(".config/*")
|
|
862
|
+
if (content.includes(".config/*")) {
|
|
1552
863
|
return;
|
|
1553
864
|
}
|
|
1554
865
|
const lines = content.split("\n");
|
|
1555
866
|
const packagesIndex = lines.findIndex((line) => line.trim().startsWith("packages:"));
|
|
1556
867
|
if (packagesIndex === -1) {
|
|
1557
868
|
content = `packages:
|
|
1558
|
-
-
|
|
869
|
+
- '.config/*'
|
|
1559
870
|
${content}`;
|
|
1560
871
|
} else {
|
|
1561
|
-
lines.splice(packagesIndex + 1, 0,
|
|
872
|
+
lines.splice(packagesIndex + 1, 0, " - '.config/*'");
|
|
1562
873
|
content = lines.join("\n");
|
|
1563
874
|
}
|
|
1564
875
|
await promises$1.writeFile(workspacePath, content);
|
|
1565
876
|
}
|
|
877
|
+
|
|
878
|
+
async function handleCheckCommand() {
|
|
879
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
880
|
+
if (!monorepoRoot) {
|
|
881
|
+
console.log(color__default.red("\u2717") + " Not a monorepo workspace");
|
|
882
|
+
process.exit(1);
|
|
883
|
+
}
|
|
884
|
+
const { valid, errors } = await validateWorkspace(monorepoRoot);
|
|
885
|
+
if (valid) {
|
|
886
|
+
console.log(color__default.green("\u2713") + " Valid monorepo workspace");
|
|
887
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
888
|
+
} else {
|
|
889
|
+
console.log(color__default.red("\u2717") + " Invalid monorepo workspace");
|
|
890
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
891
|
+
for (const error of errors) {
|
|
892
|
+
console.log(color__default.red(` \u2022 ${error}`));
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
process.exit(valid ? 0 : 1);
|
|
896
|
+
}
|
|
897
|
+
|
|
1566
898
|
async function migrateEslintConfig(monorepoRoot, files) {
|
|
1567
899
|
const configBasePath = ".config/eslint";
|
|
1568
900
|
const existingConfigPath = node_path.join(monorepoRoot, "eslint.config.js");
|
|
@@ -1570,7 +902,7 @@ async function migrateEslintConfig(monorepoRoot, files) {
|
|
|
1570
902
|
try {
|
|
1571
903
|
existingContent = await promises$1.readFile(existingConfigPath, "utf-8");
|
|
1572
904
|
} catch {
|
|
1573
|
-
|
|
905
|
+
workspace.renderEslintConfigPackage(files);
|
|
1574
906
|
return;
|
|
1575
907
|
}
|
|
1576
908
|
files[`${configBasePath}/package.json`] = {
|
|
@@ -1649,7 +981,7 @@ async function migratePrettierConfig(monorepoRoot, files) {
|
|
|
1649
981
|
try {
|
|
1650
982
|
existingContent = await promises$1.readFile(existingConfigPath, "utf-8");
|
|
1651
983
|
} catch {
|
|
1652
|
-
|
|
984
|
+
workspace.renderPrettierConfigPackage(files);
|
|
1653
985
|
return;
|
|
1654
986
|
}
|
|
1655
987
|
files[`${configBasePath}/package.json`] = {
|
|
@@ -1669,151 +1001,35 @@ async function migratePrettierConfig(monorepoRoot, files) {
|
|
|
1669
1001
|
};
|
|
1670
1002
|
files[`${configBasePath}/README.md`] = {
|
|
1671
1003
|
type: "text",
|
|
1672
|
-
content: `# \`@config/prettier\`
|
|
1673
|
-
|
|
1674
|
-
Shared Prettier configurations.
|
|
1675
|
-
|
|
1676
|
-
## Usage
|
|
1677
|
-
|
|
1678
|
-
In your package's \`.prettierrc\`:
|
|
1679
|
-
|
|
1680
|
-
\`\`\`json
|
|
1681
|
-
"@config/prettier/base"
|
|
1682
|
-
\`\`\`
|
|
1683
|
-
|
|
1684
|
-
Or in \`package.json\`:
|
|
1685
|
-
|
|
1686
|
-
\`\`\`json
|
|
1687
|
-
{
|
|
1688
|
-
"prettier": "@config/prettier/base"
|
|
1689
|
-
}
|
|
1690
|
-
\`\`\`
|
|
1691
|
-
|
|
1692
|
-
## Available Configs
|
|
1693
|
-
|
|
1694
|
-
- \`base\` - Base Prettier rules (migrated from root)
|
|
1695
|
-
`
|
|
1696
|
-
};
|
|
1697
|
-
files[`${configBasePath}/base.json`] = {
|
|
1698
|
-
type: "text",
|
|
1699
|
-
content: existingContent
|
|
1700
|
-
};
|
|
1701
|
-
}
|
|
1702
|
-
async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedSettings, scope) {
|
|
1703
|
-
const workspaceDirectories = await parseWorkspaceDirectories(monorepoRoot);
|
|
1704
|
-
const defaultDirectories = ["apps", "packages"];
|
|
1705
|
-
const hasCustomDirectories = workspaceDirectories.length > 0 && !workspaceDirectories.every((dir) => defaultDirectories.includes(dir));
|
|
1706
|
-
const packageType = await promptForInitialPackage();
|
|
1707
|
-
if (packageType === "skip") {
|
|
1708
|
-
return false;
|
|
1709
|
-
}
|
|
1710
|
-
const defaultDir = packageType === "app" ? "apps" : "packages";
|
|
1711
|
-
const packageNameInput = await p__namespace.text({
|
|
1712
|
-
message: "Package name?",
|
|
1713
|
-
initialValue: `@${scope}/`,
|
|
1714
|
-
validate: (value) => {
|
|
1715
|
-
const validationError = index.validatePackageName(value);
|
|
1716
|
-
if (validationError) return validationError;
|
|
1717
|
-
const dirName = value.includes("/") ? value.split("/").pop() : value;
|
|
1718
|
-
if (!dirName) return "Package name is required";
|
|
1719
|
-
if (!hasCustomDirectories) {
|
|
1720
|
-
const targetPath = node_path.join(monorepoRoot, defaultDir, dirName);
|
|
1721
|
-
try {
|
|
1722
|
-
const { statSync } = require$1("fs");
|
|
1723
|
-
statSync(targetPath);
|
|
1724
|
-
return `Directory ${defaultDir}/${dirName} already exists`;
|
|
1725
|
-
} catch {
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1729
|
-
});
|
|
1730
|
-
if (p__namespace.isCancel(packageNameInput)) {
|
|
1731
|
-
return false;
|
|
1732
|
-
}
|
|
1733
|
-
const scopedName = packageNameInput;
|
|
1734
|
-
const shortName = scopedName.includes("/") ? scopedName.split("/").pop() : scopedName;
|
|
1735
|
-
const packageOptions = await promptForPackageOptions(scopedName, packageType, inheritedSettings);
|
|
1736
|
-
let targetDir = defaultDir;
|
|
1737
|
-
if (hasCustomDirectories && workspaceDirectories.length > 0) {
|
|
1738
|
-
const dirChoice = await p__namespace.select({
|
|
1739
|
-
message: "Target directory",
|
|
1740
|
-
options: workspaceDirectories.map((dir) => ({
|
|
1741
|
-
value: dir,
|
|
1742
|
-
label: dir
|
|
1743
|
-
})),
|
|
1744
|
-
initialValue: workspaceDirectories.includes(defaultDir) ? defaultDir : workspaceDirectories[0]
|
|
1745
|
-
});
|
|
1746
|
-
if (p__namespace.isCancel(dirChoice)) {
|
|
1747
|
-
return false;
|
|
1748
|
-
}
|
|
1749
|
-
targetDir = dirChoice;
|
|
1750
|
-
const targetPath = node_path.join(monorepoRoot, targetDir, shortName);
|
|
1751
|
-
try {
|
|
1752
|
-
const { statSync } = require$1("fs");
|
|
1753
|
-
statSync(targetPath);
|
|
1754
|
-
p__namespace.log.error(`Directory ${targetDir}/${shortName} already exists`);
|
|
1755
|
-
return false;
|
|
1756
|
-
} catch {
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
const relativePkgPath = node_path.join(targetDir, shortName);
|
|
1760
|
-
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
1761
|
-
packageOptions.workspaceRoot = workspaceRoot;
|
|
1762
|
-
packageOptions.name = scopedName;
|
|
1763
|
-
packageOptions.packageManager = await index.resolvePackageManager(packageOptions);
|
|
1764
|
-
packageOptions.engine = await index.resolveEngine(packageOptions);
|
|
1765
|
-
packageOptions.versions = await index.resolveProjectPackageVersions(packageOptions);
|
|
1766
|
-
const workspacePackages = packageType === "app" ? await getWorkspacePackages(monorepoRoot) : [];
|
|
1767
|
-
if (workspacePackages.length > 0) {
|
|
1768
|
-
const selectedDeps = await p__namespace.multiselect({
|
|
1769
|
-
message: "Add workspace dependencies?",
|
|
1770
|
-
options: workspacePackages.map((name) => ({ value: name, label: name })),
|
|
1771
|
-
required: false
|
|
1772
|
-
});
|
|
1773
|
-
if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
|
|
1774
|
-
packageOptions.workspaceDependencies = selectedDeps;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
const outputPath = node_path.join(monorepoRoot, relativePkgPath);
|
|
1778
|
-
const spinner = p__namespace.spinner();
|
|
1779
|
-
spinner.start("Creating package...");
|
|
1780
|
-
try {
|
|
1781
|
-
const files = index.generate(packageOptions);
|
|
1782
|
-
await writeGeneratedFiles(outputPath, files);
|
|
1783
|
-
spinner.stop(color__default.green.inverse(` \u2713 Package created at ${relativePkgPath}! `));
|
|
1784
|
-
const addAnother = await p__namespace.select({
|
|
1785
|
-
message: "Add another package?",
|
|
1786
|
-
options: [
|
|
1787
|
-
{ value: "no", label: "No, I'm done" },
|
|
1788
|
-
{ value: "yes", label: "Yes, add another" }
|
|
1789
|
-
],
|
|
1790
|
-
initialValue: "no"
|
|
1791
|
-
});
|
|
1792
|
-
return !p__namespace.isCancel(addAnother) && addAnother === "yes";
|
|
1793
|
-
} catch (error) {
|
|
1794
|
-
spinner.stop("Failed to create package");
|
|
1795
|
-
p__namespace.log.error(String(error));
|
|
1796
|
-
return false;
|
|
1797
|
-
}
|
|
1004
|
+
content: `# \`@config/prettier\`
|
|
1005
|
+
|
|
1006
|
+
Shared Prettier configurations.
|
|
1007
|
+
|
|
1008
|
+
## Usage
|
|
1009
|
+
|
|
1010
|
+
In your package's \`.prettierrc\`:
|
|
1011
|
+
|
|
1012
|
+
\`\`\`json
|
|
1013
|
+
"@config/prettier/base"
|
|
1014
|
+
\`\`\`
|
|
1015
|
+
|
|
1016
|
+
Or in \`package.json\`:
|
|
1017
|
+
|
|
1018
|
+
\`\`\`json
|
|
1019
|
+
{
|
|
1020
|
+
"prettier": "@config/prettier/base"
|
|
1798
1021
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
}
|
|
1810
|
-
console.log(color__default.red("\u2717") + " Invalid monorepo workspace");
|
|
1811
|
-
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1812
|
-
for (const error of errors) {
|
|
1813
|
-
console.log(color__default.red(` \u2022 ${error}`));
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
process.exit(valid ? 0 : 1);
|
|
1022
|
+
\`\`\`
|
|
1023
|
+
|
|
1024
|
+
## Available Configs
|
|
1025
|
+
|
|
1026
|
+
- \`base\` - Base Prettier rules (migrated from root)
|
|
1027
|
+
`
|
|
1028
|
+
};
|
|
1029
|
+
files[`${configBasePath}/base.json`] = {
|
|
1030
|
+
type: "text",
|
|
1031
|
+
content: existingContent
|
|
1032
|
+
};
|
|
1817
1033
|
}
|
|
1818
1034
|
async function handleFixCommand(options) {
|
|
1819
1035
|
const monorepoRoot = await detectMonorepoRoot();
|
|
@@ -1896,48 +1112,42 @@ async function handleFixCommand(options) {
|
|
|
1896
1112
|
spinner.start("Fixing workspace...");
|
|
1897
1113
|
try {
|
|
1898
1114
|
const files = {};
|
|
1899
|
-
const tsConfigExists = await fileExists(
|
|
1900
|
-
node_path.join(monorepoRoot, ".config/typescript/package.json")
|
|
1901
|
-
);
|
|
1115
|
+
const tsConfigExists = await fileExists$1(node_path.join(monorepoRoot, ".config/typescript/package.json"));
|
|
1902
1116
|
if (!tsConfigExists) {
|
|
1903
|
-
|
|
1117
|
+
workspace.renderTypescriptConfigPackage(files);
|
|
1904
1118
|
}
|
|
1905
1119
|
if (linter === "oxlint") {
|
|
1906
|
-
const oxlintExists = await fileExists(node_path.join(monorepoRoot, ".config/oxlint/package.json"));
|
|
1907
|
-
if (!oxlintExists)
|
|
1120
|
+
const oxlintExists = await fileExists$1(node_path.join(monorepoRoot, ".config/oxlint/package.json"));
|
|
1121
|
+
if (!oxlintExists) workspace.renderOxlintConfigPackage(files);
|
|
1908
1122
|
} else if (linter === "eslint") {
|
|
1909
|
-
const eslintPkgExists = await fileExists(
|
|
1910
|
-
node_path.join(monorepoRoot, ".config/eslint/package.json")
|
|
1911
|
-
);
|
|
1123
|
+
const eslintPkgExists = await fileExists$1(node_path.join(monorepoRoot, ".config/eslint/package.json"));
|
|
1912
1124
|
if (!eslintPkgExists) {
|
|
1913
1125
|
if (existingConfigs.eslintConfigPath) {
|
|
1914
1126
|
await migrateEslintConfig(monorepoRoot, files);
|
|
1915
1127
|
} else {
|
|
1916
|
-
|
|
1128
|
+
workspace.renderEslintConfigPackage(files);
|
|
1917
1129
|
}
|
|
1918
1130
|
}
|
|
1919
1131
|
}
|
|
1920
1132
|
if (formatter === "oxfmt") {
|
|
1921
|
-
const oxfmtExists = await fileExists(node_path.join(monorepoRoot, ".config/oxfmt/package.json"));
|
|
1922
|
-
if (!oxfmtExists)
|
|
1133
|
+
const oxfmtExists = await fileExists$1(node_path.join(monorepoRoot, ".config/oxfmt/package.json"));
|
|
1134
|
+
if (!oxfmtExists) workspace.renderOxfmtConfigPackage(files);
|
|
1923
1135
|
} else if (formatter === "prettier") {
|
|
1924
|
-
const prettierPkgExists = await fileExists(
|
|
1925
|
-
node_path.join(monorepoRoot, ".config/prettier/package.json")
|
|
1926
|
-
);
|
|
1136
|
+
const prettierPkgExists = await fileExists$1(node_path.join(monorepoRoot, ".config/prettier/package.json"));
|
|
1927
1137
|
if (!prettierPkgExists) {
|
|
1928
1138
|
if (existingConfigs.prettierConfigPath) {
|
|
1929
1139
|
await migratePrettierConfig(monorepoRoot, files);
|
|
1930
1140
|
} else {
|
|
1931
|
-
|
|
1141
|
+
workspace.renderPrettierConfigPackage(files);
|
|
1932
1142
|
}
|
|
1933
1143
|
}
|
|
1934
1144
|
}
|
|
1935
1145
|
if ((linter === "biome" || formatter === "biome") && !existingConfigs.biomeConfigPath) {
|
|
1936
|
-
const versions = await
|
|
1146
|
+
const versions = await workspace.resolveMonorepoRootPackageVersions({
|
|
1937
1147
|
linter,
|
|
1938
1148
|
formatter
|
|
1939
1149
|
});
|
|
1940
|
-
const biomeVersion =
|
|
1150
|
+
const biomeVersion = workspace.getResolvedPackageVersion(versions, "@biomejs/biome");
|
|
1941
1151
|
const biomeConfig = {
|
|
1942
1152
|
$schema: `https://biomejs.dev/schemas/${biomeVersion}/schema.json`,
|
|
1943
1153
|
vcs: {
|
|
@@ -1979,15 +1189,13 @@ async function handleFixCommand(options) {
|
|
|
1979
1189
|
}
|
|
1980
1190
|
}
|
|
1981
1191
|
spinner.stop(color__default.green("\u2713") + " Workspace fixed!");
|
|
1982
|
-
const generated = Object.keys(files).filter((
|
|
1192
|
+
const generated = Object.keys(files).filter((file) => file.endsWith("package.json"));
|
|
1983
1193
|
for (const pkgFile of generated) {
|
|
1984
1194
|
const pkgName = pkgFile.replace("/package.json", "");
|
|
1985
1195
|
console.log(color__default.dim(` Generated ${pkgName}`));
|
|
1986
1196
|
}
|
|
1987
|
-
const vscodeSettingsExists = await fileExists(node_path.join(monorepoRoot, ".vscode/settings.json"));
|
|
1988
|
-
const vscodeExtensionsExists = await fileExists(
|
|
1989
|
-
node_path.join(monorepoRoot, ".vscode/extensions.json")
|
|
1990
|
-
);
|
|
1197
|
+
const vscodeSettingsExists = await fileExists$1(node_path.join(monorepoRoot, ".vscode/settings.json"));
|
|
1198
|
+
const vscodeExtensionsExists = await fileExists$1(node_path.join(monorepoRoot, ".vscode/extensions.json"));
|
|
1991
1199
|
const vscodeExists = vscodeSettingsExists && vscodeExtensionsExists;
|
|
1992
1200
|
if (!vscodeExists) {
|
|
1993
1201
|
let addVscode = false;
|
|
@@ -2002,7 +1210,7 @@ async function handleFixCommand(options) {
|
|
|
2002
1210
|
}
|
|
2003
1211
|
if (addVscode) {
|
|
2004
1212
|
const vscodeFiles = {};
|
|
2005
|
-
|
|
1213
|
+
workspace.renderVscodeFiles(vscodeFiles, linter, formatter);
|
|
2006
1214
|
for (const [filePath, file] of Object.entries(vscodeFiles)) {
|
|
2007
1215
|
const fullPath = node_path.join(monorepoRoot, filePath);
|
|
2008
1216
|
await promises$1.mkdir(node_path.dirname(fullPath), { recursive: true });
|
|
@@ -2012,319 +1220,830 @@ async function handleFixCommand(options) {
|
|
|
2012
1220
|
console.log(color__default.dim(" Generated .vscode/extensions.json"));
|
|
2013
1221
|
}
|
|
2014
1222
|
}
|
|
2015
|
-
const aiRulesExist = await fileExists(node_path.join(monorepoRoot, ".ai/workspace.md"));
|
|
2016
|
-
if (!aiRulesExist) {
|
|
2017
|
-
const platforms = await
|
|
2018
|
-
if (platforms.length > 0) {
|
|
2019
|
-
const scope = await getMonorepoScope(monorepoRoot);
|
|
2020
|
-
const aiFilesOutput = {};
|
|
2021
|
-
|
|
2022
|
-
name: scope,
|
|
2023
|
-
packageManager: "pnpm",
|
|
2024
|
-
linter,
|
|
2025
|
-
formatter,
|
|
2026
|
-
isMonorepo: true,
|
|
2027
|
-
|
|
1223
|
+
const aiRulesExist = await fileExists$1(node_path.join(monorepoRoot, ".ai/workspace.md"));
|
|
1224
|
+
if (!aiRulesExist) {
|
|
1225
|
+
const platforms = await promptForAiAgentPlatforms(isNonInteractive);
|
|
1226
|
+
if (platforms.length > 0) {
|
|
1227
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1228
|
+
const aiFilesOutput = {};
|
|
1229
|
+
workspace.renderAiFiles(aiFilesOutput, {
|
|
1230
|
+
name: scope,
|
|
1231
|
+
packageManager: "pnpm",
|
|
1232
|
+
linter,
|
|
1233
|
+
formatter,
|
|
1234
|
+
isMonorepo: true,
|
|
1235
|
+
hasTypecheck: false,
|
|
1236
|
+
platforms
|
|
1237
|
+
});
|
|
1238
|
+
for (const [filePath, file] of Object.entries(aiFilesOutput)) {
|
|
1239
|
+
const fullPath = node_path.join(monorepoRoot, filePath);
|
|
1240
|
+
await promises$1.mkdir(node_path.dirname(fullPath), { recursive: true });
|
|
1241
|
+
await promises$1.writeFile(fullPath, file.content);
|
|
1242
|
+
console.log(color__default.dim(` Generated ${filePath}`));
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
process.exit(0);
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
spinner.stop(color__default.red("\u2717") + " Failed to fix workspace");
|
|
1249
|
+
console.error(error);
|
|
1250
|
+
process.exit(1);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
function detectViteTemplate(pkg) {
|
|
1255
|
+
if (!hasPackage(pkg, "vite")) return void 0;
|
|
1256
|
+
if (hasPackage(pkg, "@react-three/fiber")) return "r3f";
|
|
1257
|
+
if (hasPackage(pkg, "react") || hasPackage(pkg, "@vitejs/plugin-react")) return "react";
|
|
1258
|
+
return "vanilla";
|
|
1259
|
+
}
|
|
1260
|
+
function renderExpectedViteConfig(template) {
|
|
1261
|
+
const isReact = template === "react" || template === "r3f";
|
|
1262
|
+
const codeSnippets = isReact ? { "vite-config-import": ["import react from '@vitejs/plugin-react';"] } : {};
|
|
1263
|
+
const viteConfig = {
|
|
1264
|
+
base: "./"
|
|
1265
|
+
};
|
|
1266
|
+
if (isReact) {
|
|
1267
|
+
viteConfig.plugins = ["$raw:react()"];
|
|
1268
|
+
}
|
|
1269
|
+
if (template === "r3f") {
|
|
1270
|
+
viteConfig.resolve = { dedupe: ["three"] };
|
|
1271
|
+
}
|
|
1272
|
+
return workspace.renderViteConfig({ viteConfig, codeSnippets });
|
|
1273
|
+
}
|
|
1274
|
+
async function detectCurrentConfig(root, isMonorepo = true) {
|
|
1275
|
+
let name = root.split(/[/\\]/).pop() ?? "workspace";
|
|
1276
|
+
let packageManager = "pnpm";
|
|
1277
|
+
let hasTypecheck = false;
|
|
1278
|
+
let viteTemplate;
|
|
1279
|
+
try {
|
|
1280
|
+
const pkgPath = path.join(root, "package.json");
|
|
1281
|
+
const content = await promises.readFile(pkgPath, "utf-8");
|
|
1282
|
+
const pkgJson = JSON.parse(content);
|
|
1283
|
+
if (pkgJson.name) {
|
|
1284
|
+
name = pkgJson.name.replace(/^@/, "").replace(/\/.*$/, "");
|
|
1285
|
+
}
|
|
1286
|
+
if (pkgJson.packageManager) {
|
|
1287
|
+
packageManager = pkgJson.packageManager.split("@")[0] ?? packageManager;
|
|
1288
|
+
}
|
|
1289
|
+
hasTypecheck = pkgJson.scripts?.typecheck != null;
|
|
1290
|
+
viteTemplate = detectViteTemplate(pkgJson);
|
|
1291
|
+
} catch {
|
|
1292
|
+
}
|
|
1293
|
+
const tooling = await workspace.detectTooling(root);
|
|
1294
|
+
const configStrategy = isMonorepo ? void 0 : await detectSinglePackageConfigStrategy(root);
|
|
1295
|
+
return {
|
|
1296
|
+
name,
|
|
1297
|
+
linter: tooling.linter ?? "oxlint",
|
|
1298
|
+
formatter: tooling.formatter ?? "prettier",
|
|
1299
|
+
packageManager,
|
|
1300
|
+
isMonorepo,
|
|
1301
|
+
configStrategy,
|
|
1302
|
+
hasTypecheck,
|
|
1303
|
+
viteTemplate
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
async function detectSinglePackageConfigStrategy(root) {
|
|
1307
|
+
const hasStealthConfig = await Promise.all([
|
|
1308
|
+
fileExists(path.join(root, ".config/tsconfig.app.json")),
|
|
1309
|
+
fileExists(path.join(root, ".config/tsconfig.node.json")),
|
|
1310
|
+
fileExists(path.join(root, ".config/prettier.json")),
|
|
1311
|
+
fileExists(path.join(root, ".config/oxlint.json"))
|
|
1312
|
+
]).then((matches) => matches.some(Boolean));
|
|
1313
|
+
return hasStealthConfig ? "stealth" : "root";
|
|
1314
|
+
}
|
|
1315
|
+
async function planExpectedFiles(config) {
|
|
1316
|
+
const { name, linter, formatter, packageManager, isMonorepo, configStrategy, hasTypecheck } = config;
|
|
1317
|
+
const versions = linter === "biome" || formatter === "biome" ? await workspace.resolveMonorepoRootPackageVersions({ linter, formatter }) : {};
|
|
1318
|
+
const aiFilesMap = {};
|
|
1319
|
+
workspace.renderAiFiles(aiFilesMap, {
|
|
1320
|
+
name,
|
|
1321
|
+
packageManager,
|
|
1322
|
+
linter,
|
|
1323
|
+
formatter,
|
|
1324
|
+
isMonorepo,
|
|
1325
|
+
configStrategy,
|
|
1326
|
+
hasTypecheck,
|
|
1327
|
+
platforms: workspace.ALL_AI_PLATFORMS
|
|
1328
|
+
});
|
|
1329
|
+
const vscodeFiles = workspace.renderVscodeFiles$1({
|
|
1330
|
+
linter,
|
|
1331
|
+
formatter,
|
|
1332
|
+
configStrategy,
|
|
1333
|
+
isMonorepo,
|
|
1334
|
+
packageManager: isPackageManagerName(packageManager) ? packageManager : void 0
|
|
1335
|
+
});
|
|
1336
|
+
const configPackages = {};
|
|
1337
|
+
if (isMonorepo) {
|
|
1338
|
+
workspace.renderTypescriptConfigPackage(configPackages);
|
|
1339
|
+
if (linter === "oxlint") {
|
|
1340
|
+
workspace.renderOxlintConfigPackage(configPackages);
|
|
1341
|
+
} else if (linter === "eslint") {
|
|
1342
|
+
workspace.renderEslintConfigPackage(configPackages);
|
|
1343
|
+
}
|
|
1344
|
+
if (formatter === "oxfmt") {
|
|
1345
|
+
workspace.renderOxfmtConfigPackage(configPackages);
|
|
1346
|
+
} else if (formatter === "prettier") {
|
|
1347
|
+
workspace.renderPrettierConfigPackage(configPackages);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
const workspaceConfig = {};
|
|
1351
|
+
const rootConfig = {};
|
|
1352
|
+
rootConfig[".editorconfig"] = workspace.renderEditorConfig();
|
|
1353
|
+
rootConfig[".gitignore"] = workspace.renderGitignore(isMonorepo ? "workspace-root" : "standalone");
|
|
1354
|
+
rootConfig[".gitattributes"] = {
|
|
1355
|
+
type: "text",
|
|
1356
|
+
content: `* text=auto eol=lf
|
|
1357
|
+
*.{cmd,[cC][mM][dD]} text eol=crlf
|
|
1358
|
+
*.{bat,[bB][aA][tT]} text eol=crlf
|
|
1359
|
+
`
|
|
1360
|
+
};
|
|
1361
|
+
if (!isMonorepo && formatter === "prettier") {
|
|
1362
|
+
rootConfig[configStrategy === "root" ? ".prettierignore" : ".config/prettierignore"] = {
|
|
1363
|
+
type: "text",
|
|
1364
|
+
content: workspace.toPrettierIgnoreContent()
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
if (!isMonorepo && config.viteTemplate != null) {
|
|
1368
|
+
rootConfig["vite.config.ts"] = renderExpectedViteConfig(config.viteTemplate);
|
|
1369
|
+
}
|
|
1370
|
+
if (linter === "biome" || formatter === "biome") {
|
|
1371
|
+
const biomeVersion = workspace.getResolvedPackageVersion(versions, "@biomejs/biome");
|
|
1372
|
+
const biomeConfig = {
|
|
1373
|
+
$schema: `https://biomejs.dev/schemas/${biomeVersion}/schema.json`,
|
|
1374
|
+
vcs: {
|
|
1375
|
+
enabled: true,
|
|
1376
|
+
clientKind: "git",
|
|
1377
|
+
useIgnoreFile: true
|
|
1378
|
+
},
|
|
1379
|
+
linter: {
|
|
1380
|
+
enabled: linter === "biome",
|
|
1381
|
+
rules: {
|
|
1382
|
+
recommended: true
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
formatter: {
|
|
1386
|
+
enabled: formatter === "biome"
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
rootConfig["biome.json"] = {
|
|
1390
|
+
type: "text",
|
|
1391
|
+
content: JSON.stringify(biomeConfig, null, 2)
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
return {
|
|
1395
|
+
"ai-files": aiFilesMap,
|
|
1396
|
+
vscode: vscodeFiles,
|
|
1397
|
+
"package-json": {},
|
|
1398
|
+
"config-packages": configPackages,
|
|
1399
|
+
"tooling-config": {},
|
|
1400
|
+
"workspace-config": workspaceConfig,
|
|
1401
|
+
"root-config": rootConfig
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
async function fileExists(path) {
|
|
1405
|
+
try {
|
|
1406
|
+
await promises.access(path, fs.constants.F_OK);
|
|
1407
|
+
return true;
|
|
1408
|
+
} catch {
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
function stripJsonComments(content) {
|
|
1413
|
+
let output = "";
|
|
1414
|
+
let inString = false;
|
|
1415
|
+
let inLineComment = false;
|
|
1416
|
+
let inBlockComment = false;
|
|
1417
|
+
let escaped = false;
|
|
1418
|
+
for (let index = 0; index < content.length; index++) {
|
|
1419
|
+
const char = content[index];
|
|
1420
|
+
const next = content[index + 1];
|
|
1421
|
+
if (inLineComment) {
|
|
1422
|
+
if (char === "\n" || char === "\r") {
|
|
1423
|
+
inLineComment = false;
|
|
1424
|
+
output += char;
|
|
1425
|
+
}
|
|
1426
|
+
continue;
|
|
1427
|
+
}
|
|
1428
|
+
if (inBlockComment) {
|
|
1429
|
+
if (char === "*" && next === "/") {
|
|
1430
|
+
inBlockComment = false;
|
|
1431
|
+
index++;
|
|
1432
|
+
}
|
|
1433
|
+
continue;
|
|
1434
|
+
}
|
|
1435
|
+
if (inString) {
|
|
1436
|
+
output += char;
|
|
1437
|
+
if (escaped) {
|
|
1438
|
+
escaped = false;
|
|
1439
|
+
} else if (char === "\\") {
|
|
1440
|
+
escaped = true;
|
|
1441
|
+
} else if (char === '"') {
|
|
1442
|
+
inString = false;
|
|
1443
|
+
}
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1446
|
+
if (char === '"') {
|
|
1447
|
+
inString = true;
|
|
1448
|
+
output += char;
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1451
|
+
if (char === "/" && next === "/") {
|
|
1452
|
+
inLineComment = true;
|
|
1453
|
+
index++;
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1456
|
+
if (char === "/" && next === "*") {
|
|
1457
|
+
inBlockComment = true;
|
|
1458
|
+
index++;
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
output += char;
|
|
1462
|
+
}
|
|
1463
|
+
return output;
|
|
1464
|
+
}
|
|
1465
|
+
function stripTrailingJsonCommas(content) {
|
|
1466
|
+
let output = "";
|
|
1467
|
+
let inString = false;
|
|
1468
|
+
let escaped = false;
|
|
1469
|
+
for (let index = 0; index < content.length; index++) {
|
|
1470
|
+
const char = content[index];
|
|
1471
|
+
if (inString) {
|
|
1472
|
+
output += char;
|
|
1473
|
+
if (escaped) {
|
|
1474
|
+
escaped = false;
|
|
1475
|
+
} else if (char === "\\") {
|
|
1476
|
+
escaped = true;
|
|
1477
|
+
} else if (char === '"') {
|
|
1478
|
+
inString = false;
|
|
1479
|
+
}
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
if (char === '"') {
|
|
1483
|
+
inString = true;
|
|
1484
|
+
output += char;
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
if (char === ",") {
|
|
1488
|
+
let lookahead = index + 1;
|
|
1489
|
+
while (/\s/.test(content[lookahead] ?? "")) lookahead++;
|
|
1490
|
+
if (content[lookahead] === "}" || content[lookahead] === "]") {
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
output += char;
|
|
1495
|
+
}
|
|
1496
|
+
return output;
|
|
1497
|
+
}
|
|
1498
|
+
function parseJsonValue(content) {
|
|
1499
|
+
return JSON.parse(stripTrailingJsonCommas(stripJsonComments(content)));
|
|
1500
|
+
}
|
|
1501
|
+
function stableJsonValue(value) {
|
|
1502
|
+
if (Array.isArray(value)) {
|
|
1503
|
+
return value.map(stableJsonValue);
|
|
1504
|
+
}
|
|
1505
|
+
if (value != null && typeof value === "object") {
|
|
1506
|
+
return Object.fromEntries(
|
|
1507
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => [key, stableJsonValue(entryValue)])
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
return value;
|
|
1511
|
+
}
|
|
1512
|
+
function jsonValuesEqual(currentContent, newContent) {
|
|
1513
|
+
try {
|
|
1514
|
+
return JSON.stringify(stableJsonValue(parseJsonValue(currentContent))) === JSON.stringify(stableJsonValue(parseJsonValue(newContent)));
|
|
1515
|
+
} catch {
|
|
1516
|
+
return false;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
function shouldCompareJsonValues(filePath) {
|
|
1520
|
+
return filePath.endsWith(".json") || filePath.endsWith(".jsonc");
|
|
1521
|
+
}
|
|
1522
|
+
function fileContentsEqual(filePath, currentContent, newContent) {
|
|
1523
|
+
if (shouldCompareJsonValues(filePath) && jsonValuesEqual(currentContent, newContent)) {
|
|
1524
|
+
return true;
|
|
1525
|
+
}
|
|
1526
|
+
return currentContent === newContent;
|
|
1527
|
+
}
|
|
1528
|
+
async function compareWithDisk(expected, root) {
|
|
1529
|
+
const categoryLabels = {
|
|
1530
|
+
"ai-files": "AI Files",
|
|
1531
|
+
"ai-files-install": "Install More AI Files",
|
|
1532
|
+
"ai-files-update": "Update Existing AI Files",
|
|
1533
|
+
vscode: "VS Code",
|
|
1534
|
+
"package-json": "package.json Scripts",
|
|
1535
|
+
"config-packages": "Config Packages",
|
|
1536
|
+
"tooling-config": "Tooling Config",
|
|
1537
|
+
"workspace-config": "Workspace Config",
|
|
1538
|
+
"root-config": "Root Config"
|
|
1539
|
+
};
|
|
1540
|
+
const categories = [];
|
|
1541
|
+
for (const [category, files] of Object.entries(expected)) {
|
|
1542
|
+
const changes = [];
|
|
1543
|
+
for (const [filePath, file] of Object.entries(files)) {
|
|
1544
|
+
if (file.type !== "text") continue;
|
|
1545
|
+
const fullPath = path.join(root, filePath);
|
|
1546
|
+
const newContent = file.content;
|
|
1547
|
+
if (await fileExists(fullPath)) {
|
|
1548
|
+
const currentContent = await promises.readFile(fullPath, "utf-8");
|
|
1549
|
+
if (fileContentsEqual(filePath, currentContent, newContent)) {
|
|
1550
|
+
changes.push({
|
|
1551
|
+
path: filePath,
|
|
1552
|
+
status: "unchanged",
|
|
1553
|
+
currentContent,
|
|
1554
|
+
newContent
|
|
1555
|
+
});
|
|
1556
|
+
} else {
|
|
1557
|
+
changes.push({
|
|
1558
|
+
path: filePath,
|
|
1559
|
+
status: "modified",
|
|
1560
|
+
currentContent,
|
|
1561
|
+
newContent
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
} else {
|
|
1565
|
+
changes.push({
|
|
1566
|
+
path: filePath,
|
|
1567
|
+
status: "added",
|
|
1568
|
+
newContent
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
if (category === "ai-files") {
|
|
1573
|
+
const newAiFiles = changes.filter((change) => change.status === "added");
|
|
1574
|
+
const modifiedAiFiles = changes.filter((change) => change.status === "modified");
|
|
1575
|
+
if (newAiFiles.length > 0) {
|
|
1576
|
+
categories.push({
|
|
1577
|
+
category: "ai-files-install",
|
|
1578
|
+
label: categoryLabels["ai-files-install"],
|
|
1579
|
+
changes: newAiFiles,
|
|
1580
|
+
hasUserModifications: false
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
if (modifiedAiFiles.length > 0) {
|
|
1584
|
+
categories.push({
|
|
1585
|
+
category: "ai-files-update",
|
|
1586
|
+
label: categoryLabels["ai-files-update"],
|
|
1587
|
+
changes: modifiedAiFiles,
|
|
1588
|
+
hasUserModifications: true
|
|
2028
1589
|
});
|
|
2029
|
-
for (const [filePath, file] of Object.entries(aiFilesOutput)) {
|
|
2030
|
-
const fullPath = node_path.join(monorepoRoot, filePath);
|
|
2031
|
-
await promises$1.mkdir(node_path.dirname(fullPath), { recursive: true });
|
|
2032
|
-
await promises$1.writeFile(fullPath, file.content);
|
|
2033
|
-
console.log(color__default.dim(` Generated ${filePath}`));
|
|
2034
|
-
}
|
|
2035
1590
|
}
|
|
1591
|
+
continue;
|
|
2036
1592
|
}
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
1593
|
+
if (changes.length === 0) continue;
|
|
1594
|
+
const hasUserModifications = changes.some((c) => c.status === "modified");
|
|
1595
|
+
categories.push({
|
|
1596
|
+
category,
|
|
1597
|
+
label: categoryLabels[category],
|
|
1598
|
+
changes,
|
|
1599
|
+
hasUserModifications
|
|
1600
|
+
});
|
|
2042
1601
|
}
|
|
1602
|
+
return categories;
|
|
1603
|
+
}
|
|
1604
|
+
function isPackageManagerName(value) {
|
|
1605
|
+
return value === "pnpm" || value === "npm" || value === "yarn";
|
|
2043
1606
|
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
1607
|
+
function hasPackage(pkg, name) {
|
|
1608
|
+
return pkg.dependencies?.[name] != null || pkg.devDependencies?.[name] != null || pkg.peerDependencies?.[name] != null;
|
|
1609
|
+
}
|
|
1610
|
+
function sortPackageMap(packageMap) {
|
|
1611
|
+
return Object.fromEntries(Object.entries(packageMap).sort(([a], [b]) => a.localeCompare(b)));
|
|
1612
|
+
}
|
|
1613
|
+
async function detectTypeScriptPackage(root, pkg) {
|
|
1614
|
+
if (hasPackage(pkg, "typescript")) return true;
|
|
1615
|
+
return await fileExists(path.join(root, "tsconfig.json")) || await fileExists(path.join(root, "tsconfig.app.json")) || await fileExists(path.join(root, ".config/tsconfig.app.json"));
|
|
1616
|
+
}
|
|
1617
|
+
function detectLibraryPackage(pkg) {
|
|
1618
|
+
return pkg.exports != null || pkg.main?.includes("dist") === true || pkg.module?.includes("dist") === true || Array.isArray(pkg.files) && pkg.files.includes("dist");
|
|
1619
|
+
}
|
|
1620
|
+
function getPackageManagerForScripts(config, pkg) {
|
|
1621
|
+
const packageManager = pkg.packageManager?.split("@")[0] ?? config.packageManager;
|
|
1622
|
+
return isPackageManagerName(packageManager) ? packageManager : "pnpm";
|
|
1623
|
+
}
|
|
1624
|
+
function getSinglePackageToolScripts(config) {
|
|
1625
|
+
const isStealth = (config.configStrategy ?? "stealth") === "stealth";
|
|
1626
|
+
const linterScripts = config.linter === "oxlint" ? workspace.packageJsonScripts.lint.oxlint(isStealth ? ".config/oxlint.json" : void 0) : config.linter === "eslint" ? workspace.packageJsonScripts.lint.eslint(isStealth ? ".config/eslint.config.js" : void 0) : workspace.packageJsonScripts.lint.biome(isStealth ? ".config" : void 0);
|
|
1627
|
+
const formatterScripts = config.formatter === "prettier" ? workspace.packageJsonScripts.format.prettier(
|
|
1628
|
+
isStealth ? ".config/prettier.json" : void 0,
|
|
1629
|
+
isStealth ? ".config/prettierignore" : void 0
|
|
1630
|
+
) : config.formatter === "oxfmt" ? workspace.packageJsonScripts.format.oxfmt(isStealth ? ".config/oxfmt.json" : "oxfmt.json") : workspace.packageJsonScripts.format.biome(isStealth ? ".config" : void 0);
|
|
1631
|
+
return workspace.mergePackageJsonScripts(linterScripts, formatterScripts);
|
|
1632
|
+
}
|
|
1633
|
+
function getLibraryBuildScripts(pkg) {
|
|
1634
|
+
if (!detectLibraryPackage(pkg)) return void 0;
|
|
1635
|
+
if (hasPackage(pkg, "tsdown") || pkg.scripts?.build === "tsdown") {
|
|
1636
|
+
return workspace.packageJsonScripts.build.tsdown;
|
|
2049
1637
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
1638
|
+
return workspace.packageJsonScripts.build.unbuild();
|
|
1639
|
+
}
|
|
1640
|
+
function getTestingScripts(pkg) {
|
|
1641
|
+
if (hasPackage(pkg, "vitest") || pkg.scripts?.test === "vitest") {
|
|
1642
|
+
return workspace.packageJsonScripts.test.vitest;
|
|
2054
1643
|
}
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
1644
|
+
return void 0;
|
|
1645
|
+
}
|
|
1646
|
+
function scriptsEqual(left, right) {
|
|
1647
|
+
const leftEntries = Object.entries(left);
|
|
1648
|
+
if (leftEntries.length !== Object.keys(right).length) return false;
|
|
1649
|
+
return leftEntries.every(([key, value]) => right[key] === value);
|
|
1650
|
+
}
|
|
1651
|
+
async function getExpectedPackageScripts(root, config, pkg) {
|
|
1652
|
+
if (config.isMonorepo) {
|
|
1653
|
+
return workspace.packageJsonScripts.monorepoRoot(config.linter, config.formatter);
|
|
1654
|
+
}
|
|
1655
|
+
const language = await detectTypeScriptPackage(root, pkg) ? "typescript" : "javascript";
|
|
1656
|
+
const isLibrary = detectLibraryPackage(pkg);
|
|
1657
|
+
const packageManagerName = getPackageManagerForScripts(config, pkg);
|
|
1658
|
+
return workspace.mergePackageJsonScripts(
|
|
1659
|
+
workspace.resolveDefaultPackageJsonScripts({
|
|
1660
|
+
language,
|
|
1661
|
+
isLibrary,
|
|
1662
|
+
packageManagerName
|
|
1663
|
+
}),
|
|
1664
|
+
getLibraryBuildScripts(pkg),
|
|
1665
|
+
getTestingScripts(pkg),
|
|
1666
|
+
getSinglePackageToolScripts(config)
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
async function getExpectedPackageDevDependencies(root, config, pkg) {
|
|
1670
|
+
const nextDevDependencies = { ...pkg.devDependencies };
|
|
1671
|
+
const shouldAddOxlintTypeAwareBackend = config.linter === "oxlint" && (config.isMonorepo || await detectTypeScriptPackage(root, pkg)) && !hasPackage(pkg, "oxlint-tsgolint");
|
|
1672
|
+
if (shouldAddOxlintTypeAwareBackend) {
|
|
1673
|
+
nextDevDependencies["oxlint-tsgolint"] = workspace.formatResolvedPackageVersion({}, "oxlint-tsgolint");
|
|
2059
1674
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
1675
|
+
return sortPackageMap(nextDevDependencies);
|
|
1676
|
+
}
|
|
1677
|
+
async function getPackageJsonScriptUpdates(root, config) {
|
|
1678
|
+
const packageJsonPath = path.join(root, "package.json");
|
|
1679
|
+
let currentContent;
|
|
1680
|
+
try {
|
|
1681
|
+
currentContent = await promises.readFile(packageJsonPath, "utf-8");
|
|
1682
|
+
} catch {
|
|
1683
|
+
return [];
|
|
1684
|
+
}
|
|
1685
|
+
const pkg = JSON.parse(currentContent);
|
|
1686
|
+
const currentScripts = pkg.scripts ?? {};
|
|
1687
|
+
const expectedScripts = await getExpectedPackageScripts(root, config, pkg);
|
|
1688
|
+
const nextScripts = workspace.mergePackageJsonScripts(currentScripts, expectedScripts);
|
|
1689
|
+
const currentDevDependencies = pkg.devDependencies ?? {};
|
|
1690
|
+
const nextDevDependencies = await getExpectedPackageDevDependencies(root, config, pkg);
|
|
1691
|
+
if (scriptsEqual(currentScripts, nextScripts) && scriptsEqual(currentDevDependencies, nextDevDependencies)) {
|
|
1692
|
+
return [
|
|
1693
|
+
{
|
|
1694
|
+
path: "package.json",
|
|
1695
|
+
status: "unchanged",
|
|
1696
|
+
currentContent,
|
|
1697
|
+
newContent: currentContent
|
|
1698
|
+
}
|
|
1699
|
+
];
|
|
1700
|
+
}
|
|
1701
|
+
const nextPackageJson = {
|
|
1702
|
+
...pkg,
|
|
1703
|
+
scripts: nextScripts
|
|
1704
|
+
};
|
|
1705
|
+
if (Object.keys(nextDevDependencies).length > 0 || pkg.devDependencies != null) {
|
|
1706
|
+
nextPackageJson.devDependencies = nextDevDependencies;
|
|
1707
|
+
}
|
|
1708
|
+
const newContent = `${JSON.stringify(nextPackageJson, null, 2)}
|
|
1709
|
+
`;
|
|
1710
|
+
return [
|
|
1711
|
+
{
|
|
1712
|
+
path: "package.json",
|
|
1713
|
+
status: "modified",
|
|
1714
|
+
currentContent,
|
|
1715
|
+
newContent
|
|
2069
1716
|
}
|
|
1717
|
+
];
|
|
1718
|
+
}
|
|
1719
|
+
function planSinglePackageOxlintConfig(config) {
|
|
1720
|
+
if (config.linter !== "oxlint" || config.isMonorepo) return void 0;
|
|
1721
|
+
const isStealth = (config.configStrategy ?? "stealth") === "stealth";
|
|
1722
|
+
const path = isStealth ? ".config/oxlint.json" : "oxlint.json";
|
|
1723
|
+
const oxlintConfig = workspace.renderOxlintConfig({
|
|
1724
|
+
schemaPath: isStealth ? "../node_modules/oxlint/configuration_schema.json" : "./node_modules/oxlint/configuration_schema.json",
|
|
1725
|
+
typescript: true
|
|
1726
|
+
});
|
|
1727
|
+
return {
|
|
1728
|
+
path,
|
|
1729
|
+
status: "added",
|
|
1730
|
+
newContent: `${JSON.stringify(oxlintConfig, null, 2)}
|
|
1731
|
+
`
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
async function getOxlintConfigReplacementUpdates(root, config) {
|
|
1735
|
+
const expected = planSinglePackageOxlintConfig(config);
|
|
1736
|
+
if (expected == null) return [];
|
|
1737
|
+
const fullPath = path.join(root, expected.path);
|
|
1738
|
+
let currentContent;
|
|
1739
|
+
try {
|
|
1740
|
+
currentContent = await promises.readFile(fullPath, "utf-8");
|
|
1741
|
+
} catch {
|
|
1742
|
+
return [expected];
|
|
2070
1743
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
1744
|
+
if (fileContentsEqual(expected.path, currentContent, expected.newContent)) {
|
|
1745
|
+
return [
|
|
1746
|
+
{
|
|
1747
|
+
...expected,
|
|
1748
|
+
status: "unchanged",
|
|
1749
|
+
currentContent,
|
|
1750
|
+
newContent: currentContent
|
|
1751
|
+
}
|
|
1752
|
+
];
|
|
1753
|
+
}
|
|
1754
|
+
return [
|
|
1755
|
+
{
|
|
1756
|
+
...expected,
|
|
1757
|
+
status: "modified",
|
|
1758
|
+
currentContent
|
|
2080
1759
|
}
|
|
1760
|
+
];
|
|
1761
|
+
}
|
|
1762
|
+
async function getWorkspaceConfigUpdates(root) {
|
|
1763
|
+
const workspacePath = path.join(root, "pnpm-workspace.yaml");
|
|
1764
|
+
const changes = [];
|
|
1765
|
+
let currentContent = "";
|
|
1766
|
+
let exists = false;
|
|
1767
|
+
try {
|
|
1768
|
+
currentContent = await promises.readFile(workspacePath, "utf-8");
|
|
1769
|
+
exists = true;
|
|
1770
|
+
} catch {
|
|
2081
1771
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
index.generateAiFiles(aiFilesOutput, {
|
|
2098
|
-
name: scope,
|
|
2099
|
-
packageManager: "pnpm",
|
|
2100
|
-
linter: plan.toLinter,
|
|
2101
|
-
formatter: plan.toFormatter,
|
|
2102
|
-
isMonorepo: true,
|
|
2103
|
-
platforms: existingPlatforms.length > 0 ? existingPlatforms : ["agents"]
|
|
1772
|
+
if (!exists) {
|
|
1773
|
+
const newContent = `manage-package-manager-versions: true
|
|
1774
|
+
|
|
1775
|
+
packages:
|
|
1776
|
+
- '.config/*'
|
|
1777
|
+
- 'apps/*'
|
|
1778
|
+
- 'packages/*'
|
|
1779
|
+
|
|
1780
|
+
onlyBuiltDependencies:
|
|
1781
|
+
- esbuild
|
|
1782
|
+
`;
|
|
1783
|
+
changes.push({
|
|
1784
|
+
path: "pnpm-workspace.yaml",
|
|
1785
|
+
status: "added",
|
|
1786
|
+
newContent
|
|
2104
1787
|
});
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
1788
|
+
return changes;
|
|
1789
|
+
}
|
|
1790
|
+
let updatedContent = currentContent;
|
|
1791
|
+
let needsUpdate = false;
|
|
1792
|
+
if (!currentContent.includes("manage-package-manager-versions")) {
|
|
1793
|
+
updatedContent = `manage-package-manager-versions: true
|
|
1794
|
+
|
|
1795
|
+
${updatedContent}`;
|
|
1796
|
+
needsUpdate = true;
|
|
1797
|
+
}
|
|
1798
|
+
if (!currentContent.includes("onlyBuiltDependencies")) {
|
|
1799
|
+
updatedContent = `${updatedContent.trimEnd()}
|
|
1800
|
+
|
|
1801
|
+
onlyBuiltDependencies:
|
|
1802
|
+
- esbuild
|
|
1803
|
+
`;
|
|
1804
|
+
needsUpdate = true;
|
|
1805
|
+
}
|
|
1806
|
+
if (!currentContent.includes(".config/*")) {
|
|
1807
|
+
const lines = updatedContent.split("\n");
|
|
1808
|
+
const packagesIndex = lines.findIndex((line) => line.trim().startsWith("packages:"));
|
|
1809
|
+
if (packagesIndex !== -1) {
|
|
1810
|
+
lines.splice(packagesIndex + 1, 0, " - '.config/*'");
|
|
1811
|
+
updatedContent = lines.join("\n");
|
|
1812
|
+
needsUpdate = true;
|
|
2110
1813
|
}
|
|
2111
1814
|
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
1815
|
+
if (needsUpdate) {
|
|
1816
|
+
changes.push({
|
|
1817
|
+
path: "pnpm-workspace.yaml",
|
|
1818
|
+
status: "modified",
|
|
1819
|
+
currentContent,
|
|
1820
|
+
newContent: updatedContent
|
|
1821
|
+
});
|
|
1822
|
+
} else {
|
|
1823
|
+
changes.push({
|
|
1824
|
+
path: "pnpm-workspace.yaml",
|
|
1825
|
+
status: "unchanged",
|
|
1826
|
+
currentContent,
|
|
1827
|
+
newContent: currentContent
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
return changes;
|
|
2116
1831
|
}
|
|
2117
|
-
async function
|
|
2118
|
-
const
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
process.exit(1);
|
|
1832
|
+
async function applyUpdates(changes, root) {
|
|
1833
|
+
for (const change of changes) {
|
|
1834
|
+
if (change.status === "unchanged") continue;
|
|
1835
|
+
const fullPath = path.join(root, change.path);
|
|
1836
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1837
|
+
await promises.writeFile(fullPath, change.newContent);
|
|
2124
1838
|
}
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
1839
|
+
}
|
|
1840
|
+
function formatFileChange(change) {
|
|
1841
|
+
const icon = change.status === "added" ? "+" : change.status === "modified" ? "~" : "=";
|
|
1842
|
+
return ` ${icon} ${change.path}`;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
const UPDATE_CATEGORY_ORDER = [
|
|
1846
|
+
"root-config",
|
|
1847
|
+
"config-packages",
|
|
1848
|
+
"tooling-config",
|
|
1849
|
+
"workspace-config",
|
|
1850
|
+
"vscode",
|
|
1851
|
+
"package-json",
|
|
1852
|
+
"ai-files",
|
|
1853
|
+
"ai-files-install",
|
|
1854
|
+
"ai-files-update"
|
|
1855
|
+
];
|
|
1856
|
+
function isMergeUpdateCategory(category) {
|
|
1857
|
+
return category === "workspace-config" || category === "package-json";
|
|
1858
|
+
}
|
|
1859
|
+
function getUpdateHint(category, status) {
|
|
1860
|
+
if (status === "added") return "new file";
|
|
1861
|
+
if (category === "package-json") return "merge update";
|
|
1862
|
+
if (category === "workspace-config") return "merge update";
|
|
1863
|
+
return "changed; overwrites if selected";
|
|
1864
|
+
}
|
|
1865
|
+
function isSelectableFileChange(change) {
|
|
1866
|
+
return change.status === "added" || change.status === "modified";
|
|
1867
|
+
}
|
|
1868
|
+
function getInitialUpdateSelections(category) {
|
|
1869
|
+
return category.changes.filter(
|
|
1870
|
+
(change) => change.status === "added" || change.status === "modified" && isMergeUpdateCategory(category.category)
|
|
1871
|
+
).map((change) => change.path);
|
|
1872
|
+
}
|
|
1873
|
+
async function promptForUpdateSelections(category) {
|
|
1874
|
+
const selectableChanges = category.changes.filter(isSelectableFileChange);
|
|
1875
|
+
const selectedFiles = await p__namespace.multiselect({
|
|
1876
|
+
message: category.label,
|
|
1877
|
+
options: selectableChanges.map((change) => ({
|
|
1878
|
+
value: change.path,
|
|
1879
|
+
label: change.path,
|
|
1880
|
+
hint: getUpdateHint(category.category, change.status)
|
|
1881
|
+
})),
|
|
1882
|
+
initialValues: getInitialUpdateSelections(category),
|
|
1883
|
+
required: false
|
|
1884
|
+
});
|
|
1885
|
+
if (p__namespace.isCancel(selectedFiles)) {
|
|
1886
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1887
|
+
process.exit(0);
|
|
2156
1888
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
const
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
1889
|
+
return selectableChanges.filter((change) => selectedFiles.includes(change.path));
|
|
1890
|
+
}
|
|
1891
|
+
async function promptForAiFileInstall(category) {
|
|
1892
|
+
const newChanges = category.changes.filter((change) => change.status === "added");
|
|
1893
|
+
const fileList = newChanges.map((change) => change.path).join(", ");
|
|
1894
|
+
const shouldInstall = await p__namespace.confirm({
|
|
1895
|
+
message: fileList ? `Install more AI files? (${fileList})` : "Install more AI files?",
|
|
1896
|
+
initialValue: true
|
|
1897
|
+
});
|
|
1898
|
+
if (p__namespace.isCancel(shouldInstall)) {
|
|
1899
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1900
|
+
process.exit(0);
|
|
2164
1901
|
}
|
|
2165
|
-
|
|
2166
|
-
|
|
1902
|
+
return shouldInstall ? newChanges : [];
|
|
1903
|
+
}
|
|
1904
|
+
function getCategoryOrder(category) {
|
|
1905
|
+
const index = UPDATE_CATEGORY_ORDER.indexOf(category);
|
|
1906
|
+
return index === -1 ? UPDATE_CATEGORY_ORDER.length : index;
|
|
1907
|
+
}
|
|
1908
|
+
function orderUpdateCategories(categories) {
|
|
1909
|
+
return [...categories].sort(
|
|
1910
|
+
(left, right) => getCategoryOrder(left.category) - getCategoryOrder(right.category)
|
|
2167
1911
|
);
|
|
2168
|
-
|
|
2169
|
-
|
|
1912
|
+
}
|
|
1913
|
+
async function collectUpdateCategories(projectRoot, config, isMonorepo) {
|
|
1914
|
+
const expected = await planExpectedFiles(config);
|
|
2170
1915
|
const categories = await compareWithDisk(expected, projectRoot);
|
|
2171
|
-
const allCategories = categories.filter((
|
|
1916
|
+
const allCategories = categories.filter((category) => category.category !== "workspace-config");
|
|
1917
|
+
const packageJsonScriptChanges = await getPackageJsonScriptUpdates(projectRoot, config);
|
|
1918
|
+
if (packageJsonScriptChanges.length > 0) {
|
|
1919
|
+
allCategories.push({
|
|
1920
|
+
category: "package-json",
|
|
1921
|
+
label: "package.json",
|
|
1922
|
+
changes: packageJsonScriptChanges,
|
|
1923
|
+
hasUserModifications: packageJsonScriptChanges.some((change) => change.status === "modified")
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
const oxlintConfigChanges = await getOxlintConfigReplacementUpdates(projectRoot, config);
|
|
1927
|
+
if (oxlintConfigChanges.length > 0) {
|
|
1928
|
+
allCategories.push({
|
|
1929
|
+
category: "tooling-config",
|
|
1930
|
+
label: "Tooling Config",
|
|
1931
|
+
changes: oxlintConfigChanges,
|
|
1932
|
+
hasUserModifications: oxlintConfigChanges.some((change) => change.status === "modified")
|
|
1933
|
+
});
|
|
1934
|
+
}
|
|
2172
1935
|
if (isMonorepo) {
|
|
2173
1936
|
const workspaceConfigChanges = await getWorkspaceConfigUpdates(projectRoot);
|
|
2174
|
-
const workspaceCategory = {
|
|
2175
|
-
category: "workspace-config",
|
|
2176
|
-
label: "Workspace Config",
|
|
2177
|
-
changes: workspaceConfigChanges,
|
|
2178
|
-
hasUserModifications: workspaceConfigChanges.some((c) => c.status === "modified")
|
|
2179
|
-
};
|
|
2180
1937
|
if (workspaceConfigChanges.length > 0) {
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
}
|
|
1938
|
+
allCategories.push({
|
|
1939
|
+
category: "workspace-config",
|
|
1940
|
+
label: "Workspace Config",
|
|
1941
|
+
changes: workspaceConfigChanges,
|
|
1942
|
+
hasUserModifications: workspaceConfigChanges.some((change) => change.status === "modified")
|
|
1943
|
+
});
|
|
2187
1944
|
}
|
|
2188
1945
|
}
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
console.log(color__default.green("\u2713") + ` Added ${newChanges.length} AI file(s)`);
|
|
2213
|
-
updatedCount++;
|
|
2214
|
-
} else {
|
|
2215
|
-
console.log(color__default.dim(` Skipped ${category.label}`));
|
|
2216
|
-
skippedCount++;
|
|
2217
|
-
}
|
|
2218
|
-
}
|
|
2219
|
-
if (hasModified) {
|
|
2220
|
-
console.log(color__default.cyan("AI Files (existing):"));
|
|
2221
|
-
for (const change of modifiedChanges) {
|
|
2222
|
-
console.log(formatFileChange(change));
|
|
2223
|
-
}
|
|
2224
|
-
console.log();
|
|
2225
|
-
if (options.yes) {
|
|
2226
|
-
console.log(color__default.dim(" (--yes mode: keeping existing AI files)"));
|
|
2227
|
-
} else {
|
|
2228
|
-
const updateExisting = await p__namespace.confirm({
|
|
2229
|
-
message: "Update existing AI files to latest template?",
|
|
2230
|
-
initialValue: false
|
|
2231
|
-
});
|
|
2232
|
-
if (!p__namespace.isCancel(updateExisting) && updateExisting) {
|
|
2233
|
-
await applyUpdates(modifiedChanges, projectRoot);
|
|
2234
|
-
console.log(color__default.green("\u2713") + " Updated existing AI files");
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
console.log();
|
|
2239
|
-
continue;
|
|
2240
|
-
}
|
|
2241
|
-
let changesToApply = [];
|
|
2242
|
-
if (options.yes) {
|
|
2243
|
-
console.log(color__default.cyan(category.label + ":"));
|
|
2244
|
-
for (const change of [...newChanges, ...modifiedChanges]) {
|
|
2245
|
-
console.log(formatFileChange(change));
|
|
2246
|
-
}
|
|
2247
|
-
console.log();
|
|
2248
|
-
if (category.category === "workspace-config") {
|
|
2249
|
-
changesToApply = [...newChanges, ...modifiedChanges];
|
|
2250
|
-
if (changesToApply.length > 0) {
|
|
2251
|
-
console.log(color__default.dim(" (--yes mode: applying merge updates)"));
|
|
2252
|
-
}
|
|
2253
|
-
} else {
|
|
2254
|
-
changesToApply = newChanges;
|
|
2255
|
-
if (newChanges.length > 0) {
|
|
2256
|
-
console.log(color__default.dim(" (--yes mode: adding new files only)"));
|
|
2257
|
-
}
|
|
2258
|
-
}
|
|
2259
|
-
} else if (hasNew && hasModified) {
|
|
2260
|
-
const allChanges = [...newChanges, ...modifiedChanges];
|
|
2261
|
-
const selectedFiles = await p__namespace.multiselect({
|
|
2262
|
-
message: `${category.label} (+ new, ~ changed)`,
|
|
2263
|
-
options: allChanges.map((change) => ({
|
|
2264
|
-
value: change.path,
|
|
2265
|
-
label: change.status === "added" ? `+ ${change.path}` : `~ ${change.path}`
|
|
2266
|
-
})),
|
|
2267
|
-
initialValues: newChanges.map((c) => c.path),
|
|
2268
|
-
// Pre-select new files
|
|
2269
|
-
required: false
|
|
2270
|
-
});
|
|
2271
|
-
if (p__namespace.isCancel(selectedFiles)) {
|
|
2272
|
-
p__namespace.cancel("Operation cancelled.");
|
|
2273
|
-
process.exit(0);
|
|
1946
|
+
return orderUpdateCategories(allCategories);
|
|
1947
|
+
}
|
|
1948
|
+
async function processUpdateCategory(category, projectRoot, options) {
|
|
1949
|
+
const newChanges = category.changes.filter((change) => change.status === "added");
|
|
1950
|
+
const modifiedChanges = category.changes.filter((change) => change.status === "modified");
|
|
1951
|
+
const hasNew = newChanges.length > 0;
|
|
1952
|
+
const hasModified = modifiedChanges.length > 0;
|
|
1953
|
+
const hasChanges = hasNew || hasModified;
|
|
1954
|
+
if (!hasChanges) {
|
|
1955
|
+
console.log(color__default.green("\u2713") + ` ${category.label}: Up to date`);
|
|
1956
|
+
return "unchanged";
|
|
1957
|
+
}
|
|
1958
|
+
let changesToApply = [];
|
|
1959
|
+
if (options.yes) {
|
|
1960
|
+
console.log(color__default.cyan(`${category.label}:`));
|
|
1961
|
+
for (const change of [...newChanges, ...modifiedChanges]) {
|
|
1962
|
+
console.log(formatFileChange(change));
|
|
1963
|
+
}
|
|
1964
|
+
console.log();
|
|
1965
|
+
if (isMergeUpdateCategory(category.category)) {
|
|
1966
|
+
changesToApply = [...newChanges, ...modifiedChanges];
|
|
1967
|
+
if (changesToApply.length > 0) {
|
|
1968
|
+
console.log(color__default.dim(" (--yes mode: applying merge updates)"));
|
|
2274
1969
|
}
|
|
2275
|
-
|
|
2276
|
-
|
|
1970
|
+
} else {
|
|
1971
|
+
changesToApply = newChanges;
|
|
1972
|
+
if (newChanges.length > 0) {
|
|
1973
|
+
console.log(color__default.dim(" (--yes mode: adding new files only)"));
|
|
2277
1974
|
}
|
|
2278
|
-
}
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
1975
|
+
}
|
|
1976
|
+
} else {
|
|
1977
|
+
changesToApply = category.category === "ai-files-install" ? await promptForAiFileInstall(category) : await promptForUpdateSelections(category);
|
|
1978
|
+
}
|
|
1979
|
+
if (changesToApply.length > 0) {
|
|
1980
|
+
await applyUpdates(changesToApply, projectRoot);
|
|
1981
|
+
const addedCount = changesToApply.filter((change) => change.status === "added").length;
|
|
1982
|
+
const updatedFilesCount = changesToApply.filter((change) => change.status === "modified").length;
|
|
1983
|
+
const parts = [];
|
|
1984
|
+
if (addedCount > 0) parts.push(`added ${addedCount}`);
|
|
1985
|
+
if (updatedFilesCount > 0) parts.push(`updated ${updatedFilesCount}`);
|
|
1986
|
+
console.log(color__default.green("\u2713") + ` ${category.label}: ${parts.join(", ")}`);
|
|
1987
|
+
console.log();
|
|
1988
|
+
return "updated";
|
|
1989
|
+
}
|
|
1990
|
+
console.log(color__default.dim(` Skipped ${category.label}`));
|
|
1991
|
+
console.log();
|
|
1992
|
+
return "skipped";
|
|
1993
|
+
}
|
|
1994
|
+
async function handleUpdateCommand(options, handleFixCommand) {
|
|
1995
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1996
|
+
const projectRoot = monorepoRoot ?? await detectPackageRoot();
|
|
1997
|
+
if (!projectRoot) {
|
|
1998
|
+
console.log(color__default.red("\u2717") + " Could not find a project root");
|
|
1999
|
+
console.log(color__default.dim(" Run this command from inside a generated project"));
|
|
2000
|
+
process.exit(1);
|
|
2001
|
+
}
|
|
2002
|
+
const isMonorepo = monorepoRoot != null;
|
|
2003
|
+
if (isMonorepo) {
|
|
2004
|
+
const { valid, errors } = await validateWorkspace(projectRoot);
|
|
2005
|
+
if (!valid) {
|
|
2006
|
+
console.log(color__default.yellow("!") + " Workspace has issues:");
|
|
2007
|
+
for (const error of errors) {
|
|
2008
|
+
console.log(color__default.dim(` \u2022 ${error}`));
|
|
2282
2009
|
}
|
|
2283
2010
|
console.log();
|
|
2284
|
-
const
|
|
2285
|
-
message:
|
|
2011
|
+
const shouldFix = options.yes || await p__namespace.confirm({
|
|
2012
|
+
message: "Run fix first to resolve these issues?",
|
|
2286
2013
|
initialValue: true
|
|
2287
2014
|
});
|
|
2288
|
-
if (p__namespace.isCancel(
|
|
2289
|
-
|
|
2290
|
-
process.exit(
|
|
2291
|
-
}
|
|
2292
|
-
if (shouldAdd) {
|
|
2293
|
-
changesToApply = newChanges;
|
|
2294
|
-
}
|
|
2295
|
-
} else if (hasModified) {
|
|
2296
|
-
console.log(color__default.cyan(category.label + ":"));
|
|
2297
|
-
for (const change of modifiedChanges) {
|
|
2298
|
-
console.log(formatFileChange(change));
|
|
2015
|
+
if (p__namespace.isCancel(shouldFix) || !shouldFix) {
|
|
2016
|
+
console.log(color__default.dim(" Run `pnpm create krispya --fix` to fix manually"));
|
|
2017
|
+
process.exit(1);
|
|
2299
2018
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2019
|
+
const preFixConfig = await detectCurrentConfig(projectRoot);
|
|
2020
|
+
await handleFixCommand({
|
|
2021
|
+
...options,
|
|
2022
|
+
linter: options.linter ?? preFixConfig.linter,
|
|
2023
|
+
formatter: options.formatter ?? preFixConfig.formatter
|
|
2304
2024
|
});
|
|
2305
|
-
if (p__namespace.isCancel(shouldUpdate)) {
|
|
2306
|
-
p__namespace.cancel("Operation cancelled.");
|
|
2307
|
-
process.exit(0);
|
|
2308
|
-
}
|
|
2309
|
-
if (shouldUpdate) {
|
|
2310
|
-
changesToApply = modifiedChanges;
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
if (changesToApply.length > 0) {
|
|
2314
|
-
await applyUpdates(changesToApply, projectRoot);
|
|
2315
|
-
const addedCount = changesToApply.filter((c) => c.status === "added").length;
|
|
2316
|
-
const updatedFilesCount = changesToApply.filter((c) => c.status === "modified").length;
|
|
2317
|
-
const parts = [];
|
|
2318
|
-
if (addedCount > 0) parts.push(`added ${addedCount}`);
|
|
2319
|
-
if (updatedFilesCount > 0) parts.push(`updated ${updatedFilesCount}`);
|
|
2320
|
-
console.log(color__default.green("\u2713") + ` ${category.label}: ${parts.join(", ")}`);
|
|
2321
|
-
updatedCount++;
|
|
2322
|
-
} else {
|
|
2323
|
-
console.log(color__default.dim(` Skipped ${category.label}`));
|
|
2324
|
-
skippedCount++;
|
|
2325
2025
|
}
|
|
2026
|
+
}
|
|
2027
|
+
const config = await detectCurrentConfig(projectRoot, isMonorepo);
|
|
2028
|
+
if (options.linter || options.formatter) {
|
|
2029
|
+
console.log(
|
|
2030
|
+
color__default.yellow("!") + " Linter/formatter migration is not part of --update in this architecture pass"
|
|
2031
|
+
);
|
|
2032
|
+
console.log(color__default.dim(" Continuing with updates for the detected current tooling"));
|
|
2326
2033
|
console.log();
|
|
2327
2034
|
}
|
|
2035
|
+
console.log(
|
|
2036
|
+
color__default.cyan("Checking for updates...") + color__default.dim(` (${config.linter}/${config.formatter})`)
|
|
2037
|
+
);
|
|
2038
|
+
console.log();
|
|
2039
|
+
const categories = await collectUpdateCategories(projectRoot, config, isMonorepo);
|
|
2040
|
+
let updatedCount = 0;
|
|
2041
|
+
let skippedCount = 0;
|
|
2042
|
+
for (const category of categories) {
|
|
2043
|
+
const result = await processUpdateCategory(category, projectRoot, options);
|
|
2044
|
+
if (result === "updated") updatedCount++;
|
|
2045
|
+
if (result === "skipped") skippedCount++;
|
|
2046
|
+
}
|
|
2328
2047
|
if (updatedCount === 0 && skippedCount === 0) {
|
|
2329
2048
|
console.log(color__default.green("\u2713") + " Everything is up to date!");
|
|
2330
2049
|
} else if (updatedCount > 0) {
|
|
@@ -2337,7 +2056,103 @@ async function handleUpdateCommand(options) {
|
|
|
2337
2056
|
}
|
|
2338
2057
|
process.exit(0);
|
|
2339
2058
|
}
|
|
2340
|
-
|
|
2059
|
+
|
|
2060
|
+
const require$2 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
|
|
2061
|
+
async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedSettings, scope, writeGeneratedFiles) {
|
|
2062
|
+
const workspaceDirectories = await parseWorkspaceDirectories(monorepoRoot);
|
|
2063
|
+
const defaultDirectories = ["apps", "packages"];
|
|
2064
|
+
const hasCustomDirectories = workspaceDirectories.length > 0 && !workspaceDirectories.every((dir) => defaultDirectories.includes(dir));
|
|
2065
|
+
const packageType = await promptForInitialPackage();
|
|
2066
|
+
if (packageType === "skip") {
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
const defaultDir = packageType === "app" ? "apps" : "packages";
|
|
2070
|
+
const packageNameInput = await p__namespace.text({
|
|
2071
|
+
message: "Package name?",
|
|
2072
|
+
initialValue: `@${scope}/`,
|
|
2073
|
+
validate: (value) => {
|
|
2074
|
+
const validationError = workspace.validatePackageName(value);
|
|
2075
|
+
if (validationError) return validationError;
|
|
2076
|
+
const dirName = value.includes("/") ? value.split("/").pop() : value;
|
|
2077
|
+
if (!dirName) return "Package name is required";
|
|
2078
|
+
if (!hasCustomDirectories) {
|
|
2079
|
+
const targetPath = node_path.join(monorepoRoot, defaultDir, dirName);
|
|
2080
|
+
try {
|
|
2081
|
+
const { statSync } = require$2("fs");
|
|
2082
|
+
statSync(targetPath);
|
|
2083
|
+
return `Directory ${defaultDir}/${dirName} already exists`;
|
|
2084
|
+
} catch {
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
});
|
|
2089
|
+
if (p__namespace.isCancel(packageNameInput)) {
|
|
2090
|
+
return false;
|
|
2091
|
+
}
|
|
2092
|
+
const scopedName = packageNameInput;
|
|
2093
|
+
const shortName = scopedName.includes("/") ? scopedName.split("/").pop() : scopedName;
|
|
2094
|
+
const packageOptions = await promptForPackageOptions(scopedName, packageType, inheritedSettings);
|
|
2095
|
+
let targetDir = defaultDir;
|
|
2096
|
+
if (hasCustomDirectories && workspaceDirectories.length > 0) {
|
|
2097
|
+
const dirChoice = await p__namespace.select({
|
|
2098
|
+
message: "Target directory",
|
|
2099
|
+
options: workspaceDirectories.map((dir) => ({
|
|
2100
|
+
value: dir,
|
|
2101
|
+
label: dir
|
|
2102
|
+
})),
|
|
2103
|
+
initialValue: workspaceDirectories.includes(defaultDir) ? defaultDir : workspaceDirectories[0]
|
|
2104
|
+
});
|
|
2105
|
+
if (p__namespace.isCancel(dirChoice)) {
|
|
2106
|
+
return false;
|
|
2107
|
+
}
|
|
2108
|
+
targetDir = dirChoice;
|
|
2109
|
+
const targetPath = node_path.join(monorepoRoot, targetDir, shortName);
|
|
2110
|
+
try {
|
|
2111
|
+
const { statSync } = require$2("fs");
|
|
2112
|
+
statSync(targetPath);
|
|
2113
|
+
p__namespace.log.error(`Directory ${targetDir}/${shortName} already exists`);
|
|
2114
|
+
return false;
|
|
2115
|
+
} catch {
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
const relativePkgPath = node_path.join(targetDir, shortName);
|
|
2119
|
+
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
2120
|
+
packageOptions.workspaceRoot = workspaceRoot;
|
|
2121
|
+
packageOptions.name = scopedName;
|
|
2122
|
+
const workspacePackages = packageType === "app" ? await getWorkspacePackages(monorepoRoot) : [];
|
|
2123
|
+
if (workspacePackages.length > 0) {
|
|
2124
|
+
const selectedDeps = await p__namespace.multiselect({
|
|
2125
|
+
message: "Add workspace dependencies?",
|
|
2126
|
+
options: workspacePackages.map((name) => ({ value: name, label: name })),
|
|
2127
|
+
required: false
|
|
2128
|
+
});
|
|
2129
|
+
if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
|
|
2130
|
+
packageOptions.workspaceDependencies = selectedDeps;
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
const outputPath = node_path.join(monorepoRoot, relativePkgPath);
|
|
2134
|
+
const spinner = p__namespace.spinner();
|
|
2135
|
+
spinner.start("Creating package...");
|
|
2136
|
+
try {
|
|
2137
|
+
const { files } = await workspace.planProject(workspace.resolveProjectPlanInput(packageOptions));
|
|
2138
|
+
await writeGeneratedFiles(outputPath, files);
|
|
2139
|
+
spinner.stop(color__default.green.inverse(` \u2713 Package created at ${relativePkgPath}! `));
|
|
2140
|
+
const addAnother = await p__namespace.select({
|
|
2141
|
+
message: "Add another package?",
|
|
2142
|
+
options: [
|
|
2143
|
+
{ value: "no", label: "No, I'm done" },
|
|
2144
|
+
{ value: "yes", label: "Yes, add another" }
|
|
2145
|
+
],
|
|
2146
|
+
initialValue: "no"
|
|
2147
|
+
});
|
|
2148
|
+
return !p__namespace.isCancel(addAnother) && addAnother === "yes";
|
|
2149
|
+
} catch (error) {
|
|
2150
|
+
spinner.stop("Failed to create package");
|
|
2151
|
+
p__namespace.log.error(String(error));
|
|
2152
|
+
return false;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
async function handleWorkspaceCommand(name, options, writeGeneratedFiles) {
|
|
2341
2156
|
const monorepoRoot = await detectMonorepoRoot();
|
|
2342
2157
|
if (!monorepoRoot) {
|
|
2343
2158
|
console.error(color__default.red("Error:") + " --workspace flag requires being inside a monorepo");
|
|
@@ -2354,7 +2169,7 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
2354
2169
|
const defaultDir = projectType === "library" ? "packages" : "apps";
|
|
2355
2170
|
const targetDir = options.dir ?? defaultDir;
|
|
2356
2171
|
const template = options.template ?? "vanilla";
|
|
2357
|
-
const baseTemplate =
|
|
2172
|
+
const baseTemplate = workspace.getBaseTemplate(template);
|
|
2358
2173
|
const scopedName = name.startsWith("@") ? name : `@${scope}/${name}`;
|
|
2359
2174
|
const fullPackagePath = node_path.join(monorepoRoot, targetDir, name);
|
|
2360
2175
|
try {
|
|
@@ -2374,7 +2189,7 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
2374
2189
|
const isLibrary = projectType === "library";
|
|
2375
2190
|
const relativePkgPath = node_path.join(targetDir, name);
|
|
2376
2191
|
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
2377
|
-
const
|
|
2192
|
+
const projectOptions = {
|
|
2378
2193
|
name: scopedName,
|
|
2379
2194
|
projectType,
|
|
2380
2195
|
libraryBundler: isLibrary ? options.bundler ?? "unbuild" : void 0,
|
|
@@ -2400,12 +2215,9 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
2400
2215
|
triplex: options.triplex ? {} : void 0
|
|
2401
2216
|
}
|
|
2402
2217
|
};
|
|
2403
|
-
generateOptions.packageManager = await index.resolvePackageManager(generateOptions);
|
|
2404
|
-
generateOptions.engine = await index.resolveEngine(generateOptions);
|
|
2405
|
-
generateOptions.versions = await index.resolveProjectPackageVersions(generateOptions);
|
|
2406
2218
|
console.log(color__default.cyan("Creating") + ` ${scopedName} in ${targetDir}/${name}...`);
|
|
2407
2219
|
try {
|
|
2408
|
-
const files =
|
|
2220
|
+
const { files } = await workspace.planProject(workspace.resolveProjectPlanInput(projectOptions));
|
|
2409
2221
|
await writeGeneratedFiles(fullPackagePath, files);
|
|
2410
2222
|
console.log(color__default.green("\u2713") + ` Created ${scopedName} at ${targetDir}/${name}`);
|
|
2411
2223
|
process.exit(0);
|
|
@@ -2415,68 +2227,126 @@ async function handleWorkspaceCommand(name, options) {
|
|
|
2415
2227
|
process.exit(1);
|
|
2416
2228
|
}
|
|
2417
2229
|
}
|
|
2418
|
-
async function
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
engine: generateOptions.engine,
|
|
2427
|
-
versions: generateOptions.versions
|
|
2230
|
+
async function handleInteractiveMonorepoMode(monorepoRoot, writeGeneratedFiles) {
|
|
2231
|
+
const choice = await p__namespace.select({
|
|
2232
|
+
message: "Detected monorepo workspace",
|
|
2233
|
+
options: [
|
|
2234
|
+
{ value: "add", label: "Add new package to this workspace" },
|
|
2235
|
+
{ value: "standalone", label: "Create single-package workspace" }
|
|
2236
|
+
],
|
|
2237
|
+
initialValue: "add"
|
|
2428
2238
|
});
|
|
2429
|
-
|
|
2430
|
-
|
|
2239
|
+
if (p__namespace.isCancel(choice)) {
|
|
2240
|
+
p__namespace.cancel("Operation cancelled.");
|
|
2241
|
+
process.exit(0);
|
|
2242
|
+
}
|
|
2243
|
+
if (choice === "add") {
|
|
2244
|
+
const inheritedSettings = await detectWorkspaceSettings(monorepoRoot);
|
|
2245
|
+
const hasSettings = Object.values(inheritedSettings).some(Boolean);
|
|
2246
|
+
if (hasSettings) {
|
|
2247
|
+
const settingsInfo = [
|
|
2248
|
+
inheritedSettings.linter && `linter: ${inheritedSettings.linter}`,
|
|
2249
|
+
inheritedSettings.formatter && `formatter: ${inheritedSettings.formatter}`,
|
|
2250
|
+
inheritedSettings.packageManager && `pm: ${inheritedSettings.packageManager.name}`
|
|
2251
|
+
].filter(Boolean).join(", ");
|
|
2252
|
+
p__namespace.log.info(`Using workspace settings (${settingsInfo})`);
|
|
2253
|
+
}
|
|
2254
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
2255
|
+
let addMore = true;
|
|
2256
|
+
while (addMore) {
|
|
2257
|
+
addMore = await createPackageInWorkspace(
|
|
2258
|
+
monorepoRoot,
|
|
2259
|
+
inheritedSettings.packageManager?.name ?? "pnpm",
|
|
2260
|
+
inheritedSettings,
|
|
2261
|
+
scope,
|
|
2262
|
+
writeGeneratedFiles
|
|
2263
|
+
);
|
|
2264
|
+
}
|
|
2265
|
+
p__namespace.note([`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"), "Next steps");
|
|
2266
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
2267
|
+
process.exit(0);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
|
|
2272
|
+
const pkg = require$1("../package.json");
|
|
2273
|
+
const META_OPTIONS = [
|
|
2274
|
+
"clearConfig",
|
|
2275
|
+
"configPath",
|
|
2276
|
+
"check",
|
|
2277
|
+
"fix",
|
|
2278
|
+
"update",
|
|
2279
|
+
"yes",
|
|
2280
|
+
"workspace",
|
|
2281
|
+
"path",
|
|
2282
|
+
"dir"
|
|
2283
|
+
];
|
|
2284
|
+
function hasConfigOptions(options) {
|
|
2285
|
+
return Object.keys(options).some(
|
|
2286
|
+
(key) => !META_OPTIONS.includes(key)
|
|
2287
|
+
);
|
|
2288
|
+
}
|
|
2289
|
+
async function writeGeneratedFiles(basePath, files) {
|
|
2290
|
+
const filePaths = Object.keys(files).sort();
|
|
2291
|
+
for (const filePath of filePaths) {
|
|
2292
|
+
const fullFilePath = node_path.join(basePath, filePath);
|
|
2293
|
+
await promises$1.mkdir(node_path.dirname(fullFilePath), { recursive: true });
|
|
2294
|
+
const file = files[filePath];
|
|
2295
|
+
if (file.type === "text") {
|
|
2296
|
+
await promises$1.writeFile(fullFilePath, file.content);
|
|
2297
|
+
} else {
|
|
2298
|
+
const response = await undici.fetch(file.url);
|
|
2299
|
+
await promises$1.writeFile(fullFilePath, response.body);
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
async function handleMonorepoCreation(projectOptions, isNonInteractive) {
|
|
2304
|
+
const packageManager = workspace.getPackageManagerName(projectOptions.packageManager);
|
|
2305
|
+
const projectPath = node_path.join(node_process.cwd(), projectOptions.name);
|
|
2431
2306
|
const spinner = p__namespace.spinner();
|
|
2432
2307
|
spinner.start("Creating monorepo workspace...");
|
|
2433
2308
|
try {
|
|
2434
|
-
const
|
|
2435
|
-
name:
|
|
2436
|
-
linter:
|
|
2437
|
-
formatter:
|
|
2438
|
-
packageManager:
|
|
2309
|
+
const planInput = workspace.resolveWorkspacePlanInput({
|
|
2310
|
+
name: projectOptions.name,
|
|
2311
|
+
linter: projectOptions.linter ?? "oxlint",
|
|
2312
|
+
formatter: projectOptions.formatter ?? "prettier",
|
|
2313
|
+
packageManager: projectOptions.packageManager ?? {
|
|
2439
2314
|
name: packageManager
|
|
2440
2315
|
},
|
|
2441
|
-
pnpmManageVersions:
|
|
2442
|
-
engine:
|
|
2443
|
-
versions:
|
|
2444
|
-
aiPlatforms:
|
|
2316
|
+
pnpmManageVersions: projectOptions.pnpmManageVersions,
|
|
2317
|
+
engine: projectOptions.engine,
|
|
2318
|
+
versions: projectOptions.versions,
|
|
2319
|
+
aiPlatforms: projectOptions.aiPlatforms,
|
|
2320
|
+
ide: projectOptions.ide ?? "vscode"
|
|
2445
2321
|
});
|
|
2446
|
-
const
|
|
2447
|
-
|
|
2448
|
-
const fullFilePath = node_path.join(projectPath, filePath);
|
|
2449
|
-
await promises$1.mkdir(node_path.dirname(fullFilePath), { recursive: true });
|
|
2450
|
-
const file = files[filePath];
|
|
2451
|
-
if (file.type === "text") {
|
|
2452
|
-
await promises$1.writeFile(fullFilePath, file.content);
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2322
|
+
const { files } = await workspace.planWorkspace(planInput);
|
|
2323
|
+
await writeGeneratedFiles(projectPath, files);
|
|
2455
2324
|
spinner.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
|
|
2456
2325
|
if (isNonInteractive) {
|
|
2457
2326
|
process.exit(0);
|
|
2458
2327
|
}
|
|
2459
2328
|
const newWorkspaceSettings = {
|
|
2460
|
-
linter:
|
|
2461
|
-
formatter:
|
|
2462
|
-
packageManager:
|
|
2329
|
+
linter: projectOptions.linter,
|
|
2330
|
+
formatter: projectOptions.formatter,
|
|
2331
|
+
packageManager: projectOptions.packageManager ?? {
|
|
2463
2332
|
name: packageManager
|
|
2464
2333
|
},
|
|
2465
|
-
engine:
|
|
2466
|
-
pnpmManageVersions:
|
|
2334
|
+
engine: projectOptions.engine,
|
|
2335
|
+
pnpmManageVersions: projectOptions.pnpmManageVersions
|
|
2467
2336
|
};
|
|
2468
|
-
const scope =
|
|
2337
|
+
const scope = projectOptions.name;
|
|
2469
2338
|
let addMore = true;
|
|
2470
2339
|
while (addMore) {
|
|
2471
2340
|
addMore = await createPackageInWorkspace(
|
|
2472
2341
|
projectPath,
|
|
2473
2342
|
packageManager,
|
|
2474
2343
|
newWorkspaceSettings,
|
|
2475
|
-
scope
|
|
2344
|
+
scope,
|
|
2345
|
+
writeGeneratedFiles
|
|
2476
2346
|
);
|
|
2477
2347
|
}
|
|
2478
2348
|
const nextSteps = [
|
|
2479
|
-
`cd ${
|
|
2349
|
+
`cd ${projectOptions.name}`,
|
|
2480
2350
|
`${packageManager} install`,
|
|
2481
2351
|
`${packageManager} run dev`
|
|
2482
2352
|
].join("\n");
|
|
@@ -2489,33 +2359,26 @@ async function handleMonorepoCreation(generateOptions, isNonInteractive) {
|
|
|
2489
2359
|
process.exit(1);
|
|
2490
2360
|
}
|
|
2491
2361
|
}
|
|
2492
|
-
async function
|
|
2493
|
-
const base =
|
|
2362
|
+
async function handleSingleWorkspaceCreation(projectOptions, isNonInteractive) {
|
|
2363
|
+
const base = projectOptions.template ? workspace.getBaseTemplate(projectOptions.template) : "vanilla";
|
|
2494
2364
|
const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
|
|
2495
|
-
|
|
2496
|
-
const
|
|
2497
|
-
|
|
2498
|
-
generateOptions.aiPlatforms = aiPlatforms;
|
|
2499
|
-
}
|
|
2500
|
-
const packageManager = index.getPackageManagerName(generateOptions.packageManager);
|
|
2501
|
-
const isLibrary = generateOptions.projectType === "library";
|
|
2502
|
-
generateOptions.packageManager = await index.resolvePackageManager(generateOptions);
|
|
2503
|
-
generateOptions.engine = await index.resolveEngine(generateOptions);
|
|
2504
|
-
generateOptions.versions = await index.resolveProjectPackageVersions(generateOptions);
|
|
2505
|
-
const projectPath = node_path.join(node_process.cwd(), generateOptions.name);
|
|
2365
|
+
projectOptions.name ??= defaultFallbackName;
|
|
2366
|
+
const packageManager = workspace.getPackageManagerName(projectOptions.packageManager);
|
|
2367
|
+
const projectPath = node_path.join(node_process.cwd(), projectOptions.name);
|
|
2506
2368
|
const spinner = p__namespace.spinner();
|
|
2507
2369
|
spinner.start("Creating project...");
|
|
2508
2370
|
try {
|
|
2509
|
-
const
|
|
2371
|
+
const planInput = workspace.resolveProjectPlanInput(projectOptions);
|
|
2372
|
+
const { files } = await workspace.planProject(planInput);
|
|
2510
2373
|
await writeGeneratedFiles(projectPath, files);
|
|
2511
2374
|
spinner.stop(color__default.green.inverse(" \u2713 Project created! "));
|
|
2512
2375
|
if (isNonInteractive) process.exit(0);
|
|
2513
|
-
const nextSteps =
|
|
2514
|
-
`cd ${
|
|
2376
|
+
const nextSteps = projectOptions.projectType === "library" ? [
|
|
2377
|
+
`cd ${projectOptions.name}`,
|
|
2515
2378
|
`${packageManager} install`,
|
|
2516
2379
|
`${packageManager} run build`
|
|
2517
2380
|
].join("\n") : [
|
|
2518
|
-
`cd ${
|
|
2381
|
+
`cd ${projectOptions.name}`,
|
|
2519
2382
|
`${packageManager} install`,
|
|
2520
2383
|
`${packageManager} run dev`
|
|
2521
2384
|
].join("\n");
|
|
@@ -2527,45 +2390,6 @@ async function handleStandaloneProjectCreation(generateOptions, isNonInteractive
|
|
|
2527
2390
|
process.exit(1);
|
|
2528
2391
|
}
|
|
2529
2392
|
}
|
|
2530
|
-
async function handleInteractiveMonorepoMode(monorepoRoot) {
|
|
2531
|
-
const choice = await p__namespace.select({
|
|
2532
|
-
message: "Detected monorepo workspace",
|
|
2533
|
-
options: [
|
|
2534
|
-
{ value: "add", label: "Add new package to this workspace" },
|
|
2535
|
-
{ value: "standalone", label: "Create standalone project" }
|
|
2536
|
-
],
|
|
2537
|
-
initialValue: "add"
|
|
2538
|
-
});
|
|
2539
|
-
if (p__namespace.isCancel(choice)) {
|
|
2540
|
-
p__namespace.cancel("Operation cancelled.");
|
|
2541
|
-
process.exit(0);
|
|
2542
|
-
}
|
|
2543
|
-
if (choice === "add") {
|
|
2544
|
-
const inheritedSettings = await detectWorkspaceSettings(monorepoRoot);
|
|
2545
|
-
const hasSettings = Object.values(inheritedSettings).some(Boolean);
|
|
2546
|
-
if (hasSettings) {
|
|
2547
|
-
const settingsInfo = [
|
|
2548
|
-
inheritedSettings.linter && `linter: ${inheritedSettings.linter}`,
|
|
2549
|
-
inheritedSettings.formatter && `formatter: ${inheritedSettings.formatter}`,
|
|
2550
|
-
inheritedSettings.packageManager && `pm: ${inheritedSettings.packageManager.name}`
|
|
2551
|
-
].filter(Boolean).join(", ");
|
|
2552
|
-
p__namespace.log.info(`Using workspace settings (${settingsInfo})`);
|
|
2553
|
-
}
|
|
2554
|
-
const scope = await getMonorepoScope(monorepoRoot);
|
|
2555
|
-
let addMore = true;
|
|
2556
|
-
while (addMore) {
|
|
2557
|
-
addMore = await createPackageInWorkspace(
|
|
2558
|
-
monorepoRoot,
|
|
2559
|
-
inheritedSettings.packageManager?.name ?? "pnpm",
|
|
2560
|
-
inheritedSettings,
|
|
2561
|
-
scope
|
|
2562
|
-
);
|
|
2563
|
-
}
|
|
2564
|
-
p__namespace.note([`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"), "Next steps");
|
|
2565
|
-
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
2566
|
-
process.exit(0);
|
|
2567
|
-
}
|
|
2568
|
-
}
|
|
2569
2393
|
async function main() {
|
|
2570
2394
|
const program = new commander.Command().name("create-krispya").description("CLI for creating Vanilla, React, and React Three Fiber projects").argument("[name]", "name for the project").option("--type <type>", "project type: app or library (default: app)").option(
|
|
2571
2395
|
"--bundler <bundler>",
|
|
@@ -2573,7 +2397,7 @@ async function main() {
|
|
|
2573
2397
|
).option(
|
|
2574
2398
|
"--template <type>",
|
|
2575
2399
|
"project template: vanilla, vanilla-js, react, react-js, r3f, r3f-js (default: vanilla)"
|
|
2576
|
-
).option("--linter <type>", "linter: eslint, oxlint, or biome (default: oxlint)").option("--formatter <type>", "formatter: prettier, oxfmt, or biome (default: prettier)").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("--package-manager <manager>", "specify package manager (e.g. npm, yarn, pnpm)").option(
|
|
2400
|
+
).option("--linter <type>", "linter: eslint, oxlint, or biome (default: oxlint)").option("--formatter <type>", "formatter: prettier, oxfmt, or biome (default: prettier)").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("--package-manager <manager>", "specify package manager (e.g. npm, yarn, pnpm)").option("--ide <ide>", "IDE files: vscode or none (default: vscode)").option(
|
|
2577
2401
|
"--pnpm-manage-versions",
|
|
2578
2402
|
"enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
|
|
2579
2403
|
).option(
|
|
@@ -2582,10 +2406,7 @@ async function main() {
|
|
|
2582
2406
|
).option(
|
|
2583
2407
|
"--node-version <version>",
|
|
2584
2408
|
'set Node.js version for engines.node field (default: "latest")'
|
|
2585
|
-
).option("--workspace", "Add package to current monorepo workspace (non-interactive)").option("--dir <directory>", "Target directory for --workspace (default: apps/ or packages/)").option("--clear-config", "Clear saved preferences").option("--config-path", "Print the path to the config file").option("--check", "Check if current directory is in a valid monorepo workspace").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(
|
|
2586
|
-
"--path <directory>",
|
|
2587
|
-
"Run in specified directory instead of current working directory"
|
|
2588
|
-
).action(async (name, options) => {
|
|
2409
|
+
).option("--workspace", "Add package to current monorepo workspace (non-interactive)").option("--dir <directory>", "Target directory for --workspace (default: apps/ or packages/)").option("--clear-config", "Clear saved preferences").option("--config-path", "Print the path to the config file").option("--check", "Check if current directory is in a valid monorepo workspace").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("--path <directory>", "Run in specified directory instead of current working directory").action(async (name, options) => {
|
|
2589
2410
|
if (options.path) {
|
|
2590
2411
|
process.chdir(options.path);
|
|
2591
2412
|
}
|
|
@@ -2598,6 +2419,10 @@ async function main() {
|
|
|
2598
2419
|
console.log(getConfigPath());
|
|
2599
2420
|
process.exit(0);
|
|
2600
2421
|
}
|
|
2422
|
+
if (options.ide && !["vscode", "none"].includes(options.ide)) {
|
|
2423
|
+
console.error(color__default.red("Error:") + ' --ide must be "vscode" or "none"');
|
|
2424
|
+
process.exit(1);
|
|
2425
|
+
}
|
|
2601
2426
|
if (name?.startsWith("-")) {
|
|
2602
2427
|
switch (name) {
|
|
2603
2428
|
case "--version":
|
|
@@ -2639,37 +2464,36 @@ async function main() {
|
|
|
2639
2464
|
await handleFixCommand(options);
|
|
2640
2465
|
}
|
|
2641
2466
|
if (options.update) {
|
|
2642
|
-
await handleUpdateCommand(options);
|
|
2467
|
+
await handleUpdateCommand(options, handleFixCommand);
|
|
2643
2468
|
}
|
|
2644
2469
|
if (options.dir && !options.workspace) {
|
|
2645
2470
|
console.error(color__default.red("Error:") + " --dir requires --workspace flag");
|
|
2646
|
-
console.log(
|
|
2647
|
-
color__default.dim(" Example: pnpm create krispya my-lib --workspace --dir examples")
|
|
2648
|
-
);
|
|
2471
|
+
console.log(color__default.dim(" Example: pnpm create krispya my-lib --workspace --dir examples"));
|
|
2649
2472
|
process.exit(1);
|
|
2650
2473
|
}
|
|
2651
2474
|
if (options.workspace) {
|
|
2652
|
-
await handleWorkspaceCommand(name, options);
|
|
2475
|
+
await handleWorkspaceCommand(name, options, writeGeneratedFiles);
|
|
2653
2476
|
}
|
|
2654
2477
|
console.clear();
|
|
2655
2478
|
p__namespace.intro(color__default.bgCyan(color__default.black(` create-krispya v${pkg.version} `)));
|
|
2656
2479
|
const monorepoRoot = await detectMonorepoRoot();
|
|
2657
2480
|
if (monorepoRoot && !hasConfigOptions(options)) {
|
|
2658
|
-
await handleInteractiveMonorepoMode(monorepoRoot);
|
|
2481
|
+
await handleInteractiveMonorepoMode(monorepoRoot, writeGeneratedFiles);
|
|
2659
2482
|
}
|
|
2660
|
-
let
|
|
2483
|
+
let projectOptions;
|
|
2661
2484
|
if (options.yes) {
|
|
2662
2485
|
const template = options.template ?? "vanilla";
|
|
2663
|
-
const baseTemplate =
|
|
2486
|
+
const baseTemplate = workspace.getBaseTemplate(template);
|
|
2664
2487
|
const defaultName = getDefaultProjectName(template);
|
|
2665
2488
|
const projectType = options.type ?? "app";
|
|
2666
|
-
|
|
2489
|
+
projectOptions = {
|
|
2667
2490
|
name: name || defaultName,
|
|
2668
2491
|
projectType,
|
|
2669
2492
|
libraryBundler: projectType === "library" ? options.bundler ?? "unbuild" : void 0,
|
|
2670
2493
|
template,
|
|
2671
2494
|
linter: options.linter ?? "oxlint",
|
|
2672
2495
|
formatter: options.formatter ?? "prettier",
|
|
2496
|
+
ide: options.ide ?? "vscode",
|
|
2673
2497
|
...baseTemplate === "r3f" && {
|
|
2674
2498
|
drei: options.drei ? {} : void 0,
|
|
2675
2499
|
handle: options.handle ? {} : void 0,
|
|
@@ -2696,6 +2520,7 @@ async function main() {
|
|
|
2696
2520
|
linter: options.linter,
|
|
2697
2521
|
formatter: options.formatter,
|
|
2698
2522
|
packageManager: options.packageManager,
|
|
2523
|
+
ide: options.ide,
|
|
2699
2524
|
engine: options.nodeVersion ? { name: "node", version: options.nodeVersion } : void 0,
|
|
2700
2525
|
pnpmManageVersions: options.pnpmManageVersions,
|
|
2701
2526
|
drei: options.drei,
|
|
@@ -2711,13 +2536,17 @@ async function main() {
|
|
|
2711
2536
|
triplex: options.triplex,
|
|
2712
2537
|
viverse: options.viverse
|
|
2713
2538
|
} : void 0;
|
|
2714
|
-
|
|
2539
|
+
projectOptions = await promptForOptions(name, presets);
|
|
2715
2540
|
}
|
|
2716
2541
|
const isNonInteractive = options.yes ?? false;
|
|
2717
|
-
|
|
2718
|
-
|
|
2542
|
+
const aiAgentPlatforms = await promptForAiAgentPlatforms(isNonInteractive);
|
|
2543
|
+
if (aiAgentPlatforms.length > 0) {
|
|
2544
|
+
projectOptions.aiPlatforms = aiAgentPlatforms;
|
|
2545
|
+
}
|
|
2546
|
+
if (projectOptions.projectType === "monorepo") {
|
|
2547
|
+
await handleMonorepoCreation(projectOptions, isNonInteractive);
|
|
2719
2548
|
} else {
|
|
2720
|
-
await
|
|
2549
|
+
await handleSingleWorkspaceCreation(projectOptions, isNonInteractive);
|
|
2721
2550
|
}
|
|
2722
2551
|
});
|
|
2723
2552
|
await program.parseAsync();
|