create-krispya 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -8
- package/dist/chunks/index.cjs +712 -325
- package/dist/chunks/index.mjs +702 -326
- package/dist/cli.cjs +1530 -587
- package/dist/cli.mjs +1534 -591
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +47 -1
- package/dist/index.d.mts +47 -1
- package/dist/index.d.ts +47 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -144,7 +144,38 @@ function formatMonorepoConfigSummary(options) {
|
|
|
144
144
|
return lines.join("\n");
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
const config = new Conf__default({
|
|
148
|
+
projectName: "create-krispya"
|
|
149
|
+
});
|
|
150
|
+
function getPreferredEditor() {
|
|
151
|
+
return config.get("preferredEditor");
|
|
152
|
+
}
|
|
153
|
+
function setPreferredEditor(editor) {
|
|
154
|
+
config.set("preferredEditor", editor);
|
|
155
|
+
}
|
|
156
|
+
function getReuseWindow() {
|
|
157
|
+
return config.get("reuseWindow") ?? false;
|
|
158
|
+
}
|
|
159
|
+
function setReuseWindow(reuse) {
|
|
160
|
+
config.set("reuseWindow", reuse);
|
|
161
|
+
}
|
|
162
|
+
function getAiFiles() {
|
|
163
|
+
return config.get("aiFiles");
|
|
164
|
+
}
|
|
165
|
+
function setAiFiles(files) {
|
|
166
|
+
config.set("aiFiles", files);
|
|
167
|
+
}
|
|
168
|
+
function clearConfig() {
|
|
169
|
+
config.clear();
|
|
170
|
+
}
|
|
171
|
+
function getConfigPath() {
|
|
172
|
+
return config.path;
|
|
173
|
+
}
|
|
174
|
+
function getCustomTemplates() {
|
|
175
|
+
return config.get("customTemplates") ?? {};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function getDefaultOptions(template, name, projectType = "app", libraryBundler, integrations, inheritedTooling) {
|
|
148
179
|
const baseTemplate = index.getBaseTemplate(template);
|
|
149
180
|
const base = {
|
|
150
181
|
name,
|
|
@@ -154,26 +185,26 @@ function getDefaultOptions(template, name, projectType = "app", libraryBundler)
|
|
|
154
185
|
packageManager: "pnpm",
|
|
155
186
|
pnpmManageVersions: true,
|
|
156
187
|
nodeVersion: "latest",
|
|
157
|
-
linter: "oxlint",
|
|
158
|
-
formatter: "oxfmt",
|
|
188
|
+
linter: inheritedTooling?.linter ?? "oxlint",
|
|
189
|
+
formatter: inheritedTooling?.formatter ?? "oxfmt",
|
|
159
190
|
// Libraries get vitest by default, apps don't
|
|
160
191
|
testing: projectType === "library" ? "vitest" : "none"
|
|
161
192
|
};
|
|
162
|
-
if (baseTemplate === "r3f") {
|
|
193
|
+
if (baseTemplate === "r3f" && integrations) {
|
|
163
194
|
return {
|
|
164
195
|
...base,
|
|
165
|
-
drei: {},
|
|
166
|
-
handle: {},
|
|
167
|
-
leva: {},
|
|
168
|
-
postprocessing: {},
|
|
169
|
-
rapier: {},
|
|
170
|
-
xr: {},
|
|
171
|
-
uikit: {},
|
|
172
|
-
offscreen: {},
|
|
173
|
-
zustand: {},
|
|
174
|
-
koota: {},
|
|
175
|
-
triplex: {},
|
|
176
|
-
viverse: {}
|
|
196
|
+
drei: integrations.includes("drei") ? {} : void 0,
|
|
197
|
+
handle: integrations.includes("handle") ? {} : void 0,
|
|
198
|
+
leva: integrations.includes("leva") ? {} : void 0,
|
|
199
|
+
postprocessing: integrations.includes("postprocessing") ? {} : void 0,
|
|
200
|
+
rapier: integrations.includes("rapier") ? {} : void 0,
|
|
201
|
+
xr: integrations.includes("xr") ? {} : void 0,
|
|
202
|
+
uikit: integrations.includes("uikit") ? {} : void 0,
|
|
203
|
+
offscreen: integrations.includes("offscreen") ? {} : void 0,
|
|
204
|
+
zustand: integrations.includes("zustand") ? {} : void 0,
|
|
205
|
+
koota: integrations.includes("koota") ? {} : void 0,
|
|
206
|
+
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
207
|
+
viverse: integrations.includes("viverse") ? {} : void 0
|
|
177
208
|
};
|
|
178
209
|
}
|
|
179
210
|
return base;
|
|
@@ -189,7 +220,33 @@ function getDefaultProjectName(template) {
|
|
|
189
220
|
return `react-three-${index.generateRandomName()}`;
|
|
190
221
|
}
|
|
191
222
|
}
|
|
192
|
-
async function
|
|
223
|
+
async function promptForR3fIntegrations() {
|
|
224
|
+
const selected = await p__namespace.multiselect({
|
|
225
|
+
message: "R3F integrations",
|
|
226
|
+
options: [
|
|
227
|
+
{ value: "drei", label: "Drei" },
|
|
228
|
+
{ value: "handle", label: "Handle" },
|
|
229
|
+
{ value: "leva", label: "Leva" },
|
|
230
|
+
{ value: "postprocessing", label: "Postprocessing" },
|
|
231
|
+
{ value: "rapier", label: "Rapier" },
|
|
232
|
+
{ value: "xr", label: "XR" },
|
|
233
|
+
{ value: "uikit", label: "UIKit" },
|
|
234
|
+
{ value: "offscreen", label: "Offscreen" },
|
|
235
|
+
{ value: "zustand", label: "Zustand" },
|
|
236
|
+
{ value: "koota", label: "Koota" },
|
|
237
|
+
{ value: "triplex", label: "Triplex" },
|
|
238
|
+
{ value: "viverse", label: "Viverse" }
|
|
239
|
+
],
|
|
240
|
+
initialValues: ["drei"],
|
|
241
|
+
required: false
|
|
242
|
+
});
|
|
243
|
+
if (p__namespace.isCancel(selected)) {
|
|
244
|
+
p__namespace.cancel("Operation cancelled.");
|
|
245
|
+
process.exit(0);
|
|
246
|
+
}
|
|
247
|
+
return selected;
|
|
248
|
+
}
|
|
249
|
+
async function promptForCustomization(template, name, projectType, integrations, inheritedTooling) {
|
|
193
250
|
let libraryBundler;
|
|
194
251
|
if (projectType === "library") {
|
|
195
252
|
const bundler = await p__namespace.select({
|
|
@@ -261,31 +318,39 @@ async function promptForCustomization(template, name, projectType) {
|
|
|
261
318
|
}
|
|
262
319
|
pnpmManageVersions = managePnpm;
|
|
263
320
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
321
|
+
let linter = inheritedTooling?.linter ?? "oxlint";
|
|
322
|
+
let formatter = inheritedTooling?.formatter ?? "oxfmt";
|
|
323
|
+
if (!inheritedTooling?.linter) {
|
|
324
|
+
const linterChoice = await p__namespace.select({
|
|
325
|
+
message: "Linter",
|
|
326
|
+
options: [
|
|
327
|
+
{ value: "oxlint", label: "Oxlint", hint: "fast, from OXC" },
|
|
328
|
+
{ value: "eslint", label: "ESLint", hint: "classic" },
|
|
329
|
+
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
330
|
+
],
|
|
331
|
+
initialValue: "oxlint"
|
|
332
|
+
});
|
|
333
|
+
if (p__namespace.isCancel(linterChoice)) {
|
|
334
|
+
p__namespace.cancel("Operation cancelled.");
|
|
335
|
+
process.exit(0);
|
|
336
|
+
}
|
|
337
|
+
linter = linterChoice;
|
|
276
338
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
p__namespace.
|
|
288
|
-
|
|
339
|
+
if (!inheritedTooling?.formatter) {
|
|
340
|
+
const formatterChoice = await p__namespace.select({
|
|
341
|
+
message: "Formatter",
|
|
342
|
+
options: [
|
|
343
|
+
{ value: "oxfmt", label: "Oxfmt", hint: "fast, Prettier-compatible" },
|
|
344
|
+
{ value: "prettier", label: "Prettier", hint: "classic" },
|
|
345
|
+
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
346
|
+
],
|
|
347
|
+
initialValue: "oxfmt"
|
|
348
|
+
});
|
|
349
|
+
if (p__namespace.isCancel(formatterChoice)) {
|
|
350
|
+
p__namespace.cancel("Operation cancelled.");
|
|
351
|
+
process.exit(0);
|
|
352
|
+
}
|
|
353
|
+
formatter = formatterChoice;
|
|
289
354
|
}
|
|
290
355
|
const testing = await p__namespace.select({
|
|
291
356
|
message: "Testing",
|
|
@@ -313,47 +378,7 @@ async function promptForCustomization(template, name, projectType) {
|
|
|
313
378
|
}
|
|
314
379
|
const baseTemplate = index.getBaseTemplate(template);
|
|
315
380
|
const finalTemplate = language === "javascript" ? `${baseTemplate}-js` : baseTemplate;
|
|
316
|
-
|
|
317
|
-
if (baseTemplate === "r3f") {
|
|
318
|
-
const selected = await p__namespace.multiselect({
|
|
319
|
-
message: "R3F integrations",
|
|
320
|
-
options: [
|
|
321
|
-
{ value: "drei", label: "Drei" },
|
|
322
|
-
{ value: "handle", label: "Handle" },
|
|
323
|
-
{ value: "leva", label: "Leva" },
|
|
324
|
-
{ value: "postprocessing", label: "Postprocessing" },
|
|
325
|
-
{ value: "rapier", label: "Rapier" },
|
|
326
|
-
{ value: "xr", label: "XR" },
|
|
327
|
-
{ value: "uikit", label: "UIKit" },
|
|
328
|
-
{ value: "offscreen", label: "Offscreen" },
|
|
329
|
-
{ value: "zustand", label: "Zustand" },
|
|
330
|
-
{ value: "koota", label: "Koota" },
|
|
331
|
-
{ value: "triplex", label: "Triplex" },
|
|
332
|
-
{ value: "viverse", label: "Viverse" }
|
|
333
|
-
],
|
|
334
|
-
initialValues: [
|
|
335
|
-
"drei",
|
|
336
|
-
"handle",
|
|
337
|
-
"leva",
|
|
338
|
-
"postprocessing",
|
|
339
|
-
"rapier",
|
|
340
|
-
"xr",
|
|
341
|
-
"uikit",
|
|
342
|
-
"offscreen",
|
|
343
|
-
"zustand",
|
|
344
|
-
"koota",
|
|
345
|
-
"triplex",
|
|
346
|
-
"viverse"
|
|
347
|
-
],
|
|
348
|
-
required: false
|
|
349
|
-
});
|
|
350
|
-
if (p__namespace.isCancel(selected)) {
|
|
351
|
-
p__namespace.cancel("Operation cancelled.");
|
|
352
|
-
process.exit(0);
|
|
353
|
-
}
|
|
354
|
-
integrations = selected;
|
|
355
|
-
}
|
|
356
|
-
return {
|
|
381
|
+
const base = {
|
|
357
382
|
name,
|
|
358
383
|
template: finalTemplate,
|
|
359
384
|
projectType,
|
|
@@ -363,8 +388,11 @@ async function promptForCustomization(template, name, projectType) {
|
|
|
363
388
|
pnpmManageVersions,
|
|
364
389
|
linter,
|
|
365
390
|
formatter,
|
|
366
|
-
testing
|
|
367
|
-
|
|
391
|
+
testing
|
|
392
|
+
};
|
|
393
|
+
if (baseTemplate === "r3f" && integrations) {
|
|
394
|
+
return {
|
|
395
|
+
...base,
|
|
368
396
|
drei: integrations.includes("drei") ? {} : void 0,
|
|
369
397
|
handle: integrations.includes("handle") ? {} : void 0,
|
|
370
398
|
leva: integrations.includes("leva") ? {} : void 0,
|
|
@@ -377,8 +405,9 @@ async function promptForCustomization(template, name, projectType) {
|
|
|
377
405
|
koota: integrations.includes("koota") ? {} : void 0,
|
|
378
406
|
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
379
407
|
viverse: integrations.includes("viverse") ? {} : void 0
|
|
380
|
-
}
|
|
381
|
-
}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return base;
|
|
382
411
|
}
|
|
383
412
|
async function promptForInitialPackage() {
|
|
384
413
|
const choice = await p__namespace.select({
|
|
@@ -423,31 +452,14 @@ async function promptForMonorepoCustomization(name) {
|
|
|
423
452
|
p__namespace.cancel("Operation cancelled.");
|
|
424
453
|
process.exit(0);
|
|
425
454
|
}
|
|
426
|
-
const
|
|
427
|
-
message: "
|
|
428
|
-
|
|
429
|
-
{ value: "pnpm", label: "pnpm" },
|
|
430
|
-
{ value: "npm", label: "npm" },
|
|
431
|
-
{ value: "yarn", label: "yarn" }
|
|
432
|
-
],
|
|
433
|
-
initialValue: "pnpm"
|
|
455
|
+
const managePnpm = await p__namespace.confirm({
|
|
456
|
+
message: "Enable manage-package-manager-versions?",
|
|
457
|
+
initialValue: true
|
|
434
458
|
});
|
|
435
|
-
if (p__namespace.isCancel(
|
|
459
|
+
if (p__namespace.isCancel(managePnpm)) {
|
|
436
460
|
p__namespace.cancel("Operation cancelled.");
|
|
437
461
|
process.exit(0);
|
|
438
462
|
}
|
|
439
|
-
let pnpmManageVersions = true;
|
|
440
|
-
if (packageManager === "pnpm") {
|
|
441
|
-
const managePnpm = await p__namespace.confirm({
|
|
442
|
-
message: "Enable manage-package-manager-versions?",
|
|
443
|
-
initialValue: true
|
|
444
|
-
});
|
|
445
|
-
if (p__namespace.isCancel(managePnpm)) {
|
|
446
|
-
p__namespace.cancel("Operation cancelled.");
|
|
447
|
-
process.exit(0);
|
|
448
|
-
}
|
|
449
|
-
pnpmManageVersions = managePnpm;
|
|
450
|
-
}
|
|
451
463
|
const linter = await p__namespace.select({
|
|
452
464
|
message: "Linter",
|
|
453
465
|
options: [
|
|
@@ -478,8 +490,8 @@ async function promptForMonorepoCustomization(name) {
|
|
|
478
490
|
name,
|
|
479
491
|
projectType: "monorepo",
|
|
480
492
|
nodeVersion,
|
|
481
|
-
packageManager,
|
|
482
|
-
pnpmManageVersions,
|
|
493
|
+
packageManager: "pnpm",
|
|
494
|
+
pnpmManageVersions: managePnpm,
|
|
483
495
|
linter,
|
|
484
496
|
formatter
|
|
485
497
|
};
|
|
@@ -497,19 +509,15 @@ async function promptForMonorepo(workspaceName) {
|
|
|
497
509
|
}),
|
|
498
510
|
"Workspace Configuration"
|
|
499
511
|
);
|
|
500
|
-
const
|
|
512
|
+
const proceed = await p__namespace.confirm({
|
|
501
513
|
message: "Proceed with these settings?",
|
|
502
|
-
|
|
503
|
-
{ value: "confirm", label: "Yes, create workspace" },
|
|
504
|
-
{ value: "customize", label: "No, let me customize" }
|
|
505
|
-
],
|
|
506
|
-
initialValue: "confirm"
|
|
514
|
+
initialValue: true
|
|
507
515
|
});
|
|
508
|
-
if (p__namespace.isCancel(
|
|
516
|
+
if (p__namespace.isCancel(proceed)) {
|
|
509
517
|
p__namespace.cancel("Operation cancelled.");
|
|
510
518
|
process.exit(0);
|
|
511
519
|
}
|
|
512
|
-
if (
|
|
520
|
+
if (proceed) {
|
|
513
521
|
return defaultOptions;
|
|
514
522
|
}
|
|
515
523
|
return promptForMonorepoCustomization(workspaceName);
|
|
@@ -549,69 +557,197 @@ async function promptForOptions(name) {
|
|
|
549
557
|
}
|
|
550
558
|
return promptForPackageOptions(projectName, projectType);
|
|
551
559
|
}
|
|
552
|
-
|
|
553
|
-
const
|
|
560
|
+
function customTemplateToOptions(customTemplate, name, projectType) {
|
|
561
|
+
const baseTemplate = customTemplate.baseTemplate;
|
|
562
|
+
const template = baseTemplate;
|
|
563
|
+
const base = {
|
|
564
|
+
name,
|
|
565
|
+
template,
|
|
566
|
+
projectType,
|
|
567
|
+
packageManager: "pnpm",
|
|
568
|
+
pnpmManageVersions: true,
|
|
569
|
+
nodeVersion: "latest",
|
|
570
|
+
linter: customTemplate.linter,
|
|
571
|
+
formatter: customTemplate.formatter,
|
|
572
|
+
testing: customTemplate.testing
|
|
573
|
+
};
|
|
574
|
+
if (baseTemplate === "r3f" && customTemplate.integrations) {
|
|
575
|
+
const integrations = customTemplate.integrations;
|
|
576
|
+
return {
|
|
577
|
+
...base,
|
|
578
|
+
drei: integrations.includes("drei") ? {} : void 0,
|
|
579
|
+
handle: integrations.includes("handle") ? {} : void 0,
|
|
580
|
+
leva: integrations.includes("leva") ? {} : void 0,
|
|
581
|
+
postprocessing: integrations.includes("postprocessing") ? {} : void 0,
|
|
582
|
+
rapier: integrations.includes("rapier") ? {} : void 0,
|
|
583
|
+
xr: integrations.includes("xr") ? {} : void 0,
|
|
584
|
+
uikit: integrations.includes("uikit") ? {} : void 0,
|
|
585
|
+
offscreen: integrations.includes("offscreen") ? {} : void 0,
|
|
586
|
+
zustand: integrations.includes("zustand") ? {} : void 0,
|
|
587
|
+
koota: integrations.includes("koota") ? {} : void 0,
|
|
588
|
+
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
589
|
+
viverse: integrations.includes("viverse") ? {} : void 0
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
return base;
|
|
593
|
+
}
|
|
594
|
+
async function promptForPackageOptions(projectName, projectType, inheritedTooling) {
|
|
595
|
+
const builtInOptions = [
|
|
596
|
+
{ value: "vanilla", label: "Vanilla" },
|
|
597
|
+
{ value: "react", label: "React" },
|
|
598
|
+
{ value: "r3f", label: "React Three Fiber" }
|
|
599
|
+
];
|
|
600
|
+
const customTemplates = getCustomTemplates();
|
|
601
|
+
const customOptions = Object.keys(customTemplates).map((name) => ({
|
|
602
|
+
value: `custom:${name}`,
|
|
603
|
+
label: name,
|
|
604
|
+
hint: "saved template"
|
|
605
|
+
}));
|
|
606
|
+
const allOptions = [...builtInOptions, ...customOptions];
|
|
607
|
+
const templateSelection = await p__namespace.select({
|
|
554
608
|
message: "Select a template",
|
|
555
|
-
options:
|
|
556
|
-
{ value: "vanilla", label: "Vanilla" },
|
|
557
|
-
{ value: "react", label: "React" },
|
|
558
|
-
{ value: "r3f", label: "React Three Fiber" }
|
|
559
|
-
],
|
|
609
|
+
options: allOptions,
|
|
560
610
|
initialValue: "vanilla"
|
|
561
611
|
});
|
|
562
|
-
if (p__namespace.isCancel(
|
|
612
|
+
if (p__namespace.isCancel(templateSelection)) {
|
|
563
613
|
p__namespace.cancel("Operation cancelled.");
|
|
564
614
|
process.exit(0);
|
|
565
615
|
}
|
|
616
|
+
const selection = templateSelection;
|
|
617
|
+
if (selection.startsWith("custom:")) {
|
|
618
|
+
const customName = selection.slice(7);
|
|
619
|
+
const customTemplate = customTemplates[customName];
|
|
620
|
+
const defaultOptions2 = customTemplateToOptions(customTemplate, projectName, projectType);
|
|
621
|
+
if (inheritedTooling?.linter) {
|
|
622
|
+
defaultOptions2.linter = inheritedTooling.linter;
|
|
623
|
+
}
|
|
624
|
+
if (inheritedTooling?.formatter) {
|
|
625
|
+
defaultOptions2.formatter = inheritedTooling.formatter;
|
|
626
|
+
}
|
|
627
|
+
const configTitle2 = inheritedTooling ? `Template: ${customName} (using workspace tooling)` : `Template: ${customName}`;
|
|
628
|
+
p__namespace.note(formatConfigSummary(defaultOptions2), configTitle2);
|
|
629
|
+
const proceed2 = await p__namespace.confirm({
|
|
630
|
+
message: "Proceed with these settings?",
|
|
631
|
+
initialValue: true
|
|
632
|
+
});
|
|
633
|
+
if (p__namespace.isCancel(proceed2)) {
|
|
634
|
+
p__namespace.cancel("Operation cancelled.");
|
|
635
|
+
process.exit(0);
|
|
636
|
+
}
|
|
637
|
+
if (proceed2) {
|
|
638
|
+
return defaultOptions2;
|
|
639
|
+
}
|
|
640
|
+
return promptForCustomization(
|
|
641
|
+
customTemplate.baseTemplate,
|
|
642
|
+
projectName,
|
|
643
|
+
projectType,
|
|
644
|
+
customTemplate.integrations,
|
|
645
|
+
inheritedTooling
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
const template = selection;
|
|
649
|
+
const baseTemplate = index.getBaseTemplate(template);
|
|
650
|
+
let integrations;
|
|
651
|
+
if (baseTemplate === "r3f") {
|
|
652
|
+
integrations = await promptForR3fIntegrations();
|
|
653
|
+
}
|
|
566
654
|
const defaultOptions = getDefaultOptions(
|
|
567
655
|
template,
|
|
568
656
|
projectName,
|
|
569
|
-
projectType
|
|
657
|
+
projectType,
|
|
658
|
+
void 0,
|
|
659
|
+
integrations,
|
|
660
|
+
inheritedTooling
|
|
570
661
|
);
|
|
571
|
-
|
|
572
|
-
|
|
662
|
+
const configTitle = inheritedTooling ? "Template Configuration (using workspace tooling)" : "Template Configuration";
|
|
663
|
+
p__namespace.note(formatConfigSummary(defaultOptions), configTitle);
|
|
664
|
+
const proceed = await p__namespace.confirm({
|
|
573
665
|
message: "Proceed with these settings?",
|
|
574
|
-
|
|
575
|
-
{ value: "confirm", label: "Yes, create project" },
|
|
576
|
-
{ value: "customize", label: "No, let me customize" }
|
|
577
|
-
],
|
|
578
|
-
initialValue: "confirm"
|
|
666
|
+
initialValue: true
|
|
579
667
|
});
|
|
580
|
-
if (p__namespace.isCancel(
|
|
668
|
+
if (p__namespace.isCancel(proceed)) {
|
|
581
669
|
p__namespace.cancel("Operation cancelled.");
|
|
582
670
|
process.exit(0);
|
|
583
671
|
}
|
|
584
|
-
if (
|
|
672
|
+
if (proceed) {
|
|
585
673
|
return defaultOptions;
|
|
586
674
|
}
|
|
587
|
-
return promptForCustomization(
|
|
588
|
-
template,
|
|
589
|
-
projectName,
|
|
590
|
-
projectType
|
|
591
|
-
);
|
|
675
|
+
return promptForCustomization(template, projectName, projectType, integrations, inheritedTooling);
|
|
592
676
|
}
|
|
593
677
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
function getReuseWindow() {
|
|
604
|
-
return config.get("reuseWindow") ?? false;
|
|
605
|
-
}
|
|
606
|
-
function setReuseWindow(reuse) {
|
|
607
|
-
config.set("reuseWindow", reuse);
|
|
678
|
+
async function checkAnyExists(paths) {
|
|
679
|
+
for (const path of paths) {
|
|
680
|
+
try {
|
|
681
|
+
await promises.access(path, promises.constants.F_OK);
|
|
682
|
+
return true;
|
|
683
|
+
} catch {
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return false;
|
|
608
687
|
}
|
|
609
|
-
function
|
|
610
|
-
|
|
688
|
+
async function validateWorkspace(monorepoRoot) {
|
|
689
|
+
const errors = [];
|
|
690
|
+
const tsConfigPath = path.join(monorepoRoot, ".config/typescript/package.json");
|
|
691
|
+
try {
|
|
692
|
+
await promises.access(tsConfigPath, promises.constants.F_OK);
|
|
693
|
+
} catch {
|
|
694
|
+
errors.push("Missing .config/typescript package");
|
|
695
|
+
}
|
|
696
|
+
const linterPaths = [
|
|
697
|
+
path.join(monorepoRoot, ".config/oxlint/package.json"),
|
|
698
|
+
path.join(monorepoRoot, ".config/eslint/package.json"),
|
|
699
|
+
path.join(monorepoRoot, "eslint.config.js"),
|
|
700
|
+
path.join(monorepoRoot, "biome.json")
|
|
701
|
+
];
|
|
702
|
+
const hasLinter = await checkAnyExists(linterPaths);
|
|
703
|
+
if (!hasLinter) {
|
|
704
|
+
errors.push(
|
|
705
|
+
"Missing linter config (.config/oxlint, .config/eslint, eslint.config.js, or biome.json)"
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
const formatterPaths = [
|
|
709
|
+
path.join(monorepoRoot, ".config/oxfmt/package.json"),
|
|
710
|
+
path.join(monorepoRoot, ".config/prettier/package.json"),
|
|
711
|
+
path.join(monorepoRoot, ".prettierrc.json"),
|
|
712
|
+
path.join(monorepoRoot, "biome.json")
|
|
713
|
+
];
|
|
714
|
+
const hasFormatter = await checkAnyExists(formatterPaths);
|
|
715
|
+
if (!hasFormatter) {
|
|
716
|
+
errors.push(
|
|
717
|
+
"Missing formatter config (.config/oxfmt, .config/prettier, .prettierrc.json, or biome.json)"
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
return { valid: errors.length === 0, errors };
|
|
611
721
|
}
|
|
612
722
|
|
|
613
723
|
const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
|
|
614
724
|
const pkg = require$1("../package.json");
|
|
725
|
+
async function fileExists(path) {
|
|
726
|
+
try {
|
|
727
|
+
await promises.access(path, fs.constants.F_OK);
|
|
728
|
+
return true;
|
|
729
|
+
} catch {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
async function writeGeneratedFiles(basePath, files) {
|
|
734
|
+
const filePaths = Object.keys(files).sort();
|
|
735
|
+
for (const filePath of filePaths) {
|
|
736
|
+
const fullFilePath = path.join(basePath, filePath);
|
|
737
|
+
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
738
|
+
const file = files[filePath];
|
|
739
|
+
if (file.type === "text") {
|
|
740
|
+
await promises.writeFile(fullFilePath, file.content);
|
|
741
|
+
} else {
|
|
742
|
+
const response = await undici.fetch(file.url);
|
|
743
|
+
await promises.writeFile(fullFilePath, response.body);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function calculateWorkspaceRoot(packagePath) {
|
|
748
|
+
const segments = packagePath.split(/[/\\]/).filter(Boolean);
|
|
749
|
+
return segments.map(() => "..").join("/");
|
|
750
|
+
}
|
|
615
751
|
async function detectMonorepoRoot() {
|
|
616
752
|
let currentDir = process$1.cwd();
|
|
617
753
|
const root = path.resolve("/");
|
|
@@ -629,473 +765,1280 @@ async function detectMonorepoRoot() {
|
|
|
629
765
|
}
|
|
630
766
|
return null;
|
|
631
767
|
}
|
|
632
|
-
async function
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
"
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
768
|
+
async function parseWorkspaceDirectories(monorepoRoot) {
|
|
769
|
+
try {
|
|
770
|
+
const workspaceFile = path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
771
|
+
const content = await promises.readFile(workspaceFile, "utf-8");
|
|
772
|
+
return index.parseWorkspaceYamlContent(content);
|
|
773
|
+
} catch {
|
|
774
|
+
return [];
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
async function detectWorkspaceTooling(monorepoRoot) {
|
|
778
|
+
try {
|
|
779
|
+
const pkgPath = path.join(monorepoRoot, "package.json");
|
|
780
|
+
const content = await promises.readFile(pkgPath, "utf-8");
|
|
781
|
+
const pkgJson = JSON.parse(content);
|
|
782
|
+
const devDeps = pkgJson.devDependencies ?? {};
|
|
783
|
+
const linter = devDeps.oxlint ? "oxlint" : devDeps.eslint ? "eslint" : devDeps["@biomejs/biome"] ? "biome" : void 0;
|
|
784
|
+
const formatter = devDeps.oxfmt ? "oxfmt" : devDeps.prettier ? "prettier" : devDeps["@biomejs/biome"] ? "biome" : void 0;
|
|
785
|
+
return { linter, formatter };
|
|
786
|
+
} catch {
|
|
787
|
+
return {};
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
async function detectExistingConfigs(monorepoRoot) {
|
|
791
|
+
const configs = {};
|
|
792
|
+
const eslintPath = path.join(monorepoRoot, "eslint.config.js");
|
|
793
|
+
if (await fileExists(eslintPath)) {
|
|
794
|
+
configs.linter = "eslint";
|
|
795
|
+
configs.eslintConfigPath = eslintPath;
|
|
796
|
+
}
|
|
797
|
+
const prettierPath = path.join(monorepoRoot, ".prettierrc.json");
|
|
798
|
+
if (await fileExists(prettierPath)) {
|
|
799
|
+
configs.formatter = "prettier";
|
|
800
|
+
configs.prettierConfigPath = prettierPath;
|
|
801
|
+
}
|
|
802
|
+
const biomePath = path.join(monorepoRoot, "biome.json");
|
|
803
|
+
if (await fileExists(biomePath)) {
|
|
804
|
+
configs.biomeConfigPath = biomePath;
|
|
805
|
+
if (!configs.linter) configs.linter = "biome";
|
|
806
|
+
if (!configs.formatter) configs.formatter = "biome";
|
|
807
|
+
}
|
|
808
|
+
return configs;
|
|
809
|
+
}
|
|
810
|
+
async function getMonorepoScope(monorepoRoot) {
|
|
811
|
+
try {
|
|
812
|
+
const pkgPath = path.join(monorepoRoot, "package.json");
|
|
813
|
+
const content = await promises.readFile(pkgPath, "utf-8");
|
|
814
|
+
const pkgJson = JSON.parse(content);
|
|
815
|
+
if (pkgJson.name) {
|
|
816
|
+
return pkgJson.name.replace(/^@/, "").replace(/\/.*$/, "");
|
|
653
817
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
if (p__namespace.isCancel(choice)) {
|
|
667
|
-
p__namespace.cancel("Operation cancelled.");
|
|
668
|
-
process.exit(0);
|
|
669
|
-
}
|
|
670
|
-
if (choice === "add") {
|
|
671
|
-
const packageType = await promptForInitialPackage();
|
|
672
|
-
if (packageType === "skip") {
|
|
673
|
-
p__namespace.cancel("Operation cancelled.");
|
|
674
|
-
process.exit(0);
|
|
675
|
-
}
|
|
676
|
-
const packageName = await p__namespace.text({
|
|
677
|
-
message: "Package name?",
|
|
678
|
-
placeholder: packageType === "app" ? "my-app" : "my-package",
|
|
679
|
-
validate: (value) => {
|
|
680
|
-
if (!value.length) return "Package name is required";
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
if (p__namespace.isCancel(packageName)) {
|
|
684
|
-
p__namespace.cancel("Operation cancelled.");
|
|
685
|
-
process.exit(0);
|
|
686
|
-
}
|
|
687
|
-
const targetDir = packageType === "app" ? "apps" : "packages";
|
|
688
|
-
const packagePath = path.join(targetDir, packageName);
|
|
689
|
-
const workspaceRoot = "../..";
|
|
690
|
-
const packageOptions = await promptForPackageOptions(packageName, packageType);
|
|
691
|
-
packageOptions.workspaceRoot = workspaceRoot;
|
|
692
|
-
packageOptions.name = packageName;
|
|
693
|
-
const packageManager2 = packageOptions.packageManager || "pnpm";
|
|
694
|
-
if (packageManager2 === "pnpm") {
|
|
695
|
-
packageOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
696
|
-
}
|
|
697
|
-
const nodeVersion2 = packageOptions.nodeVersion ?? "latest";
|
|
698
|
-
if (nodeVersion2 === "latest") {
|
|
699
|
-
packageOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
700
|
-
}
|
|
701
|
-
const versions2 = {};
|
|
702
|
-
const versionPromises2 = [];
|
|
703
|
-
const pkgIsLibrary = packageOptions.projectType === "library";
|
|
704
|
-
const pkgTesting = packageOptions.testing ?? (pkgIsLibrary ? "vitest" : "none");
|
|
705
|
-
if (pkgTesting === "vitest") {
|
|
706
|
-
versionPromises2.push(
|
|
707
|
-
index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
708
|
-
versions2.vitest = v;
|
|
709
|
-
})
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
if (!pkgIsLibrary) {
|
|
713
|
-
versionPromises2.push(
|
|
714
|
-
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
715
|
-
versions2.vite = v;
|
|
716
|
-
})
|
|
717
|
-
);
|
|
718
|
-
}
|
|
719
|
-
const linter2 = packageOptions.linter ?? "oxlint";
|
|
720
|
-
if (linter2 === "eslint") {
|
|
721
|
-
versionPromises2.push(
|
|
722
|
-
index.getLatestNpmVersion("eslint", "9.17.0").then((v) => {
|
|
723
|
-
versions2.eslint = v;
|
|
724
|
-
})
|
|
725
|
-
);
|
|
726
|
-
} else if (linter2 === "oxlint") {
|
|
727
|
-
versionPromises2.push(
|
|
728
|
-
index.getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
|
|
729
|
-
versions2.oxlint = v;
|
|
730
|
-
})
|
|
731
|
-
);
|
|
732
|
-
} else if (linter2 === "biome") {
|
|
733
|
-
versionPromises2.push(
|
|
734
|
-
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
735
|
-
versions2.biome = v;
|
|
736
|
-
})
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
const formatter2 = packageOptions.formatter ?? "oxfmt";
|
|
740
|
-
if (formatter2 === "prettier") {
|
|
741
|
-
versionPromises2.push(
|
|
742
|
-
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
743
|
-
versions2.prettier = v;
|
|
744
|
-
})
|
|
745
|
-
);
|
|
746
|
-
} else if (formatter2 === "oxfmt") {
|
|
747
|
-
versionPromises2.push(
|
|
748
|
-
index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
|
|
749
|
-
versions2.oxfmt = v;
|
|
750
|
-
})
|
|
751
|
-
);
|
|
752
|
-
} else if (formatter2 === "biome" && linter2 !== "biome") {
|
|
753
|
-
versionPromises2.push(
|
|
754
|
-
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
755
|
-
versions2.biome = v;
|
|
756
|
-
})
|
|
757
|
-
);
|
|
758
|
-
}
|
|
759
|
-
await Promise.all(versionPromises2);
|
|
760
|
-
packageOptions.versions = versions2;
|
|
761
|
-
const basePath2 = path.join(monorepoRoot, packagePath);
|
|
762
|
-
const s2 = p__namespace.spinner();
|
|
763
|
-
s2.start("Creating package...");
|
|
818
|
+
} catch {
|
|
819
|
+
}
|
|
820
|
+
return monorepoRoot.split(/[/\\]/).pop() ?? "workspace";
|
|
821
|
+
}
|
|
822
|
+
async function getWorkspacePackages(monorepoRoot) {
|
|
823
|
+
const packagesDir = path.join(monorepoRoot, "packages");
|
|
824
|
+
const packages = [];
|
|
825
|
+
try {
|
|
826
|
+
const { readdir } = await import('fs/promises');
|
|
827
|
+
const entries = await readdir(packagesDir, { withFileTypes: true });
|
|
828
|
+
for (const entry of entries) {
|
|
829
|
+
if (entry.isDirectory()) {
|
|
764
830
|
try {
|
|
765
|
-
const
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
} else {
|
|
774
|
-
const response = await undici.fetch(file.url);
|
|
775
|
-
await promises.writeFile(fullFilePath, response.body);
|
|
776
|
-
}
|
|
831
|
+
const pkgJsonPath = path.join(packagesDir, entry.name, "package.json");
|
|
832
|
+
const content = await promises.readFile(pkgJsonPath, "utf-8");
|
|
833
|
+
const pkgJson = JSON.parse(content);
|
|
834
|
+
if (pkgJson.name) {
|
|
835
|
+
packages.push({
|
|
836
|
+
name: pkgJson.name,
|
|
837
|
+
path: `packages/${entry.name}`
|
|
838
|
+
});
|
|
777
839
|
}
|
|
778
|
-
|
|
779
|
-
const isLibrary2 = packageOptions.projectType === "library";
|
|
780
|
-
const nextSteps = isLibrary2 ? [
|
|
781
|
-
`cd ${packagePath}`,
|
|
782
|
-
`${packageManager2} install`,
|
|
783
|
-
`${packageManager2} run build`
|
|
784
|
-
].join("\n") : [
|
|
785
|
-
`cd ${packagePath}`,
|
|
786
|
-
`${packageManager2} install`,
|
|
787
|
-
`${packageManager2} run dev`
|
|
788
|
-
].join("\n");
|
|
789
|
-
p__namespace.note(nextSteps, "Next steps");
|
|
790
|
-
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
791
|
-
process.exit(0);
|
|
792
|
-
} catch (error) {
|
|
793
|
-
s2.stop("Failed to create package");
|
|
794
|
-
p__namespace.log.error(String(error));
|
|
795
|
-
process.exit(1);
|
|
840
|
+
} catch {
|
|
796
841
|
}
|
|
797
842
|
}
|
|
798
843
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
844
|
+
} catch {
|
|
845
|
+
}
|
|
846
|
+
return packages;
|
|
847
|
+
}
|
|
848
|
+
async function ensureConfigInWorkspace(monorepoRoot) {
|
|
849
|
+
const workspacePath = path.join(monorepoRoot, "pnpm-workspace.yaml");
|
|
850
|
+
let content;
|
|
851
|
+
try {
|
|
852
|
+
content = await promises.readFile(workspacePath, "utf-8");
|
|
853
|
+
} catch {
|
|
854
|
+
content = `packages:
|
|
855
|
+
- ".config/*"
|
|
856
|
+
- "packages/*"
|
|
857
|
+
`;
|
|
858
|
+
await promises.writeFile(workspacePath, content);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
if (content.includes(".config/*") || content.includes('".config/*"')) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const lines = content.split("\n");
|
|
865
|
+
const packagesIndex = lines.findIndex(
|
|
866
|
+
(line) => line.trim().startsWith("packages:")
|
|
867
|
+
);
|
|
868
|
+
if (packagesIndex === -1) {
|
|
869
|
+
content = `packages:
|
|
870
|
+
- ".config/*"
|
|
871
|
+
${content}`;
|
|
872
|
+
} else {
|
|
873
|
+
lines.splice(packagesIndex + 1, 0, ' - ".config/*"');
|
|
874
|
+
content = lines.join("\n");
|
|
875
|
+
}
|
|
876
|
+
await promises.writeFile(workspacePath, content);
|
|
877
|
+
}
|
|
878
|
+
async function migrateEslintConfig(monorepoRoot, files) {
|
|
879
|
+
const configBasePath = ".config/eslint";
|
|
880
|
+
const existingConfigPath = path.join(monorepoRoot, "eslint.config.js");
|
|
881
|
+
let existingContent;
|
|
882
|
+
try {
|
|
883
|
+
existingContent = await promises.readFile(existingConfigPath, "utf-8");
|
|
884
|
+
} catch {
|
|
885
|
+
index.generateEslintConfigPackage(files);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
files[`${configBasePath}/package.json`] = {
|
|
889
|
+
type: "text",
|
|
890
|
+
content: JSON.stringify(
|
|
891
|
+
{
|
|
892
|
+
name: "@config/eslint",
|
|
893
|
+
version: "0.1.0",
|
|
894
|
+
private: true,
|
|
895
|
+
type: "module",
|
|
896
|
+
exports: {
|
|
897
|
+
"./base": "./base.js",
|
|
898
|
+
"./react": "./react.js"
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
null,
|
|
902
|
+
2
|
|
903
|
+
)
|
|
904
|
+
};
|
|
905
|
+
files[`${configBasePath}/README.md`] = {
|
|
906
|
+
type: "text",
|
|
907
|
+
content: `# \`@config/eslint\`
|
|
908
|
+
|
|
909
|
+
Shared ESLint configurations.
|
|
910
|
+
|
|
911
|
+
## Usage
|
|
912
|
+
|
|
913
|
+
In your package's \`eslint.config.js\`:
|
|
914
|
+
|
|
915
|
+
\`\`\`js
|
|
916
|
+
import base from "@config/eslint/base";
|
|
917
|
+
|
|
918
|
+
export default [...base];
|
|
919
|
+
\`\`\`
|
|
920
|
+
|
|
921
|
+
## Available Configs
|
|
922
|
+
|
|
923
|
+
- \`base\` - Base ESLint rules (migrated from root)
|
|
924
|
+
- \`react\` - React-specific rules
|
|
925
|
+
`
|
|
926
|
+
};
|
|
927
|
+
files[`${configBasePath}/base.js`] = {
|
|
928
|
+
type: "text",
|
|
929
|
+
content: existingContent
|
|
930
|
+
};
|
|
931
|
+
files[`${configBasePath}/react.js`] = {
|
|
932
|
+
type: "text",
|
|
933
|
+
content: `import react from "eslint-plugin-react";
|
|
934
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
935
|
+
|
|
936
|
+
export default [
|
|
937
|
+
{
|
|
938
|
+
plugins: {
|
|
939
|
+
react,
|
|
940
|
+
"react-hooks": reactHooks,
|
|
941
|
+
},
|
|
942
|
+
rules: {
|
|
943
|
+
...react.configs.recommended.rules,
|
|
944
|
+
...reactHooks.configs.recommended.rules,
|
|
945
|
+
"react/react-in-jsx-scope": "off",
|
|
946
|
+
},
|
|
947
|
+
settings: {
|
|
948
|
+
react: {
|
|
949
|
+
version: "detect",
|
|
950
|
+
},
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
];
|
|
954
|
+
`
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
async function migratePrettierConfig(monorepoRoot, files) {
|
|
958
|
+
const configBasePath = ".config/prettier";
|
|
959
|
+
const existingConfigPath = path.join(monorepoRoot, ".prettierrc.json");
|
|
960
|
+
let existingContent;
|
|
961
|
+
try {
|
|
962
|
+
existingContent = await promises.readFile(existingConfigPath, "utf-8");
|
|
963
|
+
} catch {
|
|
964
|
+
index.generatePrettierConfigPackage(files);
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
files[`${configBasePath}/package.json`] = {
|
|
968
|
+
type: "text",
|
|
969
|
+
content: JSON.stringify(
|
|
970
|
+
{
|
|
971
|
+
name: "@config/prettier",
|
|
972
|
+
version: "0.1.0",
|
|
973
|
+
private: true,
|
|
974
|
+
exports: {
|
|
975
|
+
"./base": "./base.json"
|
|
976
|
+
}
|
|
977
|
+
},
|
|
978
|
+
null,
|
|
979
|
+
2
|
|
980
|
+
)
|
|
981
|
+
};
|
|
982
|
+
files[`${configBasePath}/README.md`] = {
|
|
983
|
+
type: "text",
|
|
984
|
+
content: `# \`@config/prettier\`
|
|
985
|
+
|
|
986
|
+
Shared Prettier configurations.
|
|
987
|
+
|
|
988
|
+
## Usage
|
|
989
|
+
|
|
990
|
+
In your package's \`.prettierrc\`:
|
|
991
|
+
|
|
992
|
+
\`\`\`json
|
|
993
|
+
"@config/prettier/base"
|
|
994
|
+
\`\`\`
|
|
995
|
+
|
|
996
|
+
Or in \`package.json\`:
|
|
997
|
+
|
|
998
|
+
\`\`\`json
|
|
999
|
+
{
|
|
1000
|
+
"prettier": "@config/prettier/base"
|
|
1001
|
+
}
|
|
1002
|
+
\`\`\`
|
|
1003
|
+
|
|
1004
|
+
## Available Configs
|
|
1005
|
+
|
|
1006
|
+
- \`base\` - Base Prettier rules (migrated from root)
|
|
1007
|
+
`
|
|
1008
|
+
};
|
|
1009
|
+
files[`${configBasePath}/base.json`] = {
|
|
1010
|
+
type: "text",
|
|
1011
|
+
content: existingContent
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedTooling, scope) {
|
|
1015
|
+
const workspaceDirectories = await parseWorkspaceDirectories(monorepoRoot);
|
|
1016
|
+
const defaultDirectories = ["apps", "packages"];
|
|
1017
|
+
const hasCustomDirectories = workspaceDirectories.length > 0 && !workspaceDirectories.every((dir) => defaultDirectories.includes(dir));
|
|
1018
|
+
const packageType = await promptForInitialPackage();
|
|
1019
|
+
if (packageType === "skip") {
|
|
1020
|
+
return false;
|
|
1021
|
+
}
|
|
1022
|
+
const defaultDir = packageType === "app" ? "apps" : "packages";
|
|
1023
|
+
const packageNameInput = await p__namespace.text({
|
|
1024
|
+
message: "Package name?",
|
|
1025
|
+
initialValue: `@${scope}/`,
|
|
1026
|
+
validate: (value) => {
|
|
1027
|
+
const validationError = index.validatePackageName(value);
|
|
1028
|
+
if (validationError) return validationError;
|
|
1029
|
+
const dirName = value.includes("/") ? value.split("/").pop() : value;
|
|
1030
|
+
if (!dirName) return "Package name is required";
|
|
1031
|
+
if (!hasCustomDirectories) {
|
|
1032
|
+
const targetPath = path.join(monorepoRoot, defaultDir, dirName);
|
|
1033
|
+
try {
|
|
1034
|
+
const { statSync } = require$1("fs");
|
|
1035
|
+
statSync(targetPath);
|
|
1036
|
+
return `Directory ${defaultDir}/${dirName} already exists`;
|
|
1037
|
+
} catch {
|
|
1038
|
+
}
|
|
838
1039
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
if (p__namespace.isCancel(packageNameInput)) {
|
|
1043
|
+
return false;
|
|
1044
|
+
}
|
|
1045
|
+
const scopedName = packageNameInput;
|
|
1046
|
+
const shortName = scopedName.includes("/") ? scopedName.split("/").pop() : scopedName;
|
|
1047
|
+
const packageOptions = await promptForPackageOptions(
|
|
1048
|
+
scopedName,
|
|
1049
|
+
packageType,
|
|
1050
|
+
inheritedTooling
|
|
1051
|
+
);
|
|
1052
|
+
let targetDir = defaultDir;
|
|
1053
|
+
if (hasCustomDirectories && workspaceDirectories.length > 0) {
|
|
1054
|
+
const dirChoice = await p__namespace.select({
|
|
1055
|
+
message: "Target directory",
|
|
1056
|
+
options: workspaceDirectories.map((dir) => ({
|
|
1057
|
+
value: dir,
|
|
1058
|
+
label: dir
|
|
1059
|
+
})),
|
|
1060
|
+
initialValue: workspaceDirectories.includes(defaultDir) ? defaultDir : workspaceDirectories[0]
|
|
1061
|
+
});
|
|
1062
|
+
if (p__namespace.isCancel(dirChoice)) {
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
targetDir = dirChoice;
|
|
1066
|
+
const targetPath = path.join(monorepoRoot, targetDir, shortName);
|
|
1067
|
+
try {
|
|
1068
|
+
const { statSync } = require$1("fs");
|
|
1069
|
+
statSync(targetPath);
|
|
1070
|
+
p__namespace.log.error(`Directory ${targetDir}/${shortName} already exists`);
|
|
1071
|
+
return false;
|
|
1072
|
+
} catch {
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
const relativePkgPath = path.join(targetDir, shortName);
|
|
1076
|
+
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
1077
|
+
packageOptions.workspaceRoot = workspaceRoot;
|
|
1078
|
+
packageOptions.name = scopedName;
|
|
1079
|
+
if (packageManager === "pnpm") {
|
|
1080
|
+
packageOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1081
|
+
} else if (packageManager === "yarn") {
|
|
1082
|
+
packageOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1083
|
+
} else if (packageManager === "npm") {
|
|
1084
|
+
packageOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
1085
|
+
}
|
|
1086
|
+
const nodeVersion = packageOptions.nodeVersion ?? "latest";
|
|
1087
|
+
if (nodeVersion === "latest") {
|
|
1088
|
+
packageOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1089
|
+
}
|
|
1090
|
+
const versions = {};
|
|
1091
|
+
const versionPromises = [];
|
|
1092
|
+
const pkgIsLibrary = packageOptions.projectType === "library";
|
|
1093
|
+
const pkgTesting = packageOptions.testing ?? (pkgIsLibrary ? "vitest" : "none");
|
|
1094
|
+
if (pkgTesting === "vitest") {
|
|
1095
|
+
versionPromises.push(
|
|
1096
|
+
index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
1097
|
+
versions.vitest = v;
|
|
1098
|
+
})
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
if (!pkgIsLibrary) {
|
|
1102
|
+
versionPromises.push(
|
|
1103
|
+
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
1104
|
+
versions.vite = v;
|
|
1105
|
+
})
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
const linter = packageOptions.linter ?? "oxlint";
|
|
1109
|
+
if (linter === "eslint") {
|
|
1110
|
+
versionPromises.push(
|
|
1111
|
+
index.getLatestNpmVersion("eslint", "9.17.0").then((v) => {
|
|
1112
|
+
versions.eslint = v;
|
|
1113
|
+
})
|
|
1114
|
+
);
|
|
1115
|
+
} else if (linter === "oxlint") {
|
|
1116
|
+
versionPromises.push(
|
|
1117
|
+
index.getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
|
|
1118
|
+
versions.oxlint = v;
|
|
1119
|
+
})
|
|
1120
|
+
);
|
|
1121
|
+
} else if (linter === "biome") {
|
|
1122
|
+
versionPromises.push(
|
|
1123
|
+
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1124
|
+
versions.biome = v;
|
|
1125
|
+
})
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
const formatter = packageOptions.formatter ?? "oxfmt";
|
|
1129
|
+
if (formatter === "prettier") {
|
|
1130
|
+
versionPromises.push(
|
|
1131
|
+
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
1132
|
+
versions.prettier = v;
|
|
1133
|
+
})
|
|
1134
|
+
);
|
|
1135
|
+
} else if (formatter === "oxfmt") {
|
|
1136
|
+
versionPromises.push(
|
|
1137
|
+
index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
|
|
1138
|
+
versions.oxfmt = v;
|
|
1139
|
+
})
|
|
1140
|
+
);
|
|
1141
|
+
} else if (formatter === "biome" && linter !== "biome") {
|
|
1142
|
+
versionPromises.push(
|
|
1143
|
+
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1144
|
+
versions.biome = v;
|
|
1145
|
+
})
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
await Promise.all(versionPromises);
|
|
1149
|
+
packageOptions.versions = versions;
|
|
1150
|
+
if (packageType === "app") {
|
|
1151
|
+
const workspacePackages = await getWorkspacePackages(monorepoRoot);
|
|
1152
|
+
if (workspacePackages.length > 0) {
|
|
1153
|
+
const selectedDeps = await p__namespace.multiselect({
|
|
1154
|
+
message: "Add workspace dependencies?",
|
|
1155
|
+
options: workspacePackages.map((pkgInfo) => ({
|
|
1156
|
+
value: pkgInfo.name,
|
|
1157
|
+
label: pkgInfo.name.replace(/^@[^/]+\//, "")
|
|
1158
|
+
})),
|
|
1159
|
+
required: false
|
|
1160
|
+
});
|
|
1161
|
+
if (!p__namespace.isCancel(selectedDeps) && selectedDeps.length > 0) {
|
|
1162
|
+
packageOptions.workspaceDependencies = selectedDeps;
|
|
842
1163
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const outputPath = path.join(monorepoRoot, relativePkgPath);
|
|
1167
|
+
const spinner = p__namespace.spinner();
|
|
1168
|
+
spinner.start("Creating package...");
|
|
1169
|
+
try {
|
|
1170
|
+
const files = index.generate(packageOptions);
|
|
1171
|
+
await writeGeneratedFiles(outputPath, files);
|
|
1172
|
+
spinner.stop(
|
|
1173
|
+
color__default.green.inverse(` \u2713 Package created at ${relativePkgPath}! `)
|
|
1174
|
+
);
|
|
1175
|
+
const addAnother = await p__namespace.select({
|
|
1176
|
+
message: "Add another package?",
|
|
1177
|
+
options: [
|
|
1178
|
+
{ value: "no", label: "No, I'm done" },
|
|
1179
|
+
{ value: "yes", label: "Yes, add another" }
|
|
1180
|
+
],
|
|
1181
|
+
initialValue: "no"
|
|
1182
|
+
});
|
|
1183
|
+
return !p__namespace.isCancel(addAnother) && addAnother === "yes";
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
spinner.stop("Failed to create package");
|
|
1186
|
+
p__namespace.log.error(String(error));
|
|
1187
|
+
return false;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
async function promptAndOpenEditor(projectPath) {
|
|
1191
|
+
const savedEditor = getPreferredEditor();
|
|
1192
|
+
let selectedEditor;
|
|
1193
|
+
if (savedEditor && savedEditor !== "skip") {
|
|
1194
|
+
const useDefault = await p__namespace.confirm({
|
|
1195
|
+
message: `Open in editor? ${color__default.dim(`(${editorNames[savedEditor]})`)}`,
|
|
1196
|
+
initialValue: true
|
|
1197
|
+
});
|
|
1198
|
+
if (p__namespace.isCancel(useDefault)) {
|
|
1199
|
+
selectedEditor = void 0;
|
|
1200
|
+
} else if (useDefault) {
|
|
1201
|
+
selectedEditor = savedEditor;
|
|
1202
|
+
} else {
|
|
1203
|
+
selectedEditor = "skip";
|
|
1204
|
+
}
|
|
1205
|
+
} else {
|
|
1206
|
+
const openEditor = await p__namespace.select({
|
|
1207
|
+
message: "Open project in editor?",
|
|
1208
|
+
options: [
|
|
1209
|
+
{ value: "skip", label: "Skip" },
|
|
1210
|
+
{ value: "cursor", label: "Cursor" },
|
|
1211
|
+
{ value: "code", label: "VS Code" },
|
|
1212
|
+
{ value: "webstorm", label: "WebStorm" }
|
|
1213
|
+
],
|
|
1214
|
+
initialValue: "skip"
|
|
1215
|
+
});
|
|
1216
|
+
if (!p__namespace.isCancel(openEditor)) {
|
|
1217
|
+
selectedEditor = openEditor;
|
|
1218
|
+
const saveChoice = await p__namespace.confirm({
|
|
1219
|
+
message: `Save ${editorNames[selectedEditor] ?? "Skip"} as default editor?`,
|
|
1220
|
+
initialValue: true
|
|
1221
|
+
});
|
|
1222
|
+
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1223
|
+
setPreferredEditor(selectedEditor);
|
|
1224
|
+
if (selectedEditor === "cursor" || selectedEditor === "code") {
|
|
1225
|
+
const reuseChoice = await p__namespace.confirm({
|
|
1226
|
+
message: "Reuse current window when opening projects?",
|
|
1227
|
+
initialValue: false
|
|
874
1228
|
});
|
|
875
|
-
if (!p__namespace.isCancel(
|
|
876
|
-
|
|
877
|
-
const packagePath = path.join(targetDir, packageName);
|
|
878
|
-
const packageOptions = await promptForPackageOptions(packageName, initialPackage);
|
|
879
|
-
packageOptions.workspaceRoot = "../..";
|
|
880
|
-
packageOptions.name = packageName;
|
|
881
|
-
const pkgManager = packageOptions.packageManager || "pnpm";
|
|
882
|
-
const versions2 = {};
|
|
883
|
-
const versionPromises2 = [];
|
|
884
|
-
const initPkgIsLibrary = packageOptions.projectType === "library";
|
|
885
|
-
const initPkgTesting = packageOptions.testing ?? (initPkgIsLibrary ? "vitest" : "none");
|
|
886
|
-
if (initPkgTesting === "vitest") {
|
|
887
|
-
versionPromises2.push(
|
|
888
|
-
index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
889
|
-
versions2.vitest = v;
|
|
890
|
-
})
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
if (!initPkgIsLibrary) {
|
|
894
|
-
versionPromises2.push(
|
|
895
|
-
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
896
|
-
versions2.vite = v;
|
|
897
|
-
})
|
|
898
|
-
);
|
|
899
|
-
}
|
|
900
|
-
await Promise.all(versionPromises2);
|
|
901
|
-
packageOptions.versions = versions2;
|
|
902
|
-
s2.start("Creating initial package...");
|
|
903
|
-
const packageFiles = index.generate(packageOptions);
|
|
904
|
-
const packageFilePaths = Object.keys(packageFiles).sort();
|
|
905
|
-
const packageBasePath = path.join(basePath2, packagePath);
|
|
906
|
-
for (const filePath of packageFilePaths) {
|
|
907
|
-
const fullFilePath = path.join(packageBasePath, filePath);
|
|
908
|
-
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
909
|
-
const file = packageFiles[filePath];
|
|
910
|
-
if (file.type === "text") {
|
|
911
|
-
await promises.writeFile(fullFilePath, file.content);
|
|
912
|
-
} else {
|
|
913
|
-
const response = await undici.fetch(file.url);
|
|
914
|
-
await promises.writeFile(fullFilePath, response.body);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
s2.stop("Initial package created!");
|
|
1229
|
+
if (!p__namespace.isCancel(reuseChoice)) {
|
|
1230
|
+
setReuseWindow(reuseChoice);
|
|
918
1231
|
}
|
|
919
1232
|
}
|
|
920
|
-
const nextSteps = [
|
|
921
|
-
`cd ${generateOptions.name}`,
|
|
922
|
-
`${packageManager2} install`,
|
|
923
|
-
`${packageManager2} run dev`
|
|
924
|
-
].join("\n");
|
|
925
|
-
p__namespace.note(nextSteps, "Next steps");
|
|
926
|
-
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
927
|
-
process.exit(0);
|
|
928
|
-
} catch (error) {
|
|
929
|
-
s2.stop("Failed to create monorepo workspace");
|
|
930
|
-
p__namespace.log.error(String(error));
|
|
931
|
-
process.exit(1);
|
|
932
1233
|
}
|
|
933
1234
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1235
|
+
}
|
|
1236
|
+
if (selectedEditor && selectedEditor !== "skip") {
|
|
1237
|
+
try {
|
|
1238
|
+
await openInEditor(
|
|
1239
|
+
selectedEditor,
|
|
1240
|
+
projectPath,
|
|
1241
|
+
getReuseWindow()
|
|
1242
|
+
);
|
|
1243
|
+
p__namespace.log.success(`Opening in ${editorNames[selectedEditor]}...`);
|
|
1244
|
+
} catch {
|
|
1245
|
+
p__namespace.log.warn(
|
|
1246
|
+
`Could not open ${editorNames[selectedEditor]}. Make sure the CLI command is in your PATH.`
|
|
1247
|
+
);
|
|
940
1248
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
async function handleCheckCommand() {
|
|
1252
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1253
|
+
if (!monorepoRoot) {
|
|
1254
|
+
console.log(color__default.red("\u2717") + " Not a monorepo workspace");
|
|
1255
|
+
process.exit(1);
|
|
1256
|
+
}
|
|
1257
|
+
const { valid, errors } = await validateWorkspace(monorepoRoot);
|
|
1258
|
+
if (valid) {
|
|
1259
|
+
console.log(color__default.green("\u2713") + " Valid monorepo workspace");
|
|
1260
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1261
|
+
} else {
|
|
1262
|
+
console.log(color__default.red("\u2717") + " Invalid monorepo workspace");
|
|
1263
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1264
|
+
for (const error of errors) {
|
|
1265
|
+
console.log(color__default.red(` \u2022 ${error}`));
|
|
944
1266
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1267
|
+
}
|
|
1268
|
+
process.exit(valid ? 0 : 1);
|
|
1269
|
+
}
|
|
1270
|
+
async function handleFixCommand(options) {
|
|
1271
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1272
|
+
if (!monorepoRoot) {
|
|
1273
|
+
console.log(color__default.red("\u2717") + " Not a monorepo workspace");
|
|
1274
|
+
console.log(color__default.dim(" Run this command from within a monorepo"));
|
|
1275
|
+
process.exit(1);
|
|
1276
|
+
}
|
|
1277
|
+
const { valid, errors } = await validateWorkspace(monorepoRoot);
|
|
1278
|
+
if (valid) {
|
|
1279
|
+
console.log(color__default.green("\u2713") + " Workspace is already valid");
|
|
1280
|
+
console.log(color__default.dim(` ${monorepoRoot}`));
|
|
1281
|
+
process.exit(0);
|
|
1282
|
+
}
|
|
1283
|
+
console.log(color__default.yellow("!") + " Invalid monorepo workspace");
|
|
1284
|
+
for (const error of errors) {
|
|
1285
|
+
console.log(color__default.dim(` \u2022 ${error}`));
|
|
1286
|
+
}
|
|
1287
|
+
console.log();
|
|
1288
|
+
const tooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1289
|
+
const existingConfigs = await detectExistingConfigs(monorepoRoot);
|
|
1290
|
+
const detectedLinter = tooling.linter ?? existingConfigs.linter ?? "oxlint";
|
|
1291
|
+
const detectedFormatter = tooling.formatter ?? existingConfigs.formatter ?? "oxfmt";
|
|
1292
|
+
const isNonInteractive = options.linter && options.formatter;
|
|
1293
|
+
let linter;
|
|
1294
|
+
let formatter;
|
|
1295
|
+
if (isNonInteractive) {
|
|
1296
|
+
linter = options.linter;
|
|
1297
|
+
formatter = options.formatter;
|
|
1298
|
+
} else {
|
|
1299
|
+
const linterChoice = await p__namespace.select({
|
|
1300
|
+
message: "Linter",
|
|
1301
|
+
options: [
|
|
1302
|
+
{
|
|
1303
|
+
value: "oxlint",
|
|
1304
|
+
label: "oxlint" + (tooling.linter === "oxlint" ? color__default.dim(" (installed)") : "")
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
value: "eslint",
|
|
1308
|
+
label: "eslint" + (tooling.linter === "eslint" || existingConfigs.linter === "eslint" ? color__default.dim(" (installed)") : "")
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
value: "biome",
|
|
1312
|
+
label: "biome" + (tooling.linter === "biome" ? color__default.dim(" (installed)") : "")
|
|
1313
|
+
}
|
|
1314
|
+
],
|
|
1315
|
+
initialValue: detectedLinter
|
|
1316
|
+
});
|
|
1317
|
+
if (p__namespace.isCancel(linterChoice)) {
|
|
1318
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1319
|
+
process.exit(0);
|
|
955
1320
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1321
|
+
const formatterChoice = await p__namespace.select({
|
|
1322
|
+
message: "Formatter",
|
|
1323
|
+
options: [
|
|
1324
|
+
{
|
|
1325
|
+
value: "oxfmt",
|
|
1326
|
+
label: "oxfmt" + (tooling.formatter === "oxfmt" ? color__default.dim(" (installed)") : "")
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
value: "prettier",
|
|
1330
|
+
label: "prettier" + (tooling.formatter === "prettier" || existingConfigs.formatter === "prettier" ? color__default.dim(" (installed)") : "")
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
value: "biome",
|
|
1334
|
+
label: "biome" + (tooling.formatter === "biome" ? color__default.dim(" (installed)") : "")
|
|
1335
|
+
}
|
|
1336
|
+
],
|
|
1337
|
+
initialValue: detectedFormatter
|
|
1338
|
+
});
|
|
1339
|
+
if (p__namespace.isCancel(formatterChoice)) {
|
|
1340
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1341
|
+
process.exit(0);
|
|
962
1342
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1343
|
+
linter = linterChoice;
|
|
1344
|
+
formatter = formatterChoice;
|
|
1345
|
+
}
|
|
1346
|
+
console.log();
|
|
1347
|
+
const spinner = p__namespace.spinner();
|
|
1348
|
+
spinner.start("Fixing workspace...");
|
|
1349
|
+
try {
|
|
1350
|
+
const files = {};
|
|
1351
|
+
const tsConfigExists = await fileExists(
|
|
1352
|
+
path.join(monorepoRoot, ".config/typescript/package.json")
|
|
1353
|
+
);
|
|
1354
|
+
if (!tsConfigExists) {
|
|
1355
|
+
index.generateTypescriptConfigPackage(files);
|
|
1356
|
+
}
|
|
1357
|
+
if (linter === "oxlint") {
|
|
1358
|
+
const oxlintExists = await fileExists(
|
|
1359
|
+
path.join(monorepoRoot, ".config/oxlint/package.json")
|
|
975
1360
|
);
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
})
|
|
1361
|
+
if (!oxlintExists) index.generateOxlintConfigPackage(files);
|
|
1362
|
+
} else if (linter === "eslint") {
|
|
1363
|
+
const eslintPkgExists = await fileExists(
|
|
1364
|
+
path.join(monorepoRoot, ".config/eslint/package.json")
|
|
981
1365
|
);
|
|
1366
|
+
if (!eslintPkgExists) {
|
|
1367
|
+
if (existingConfigs.eslintConfigPath) {
|
|
1368
|
+
await migrateEslintConfig(monorepoRoot, files);
|
|
1369
|
+
} else {
|
|
1370
|
+
index.generateEslintConfigPackage(files);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
982
1373
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
987
|
-
versions.prettier = v;
|
|
988
|
-
})
|
|
989
|
-
);
|
|
990
|
-
} else if (formatter === "oxfmt") {
|
|
991
|
-
versionPromises.push(
|
|
992
|
-
index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
|
|
993
|
-
versions.oxfmt = v;
|
|
994
|
-
})
|
|
1374
|
+
if (formatter === "oxfmt") {
|
|
1375
|
+
const oxfmtExists = await fileExists(
|
|
1376
|
+
path.join(monorepoRoot, ".config/oxfmt/package.json")
|
|
995
1377
|
);
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
})
|
|
1378
|
+
if (!oxfmtExists) index.generateOxfmtConfigPackage(files);
|
|
1379
|
+
} else if (formatter === "prettier") {
|
|
1380
|
+
const prettierPkgExists = await fileExists(
|
|
1381
|
+
path.join(monorepoRoot, ".config/prettier/package.json")
|
|
1001
1382
|
);
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
const basePath = path.join(process$1.cwd(), generateOptions.name);
|
|
1006
|
-
const s = p__namespace.spinner();
|
|
1007
|
-
s.start("Creating project...");
|
|
1008
|
-
try {
|
|
1009
|
-
const files = index.generate(generateOptions);
|
|
1010
|
-
const filePaths = Object.keys(files).sort();
|
|
1011
|
-
for (const filePath of filePaths) {
|
|
1012
|
-
const fullFilePath = path.join(basePath, filePath);
|
|
1013
|
-
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
1014
|
-
const file = files[filePath];
|
|
1015
|
-
if (file.type === "text") {
|
|
1016
|
-
await promises.writeFile(fullFilePath, file.content);
|
|
1383
|
+
if (!prettierPkgExists) {
|
|
1384
|
+
if (existingConfigs.prettierConfigPath) {
|
|
1385
|
+
await migratePrettierConfig(monorepoRoot, files);
|
|
1017
1386
|
} else {
|
|
1018
|
-
|
|
1019
|
-
|
|
1387
|
+
index.generatePrettierConfigPackage(files);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if ((linter === "biome" || formatter === "biome") && !existingConfigs.biomeConfigPath) {
|
|
1392
|
+
const biomeConfig = {
|
|
1393
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
1394
|
+
vcs: {
|
|
1395
|
+
enabled: true,
|
|
1396
|
+
clientKind: "git",
|
|
1397
|
+
useIgnoreFile: true
|
|
1398
|
+
},
|
|
1399
|
+
linter: {
|
|
1400
|
+
enabled: linter === "biome",
|
|
1401
|
+
rules: {
|
|
1402
|
+
recommended: true
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1405
|
+
formatter: {
|
|
1406
|
+
enabled: formatter === "biome"
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
files["biome.json"] = {
|
|
1410
|
+
type: "text",
|
|
1411
|
+
content: JSON.stringify(biomeConfig, null, 2)
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
for (const [filePath, file] of Object.entries(files)) {
|
|
1415
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1416
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1417
|
+
await promises.writeFile(fullPath, file.content);
|
|
1418
|
+
}
|
|
1419
|
+
await ensureConfigInWorkspace(monorepoRoot);
|
|
1420
|
+
if (existingConfigs.eslintConfigPath && linter === "eslint") {
|
|
1421
|
+
try {
|
|
1422
|
+
await promises.unlink(existingConfigs.eslintConfigPath);
|
|
1423
|
+
} catch {
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (existingConfigs.prettierConfigPath && formatter === "prettier") {
|
|
1427
|
+
try {
|
|
1428
|
+
await promises.unlink(existingConfigs.prettierConfigPath);
|
|
1429
|
+
} catch {
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
spinner.stop(color__default.green("\u2713") + " Workspace fixed!");
|
|
1433
|
+
const generated = Object.keys(files).filter(
|
|
1434
|
+
(f) => f.endsWith("package.json")
|
|
1435
|
+
);
|
|
1436
|
+
for (const pkgFile of generated) {
|
|
1437
|
+
const pkgName = pkgFile.replace("/package.json", "");
|
|
1438
|
+
console.log(color__default.dim(` Generated ${pkgName}`));
|
|
1439
|
+
}
|
|
1440
|
+
const vscodeSettingsExists = await fileExists(
|
|
1441
|
+
path.join(monorepoRoot, ".vscode/settings.json")
|
|
1442
|
+
);
|
|
1443
|
+
const vscodeExtensionsExists = await fileExists(
|
|
1444
|
+
path.join(monorepoRoot, ".vscode/extensions.json")
|
|
1445
|
+
);
|
|
1446
|
+
const vscodeExists = vscodeSettingsExists && vscodeExtensionsExists;
|
|
1447
|
+
if (!vscodeExists) {
|
|
1448
|
+
let addVscode = false;
|
|
1449
|
+
if (isNonInteractive) {
|
|
1450
|
+
addVscode = true;
|
|
1451
|
+
} else {
|
|
1452
|
+
const vscodeChoice = await p__namespace.confirm({
|
|
1453
|
+
message: "Generate VS Code settings?",
|
|
1454
|
+
initialValue: true
|
|
1455
|
+
});
|
|
1456
|
+
addVscode = !p__namespace.isCancel(vscodeChoice) && vscodeChoice;
|
|
1457
|
+
}
|
|
1458
|
+
if (addVscode) {
|
|
1459
|
+
const vscodeFiles = {};
|
|
1460
|
+
index.generateVscodeFiles(vscodeFiles, linter, formatter);
|
|
1461
|
+
for (const [filePath, file] of Object.entries(vscodeFiles)) {
|
|
1462
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1463
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1464
|
+
await promises.writeFile(fullPath, file.content);
|
|
1020
1465
|
}
|
|
1466
|
+
console.log(color__default.dim(" Generated .vscode/settings.json"));
|
|
1467
|
+
console.log(color__default.dim(" Generated .vscode/extensions.json"));
|
|
1021
1468
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1469
|
+
}
|
|
1470
|
+
const aiFilePaths = {
|
|
1471
|
+
"cursor-rules": ".cursor/rules",
|
|
1472
|
+
"agents-md": "AGENTS.md",
|
|
1473
|
+
"claude-md": "CLAUDE.md",
|
|
1474
|
+
"copilot-md": ".github/copilot-instructions.md"
|
|
1475
|
+
};
|
|
1476
|
+
const existingAiFiles = [];
|
|
1477
|
+
for (const [choice, path$1] of Object.entries(aiFilePaths)) {
|
|
1478
|
+
if (await fileExists(path.join(monorepoRoot, path$1))) {
|
|
1479
|
+
existingAiFiles.push(choice);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
let selectedAiFiles = [];
|
|
1483
|
+
const savedAiFiles = getAiFiles();
|
|
1484
|
+
const availableChoices = ["cursor-rules", "agents-md", "claude-md", "copilot-md"].filter((c) => !existingAiFiles.includes(c));
|
|
1485
|
+
if (availableChoices.length === 0) {
|
|
1486
|
+
} else if (isNonInteractive) {
|
|
1487
|
+
const preferred = savedAiFiles ?? ["cursor-rules"];
|
|
1488
|
+
selectedAiFiles = preferred.filter((f) => availableChoices.includes(f));
|
|
1489
|
+
} else if (savedAiFiles && savedAiFiles.length > 0) {
|
|
1490
|
+
const availableSaved = savedAiFiles.filter(
|
|
1491
|
+
(f) => availableChoices.includes(f)
|
|
1492
|
+
);
|
|
1493
|
+
if (availableSaved.length > 0) {
|
|
1494
|
+
const savedLabels = availableSaved.map((f) => aiFilePaths[f]).join(", ");
|
|
1037
1495
|
const useDefault = await p__namespace.confirm({
|
|
1038
|
-
message: `
|
|
1496
|
+
message: `Generate AI instruction files? ${color__default.dim(
|
|
1497
|
+
`(${savedLabels})`
|
|
1498
|
+
)}`,
|
|
1039
1499
|
initialValue: true
|
|
1040
1500
|
});
|
|
1041
|
-
if (p__namespace.isCancel(useDefault)) {
|
|
1042
|
-
|
|
1043
|
-
} else if (useDefault) {
|
|
1044
|
-
selectedEditor = savedEditor;
|
|
1045
|
-
} else {
|
|
1046
|
-
selectedEditor = "skip";
|
|
1501
|
+
if (!p__namespace.isCancel(useDefault) && useDefault) {
|
|
1502
|
+
selectedAiFiles = availableSaved;
|
|
1047
1503
|
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1504
|
+
}
|
|
1505
|
+
} else {
|
|
1506
|
+
const aiFilesChoice = await p__namespace.multiselect({
|
|
1507
|
+
message: "Generate AI instruction files?",
|
|
1508
|
+
options: availableChoices.map((c) => ({
|
|
1509
|
+
value: c,
|
|
1510
|
+
label: aiFilePaths[c],
|
|
1511
|
+
hint: c === "cursor-rules" ? "Cursor AI" : c === "agents-md" ? "GitHub Copilot, general" : c === "claude-md" ? "Claude" : "GitHub Copilot"
|
|
1512
|
+
})),
|
|
1513
|
+
required: false
|
|
1514
|
+
});
|
|
1515
|
+
if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
|
|
1516
|
+
selectedAiFiles = aiFilesChoice;
|
|
1517
|
+
const saveChoice = await p__namespace.confirm({
|
|
1518
|
+
message: "Save as default for future?",
|
|
1519
|
+
initialValue: true
|
|
1058
1520
|
});
|
|
1059
|
-
if (!p__namespace.isCancel(
|
|
1060
|
-
|
|
1061
|
-
const saveChoice = await p__namespace.confirm({
|
|
1062
|
-
message: `Save ${editorNames[selectedEditor] ?? "Skip"} as default editor?`,
|
|
1063
|
-
initialValue: true
|
|
1064
|
-
});
|
|
1065
|
-
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1066
|
-
setPreferredEditor(selectedEditor);
|
|
1067
|
-
if (selectedEditor === "cursor" || selectedEditor === "code") {
|
|
1068
|
-
const reuseChoice = await p__namespace.confirm({
|
|
1069
|
-
message: "Reuse current window when opening projects?",
|
|
1070
|
-
initialValue: false
|
|
1071
|
-
});
|
|
1072
|
-
if (!p__namespace.isCancel(reuseChoice)) {
|
|
1073
|
-
setReuseWindow(reuseChoice);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1521
|
+
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1522
|
+
setAiFiles(selectedAiFiles);
|
|
1077
1523
|
}
|
|
1078
1524
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1525
|
+
}
|
|
1526
|
+
if (selectedAiFiles.length > 0) {
|
|
1527
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1528
|
+
const aiFilesOutput = {};
|
|
1529
|
+
index.generateAiFiles(aiFilesOutput, {
|
|
1530
|
+
name: scope,
|
|
1531
|
+
packageManager: "pnpm",
|
|
1532
|
+
linter,
|
|
1533
|
+
formatter,
|
|
1534
|
+
aiFiles: selectedAiFiles
|
|
1535
|
+
});
|
|
1536
|
+
for (const [filePath, file] of Object.entries(aiFilesOutput)) {
|
|
1537
|
+
const fullPath = path.join(monorepoRoot, filePath);
|
|
1538
|
+
await promises.mkdir(path.dirname(fullPath), { recursive: true });
|
|
1539
|
+
await promises.writeFile(fullPath, file.content);
|
|
1540
|
+
console.log(color__default.dim(` Generated ${filePath}`));
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
process.exit(0);
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
spinner.stop(color__default.red("\u2717") + " Failed to fix workspace");
|
|
1546
|
+
console.error(error);
|
|
1547
|
+
process.exit(1);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
async function handleWorkspaceCommand(name, options) {
|
|
1551
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
1552
|
+
if (!monorepoRoot) {
|
|
1553
|
+
console.error(
|
|
1554
|
+
color__default.red("Error:") + " --workspace flag requires being inside a monorepo"
|
|
1555
|
+
);
|
|
1556
|
+
process.exit(1);
|
|
1557
|
+
}
|
|
1558
|
+
if (!name) {
|
|
1559
|
+
console.error(
|
|
1560
|
+
color__default.red("Error:") + " Package name is required with --workspace flag"
|
|
1561
|
+
);
|
|
1562
|
+
console.log(
|
|
1563
|
+
color__default.dim(
|
|
1564
|
+
" Example: pnpm create krispya my-lib --workspace --type library"
|
|
1565
|
+
)
|
|
1566
|
+
);
|
|
1567
|
+
process.exit(1);
|
|
1568
|
+
}
|
|
1569
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1570
|
+
const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1571
|
+
const projectType = options.type ?? "app";
|
|
1572
|
+
const defaultDir = projectType === "library" ? "packages" : "apps";
|
|
1573
|
+
const targetDir = options.dir ?? defaultDir;
|
|
1574
|
+
const template = options.template ?? "vanilla";
|
|
1575
|
+
const baseTemplate = index.getBaseTemplate(template);
|
|
1576
|
+
const scopedName = name.startsWith("@") ? name : `@${scope}/${name}`;
|
|
1577
|
+
const fullPackagePath = path.join(monorepoRoot, targetDir, name);
|
|
1578
|
+
try {
|
|
1579
|
+
await promises.access(fullPackagePath, fs.constants.F_OK);
|
|
1580
|
+
console.error(
|
|
1581
|
+
color__default.red("Error:") + ` Directory ${targetDir}/${name} already exists`
|
|
1582
|
+
);
|
|
1583
|
+
process.exit(1);
|
|
1584
|
+
} catch {
|
|
1585
|
+
}
|
|
1586
|
+
const versions = {};
|
|
1587
|
+
const versionPromises = [];
|
|
1588
|
+
const isLibrary = projectType === "library";
|
|
1589
|
+
if (!isLibrary) {
|
|
1590
|
+
versionPromises.push(
|
|
1591
|
+
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
1592
|
+
versions.vite = v;
|
|
1593
|
+
})
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
const linter = inheritedTooling.linter ?? options.linter ?? "oxlint";
|
|
1597
|
+
const formatter = inheritedTooling.formatter ?? options.formatter ?? "oxfmt";
|
|
1598
|
+
await Promise.all(versionPromises);
|
|
1599
|
+
const relativePkgPath = path.join(targetDir, name);
|
|
1600
|
+
const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
|
|
1601
|
+
const generateOptions = {
|
|
1602
|
+
name: scopedName,
|
|
1603
|
+
projectType,
|
|
1604
|
+
libraryBundler: isLibrary ? options.bundler ?? "unbuild" : void 0,
|
|
1605
|
+
template,
|
|
1606
|
+
linter,
|
|
1607
|
+
formatter,
|
|
1608
|
+
workspaceRoot,
|
|
1609
|
+
versions,
|
|
1610
|
+
...baseTemplate === "r3f" && {
|
|
1611
|
+
drei: options.drei ? {} : void 0,
|
|
1612
|
+
handle: options.handle ? {} : void 0,
|
|
1613
|
+
leva: options.leva ? {} : void 0,
|
|
1614
|
+
postprocessing: options.postprocessing ? {} : void 0,
|
|
1615
|
+
rapier: options.rapier ? {} : void 0,
|
|
1616
|
+
xr: options.xr ? {} : void 0,
|
|
1617
|
+
uikit: options.uikit ? {} : void 0,
|
|
1618
|
+
offscreen: options.offscreen ? {} : void 0,
|
|
1619
|
+
zustand: options.zustand ? {} : void 0,
|
|
1620
|
+
koota: options.koota ? {} : void 0,
|
|
1621
|
+
viverse: options.viverse ? {} : void 0,
|
|
1622
|
+
triplex: options.triplex ? {} : void 0
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
console.log(
|
|
1626
|
+
color__default.cyan("Creating") + ` ${scopedName} in ${targetDir}/${name}...`
|
|
1627
|
+
);
|
|
1628
|
+
try {
|
|
1629
|
+
const files = index.generate(generateOptions);
|
|
1630
|
+
await writeGeneratedFiles(fullPackagePath, files);
|
|
1631
|
+
console.log(
|
|
1632
|
+
color__default.green("\u2713") + ` Created ${scopedName} at ${targetDir}/${name}`
|
|
1633
|
+
);
|
|
1634
|
+
process.exit(0);
|
|
1635
|
+
} catch (error) {
|
|
1636
|
+
console.error(color__default.red("Error:") + " Failed to create package");
|
|
1637
|
+
console.error(String(error));
|
|
1638
|
+
process.exit(1);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
async function handleMonorepoCreation(generateOptions) {
|
|
1642
|
+
const { generateMonorepo } = await import('./chunks/index.cjs').then(function (n) { return n.monorepo; });
|
|
1643
|
+
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1644
|
+
if (packageManager === "pnpm") {
|
|
1645
|
+
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1646
|
+
} else if (packageManager === "yarn") {
|
|
1647
|
+
generateOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1648
|
+
} else if (packageManager === "npm") {
|
|
1649
|
+
generateOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
1650
|
+
}
|
|
1651
|
+
const nodeVersion = generateOptions.nodeVersion ?? "latest";
|
|
1652
|
+
if (nodeVersion === "latest") {
|
|
1653
|
+
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1654
|
+
}
|
|
1655
|
+
const savedAiFiles = getAiFiles();
|
|
1656
|
+
let selectedAiFiles = [];
|
|
1657
|
+
if (savedAiFiles && savedAiFiles.length > 0) {
|
|
1658
|
+
const aiFileLabels = {
|
|
1659
|
+
"cursor-rules": ".cursor/rules",
|
|
1660
|
+
"agents-md": "AGENTS.md",
|
|
1661
|
+
"claude-md": "CLAUDE.md",
|
|
1662
|
+
"copilot-md": ".github/copilot-instructions.md"
|
|
1663
|
+
};
|
|
1664
|
+
const savedLabels = savedAiFiles.map((f) => aiFileLabels[f]).join(", ");
|
|
1665
|
+
const useDefault = await p__namespace.confirm({
|
|
1666
|
+
message: `Generate AI instruction files? ${color__default.dim(
|
|
1667
|
+
`(${savedLabels})`
|
|
1668
|
+
)}`,
|
|
1669
|
+
initialValue: true
|
|
1670
|
+
});
|
|
1671
|
+
if (!p__namespace.isCancel(useDefault) && useDefault) {
|
|
1672
|
+
selectedAiFiles = savedAiFiles;
|
|
1673
|
+
}
|
|
1674
|
+
} else {
|
|
1675
|
+
const aiFilesChoice = await p__namespace.multiselect({
|
|
1676
|
+
message: "Generate AI instruction files?",
|
|
1677
|
+
options: [
|
|
1678
|
+
{ value: "cursor-rules", label: ".cursor/rules", hint: "Cursor AI" },
|
|
1679
|
+
{
|
|
1680
|
+
value: "agents-md",
|
|
1681
|
+
label: "AGENTS.md",
|
|
1682
|
+
hint: "GitHub Copilot, general"
|
|
1683
|
+
},
|
|
1684
|
+
{ value: "claude-md", label: "CLAUDE.md", hint: "Claude" },
|
|
1685
|
+
{
|
|
1686
|
+
value: "copilot-md",
|
|
1687
|
+
label: ".github/copilot-instructions.md",
|
|
1688
|
+
hint: "GitHub Copilot"
|
|
1091
1689
|
}
|
|
1690
|
+
],
|
|
1691
|
+
required: false
|
|
1692
|
+
});
|
|
1693
|
+
if (!p__namespace.isCancel(aiFilesChoice) && aiFilesChoice.length > 0) {
|
|
1694
|
+
selectedAiFiles = aiFilesChoice;
|
|
1695
|
+
const saveChoice = await p__namespace.confirm({
|
|
1696
|
+
message: "Save as default for future monorepos?",
|
|
1697
|
+
initialValue: true
|
|
1698
|
+
});
|
|
1699
|
+
if (!p__namespace.isCancel(saveChoice) && saveChoice) {
|
|
1700
|
+
setAiFiles(selectedAiFiles);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
const projectPath = path.join(process$1.cwd(), generateOptions.name);
|
|
1705
|
+
const spinner = p__namespace.spinner();
|
|
1706
|
+
spinner.start("Creating monorepo workspace...");
|
|
1707
|
+
try {
|
|
1708
|
+
const { files } = generateMonorepo({
|
|
1709
|
+
name: generateOptions.name,
|
|
1710
|
+
linter: generateOptions.linter ?? "oxlint",
|
|
1711
|
+
formatter: generateOptions.formatter ?? "oxfmt",
|
|
1712
|
+
packageManager,
|
|
1713
|
+
pnpmVersion: generateOptions.pnpmVersion,
|
|
1714
|
+
pnpmManageVersions: generateOptions.pnpmManageVersions,
|
|
1715
|
+
nodeVersion: generateOptions.nodeVersion,
|
|
1716
|
+
aiFiles: selectedAiFiles.length > 0 ? selectedAiFiles : void 0
|
|
1717
|
+
});
|
|
1718
|
+
const filePaths = Object.keys(files).sort();
|
|
1719
|
+
for (const filePath of filePaths) {
|
|
1720
|
+
const fullFilePath = path.join(projectPath, filePath);
|
|
1721
|
+
await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
|
|
1722
|
+
const file = files[filePath];
|
|
1723
|
+
if (file.type === "text") {
|
|
1724
|
+
await promises.writeFile(fullFilePath, file.content);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
spinner.stop(color__default.green.inverse(" \u2713 Monorepo workspace created! "));
|
|
1728
|
+
const newMonorepoTooling = {
|
|
1729
|
+
linter: generateOptions.linter,
|
|
1730
|
+
formatter: generateOptions.formatter
|
|
1731
|
+
};
|
|
1732
|
+
const scope = generateOptions.name;
|
|
1733
|
+
let addMore = true;
|
|
1734
|
+
while (addMore) {
|
|
1735
|
+
addMore = await createPackageInWorkspace(
|
|
1736
|
+
projectPath,
|
|
1737
|
+
packageManager,
|
|
1738
|
+
newMonorepoTooling,
|
|
1739
|
+
scope
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
const nextSteps = [
|
|
1743
|
+
`cd ${generateOptions.name}`,
|
|
1744
|
+
`${packageManager} install`,
|
|
1745
|
+
`${packageManager} run dev`
|
|
1746
|
+
].join("\n");
|
|
1747
|
+
p__namespace.note(nextSteps, "Next steps");
|
|
1748
|
+
await promptAndOpenEditor(projectPath);
|
|
1749
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1750
|
+
process.exit(0);
|
|
1751
|
+
} catch (error) {
|
|
1752
|
+
spinner.stop("Failed to create monorepo workspace");
|
|
1753
|
+
p__namespace.log.error(String(error));
|
|
1754
|
+
process.exit(1);
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
async function handleStandaloneProjectCreation(generateOptions) {
|
|
1758
|
+
const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
|
|
1759
|
+
const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
|
|
1760
|
+
generateOptions.name ??= defaultFallbackName;
|
|
1761
|
+
const packageManager = generateOptions.packageManager || "pnpm";
|
|
1762
|
+
if (packageManager === "pnpm") {
|
|
1763
|
+
generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
|
|
1764
|
+
} else if (packageManager === "yarn") {
|
|
1765
|
+
generateOptions.yarnVersion = await index.getLatestYarnVersion();
|
|
1766
|
+
} else if (packageManager === "npm") {
|
|
1767
|
+
generateOptions.npmVersion = await index.getLatestNpmCliVersion();
|
|
1768
|
+
}
|
|
1769
|
+
const nodeVersion = generateOptions.nodeVersion ?? "latest";
|
|
1770
|
+
if (nodeVersion === "latest") {
|
|
1771
|
+
generateOptions.nodeVersion = await index.getLatestNodeVersion();
|
|
1772
|
+
}
|
|
1773
|
+
const versions = {};
|
|
1774
|
+
const versionPromises = [];
|
|
1775
|
+
const isLibrary = generateOptions.projectType === "library";
|
|
1776
|
+
const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
|
|
1777
|
+
if (testing === "vitest") {
|
|
1778
|
+
versionPromises.push(
|
|
1779
|
+
index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
1780
|
+
versions.vitest = v;
|
|
1781
|
+
})
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
if (!isLibrary) {
|
|
1785
|
+
versionPromises.push(
|
|
1786
|
+
index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
1787
|
+
versions.vite = v;
|
|
1788
|
+
})
|
|
1789
|
+
);
|
|
1790
|
+
}
|
|
1791
|
+
const linter = generateOptions.linter ?? "oxlint";
|
|
1792
|
+
if (linter === "eslint") {
|
|
1793
|
+
versionPromises.push(
|
|
1794
|
+
index.getLatestNpmVersion("eslint", "9.17.0").then((v) => {
|
|
1795
|
+
versions.eslint = v;
|
|
1796
|
+
})
|
|
1797
|
+
);
|
|
1798
|
+
} else if (linter === "oxlint") {
|
|
1799
|
+
versionPromises.push(
|
|
1800
|
+
index.getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
|
|
1801
|
+
versions.oxlint = v;
|
|
1802
|
+
})
|
|
1803
|
+
);
|
|
1804
|
+
} else if (linter === "biome") {
|
|
1805
|
+
versionPromises.push(
|
|
1806
|
+
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1807
|
+
versions.biome = v;
|
|
1808
|
+
})
|
|
1809
|
+
);
|
|
1810
|
+
}
|
|
1811
|
+
const formatter = generateOptions.formatter ?? "oxfmt";
|
|
1812
|
+
if (formatter === "prettier") {
|
|
1813
|
+
versionPromises.push(
|
|
1814
|
+
index.getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
1815
|
+
versions.prettier = v;
|
|
1816
|
+
})
|
|
1817
|
+
);
|
|
1818
|
+
} else if (formatter === "oxfmt") {
|
|
1819
|
+
versionPromises.push(
|
|
1820
|
+
index.getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
|
|
1821
|
+
versions.oxfmt = v;
|
|
1822
|
+
})
|
|
1823
|
+
);
|
|
1824
|
+
} else if (formatter === "biome" && linter !== "biome") {
|
|
1825
|
+
versionPromises.push(
|
|
1826
|
+
index.getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
1827
|
+
versions.biome = v;
|
|
1828
|
+
})
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1831
|
+
await Promise.all(versionPromises);
|
|
1832
|
+
generateOptions.versions = versions;
|
|
1833
|
+
const projectPath = path.join(process$1.cwd(), generateOptions.name);
|
|
1834
|
+
const spinner = p__namespace.spinner();
|
|
1835
|
+
spinner.start("Creating project...");
|
|
1836
|
+
try {
|
|
1837
|
+
const files = index.generate(generateOptions);
|
|
1838
|
+
await writeGeneratedFiles(projectPath, files);
|
|
1839
|
+
spinner.stop(color__default.green.inverse(" \u2713 Project created! "));
|
|
1840
|
+
const nextSteps = isLibrary ? [
|
|
1841
|
+
`cd ${generateOptions.name}`,
|
|
1842
|
+
`${packageManager} install`,
|
|
1843
|
+
`${packageManager} run build`
|
|
1844
|
+
].join("\n") : [
|
|
1845
|
+
`cd ${generateOptions.name}`,
|
|
1846
|
+
`${packageManager} install`,
|
|
1847
|
+
`${packageManager} run dev`
|
|
1848
|
+
].join("\n");
|
|
1849
|
+
p__namespace.note(nextSteps, "Next steps");
|
|
1850
|
+
await promptAndOpenEditor(projectPath);
|
|
1851
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1852
|
+
} catch (error) {
|
|
1853
|
+
spinner.stop("Failed to create project");
|
|
1854
|
+
p__namespace.log.error(String(error));
|
|
1855
|
+
process.exit(1);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
async function handleInteractiveMonorepoMode(monorepoRoot) {
|
|
1859
|
+
const choice = await p__namespace.select({
|
|
1860
|
+
message: "Detected monorepo workspace",
|
|
1861
|
+
options: [
|
|
1862
|
+
{ value: "add", label: "Add new package to this workspace" },
|
|
1863
|
+
{ value: "standalone", label: "Create standalone project" }
|
|
1864
|
+
],
|
|
1865
|
+
initialValue: "add"
|
|
1866
|
+
});
|
|
1867
|
+
if (p__namespace.isCancel(choice)) {
|
|
1868
|
+
p__namespace.cancel("Operation cancelled.");
|
|
1869
|
+
process.exit(0);
|
|
1870
|
+
}
|
|
1871
|
+
if (choice === "add") {
|
|
1872
|
+
const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
|
|
1873
|
+
if (inheritedTooling.linter || inheritedTooling.formatter) {
|
|
1874
|
+
const toolingInfo = [
|
|
1875
|
+
inheritedTooling.linter && `linter: ${inheritedTooling.linter}`,
|
|
1876
|
+
inheritedTooling.formatter && `formatter: ${inheritedTooling.formatter}`
|
|
1877
|
+
].filter(Boolean).join(", ");
|
|
1878
|
+
p__namespace.log.info(`Using workspace tooling (${toolingInfo})`);
|
|
1879
|
+
}
|
|
1880
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
1881
|
+
let addMore = true;
|
|
1882
|
+
while (addMore) {
|
|
1883
|
+
addMore = await createPackageInWorkspace(
|
|
1884
|
+
monorepoRoot,
|
|
1885
|
+
"pnpm",
|
|
1886
|
+
inheritedTooling,
|
|
1887
|
+
scope
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
p__namespace.note(
|
|
1891
|
+
[`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"),
|
|
1892
|
+
"Next steps"
|
|
1893
|
+
);
|
|
1894
|
+
await promptAndOpenEditor(monorepoRoot);
|
|
1895
|
+
p__namespace.outro(color__default.green("Happy coding! \u2728"));
|
|
1896
|
+
process.exit(0);
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
async function main() {
|
|
1900
|
+
const program = new commander.Command().name("create-krispya").description(
|
|
1901
|
+
"CLI for creating Vanilla, React, and React Three Fiber projects"
|
|
1902
|
+
).argument("[name]", "name for the project").option("--type <type>", "project type: app or library (default: app)").option(
|
|
1903
|
+
"--bundler <bundler>",
|
|
1904
|
+
"library bundler: unbuild or tsdown (default: unbuild, only for libraries)"
|
|
1905
|
+
).option(
|
|
1906
|
+
"--template <type>",
|
|
1907
|
+
"project template: vanilla, vanilla-js, react, react-js, r3f, r3f-js (default: vanilla)"
|
|
1908
|
+
).option(
|
|
1909
|
+
"--linter <type>",
|
|
1910
|
+
"linter: eslint, oxlint, or biome (default: oxlint)"
|
|
1911
|
+
).option(
|
|
1912
|
+
"--formatter <type>",
|
|
1913
|
+
"formatter: prettier, oxfmt, or biome (default: oxfmt)"
|
|
1914
|
+
).option("--drei", "add @react-three/drei (r3f only)").option("--handle", "add @react-three/handle (r3f only)").option("--leva", "add leva (r3f only)").option("--postprocessing", "add @react-three/postprocessing (r3f only)").option("--rapier", "add @react-three/rapier (r3f only)").option("--xr", "add @react-three/xr (r3f only)").option("--uikit", "add @react-three/uikit (r3f only)").option("--offscreen", "add @react-three/offscreen (r3f only)").option("--zustand", "add zustand (r3f only)").option("--koota", "add koota (r3f only)").option("--triplex", "set up triplex development environment (r3f only)").option("--viverse", "set up viverse deployment (r3f only)").option(
|
|
1915
|
+
"--package-manager <manager>",
|
|
1916
|
+
"specify package manager (e.g. npm, yarn, pnpm)"
|
|
1917
|
+
).option(
|
|
1918
|
+
"--pnpm-manage-versions",
|
|
1919
|
+
"enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
|
|
1920
|
+
).option(
|
|
1921
|
+
"--no-pnpm-manage-versions",
|
|
1922
|
+
"disable manage-package-manager-versions in pnpm-workspace.yaml"
|
|
1923
|
+
).option(
|
|
1924
|
+
"--node-version <version>",
|
|
1925
|
+
'set Node.js version for engines.node field (default: "latest")'
|
|
1926
|
+
).option(
|
|
1927
|
+
"--workspace",
|
|
1928
|
+
"Add package to current monorepo workspace (non-interactive)"
|
|
1929
|
+
).option(
|
|
1930
|
+
"--dir <directory>",
|
|
1931
|
+
"Target directory for --workspace (default: apps/ or packages/)"
|
|
1932
|
+
).option("--clear-config", "Clear saved preferences (e.g. editor choice)").option("--config-path", "Print the path to the config file").option(
|
|
1933
|
+
"--check",
|
|
1934
|
+
"Check if current directory is in a valid monorepo workspace"
|
|
1935
|
+
).option("--fix", "Fix monorepo by generating missing .config packages").option(
|
|
1936
|
+
"--path <directory>",
|
|
1937
|
+
"Run in specified directory instead of current working directory"
|
|
1938
|
+
).action(async (name, options) => {
|
|
1939
|
+
if (options.path) {
|
|
1940
|
+
process.chdir(options.path);
|
|
1941
|
+
}
|
|
1942
|
+
if (options.clearConfig) {
|
|
1943
|
+
clearConfig();
|
|
1944
|
+
console.log("Configuration cleared.");
|
|
1945
|
+
process.exit(0);
|
|
1946
|
+
}
|
|
1947
|
+
if (options.configPath) {
|
|
1948
|
+
console.log(getConfigPath());
|
|
1949
|
+
process.exit(0);
|
|
1950
|
+
}
|
|
1951
|
+
if (name?.startsWith("-")) {
|
|
1952
|
+
switch (name) {
|
|
1953
|
+
case "--version":
|
|
1954
|
+
case "-V":
|
|
1955
|
+
console.log(pkg.version);
|
|
1956
|
+
process.exit(0);
|
|
1957
|
+
case "--help":
|
|
1958
|
+
case "-h":
|
|
1959
|
+
program.help();
|
|
1960
|
+
break;
|
|
1961
|
+
case "--clear-config":
|
|
1962
|
+
clearConfig();
|
|
1963
|
+
console.log("Configuration cleared.");
|
|
1964
|
+
process.exit(0);
|
|
1965
|
+
case "--config-path":
|
|
1966
|
+
console.log(getConfigPath());
|
|
1967
|
+
process.exit(0);
|
|
1968
|
+
case "--check":
|
|
1969
|
+
await handleCheckCommand();
|
|
1970
|
+
break;
|
|
1971
|
+
case "--fix":
|
|
1972
|
+
options.fix = true;
|
|
1973
|
+
break;
|
|
1974
|
+
default:
|
|
1975
|
+
console.error(color__default.red(`Unknown option: ${name}`));
|
|
1976
|
+
process.exit(1);
|
|
1092
1977
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1978
|
+
}
|
|
1979
|
+
if (options.check) {
|
|
1980
|
+
await handleCheckCommand();
|
|
1981
|
+
}
|
|
1982
|
+
if (options.fix) {
|
|
1983
|
+
await handleFixCommand(options);
|
|
1984
|
+
}
|
|
1985
|
+
if (options.dir && !options.workspace) {
|
|
1986
|
+
console.error(color__default.red("Error:") + " --dir requires --workspace flag");
|
|
1987
|
+
console.log(
|
|
1988
|
+
color__default.dim(
|
|
1989
|
+
" Example: pnpm create krispya my-lib --workspace --dir examples"
|
|
1990
|
+
)
|
|
1991
|
+
);
|
|
1097
1992
|
process.exit(1);
|
|
1098
1993
|
}
|
|
1994
|
+
if (options.workspace) {
|
|
1995
|
+
await handleWorkspaceCommand(name, options);
|
|
1996
|
+
}
|
|
1997
|
+
console.clear();
|
|
1998
|
+
p__namespace.intro(color__default.bgCyan(color__default.black(` create-krispya v${pkg.version} `)));
|
|
1999
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
2000
|
+
if (monorepoRoot && Object.keys(options).length === 0) {
|
|
2001
|
+
await handleInteractiveMonorepoMode(monorepoRoot);
|
|
2002
|
+
}
|
|
2003
|
+
let generateOptions;
|
|
2004
|
+
if (Object.keys(options).length > 0) {
|
|
2005
|
+
const template = options.template ?? "vanilla";
|
|
2006
|
+
const baseTemplate = index.getBaseTemplate(template);
|
|
2007
|
+
const defaultName = getDefaultProjectName(template);
|
|
2008
|
+
const projectType = options.type ?? "app";
|
|
2009
|
+
generateOptions = {
|
|
2010
|
+
name: name || defaultName,
|
|
2011
|
+
projectType,
|
|
2012
|
+
libraryBundler: projectType === "library" ? options.bundler ?? "unbuild" : void 0,
|
|
2013
|
+
template,
|
|
2014
|
+
linter: options.linter ?? "oxlint",
|
|
2015
|
+
formatter: options.formatter ?? "oxfmt",
|
|
2016
|
+
...baseTemplate === "r3f" && {
|
|
2017
|
+
drei: options.drei ? {} : void 0,
|
|
2018
|
+
handle: options.handle ? {} : void 0,
|
|
2019
|
+
leva: options.leva ? {} : void 0,
|
|
2020
|
+
postprocessing: options.postprocessing ? {} : void 0,
|
|
2021
|
+
rapier: options.rapier ? {} : void 0,
|
|
2022
|
+
xr: options.xr ? {} : void 0,
|
|
2023
|
+
uikit: options.uikit ? {} : void 0,
|
|
2024
|
+
offscreen: options.offscreen ? {} : void 0,
|
|
2025
|
+
zustand: options.zustand ? {} : void 0,
|
|
2026
|
+
koota: options.koota ? {} : void 0,
|
|
2027
|
+
viverse: options.viverse ? {} : void 0,
|
|
2028
|
+
triplex: options.triplex ? {} : void 0
|
|
2029
|
+
},
|
|
2030
|
+
packageManager: options.packageManager,
|
|
2031
|
+
pnpmManageVersions: options.pnpmManageVersions,
|
|
2032
|
+
nodeVersion: options.nodeVersion ?? "latest"
|
|
2033
|
+
};
|
|
2034
|
+
} else {
|
|
2035
|
+
generateOptions = await promptForOptions(name);
|
|
2036
|
+
}
|
|
2037
|
+
if (generateOptions.projectType === "monorepo") {
|
|
2038
|
+
await handleMonorepoCreation(generateOptions);
|
|
2039
|
+
} else {
|
|
2040
|
+
await handleStandaloneProjectCreation(generateOptions);
|
|
2041
|
+
}
|
|
1099
2042
|
});
|
|
1100
2043
|
await program.parseAsync();
|
|
1101
2044
|
}
|