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