create-krispya 0.4.8 → 0.5.1
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 +89 -30
- package/dist/chunks/index.cjs +2360 -0
- package/dist/chunks/index.mjs +2346 -0
- package/dist/cli.cjs +816 -195
- package/dist/cli.mjs +818 -197
- package/dist/index.cjs +10 -1714
- package/dist/index.d.cts +84 -79
- package/dist/index.d.mts +84 -79
- package/dist/index.d.ts +84 -79
- package/dist/index.mjs +2 -1707
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,80 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
3
|
import { cwd } from 'process';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { join, dirname, resolve } from 'path';
|
|
5
|
+
import { mkdir, writeFile, access, readFile } from 'fs/promises';
|
|
6
|
+
import { constants } from 'fs';
|
|
7
7
|
import { Command } from 'commander';
|
|
8
8
|
import * as p from '@clack/prompts';
|
|
9
9
|
import color from 'chalk';
|
|
10
10
|
import { fetch } from 'undici';
|
|
11
11
|
import { spawn } from 'child_process';
|
|
12
|
+
import { g as getBaseTemplate, a as getLanguageFromTemplate, b as generateRandomName, c as getLatestPnpmVersion, d as getLatestNodeVersion, e as getLatestNpmVersion, f as generate } from './chunks/index.mjs';
|
|
12
13
|
import Conf from 'conf';
|
|
13
14
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
15
|
+
const editorNames = {
|
|
16
|
+
cursor: "Cursor",
|
|
17
|
+
code: "VS Code",
|
|
18
|
+
webstorm: "WebStorm",
|
|
19
|
+
skip: "Skip"
|
|
20
|
+
};
|
|
21
|
+
function openInEditor(editor, path, reuseWindow) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
const isWindows = process.platform === "win32";
|
|
24
|
+
const useReuseFlag = reuseWindow && (editor === "cursor" || editor === "code");
|
|
25
|
+
const args = useReuseFlag ? ["-r", path] : [path];
|
|
26
|
+
const child = isWindows ? spawn(`${editor} ${useReuseFlag ? "-r " : ""}"${path}"`, {
|
|
27
|
+
detached: true,
|
|
28
|
+
stdio: "ignore",
|
|
29
|
+
shell: true
|
|
30
|
+
}) : spawn(editor, args, {
|
|
31
|
+
detached: true,
|
|
32
|
+
stdio: "ignore"
|
|
33
|
+
});
|
|
34
|
+
child.on("error", reject);
|
|
35
|
+
child.unref();
|
|
36
|
+
setTimeout(resolve, 100);
|
|
37
|
+
});
|
|
31
38
|
}
|
|
32
39
|
|
|
33
|
-
const require$1 = createRequire(import.meta.url);
|
|
34
|
-
const pkg = require$1("../package.json");
|
|
35
|
-
function getDefaultProjectName(template) {
|
|
36
|
-
const base = getBaseTemplate(template);
|
|
37
|
-
switch (base) {
|
|
38
|
-
case "vanilla":
|
|
39
|
-
return `vanilla-${generateRandomName()}`;
|
|
40
|
-
case "react":
|
|
41
|
-
return `react-${generateRandomName()}`;
|
|
42
|
-
case "r3f":
|
|
43
|
-
return `react-three-${generateRandomName()}`;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function getDefaultOptions(template, name, projectType = "app", libraryBundler) {
|
|
47
|
-
const baseTemplate = getBaseTemplate(template);
|
|
48
|
-
const base = {
|
|
49
|
-
name,
|
|
50
|
-
template,
|
|
51
|
-
projectType,
|
|
52
|
-
libraryBundler: projectType === "library" ? libraryBundler ?? "unbuild" : void 0,
|
|
53
|
-
packageManager: "pnpm",
|
|
54
|
-
pnpmManageVersions: true,
|
|
55
|
-
nodeVersion: "latest",
|
|
56
|
-
linter: "oxlint",
|
|
57
|
-
formatter: "oxfmt"
|
|
58
|
-
};
|
|
59
|
-
if (baseTemplate === "r3f") {
|
|
60
|
-
return {
|
|
61
|
-
...base,
|
|
62
|
-
drei: {},
|
|
63
|
-
handle: {},
|
|
64
|
-
leva: {},
|
|
65
|
-
postprocessing: {},
|
|
66
|
-
rapier: {},
|
|
67
|
-
xr: {},
|
|
68
|
-
uikit: {},
|
|
69
|
-
offscreen: {},
|
|
70
|
-
zustand: {},
|
|
71
|
-
koota: {},
|
|
72
|
-
triplex: {},
|
|
73
|
-
viverse: {}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
return base;
|
|
77
|
-
}
|
|
78
40
|
function formatConfigSummary(options) {
|
|
79
41
|
const lines = [];
|
|
80
42
|
const VALUE_COL = 27;
|
|
@@ -88,6 +50,12 @@ function formatConfigSummary(options) {
|
|
|
88
50
|
return lang === "typescript" ? "TypeScript" : lang === "javascript" ? "JavaScript" : lang;
|
|
89
51
|
};
|
|
90
52
|
const projectType = options.projectType ?? "app";
|
|
53
|
+
const baseTemplate = options.template ? getBaseTemplate(options.template) : "vanilla";
|
|
54
|
+
if (baseTemplate === "react") {
|
|
55
|
+
lines.push(formatRow("Framework", "React"));
|
|
56
|
+
} else if (baseTemplate === "r3f") {
|
|
57
|
+
lines.push(formatRow("Framework", "React Three Fiber"));
|
|
58
|
+
}
|
|
91
59
|
const language = options.template ? getLanguageFromTemplate(options.template) : "typescript";
|
|
92
60
|
lines.push(formatRow("Language", formatLanguage(language)));
|
|
93
61
|
if (projectType === "library") {
|
|
@@ -107,7 +75,8 @@ function formatConfigSummary(options) {
|
|
|
107
75
|
if (options.formatter) {
|
|
108
76
|
lines.push(formatRow("Formatter", options.formatter));
|
|
109
77
|
}
|
|
110
|
-
|
|
78
|
+
const testing = options.testing ?? (projectType === "library" ? "vitest" : "none");
|
|
79
|
+
lines.push(formatRow("Testing", testing));
|
|
111
80
|
if (options.template && getBaseTemplate(options.template) === "r3f") {
|
|
112
81
|
const integrationNames = [
|
|
113
82
|
options.drei && "drei",
|
|
@@ -134,7 +103,123 @@ function formatConfigSummary(options) {
|
|
|
134
103
|
}
|
|
135
104
|
return lines.join("\n");
|
|
136
105
|
}
|
|
137
|
-
|
|
106
|
+
function formatMonorepoConfigSummary(options) {
|
|
107
|
+
const lines = [];
|
|
108
|
+
const VALUE_COL = 27;
|
|
109
|
+
const formatRow = (label, value, indent = "") => {
|
|
110
|
+
const fullLabel = indent + label;
|
|
111
|
+
const dotCount = Math.max(1, VALUE_COL - fullLabel.length - 1);
|
|
112
|
+
const dots = color.gray(".".repeat(dotCount));
|
|
113
|
+
return `${indent}${label} ${dots} ${value}`;
|
|
114
|
+
};
|
|
115
|
+
lines.push(formatRow("Node version", options.nodeVersion || "latest"));
|
|
116
|
+
lines.push(formatRow("Package manager", options.packageManager || "pnpm"));
|
|
117
|
+
if (options.packageManager === "pnpm") {
|
|
118
|
+
const versionManaged = options.pnpmManageVersions ? "yes" : "no";
|
|
119
|
+
lines.push(formatRow("\u21B3 Version managed", versionManaged, ""));
|
|
120
|
+
}
|
|
121
|
+
lines.push(formatRow("Linter", options.linter));
|
|
122
|
+
lines.push(formatRow("Formatter", options.formatter));
|
|
123
|
+
return lines.join("\n");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const config = new Conf({
|
|
127
|
+
projectName: "create-krispya"
|
|
128
|
+
});
|
|
129
|
+
function getPreferredEditor() {
|
|
130
|
+
return config.get("preferredEditor");
|
|
131
|
+
}
|
|
132
|
+
function setPreferredEditor(editor) {
|
|
133
|
+
config.set("preferredEditor", editor);
|
|
134
|
+
}
|
|
135
|
+
function getReuseWindow() {
|
|
136
|
+
return config.get("reuseWindow") ?? false;
|
|
137
|
+
}
|
|
138
|
+
function setReuseWindow(reuse) {
|
|
139
|
+
config.set("reuseWindow", reuse);
|
|
140
|
+
}
|
|
141
|
+
function clearConfig() {
|
|
142
|
+
config.clear();
|
|
143
|
+
}
|
|
144
|
+
function getConfigPath() {
|
|
145
|
+
return config.path;
|
|
146
|
+
}
|
|
147
|
+
function getCustomTemplates() {
|
|
148
|
+
return config.get("customTemplates") ?? {};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function getDefaultOptions(template, name, projectType = "app", libraryBundler, integrations, inheritedTooling) {
|
|
152
|
+
const baseTemplate = getBaseTemplate(template);
|
|
153
|
+
const base = {
|
|
154
|
+
name,
|
|
155
|
+
template,
|
|
156
|
+
projectType,
|
|
157
|
+
libraryBundler: projectType === "library" ? libraryBundler ?? "unbuild" : void 0,
|
|
158
|
+
packageManager: "pnpm",
|
|
159
|
+
pnpmManageVersions: true,
|
|
160
|
+
nodeVersion: "latest",
|
|
161
|
+
linter: inheritedTooling?.linter ?? "oxlint",
|
|
162
|
+
formatter: inheritedTooling?.formatter ?? "oxfmt",
|
|
163
|
+
// Libraries get vitest by default, apps don't
|
|
164
|
+
testing: projectType === "library" ? "vitest" : "none"
|
|
165
|
+
};
|
|
166
|
+
if (baseTemplate === "r3f" && integrations) {
|
|
167
|
+
return {
|
|
168
|
+
...base,
|
|
169
|
+
drei: integrations.includes("drei") ? {} : void 0,
|
|
170
|
+
handle: integrations.includes("handle") ? {} : void 0,
|
|
171
|
+
leva: integrations.includes("leva") ? {} : void 0,
|
|
172
|
+
postprocessing: integrations.includes("postprocessing") ? {} : void 0,
|
|
173
|
+
rapier: integrations.includes("rapier") ? {} : void 0,
|
|
174
|
+
xr: integrations.includes("xr") ? {} : void 0,
|
|
175
|
+
uikit: integrations.includes("uikit") ? {} : void 0,
|
|
176
|
+
offscreen: integrations.includes("offscreen") ? {} : void 0,
|
|
177
|
+
zustand: integrations.includes("zustand") ? {} : void 0,
|
|
178
|
+
koota: integrations.includes("koota") ? {} : void 0,
|
|
179
|
+
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
180
|
+
viverse: integrations.includes("viverse") ? {} : void 0
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return base;
|
|
184
|
+
}
|
|
185
|
+
function getDefaultProjectName(template) {
|
|
186
|
+
const base = getBaseTemplate(template);
|
|
187
|
+
switch (base) {
|
|
188
|
+
case "vanilla":
|
|
189
|
+
return `vanilla-${generateRandomName()}`;
|
|
190
|
+
case "react":
|
|
191
|
+
return `react-${generateRandomName()}`;
|
|
192
|
+
case "r3f":
|
|
193
|
+
return `react-three-${generateRandomName()}`;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async function promptForR3fIntegrations() {
|
|
197
|
+
const selected = await p.multiselect({
|
|
198
|
+
message: "R3F integrations",
|
|
199
|
+
options: [
|
|
200
|
+
{ value: "drei", label: "Drei" },
|
|
201
|
+
{ value: "handle", label: "Handle" },
|
|
202
|
+
{ value: "leva", label: "Leva" },
|
|
203
|
+
{ value: "postprocessing", label: "Postprocessing" },
|
|
204
|
+
{ value: "rapier", label: "Rapier" },
|
|
205
|
+
{ value: "xr", label: "XR" },
|
|
206
|
+
{ value: "uikit", label: "UIKit" },
|
|
207
|
+
{ value: "offscreen", label: "Offscreen" },
|
|
208
|
+
{ value: "zustand", label: "Zustand" },
|
|
209
|
+
{ value: "koota", label: "Koota" },
|
|
210
|
+
{ value: "triplex", label: "Triplex" },
|
|
211
|
+
{ value: "viverse", label: "Viverse" }
|
|
212
|
+
],
|
|
213
|
+
initialValues: ["drei"],
|
|
214
|
+
required: false
|
|
215
|
+
});
|
|
216
|
+
if (p.isCancel(selected)) {
|
|
217
|
+
p.cancel("Operation cancelled.");
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
return selected;
|
|
221
|
+
}
|
|
222
|
+
async function promptForCustomization(template, name, projectType, integrations, inheritedTooling) {
|
|
138
223
|
let libraryBundler;
|
|
139
224
|
if (projectType === "library") {
|
|
140
225
|
const bundler = await p.select({
|
|
@@ -206,29 +291,49 @@ async function promptForCustomization(template, name, projectType) {
|
|
|
206
291
|
}
|
|
207
292
|
pnpmManageVersions = managePnpm;
|
|
208
293
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
294
|
+
let linter = inheritedTooling?.linter ?? "oxlint";
|
|
295
|
+
let formatter = inheritedTooling?.formatter ?? "oxfmt";
|
|
296
|
+
if (!inheritedTooling?.linter) {
|
|
297
|
+
const linterChoice = await p.select({
|
|
298
|
+
message: "Linter",
|
|
299
|
+
options: [
|
|
300
|
+
{ value: "oxlint", label: "Oxlint", hint: "fast, from OXC" },
|
|
301
|
+
{ value: "eslint", label: "ESLint", hint: "classic" },
|
|
302
|
+
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
303
|
+
],
|
|
304
|
+
initialValue: "oxlint"
|
|
305
|
+
});
|
|
306
|
+
if (p.isCancel(linterChoice)) {
|
|
307
|
+
p.cancel("Operation cancelled.");
|
|
308
|
+
process.exit(0);
|
|
309
|
+
}
|
|
310
|
+
linter = linterChoice;
|
|
221
311
|
}
|
|
222
|
-
|
|
223
|
-
|
|
312
|
+
if (!inheritedTooling?.formatter) {
|
|
313
|
+
const formatterChoice = await p.select({
|
|
314
|
+
message: "Formatter",
|
|
315
|
+
options: [
|
|
316
|
+
{ value: "oxfmt", label: "Oxfmt", hint: "fast, Prettier-compatible" },
|
|
317
|
+
{ value: "prettier", label: "Prettier", hint: "classic" },
|
|
318
|
+
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
319
|
+
],
|
|
320
|
+
initialValue: "oxfmt"
|
|
321
|
+
});
|
|
322
|
+
if (p.isCancel(formatterChoice)) {
|
|
323
|
+
p.cancel("Operation cancelled.");
|
|
324
|
+
process.exit(0);
|
|
325
|
+
}
|
|
326
|
+
formatter = formatterChoice;
|
|
327
|
+
}
|
|
328
|
+
const testing = await p.select({
|
|
329
|
+
message: "Testing",
|
|
224
330
|
options: [
|
|
225
|
-
{ value: "
|
|
226
|
-
{ value: "
|
|
227
|
-
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
331
|
+
{ value: "vitest", label: "Vitest", hint: "fast, Vite-native" },
|
|
332
|
+
{ value: "none", label: "None" }
|
|
228
333
|
],
|
|
229
|
-
initialValue: "
|
|
334
|
+
initialValue: projectType === "library" ? "vitest" : "none"
|
|
230
335
|
});
|
|
231
|
-
if (p.isCancel(
|
|
336
|
+
if (p.isCancel(testing)) {
|
|
232
337
|
p.cancel("Operation cancelled.");
|
|
233
338
|
process.exit(0);
|
|
234
339
|
}
|
|
@@ -246,47 +351,7 @@ async function promptForCustomization(template, name, projectType) {
|
|
|
246
351
|
}
|
|
247
352
|
const baseTemplate = getBaseTemplate(template);
|
|
248
353
|
const finalTemplate = language === "javascript" ? `${baseTemplate}-js` : baseTemplate;
|
|
249
|
-
|
|
250
|
-
if (baseTemplate === "r3f") {
|
|
251
|
-
const selected = await p.multiselect({
|
|
252
|
-
message: "R3F integrations",
|
|
253
|
-
options: [
|
|
254
|
-
{ value: "drei", label: "Drei" },
|
|
255
|
-
{ value: "handle", label: "Handle" },
|
|
256
|
-
{ value: "leva", label: "Leva" },
|
|
257
|
-
{ value: "postprocessing", label: "Postprocessing" },
|
|
258
|
-
{ value: "rapier", label: "Rapier" },
|
|
259
|
-
{ value: "xr", label: "XR" },
|
|
260
|
-
{ value: "uikit", label: "UIKit" },
|
|
261
|
-
{ value: "offscreen", label: "Offscreen" },
|
|
262
|
-
{ value: "zustand", label: "Zustand" },
|
|
263
|
-
{ value: "koota", label: "Koota" },
|
|
264
|
-
{ value: "triplex", label: "Triplex" },
|
|
265
|
-
{ value: "viverse", label: "Viverse" }
|
|
266
|
-
],
|
|
267
|
-
initialValues: [
|
|
268
|
-
"drei",
|
|
269
|
-
"handle",
|
|
270
|
-
"leva",
|
|
271
|
-
"postprocessing",
|
|
272
|
-
"rapier",
|
|
273
|
-
"xr",
|
|
274
|
-
"uikit",
|
|
275
|
-
"offscreen",
|
|
276
|
-
"zustand",
|
|
277
|
-
"koota",
|
|
278
|
-
"triplex",
|
|
279
|
-
"viverse"
|
|
280
|
-
],
|
|
281
|
-
required: false
|
|
282
|
-
});
|
|
283
|
-
if (p.isCancel(selected)) {
|
|
284
|
-
p.cancel("Operation cancelled.");
|
|
285
|
-
process.exit(0);
|
|
286
|
-
}
|
|
287
|
-
integrations = selected;
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
354
|
+
const base = {
|
|
290
355
|
name,
|
|
291
356
|
template: finalTemplate,
|
|
292
357
|
projectType,
|
|
@@ -296,7 +361,11 @@ async function promptForCustomization(template, name, projectType) {
|
|
|
296
361
|
pnpmManageVersions,
|
|
297
362
|
linter,
|
|
298
363
|
formatter,
|
|
299
|
-
|
|
364
|
+
testing
|
|
365
|
+
};
|
|
366
|
+
if (baseTemplate === "r3f" && integrations) {
|
|
367
|
+
return {
|
|
368
|
+
...base,
|
|
300
369
|
drei: integrations.includes("drei") ? {} : void 0,
|
|
301
370
|
handle: integrations.includes("handle") ? {} : void 0,
|
|
302
371
|
leva: integrations.includes("leva") ? {} : void 0,
|
|
@@ -309,9 +378,140 @@ async function promptForCustomization(template, name, projectType) {
|
|
|
309
378
|
koota: integrations.includes("koota") ? {} : void 0,
|
|
310
379
|
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
311
380
|
viverse: integrations.includes("viverse") ? {} : void 0
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return base;
|
|
384
|
+
}
|
|
385
|
+
async function promptForInitialPackage() {
|
|
386
|
+
const choice = await p.select({
|
|
387
|
+
message: "Add an initial package?",
|
|
388
|
+
options: [
|
|
389
|
+
{ value: "app", label: "Application" },
|
|
390
|
+
{ value: "library", label: "Library" },
|
|
391
|
+
{ value: "skip", label: "Skip" }
|
|
392
|
+
],
|
|
393
|
+
initialValue: "app"
|
|
394
|
+
});
|
|
395
|
+
if (p.isCancel(choice)) {
|
|
396
|
+
p.cancel("Operation cancelled.");
|
|
397
|
+
process.exit(0);
|
|
398
|
+
}
|
|
399
|
+
return choice;
|
|
400
|
+
}
|
|
401
|
+
function getDefaultMonorepoOptions(name) {
|
|
402
|
+
return {
|
|
403
|
+
name,
|
|
404
|
+
projectType: "monorepo",
|
|
405
|
+
packageManager: "pnpm",
|
|
406
|
+
pnpmManageVersions: true,
|
|
407
|
+
nodeVersion: "latest",
|
|
408
|
+
linter: "oxlint",
|
|
409
|
+
formatter: "oxfmt"
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
async function promptForMonorepoCustomization(name) {
|
|
413
|
+
const nodeVersion = await p.text({
|
|
414
|
+
message: "Node.js version",
|
|
415
|
+
placeholder: "latest",
|
|
416
|
+
defaultValue: "latest",
|
|
417
|
+
validate: (value) => {
|
|
418
|
+
if (!value.length) return "Required";
|
|
419
|
+
if (value !== "latest" && !/^\d+(\.\d+(\.\d+)?)?$/.test(value)) {
|
|
420
|
+
return 'Must be "latest" or a valid semver (e.g., "22" or "22.13.0")';
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
if (p.isCancel(nodeVersion)) {
|
|
425
|
+
p.cancel("Operation cancelled.");
|
|
426
|
+
process.exit(0);
|
|
427
|
+
}
|
|
428
|
+
const packageManager = await p.select({
|
|
429
|
+
message: "Package manager",
|
|
430
|
+
options: [
|
|
431
|
+
{ value: "pnpm", label: "pnpm" },
|
|
432
|
+
{ value: "npm", label: "npm" },
|
|
433
|
+
{ value: "yarn", label: "yarn" }
|
|
434
|
+
],
|
|
435
|
+
initialValue: "pnpm"
|
|
436
|
+
});
|
|
437
|
+
if (p.isCancel(packageManager)) {
|
|
438
|
+
p.cancel("Operation cancelled.");
|
|
439
|
+
process.exit(0);
|
|
440
|
+
}
|
|
441
|
+
let pnpmManageVersions = true;
|
|
442
|
+
if (packageManager === "pnpm") {
|
|
443
|
+
const managePnpm = await p.confirm({
|
|
444
|
+
message: "Enable manage-package-manager-versions?",
|
|
445
|
+
initialValue: true
|
|
446
|
+
});
|
|
447
|
+
if (p.isCancel(managePnpm)) {
|
|
448
|
+
p.cancel("Operation cancelled.");
|
|
449
|
+
process.exit(0);
|
|
312
450
|
}
|
|
451
|
+
pnpmManageVersions = managePnpm;
|
|
452
|
+
}
|
|
453
|
+
const linter = await p.select({
|
|
454
|
+
message: "Linter",
|
|
455
|
+
options: [
|
|
456
|
+
{ value: "oxlint", label: "Oxlint", hint: "fast, from OXC" },
|
|
457
|
+
{ value: "eslint", label: "ESLint", hint: "classic" },
|
|
458
|
+
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
459
|
+
],
|
|
460
|
+
initialValue: "oxlint"
|
|
461
|
+
});
|
|
462
|
+
if (p.isCancel(linter)) {
|
|
463
|
+
p.cancel("Operation cancelled.");
|
|
464
|
+
process.exit(0);
|
|
465
|
+
}
|
|
466
|
+
const formatter = await p.select({
|
|
467
|
+
message: "Formatter",
|
|
468
|
+
options: [
|
|
469
|
+
{ value: "oxfmt", label: "Oxfmt", hint: "fast, Prettier-compatible" },
|
|
470
|
+
{ value: "prettier", label: "Prettier", hint: "classic" },
|
|
471
|
+
{ value: "biome", label: "Biome", hint: "all-in-one" }
|
|
472
|
+
],
|
|
473
|
+
initialValue: "oxfmt"
|
|
474
|
+
});
|
|
475
|
+
if (p.isCancel(formatter)) {
|
|
476
|
+
p.cancel("Operation cancelled.");
|
|
477
|
+
process.exit(0);
|
|
478
|
+
}
|
|
479
|
+
return {
|
|
480
|
+
name,
|
|
481
|
+
projectType: "monorepo",
|
|
482
|
+
nodeVersion,
|
|
483
|
+
packageManager,
|
|
484
|
+
pnpmManageVersions,
|
|
485
|
+
linter,
|
|
486
|
+
formatter
|
|
313
487
|
};
|
|
314
488
|
}
|
|
489
|
+
async function promptForMonorepo(workspaceName) {
|
|
490
|
+
const defaultOptions = getDefaultMonorepoOptions(workspaceName);
|
|
491
|
+
p.note(
|
|
492
|
+
formatMonorepoConfigSummary({
|
|
493
|
+
name: defaultOptions.name,
|
|
494
|
+
nodeVersion: defaultOptions.nodeVersion ?? "latest",
|
|
495
|
+
packageManager: defaultOptions.packageManager ?? "pnpm",
|
|
496
|
+
pnpmManageVersions: defaultOptions.pnpmManageVersions,
|
|
497
|
+
linter: defaultOptions.linter ?? "oxlint",
|
|
498
|
+
formatter: defaultOptions.formatter ?? "oxfmt"
|
|
499
|
+
}),
|
|
500
|
+
"Workspace Configuration"
|
|
501
|
+
);
|
|
502
|
+
const proceed = await p.confirm({
|
|
503
|
+
message: "Proceed with these settings?",
|
|
504
|
+
initialValue: true
|
|
505
|
+
});
|
|
506
|
+
if (p.isCancel(proceed)) {
|
|
507
|
+
p.cancel("Operation cancelled.");
|
|
508
|
+
process.exit(0);
|
|
509
|
+
}
|
|
510
|
+
if (proceed) {
|
|
511
|
+
return defaultOptions;
|
|
512
|
+
}
|
|
513
|
+
return promptForMonorepoCustomization(workspaceName);
|
|
514
|
+
}
|
|
315
515
|
async function promptForOptions(name) {
|
|
316
516
|
let projectName = name;
|
|
317
517
|
if (!projectName) {
|
|
@@ -333,7 +533,8 @@ async function promptForOptions(name) {
|
|
|
333
533
|
message: "Project type",
|
|
334
534
|
options: [
|
|
335
535
|
{ value: "app", label: "Application" },
|
|
336
|
-
{ value: "library", label: "Library" }
|
|
536
|
+
{ value: "library", label: "Library" },
|
|
537
|
+
{ value: "monorepo", label: "Monorepo" }
|
|
337
538
|
],
|
|
338
539
|
initialValue: "app"
|
|
339
540
|
});
|
|
@@ -341,91 +542,408 @@ async function promptForOptions(name) {
|
|
|
341
542
|
p.cancel("Operation cancelled.");
|
|
342
543
|
process.exit(0);
|
|
343
544
|
}
|
|
344
|
-
|
|
545
|
+
if (projectType === "monorepo") {
|
|
546
|
+
return promptForMonorepo(projectName);
|
|
547
|
+
}
|
|
548
|
+
return promptForPackageOptions(projectName, projectType);
|
|
549
|
+
}
|
|
550
|
+
function customTemplateToOptions(customTemplate, name, projectType) {
|
|
551
|
+
const baseTemplate = customTemplate.baseTemplate;
|
|
552
|
+
const template = baseTemplate;
|
|
553
|
+
const base = {
|
|
554
|
+
name,
|
|
555
|
+
template,
|
|
556
|
+
projectType,
|
|
557
|
+
packageManager: "pnpm",
|
|
558
|
+
pnpmManageVersions: true,
|
|
559
|
+
nodeVersion: "latest",
|
|
560
|
+
linter: customTemplate.linter,
|
|
561
|
+
formatter: customTemplate.formatter,
|
|
562
|
+
testing: customTemplate.testing
|
|
563
|
+
};
|
|
564
|
+
if (baseTemplate === "r3f" && customTemplate.integrations) {
|
|
565
|
+
const integrations = customTemplate.integrations;
|
|
566
|
+
return {
|
|
567
|
+
...base,
|
|
568
|
+
drei: integrations.includes("drei") ? {} : void 0,
|
|
569
|
+
handle: integrations.includes("handle") ? {} : void 0,
|
|
570
|
+
leva: integrations.includes("leva") ? {} : void 0,
|
|
571
|
+
postprocessing: integrations.includes("postprocessing") ? {} : void 0,
|
|
572
|
+
rapier: integrations.includes("rapier") ? {} : void 0,
|
|
573
|
+
xr: integrations.includes("xr") ? {} : void 0,
|
|
574
|
+
uikit: integrations.includes("uikit") ? {} : void 0,
|
|
575
|
+
offscreen: integrations.includes("offscreen") ? {} : void 0,
|
|
576
|
+
zustand: integrations.includes("zustand") ? {} : void 0,
|
|
577
|
+
koota: integrations.includes("koota") ? {} : void 0,
|
|
578
|
+
triplex: integrations.includes("triplex") ? {} : void 0,
|
|
579
|
+
viverse: integrations.includes("viverse") ? {} : void 0
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
return base;
|
|
583
|
+
}
|
|
584
|
+
async function promptForPackageOptions(projectName, projectType, inheritedTooling) {
|
|
585
|
+
const builtInOptions = [
|
|
586
|
+
{ value: "vanilla", label: "Vanilla" },
|
|
587
|
+
{ value: "react", label: "React" },
|
|
588
|
+
{ value: "r3f", label: "React Three Fiber" }
|
|
589
|
+
];
|
|
590
|
+
const customTemplates = getCustomTemplates();
|
|
591
|
+
const customOptions = Object.keys(customTemplates).map((name) => ({
|
|
592
|
+
value: `custom:${name}`,
|
|
593
|
+
label: name,
|
|
594
|
+
hint: "saved template"
|
|
595
|
+
}));
|
|
596
|
+
const allOptions = [...builtInOptions, ...customOptions];
|
|
597
|
+
const templateSelection = await p.select({
|
|
345
598
|
message: "Select a template",
|
|
346
|
-
options:
|
|
347
|
-
{ value: "vanilla", label: "Vanilla" },
|
|
348
|
-
{ value: "react", label: "React" },
|
|
349
|
-
{ value: "r3f", label: "React Three Fiber" }
|
|
350
|
-
],
|
|
599
|
+
options: allOptions,
|
|
351
600
|
initialValue: "vanilla"
|
|
352
601
|
});
|
|
353
|
-
if (p.isCancel(
|
|
602
|
+
if (p.isCancel(templateSelection)) {
|
|
354
603
|
p.cancel("Operation cancelled.");
|
|
355
604
|
process.exit(0);
|
|
356
605
|
}
|
|
606
|
+
const selection = templateSelection;
|
|
607
|
+
if (selection.startsWith("custom:")) {
|
|
608
|
+
const customName = selection.slice(7);
|
|
609
|
+
const customTemplate = customTemplates[customName];
|
|
610
|
+
const defaultOptions2 = customTemplateToOptions(customTemplate, projectName, projectType);
|
|
611
|
+
if (inheritedTooling?.linter) {
|
|
612
|
+
defaultOptions2.linter = inheritedTooling.linter;
|
|
613
|
+
}
|
|
614
|
+
if (inheritedTooling?.formatter) {
|
|
615
|
+
defaultOptions2.formatter = inheritedTooling.formatter;
|
|
616
|
+
}
|
|
617
|
+
const configTitle2 = inheritedTooling ? `Template: ${customName} (using workspace tooling)` : `Template: ${customName}`;
|
|
618
|
+
p.note(formatConfigSummary(defaultOptions2), configTitle2);
|
|
619
|
+
const proceed2 = await p.confirm({
|
|
620
|
+
message: "Proceed with these settings?",
|
|
621
|
+
initialValue: true
|
|
622
|
+
});
|
|
623
|
+
if (p.isCancel(proceed2)) {
|
|
624
|
+
p.cancel("Operation cancelled.");
|
|
625
|
+
process.exit(0);
|
|
626
|
+
}
|
|
627
|
+
if (proceed2) {
|
|
628
|
+
return defaultOptions2;
|
|
629
|
+
}
|
|
630
|
+
return promptForCustomization(
|
|
631
|
+
customTemplate.baseTemplate,
|
|
632
|
+
projectName,
|
|
633
|
+
projectType,
|
|
634
|
+
customTemplate.integrations,
|
|
635
|
+
inheritedTooling
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
const template = selection;
|
|
639
|
+
const baseTemplate = getBaseTemplate(template);
|
|
640
|
+
let integrations;
|
|
641
|
+
if (baseTemplate === "r3f") {
|
|
642
|
+
integrations = await promptForR3fIntegrations();
|
|
643
|
+
}
|
|
357
644
|
const defaultOptions = getDefaultOptions(
|
|
358
645
|
template,
|
|
359
646
|
projectName,
|
|
360
|
-
projectType
|
|
647
|
+
projectType,
|
|
648
|
+
void 0,
|
|
649
|
+
integrations,
|
|
650
|
+
inheritedTooling
|
|
361
651
|
);
|
|
362
|
-
|
|
363
|
-
|
|
652
|
+
const configTitle = inheritedTooling ? "Template Configuration (using workspace tooling)" : "Template Configuration";
|
|
653
|
+
p.note(formatConfigSummary(defaultOptions), configTitle);
|
|
654
|
+
const proceed = await p.confirm({
|
|
364
655
|
message: "Proceed with these settings?",
|
|
365
|
-
|
|
366
|
-
{ value: "confirm", label: "Yes, create project" },
|
|
367
|
-
{ value: "customize", label: "No, let me customize" }
|
|
368
|
-
],
|
|
369
|
-
initialValue: "confirm"
|
|
656
|
+
initialValue: true
|
|
370
657
|
});
|
|
371
|
-
if (p.isCancel(
|
|
658
|
+
if (p.isCancel(proceed)) {
|
|
372
659
|
p.cancel("Operation cancelled.");
|
|
373
660
|
process.exit(0);
|
|
374
661
|
}
|
|
375
|
-
if (
|
|
662
|
+
if (proceed) {
|
|
376
663
|
return defaultOptions;
|
|
377
664
|
}
|
|
378
|
-
return promptForCustomization(
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
665
|
+
return promptForCustomization(template, projectName, projectType, integrations, inheritedTooling);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const require$1 = createRequire(import.meta.url);
|
|
669
|
+
const pkg = require$1("../package.json");
|
|
670
|
+
async function detectMonorepoRoot() {
|
|
671
|
+
let currentDir = cwd();
|
|
672
|
+
const root = resolve("/");
|
|
673
|
+
while (currentDir !== root) {
|
|
674
|
+
const workspaceFile = join(currentDir, "pnpm-workspace.yaml");
|
|
675
|
+
try {
|
|
676
|
+
await access(workspaceFile, constants.F_OK);
|
|
677
|
+
const content = await readFile(workspaceFile, "utf-8");
|
|
678
|
+
if (content.includes("packages:")) {
|
|
679
|
+
return currentDir;
|
|
680
|
+
}
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
currentDir = dirname(currentDir);
|
|
684
|
+
}
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
async function detectWorkspaceTooling(monorepoRoot) {
|
|
688
|
+
try {
|
|
689
|
+
const pkgPath = join(monorepoRoot, "package.json");
|
|
690
|
+
const content = await readFile(pkgPath, "utf-8");
|
|
691
|
+
const pkg2 = JSON.parse(content);
|
|
692
|
+
const devDeps = pkg2.devDependencies ?? {};
|
|
693
|
+
const linter = devDeps.oxlint ? "oxlint" : devDeps.eslint ? "eslint" : devDeps["@biomejs/biome"] ? "biome" : void 0;
|
|
694
|
+
const formatter = devDeps.oxfmt ? "oxfmt" : devDeps.prettier ? "prettier" : devDeps["@biomejs/biome"] ? "biome" : void 0;
|
|
695
|
+
return { linter, formatter };
|
|
696
|
+
} catch {
|
|
697
|
+
return {};
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
async function getMonorepoScope(monorepoRoot) {
|
|
701
|
+
try {
|
|
702
|
+
const pkgPath = join(monorepoRoot, "package.json");
|
|
703
|
+
const content = await readFile(pkgPath, "utf-8");
|
|
704
|
+
const pkg2 = JSON.parse(content);
|
|
705
|
+
if (pkg2.name) {
|
|
706
|
+
return pkg2.name.replace(/^@/, "").replace(/\/.*$/, "");
|
|
707
|
+
}
|
|
708
|
+
} catch {
|
|
709
|
+
}
|
|
710
|
+
return monorepoRoot.split(/[/\\]/).pop() ?? "workspace";
|
|
711
|
+
}
|
|
712
|
+
async function getWorkspacePackages(monorepoRoot) {
|
|
713
|
+
const packagesDir = join(monorepoRoot, "packages");
|
|
714
|
+
const packages = [];
|
|
715
|
+
try {
|
|
716
|
+
const { readdir } = await import('fs/promises');
|
|
717
|
+
const entries = await readdir(packagesDir, { withFileTypes: true });
|
|
718
|
+
for (const entry of entries) {
|
|
719
|
+
if (entry.isDirectory()) {
|
|
720
|
+
try {
|
|
721
|
+
const pkgJsonPath = join(packagesDir, entry.name, "package.json");
|
|
722
|
+
const content = await readFile(pkgJsonPath, "utf-8");
|
|
723
|
+
const pkg2 = JSON.parse(content);
|
|
724
|
+
if (pkg2.name) {
|
|
725
|
+
packages.push({ name: pkg2.name, path: `packages/${entry.name}` });
|
|
726
|
+
}
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
} catch {
|
|
732
|
+
}
|
|
733
|
+
return packages;
|
|
734
|
+
}
|
|
735
|
+
async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedTooling, scope) {
|
|
736
|
+
const packageType = await promptForInitialPackage();
|
|
737
|
+
if (packageType === "skip") {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
const packageNameInput = await p.text({
|
|
741
|
+
message: "Package name?",
|
|
742
|
+
placeholder: `Scoped to @${scope}/`,
|
|
743
|
+
validate: (value) => {
|
|
744
|
+
if (!value.length) return "Package name is required";
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
if (p.isCancel(packageNameInput)) {
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
const shortName = packageNameInput;
|
|
751
|
+
const scopedName = `@${scope}/${shortName}`;
|
|
752
|
+
const targetDir = packageType === "app" ? "apps" : "packages";
|
|
753
|
+
const packagePath = join(targetDir, shortName);
|
|
754
|
+
const workspaceRoot = "../..";
|
|
755
|
+
const packageOptions = await promptForPackageOptions(
|
|
756
|
+
scopedName,
|
|
757
|
+
packageType,
|
|
758
|
+
inheritedTooling
|
|
382
759
|
);
|
|
760
|
+
packageOptions.workspaceRoot = workspaceRoot;
|
|
761
|
+
packageOptions.name = scopedName;
|
|
762
|
+
if (packageManager === "pnpm") {
|
|
763
|
+
packageOptions.pnpmVersion = await getLatestPnpmVersion();
|
|
764
|
+
}
|
|
765
|
+
const nodeVersion = packageOptions.nodeVersion ?? "latest";
|
|
766
|
+
if (nodeVersion === "latest") {
|
|
767
|
+
packageOptions.nodeVersion = await getLatestNodeVersion();
|
|
768
|
+
}
|
|
769
|
+
const versions = {};
|
|
770
|
+
const versionPromises = [];
|
|
771
|
+
const pkgIsLibrary = packageOptions.projectType === "library";
|
|
772
|
+
const pkgTesting = packageOptions.testing ?? (pkgIsLibrary ? "vitest" : "none");
|
|
773
|
+
if (pkgTesting === "vitest") {
|
|
774
|
+
versionPromises.push(
|
|
775
|
+
getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
776
|
+
versions.vitest = v;
|
|
777
|
+
})
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
if (!pkgIsLibrary) {
|
|
781
|
+
versionPromises.push(
|
|
782
|
+
getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
783
|
+
versions.vite = v;
|
|
784
|
+
})
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
const linter = packageOptions.linter ?? "oxlint";
|
|
788
|
+
if (linter === "eslint") {
|
|
789
|
+
versionPromises.push(
|
|
790
|
+
getLatestNpmVersion("eslint", "9.17.0").then((v) => {
|
|
791
|
+
versions.eslint = v;
|
|
792
|
+
})
|
|
793
|
+
);
|
|
794
|
+
} else if (linter === "oxlint") {
|
|
795
|
+
versionPromises.push(
|
|
796
|
+
getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
|
|
797
|
+
versions.oxlint = v;
|
|
798
|
+
})
|
|
799
|
+
);
|
|
800
|
+
} else if (linter === "biome") {
|
|
801
|
+
versionPromises.push(
|
|
802
|
+
getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
803
|
+
versions.biome = v;
|
|
804
|
+
})
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
const formatter = packageOptions.formatter ?? "oxfmt";
|
|
808
|
+
if (formatter === "prettier") {
|
|
809
|
+
versionPromises.push(
|
|
810
|
+
getLatestNpmVersion("prettier", "3.4.2").then((v) => {
|
|
811
|
+
versions.prettier = v;
|
|
812
|
+
})
|
|
813
|
+
);
|
|
814
|
+
} else if (formatter === "oxfmt") {
|
|
815
|
+
versionPromises.push(
|
|
816
|
+
getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
|
|
817
|
+
versions.oxfmt = v;
|
|
818
|
+
})
|
|
819
|
+
);
|
|
820
|
+
} else if (formatter === "biome" && linter !== "biome") {
|
|
821
|
+
versionPromises.push(
|
|
822
|
+
getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
|
|
823
|
+
versions.biome = v;
|
|
824
|
+
})
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
await Promise.all(versionPromises);
|
|
828
|
+
packageOptions.versions = versions;
|
|
829
|
+
if (packageType === "app") {
|
|
830
|
+
const workspacePackages = await getWorkspacePackages(monorepoRoot);
|
|
831
|
+
if (workspacePackages.length > 0) {
|
|
832
|
+
const selectedDeps = await p.multiselect({
|
|
833
|
+
message: "Add workspace dependencies?",
|
|
834
|
+
options: workspacePackages.map((pkg2) => ({
|
|
835
|
+
value: pkg2.name,
|
|
836
|
+
label: pkg2.name.replace(/^@[^/]+\//, "")
|
|
837
|
+
})),
|
|
838
|
+
required: false
|
|
839
|
+
});
|
|
840
|
+
if (!p.isCancel(selectedDeps) && selectedDeps.length > 0) {
|
|
841
|
+
packageOptions.workspaceDependencies = selectedDeps;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
const basePath = join(monorepoRoot, packagePath);
|
|
846
|
+
const s = p.spinner();
|
|
847
|
+
s.start("Creating package...");
|
|
848
|
+
try {
|
|
849
|
+
const files = generate(packageOptions);
|
|
850
|
+
const filePaths = Object.keys(files).sort();
|
|
851
|
+
for (const filePath of filePaths) {
|
|
852
|
+
const fullFilePath = join(basePath, filePath);
|
|
853
|
+
await mkdir(dirname(fullFilePath), { recursive: true });
|
|
854
|
+
const file = files[filePath];
|
|
855
|
+
if (file.type === "text") {
|
|
856
|
+
await writeFile(fullFilePath, file.content);
|
|
857
|
+
} else {
|
|
858
|
+
const response = await fetch(file.url);
|
|
859
|
+
await writeFile(fullFilePath, response.body);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
s.stop(color.green.inverse(` \u2713 Package created at ${packagePath}! `));
|
|
863
|
+
const addAnother = await p.select({
|
|
864
|
+
message: "Add another package?",
|
|
865
|
+
options: [
|
|
866
|
+
{ value: "no", label: "No, I'm done" },
|
|
867
|
+
{ value: "yes", label: "Yes, add another" }
|
|
868
|
+
],
|
|
869
|
+
initialValue: "no"
|
|
870
|
+
});
|
|
871
|
+
return !p.isCancel(addAnother) && addAnother === "yes";
|
|
872
|
+
} catch (error) {
|
|
873
|
+
s.stop("Failed to create package");
|
|
874
|
+
p.log.error(String(error));
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
383
877
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const isWindows = process.platform === "win32";
|
|
392
|
-
const useReuseFlag = reuseWindow && (editor === "cursor" || editor === "code");
|
|
393
|
-
const args = useReuseFlag ? ["-r", path] : [path];
|
|
394
|
-
const child = isWindows ? spawn(`${editor} ${useReuseFlag ? "-r " : ""}"${path}"`, {
|
|
395
|
-
detached: true,
|
|
396
|
-
stdio: "ignore",
|
|
397
|
-
shell: true
|
|
398
|
-
}) : spawn(editor, args, {
|
|
399
|
-
detached: true,
|
|
400
|
-
stdio: "ignore"
|
|
878
|
+
async function promptAndOpenEditor(basePath) {
|
|
879
|
+
const savedEditor = getPreferredEditor();
|
|
880
|
+
let selectedEditor;
|
|
881
|
+
if (savedEditor && savedEditor !== "skip") {
|
|
882
|
+
const useDefault = await p.confirm({
|
|
883
|
+
message: `Open in editor? ${color.dim(`(${editorNames[savedEditor]})`)}`,
|
|
884
|
+
initialValue: true
|
|
401
885
|
});
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
886
|
+
if (p.isCancel(useDefault)) {
|
|
887
|
+
selectedEditor = void 0;
|
|
888
|
+
} else if (useDefault) {
|
|
889
|
+
selectedEditor = savedEditor;
|
|
890
|
+
} else {
|
|
891
|
+
selectedEditor = "skip";
|
|
892
|
+
}
|
|
893
|
+
} else {
|
|
894
|
+
const openEditor = await p.select({
|
|
895
|
+
message: "Open project in editor?",
|
|
896
|
+
options: [
|
|
897
|
+
{ value: "skip", label: "Skip" },
|
|
898
|
+
{ value: "cursor", label: "Cursor" },
|
|
899
|
+
{ value: "code", label: "VS Code" },
|
|
900
|
+
{ value: "webstorm", label: "WebStorm" }
|
|
901
|
+
],
|
|
902
|
+
initialValue: "skip"
|
|
903
|
+
});
|
|
904
|
+
if (!p.isCancel(openEditor)) {
|
|
905
|
+
selectedEditor = openEditor;
|
|
906
|
+
const saveChoice = await p.confirm({
|
|
907
|
+
message: `Save ${editorNames[selectedEditor] ?? "Skip"} as default editor?`,
|
|
908
|
+
initialValue: true
|
|
909
|
+
});
|
|
910
|
+
if (!p.isCancel(saveChoice) && saveChoice) {
|
|
911
|
+
setPreferredEditor(selectedEditor);
|
|
912
|
+
if (selectedEditor === "cursor" || selectedEditor === "code") {
|
|
913
|
+
const reuseChoice = await p.confirm({
|
|
914
|
+
message: "Reuse current window when opening projects?",
|
|
915
|
+
initialValue: false
|
|
916
|
+
});
|
|
917
|
+
if (!p.isCancel(reuseChoice)) {
|
|
918
|
+
setReuseWindow(reuseChoice);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (selectedEditor && selectedEditor !== "skip") {
|
|
925
|
+
try {
|
|
926
|
+
await openInEditor(
|
|
927
|
+
selectedEditor,
|
|
928
|
+
basePath,
|
|
929
|
+
getReuseWindow()
|
|
930
|
+
);
|
|
931
|
+
p.log.success(`Opening in ${editorNames[selectedEditor]}...`);
|
|
932
|
+
} catch {
|
|
933
|
+
p.log.warn(
|
|
934
|
+
`Could not open ${editorNames[selectedEditor]}. Make sure the CLI command is in your PATH.`
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
406
938
|
}
|
|
407
939
|
async function main() {
|
|
408
|
-
const program = new Command().name("create-krispya").description(
|
|
409
|
-
"CLI for creating Vanilla, React, and React Three Fiber projects"
|
|
410
|
-
).argument("[name]", "name for the project").option(
|
|
411
|
-
"--type <type>",
|
|
412
|
-
"project type: app or library (default: app)"
|
|
413
|
-
).option(
|
|
940
|
+
const program = new 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(
|
|
414
941
|
"--bundler <bundler>",
|
|
415
942
|
"library bundler: unbuild or tsdown (default: unbuild, only for libraries)"
|
|
416
943
|
).option(
|
|
417
944
|
"--template <type>",
|
|
418
945
|
"project template: vanilla, vanilla-js, react, react-js, r3f, r3f-js (default: vanilla)"
|
|
419
|
-
).option(
|
|
420
|
-
"--linter <type>",
|
|
421
|
-
"linter: eslint, oxlint, or biome (default: oxlint)"
|
|
422
|
-
).option(
|
|
423
|
-
"--formatter <type>",
|
|
424
|
-
"formatter: prettier, oxfmt, or biome (default: oxfmt)"
|
|
425
|
-
).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(
|
|
426
|
-
"--package-manager <manager>",
|
|
427
|
-
"specify package manager (e.g. npm, yarn, pnpm)"
|
|
428
|
-
).option(
|
|
946
|
+
).option("--linter <type>", "linter: eslint, oxlint, or biome (default: oxlint)").option("--formatter <type>", "formatter: prettier, oxfmt, or biome (default: oxfmt)").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(
|
|
429
947
|
"--pnpm-manage-versions",
|
|
430
948
|
"enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
|
|
431
949
|
).option(
|
|
@@ -434,14 +952,55 @@ async function main() {
|
|
|
434
952
|
).option(
|
|
435
953
|
"--node-version <version>",
|
|
436
954
|
'set Node.js version for engines.node field (default: "latest")'
|
|
437
|
-
).option("-y, --yes", "Skip prompts and use default values").option("--clear-config", "Clear saved preferences (e.g. editor choice)").action(async (name, options) => {
|
|
955
|
+
).option("-y, --yes", "Skip prompts and use default values").option("--clear-config", "Clear saved preferences (e.g. editor choice)").option("--config-path", "Print the path to the config file").action(async (name, options) => {
|
|
438
956
|
if (options.clearConfig) {
|
|
439
957
|
clearConfig();
|
|
440
958
|
console.log("Configuration cleared.");
|
|
441
959
|
process.exit(0);
|
|
442
960
|
}
|
|
961
|
+
if (options.configPath) {
|
|
962
|
+
console.log(getConfigPath());
|
|
963
|
+
process.exit(0);
|
|
964
|
+
}
|
|
443
965
|
console.clear();
|
|
444
966
|
p.intro(color.bgCyan(color.black(` create-krispya v${pkg.version} `)));
|
|
967
|
+
const monorepoRoot = await detectMonorepoRoot();
|
|
968
|
+
if (monorepoRoot && Object.keys(options).length === 0) {
|
|
969
|
+
const choice = await p.select({
|
|
970
|
+
message: "Detected monorepo workspace",
|
|
971
|
+
options: [
|
|
972
|
+
{ value: "add", label: "Add new package to this workspace" },
|
|
973
|
+
{ value: "standalone", label: "Create standalone project" }
|
|
974
|
+
],
|
|
975
|
+
initialValue: "add"
|
|
976
|
+
});
|
|
977
|
+
if (p.isCancel(choice)) {
|
|
978
|
+
p.cancel("Operation cancelled.");
|
|
979
|
+
process.exit(0);
|
|
980
|
+
}
|
|
981
|
+
if (choice === "add") {
|
|
982
|
+
const inheritedTooling = await detectWorkspaceTooling(monorepoRoot);
|
|
983
|
+
if (inheritedTooling.linter || inheritedTooling.formatter) {
|
|
984
|
+
const toolingInfo = [
|
|
985
|
+
inheritedTooling.linter && `linter: ${inheritedTooling.linter}`,
|
|
986
|
+
inheritedTooling.formatter && `formatter: ${inheritedTooling.formatter}`
|
|
987
|
+
].filter(Boolean).join(", ");
|
|
988
|
+
p.log.info(`Using workspace tooling (${toolingInfo})`);
|
|
989
|
+
}
|
|
990
|
+
const scope = await getMonorepoScope(monorepoRoot);
|
|
991
|
+
let addMore = true;
|
|
992
|
+
while (addMore) {
|
|
993
|
+
addMore = await createPackageInWorkspace(monorepoRoot, "pnpm", inheritedTooling, scope);
|
|
994
|
+
}
|
|
995
|
+
p.note(
|
|
996
|
+
[`cd ${monorepoRoot}`, "pnpm install", "pnpm run dev"].join("\n"),
|
|
997
|
+
"Next steps"
|
|
998
|
+
);
|
|
999
|
+
await promptAndOpenEditor(monorepoRoot);
|
|
1000
|
+
p.outro(color.green("Happy coding! \u2728"));
|
|
1001
|
+
process.exit(0);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
445
1004
|
let generateOptions;
|
|
446
1005
|
if (Object.keys(options).length > 0) {
|
|
447
1006
|
const template = options.template ?? "vanilla";
|
|
@@ -476,6 +1035,63 @@ async function main() {
|
|
|
476
1035
|
} else {
|
|
477
1036
|
generateOptions = await promptForOptions(name);
|
|
478
1037
|
}
|
|
1038
|
+
if (generateOptions.projectType === "monorepo") {
|
|
1039
|
+
const { generateMonorepo } = await import('./chunks/index.mjs').then(function (n) { return n.m; });
|
|
1040
|
+
const packageManager2 = generateOptions.packageManager || "pnpm";
|
|
1041
|
+
if (packageManager2 === "pnpm") {
|
|
1042
|
+
generateOptions.pnpmVersion = await getLatestPnpmVersion();
|
|
1043
|
+
}
|
|
1044
|
+
const nodeVersion2 = generateOptions.nodeVersion ?? "latest";
|
|
1045
|
+
if (nodeVersion2 === "latest") {
|
|
1046
|
+
generateOptions.nodeVersion = await getLatestNodeVersion();
|
|
1047
|
+
}
|
|
1048
|
+
const basePath2 = join(cwd(), generateOptions.name);
|
|
1049
|
+
const s2 = p.spinner();
|
|
1050
|
+
s2.start("Creating monorepo workspace...");
|
|
1051
|
+
try {
|
|
1052
|
+
const { files } = generateMonorepo({
|
|
1053
|
+
name: generateOptions.name,
|
|
1054
|
+
linter: generateOptions.linter ?? "oxlint",
|
|
1055
|
+
formatter: generateOptions.formatter ?? "oxfmt",
|
|
1056
|
+
packageManager: packageManager2,
|
|
1057
|
+
pnpmVersion: generateOptions.pnpmVersion,
|
|
1058
|
+
pnpmManageVersions: generateOptions.pnpmManageVersions,
|
|
1059
|
+
nodeVersion: generateOptions.nodeVersion
|
|
1060
|
+
});
|
|
1061
|
+
const filePaths = Object.keys(files).sort();
|
|
1062
|
+
for (const filePath of filePaths) {
|
|
1063
|
+
const fullFilePath = join(basePath2, filePath);
|
|
1064
|
+
await mkdir(dirname(fullFilePath), { recursive: true });
|
|
1065
|
+
const file = files[filePath];
|
|
1066
|
+
if (file.type === "text") {
|
|
1067
|
+
await writeFile(fullFilePath, file.content);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
s2.stop(color.green.inverse(" \u2713 Monorepo workspace created! "));
|
|
1071
|
+
const newMonorepoTooling = {
|
|
1072
|
+
linter: generateOptions.linter,
|
|
1073
|
+
formatter: generateOptions.formatter
|
|
1074
|
+
};
|
|
1075
|
+
const scope = generateOptions.name;
|
|
1076
|
+
let addMore = true;
|
|
1077
|
+
while (addMore) {
|
|
1078
|
+
addMore = await createPackageInWorkspace(basePath2, packageManager2, newMonorepoTooling, scope);
|
|
1079
|
+
}
|
|
1080
|
+
const nextSteps = [
|
|
1081
|
+
`cd ${generateOptions.name}`,
|
|
1082
|
+
`${packageManager2} install`,
|
|
1083
|
+
`${packageManager2} run dev`
|
|
1084
|
+
].join("\n");
|
|
1085
|
+
p.note(nextSteps, "Next steps");
|
|
1086
|
+
await promptAndOpenEditor(basePath2);
|
|
1087
|
+
p.outro(color.green("Happy coding! \u2728"));
|
|
1088
|
+
process.exit(0);
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
s2.stop("Failed to create monorepo workspace");
|
|
1091
|
+
p.log.error(String(error));
|
|
1092
|
+
process.exit(1);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
479
1095
|
const base = generateOptions.template ? getBaseTemplate(generateOptions.template) : "vanilla";
|
|
480
1096
|
const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
|
|
481
1097
|
generateOptions.name ??= defaultFallbackName;
|
|
@@ -488,12 +1104,17 @@ async function main() {
|
|
|
488
1104
|
generateOptions.nodeVersion = await getLatestNodeVersion();
|
|
489
1105
|
}
|
|
490
1106
|
const versions = {};
|
|
491
|
-
const versionPromises = [
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
1107
|
+
const versionPromises = [];
|
|
1108
|
+
const isLibrary = generateOptions.projectType === "library";
|
|
1109
|
+
const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
|
|
1110
|
+
if (testing === "vitest") {
|
|
1111
|
+
versionPromises.push(
|
|
1112
|
+
getLatestNpmVersion("vitest", "4.0.0").then((v) => {
|
|
1113
|
+
versions.vitest = v;
|
|
1114
|
+
})
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
if (!isLibrary) {
|
|
497
1118
|
versionPromises.push(
|
|
498
1119
|
getLatestNpmVersion("vite", "6.3.4").then((v) => {
|
|
499
1120
|
versions.vite = v;
|
|
@@ -559,9 +1180,9 @@ async function main() {
|
|
|
559
1180
|
await writeFile(fullFilePath, response.body);
|
|
560
1181
|
}
|
|
561
1182
|
}
|
|
562
|
-
s.stop("Project created!");
|
|
563
|
-
const
|
|
564
|
-
const nextSteps =
|
|
1183
|
+
s.stop(color.green.inverse(" \u2713 Project created! "));
|
|
1184
|
+
const isLibrary2 = generateOptions.projectType === "library";
|
|
1185
|
+
const nextSteps = isLibrary2 ? [
|
|
565
1186
|
`cd ${generateOptions.name}`,
|
|
566
1187
|
`${packageManager} install`,
|
|
567
1188
|
`${packageManager} run build`
|