create-krispya 0.4.7 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -3,14 +3,16 @@
3
3
 
4
4
  const module$1 = require('module');
5
5
  const process$1 = require('process');
6
- const index = require('./index.cjs');
7
6
  const path = require('path');
8
7
  const promises = require('fs/promises');
8
+ const fs = require('fs');
9
9
  const commander = require('commander');
10
10
  const p = require('@clack/prompts');
11
11
  const color = require('chalk');
12
12
  const undici = require('undici');
13
13
  const child_process = require('child_process');
14
+ const index = require('./chunks/index.cjs');
15
+ const Conf = require('conf');
14
16
 
15
17
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
16
18
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -29,52 +31,33 @@ function _interopNamespaceCompat(e) {
29
31
 
30
32
  const p__namespace = /*#__PURE__*/_interopNamespaceCompat(p);
31
33
  const color__default = /*#__PURE__*/_interopDefaultCompat(color);
34
+ const Conf__default = /*#__PURE__*/_interopDefaultCompat(Conf);
32
35
 
33
- 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)));
34
- const pkg = require$1("../package.json");
35
- function getDefaultProjectName(template) {
36
- const base = index.getBaseTemplate(template);
37
- switch (base) {
38
- case "vanilla":
39
- return `vanilla-${index.generateRandomName()}`;
40
- case "react":
41
- return `react-${index.generateRandomName()}`;
42
- case "r3f":
43
- return `react-three-${index.generateRandomName()}`;
44
- }
45
- }
46
- function getDefaultOptions(template, name, projectType = "app", libraryBundler) {
47
- const baseTemplate = index.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;
36
+ const editorNames = {
37
+ cursor: "Cursor",
38
+ code: "VS Code",
39
+ webstorm: "WebStorm",
40
+ skip: "Skip"
41
+ };
42
+ function openInEditor(editor, path, reuseWindow) {
43
+ return new Promise((resolve, reject) => {
44
+ const isWindows = process.platform === "win32";
45
+ const useReuseFlag = reuseWindow && (editor === "cursor" || editor === "code");
46
+ const args = useReuseFlag ? ["-r", path] : [path];
47
+ const child = isWindows ? child_process.spawn(`${editor} ${useReuseFlag ? "-r " : ""}"${path}"`, {
48
+ detached: true,
49
+ stdio: "ignore",
50
+ shell: true
51
+ }) : child_process.spawn(editor, args, {
52
+ detached: true,
53
+ stdio: "ignore"
54
+ });
55
+ child.on("error", reject);
56
+ child.unref();
57
+ setTimeout(resolve, 100);
58
+ });
77
59
  }
60
+
78
61
  function formatConfigSummary(options) {
79
62
  const lines = [];
80
63
  const VALUE_COL = 27;
@@ -88,6 +71,12 @@ function formatConfigSummary(options) {
88
71
  return lang === "typescript" ? "TypeScript" : lang === "javascript" ? "JavaScript" : lang;
89
72
  };
90
73
  const projectType = options.projectType ?? "app";
74
+ const baseTemplate = options.template ? index.getBaseTemplate(options.template) : "vanilla";
75
+ if (baseTemplate === "react") {
76
+ lines.push(formatRow("Framework", "React"));
77
+ } else if (baseTemplate === "r3f") {
78
+ lines.push(formatRow("Framework", "React Three Fiber"));
79
+ }
91
80
  const language = options.template ? index.getLanguageFromTemplate(options.template) : "typescript";
92
81
  lines.push(formatRow("Language", formatLanguage(language)));
93
82
  if (projectType === "library") {
@@ -107,7 +96,8 @@ function formatConfigSummary(options) {
107
96
  if (options.formatter) {
108
97
  lines.push(formatRow("Formatter", options.formatter));
109
98
  }
110
- lines.push(formatRow("Testing", "vitest"));
99
+ const testing = options.testing ?? (projectType === "library" ? "vitest" : "none");
100
+ lines.push(formatRow("Testing", testing));
111
101
  if (options.template && index.getBaseTemplate(options.template) === "r3f") {
112
102
  const integrationNames = [
113
103
  options.drei && "drei",
@@ -134,6 +124,71 @@ function formatConfigSummary(options) {
134
124
  }
135
125
  return lines.join("\n");
136
126
  }
127
+ function formatMonorepoConfigSummary(options) {
128
+ const lines = [];
129
+ const VALUE_COL = 27;
130
+ const formatRow = (label, value, indent = "") => {
131
+ const fullLabel = indent + label;
132
+ const dotCount = Math.max(1, VALUE_COL - fullLabel.length - 1);
133
+ const dots = color__default.gray(".".repeat(dotCount));
134
+ return `${indent}${label} ${dots} ${value}`;
135
+ };
136
+ lines.push(formatRow("Node version", options.nodeVersion || "latest"));
137
+ lines.push(formatRow("Package manager", options.packageManager || "pnpm"));
138
+ if (options.packageManager === "pnpm") {
139
+ const versionManaged = options.pnpmManageVersions ? "yes" : "no";
140
+ lines.push(formatRow("\u21B3 Version managed", versionManaged, ""));
141
+ }
142
+ lines.push(formatRow("Linter", options.linter));
143
+ lines.push(formatRow("Formatter", options.formatter));
144
+ return lines.join("\n");
145
+ }
146
+
147
+ function getDefaultOptions(template, name, projectType = "app", libraryBundler) {
148
+ const baseTemplate = index.getBaseTemplate(template);
149
+ const base = {
150
+ name,
151
+ template,
152
+ projectType,
153
+ libraryBundler: projectType === "library" ? libraryBundler ?? "unbuild" : void 0,
154
+ packageManager: "pnpm",
155
+ pnpmManageVersions: true,
156
+ nodeVersion: "latest",
157
+ linter: "oxlint",
158
+ formatter: "oxfmt",
159
+ // Libraries get vitest by default, apps don't
160
+ testing: projectType === "library" ? "vitest" : "none"
161
+ };
162
+ if (baseTemplate === "r3f") {
163
+ return {
164
+ ...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: {}
177
+ };
178
+ }
179
+ return base;
180
+ }
181
+ function getDefaultProjectName(template) {
182
+ const base = index.getBaseTemplate(template);
183
+ switch (base) {
184
+ case "vanilla":
185
+ return `vanilla-${index.generateRandomName()}`;
186
+ case "react":
187
+ return `react-${index.generateRandomName()}`;
188
+ case "r3f":
189
+ return `react-three-${index.generateRandomName()}`;
190
+ }
191
+ }
137
192
  async function promptForCustomization(template, name, projectType) {
138
193
  let libraryBundler;
139
194
  if (projectType === "library") {
@@ -232,6 +287,18 @@ async function promptForCustomization(template, name, projectType) {
232
287
  p__namespace.cancel("Operation cancelled.");
233
288
  process.exit(0);
234
289
  }
290
+ const testing = await p__namespace.select({
291
+ message: "Testing",
292
+ options: [
293
+ { value: "vitest", label: "Vitest", hint: "fast, Vite-native" },
294
+ { value: "none", label: "None" }
295
+ ],
296
+ initialValue: projectType === "library" ? "vitest" : "none"
297
+ });
298
+ if (p__namespace.isCancel(testing)) {
299
+ p__namespace.cancel("Operation cancelled.");
300
+ process.exit(0);
301
+ }
235
302
  const language = await p__namespace.select({
236
303
  message: "Language",
237
304
  options: [
@@ -296,6 +363,7 @@ async function promptForCustomization(template, name, projectType) {
296
363
  pnpmManageVersions,
297
364
  linter,
298
365
  formatter,
366
+ testing,
299
367
  ...baseTemplate === "r3f" && {
300
368
  drei: integrations.includes("drei") ? {} : void 0,
301
369
  handle: integrations.includes("handle") ? {} : void 0,
@@ -312,6 +380,140 @@ async function promptForCustomization(template, name, projectType) {
312
380
  }
313
381
  };
314
382
  }
383
+ async function promptForInitialPackage() {
384
+ const choice = await p__namespace.select({
385
+ message: "Add an initial package?",
386
+ options: [
387
+ { value: "app", label: "Application" },
388
+ { value: "library", label: "Library" },
389
+ { value: "skip", label: "Skip" }
390
+ ],
391
+ initialValue: "app"
392
+ });
393
+ if (p__namespace.isCancel(choice)) {
394
+ p__namespace.cancel("Operation cancelled.");
395
+ process.exit(0);
396
+ }
397
+ return choice;
398
+ }
399
+ function getDefaultMonorepoOptions(name) {
400
+ return {
401
+ name,
402
+ projectType: "monorepo",
403
+ packageManager: "pnpm",
404
+ pnpmManageVersions: true,
405
+ nodeVersion: "latest",
406
+ linter: "oxlint",
407
+ formatter: "oxfmt"
408
+ };
409
+ }
410
+ async function promptForMonorepoCustomization(name) {
411
+ const nodeVersion = await p__namespace.text({
412
+ message: "Node.js version",
413
+ placeholder: "latest",
414
+ defaultValue: "latest",
415
+ validate: (value) => {
416
+ if (!value.length) return "Required";
417
+ if (value !== "latest" && !/^\d+(\.\d+(\.\d+)?)?$/.test(value)) {
418
+ return 'Must be "latest" or a valid semver (e.g., "22" or "22.13.0")';
419
+ }
420
+ }
421
+ });
422
+ if (p__namespace.isCancel(nodeVersion)) {
423
+ p__namespace.cancel("Operation cancelled.");
424
+ process.exit(0);
425
+ }
426
+ const packageManager = await p__namespace.select({
427
+ message: "Package manager",
428
+ options: [
429
+ { value: "pnpm", label: "pnpm" },
430
+ { value: "npm", label: "npm" },
431
+ { value: "yarn", label: "yarn" }
432
+ ],
433
+ initialValue: "pnpm"
434
+ });
435
+ if (p__namespace.isCancel(packageManager)) {
436
+ p__namespace.cancel("Operation cancelled.");
437
+ process.exit(0);
438
+ }
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
+ const linter = await p__namespace.select({
452
+ message: "Linter",
453
+ options: [
454
+ { value: "oxlint", label: "Oxlint", hint: "fast, from OXC" },
455
+ { value: "eslint", label: "ESLint", hint: "classic" },
456
+ { value: "biome", label: "Biome", hint: "all-in-one" }
457
+ ],
458
+ initialValue: "oxlint"
459
+ });
460
+ if (p__namespace.isCancel(linter)) {
461
+ p__namespace.cancel("Operation cancelled.");
462
+ process.exit(0);
463
+ }
464
+ const formatter = await p__namespace.select({
465
+ message: "Formatter",
466
+ options: [
467
+ { value: "oxfmt", label: "Oxfmt", hint: "fast, Prettier-compatible" },
468
+ { value: "prettier", label: "Prettier", hint: "classic" },
469
+ { value: "biome", label: "Biome", hint: "all-in-one" }
470
+ ],
471
+ initialValue: "oxfmt"
472
+ });
473
+ if (p__namespace.isCancel(formatter)) {
474
+ p__namespace.cancel("Operation cancelled.");
475
+ process.exit(0);
476
+ }
477
+ return {
478
+ name,
479
+ projectType: "monorepo",
480
+ nodeVersion,
481
+ packageManager,
482
+ pnpmManageVersions,
483
+ linter,
484
+ formatter
485
+ };
486
+ }
487
+ async function promptForMonorepo(workspaceName) {
488
+ const defaultOptions = getDefaultMonorepoOptions(workspaceName);
489
+ p__namespace.note(
490
+ formatMonorepoConfigSummary({
491
+ name: defaultOptions.name,
492
+ nodeVersion: defaultOptions.nodeVersion ?? "latest",
493
+ packageManager: defaultOptions.packageManager ?? "pnpm",
494
+ pnpmManageVersions: defaultOptions.pnpmManageVersions,
495
+ linter: defaultOptions.linter ?? "oxlint",
496
+ formatter: defaultOptions.formatter ?? "oxfmt"
497
+ }),
498
+ "Workspace Configuration"
499
+ );
500
+ const action = await p__namespace.select({
501
+ message: "Proceed with these settings?",
502
+ options: [
503
+ { value: "confirm", label: "Yes, create workspace" },
504
+ { value: "customize", label: "No, let me customize" }
505
+ ],
506
+ initialValue: "confirm"
507
+ });
508
+ if (p__namespace.isCancel(action)) {
509
+ p__namespace.cancel("Operation cancelled.");
510
+ process.exit(0);
511
+ }
512
+ if (action === "confirm") {
513
+ return defaultOptions;
514
+ }
515
+ return promptForMonorepoCustomization(workspaceName);
516
+ }
315
517
  async function promptForOptions(name) {
316
518
  let projectName = name;
317
519
  if (!projectName) {
@@ -333,7 +535,8 @@ async function promptForOptions(name) {
333
535
  message: "Project type",
334
536
  options: [
335
537
  { value: "app", label: "Application" },
336
- { value: "library", label: "Library" }
538
+ { value: "library", label: "Library" },
539
+ { value: "monorepo", label: "Monorepo" }
337
540
  ],
338
541
  initialValue: "app"
339
542
  });
@@ -341,6 +544,12 @@ async function promptForOptions(name) {
341
544
  p__namespace.cancel("Operation cancelled.");
342
545
  process.exit(0);
343
546
  }
547
+ if (projectType === "monorepo") {
548
+ return promptForMonorepo(projectName);
549
+ }
550
+ return promptForPackageOptions(projectName, projectType);
551
+ }
552
+ async function promptForPackageOptions(projectName, projectType) {
344
553
  const template = await p__namespace.select({
345
554
  message: "Select a template",
346
555
  options: [
@@ -381,46 +590,53 @@ async function promptForOptions(name) {
381
590
  projectType
382
591
  );
383
592
  }
384
- function openInEditor(editor, path) {
385
- return new Promise((resolve, reject) => {
386
- const isWindows = process.platform === "win32";
387
- const reuseWindow = (editor === "cursor" || editor === "code") && process.env.TERM_PROGRAM === "vscode";
388
- const args = reuseWindow ? ["-r", path] : [path];
389
- const child = isWindows ? child_process.spawn(`${editor} ${reuseWindow ? "-r " : ""}"${path}"`, {
390
- detached: true,
391
- stdio: "ignore",
392
- shell: true
393
- }) : child_process.spawn(editor, args, {
394
- detached: true,
395
- stdio: "ignore"
396
- });
397
- child.on("error", reject);
398
- child.unref();
399
- setTimeout(resolve, 100);
400
- });
593
+
594
+ const config = new Conf__default({
595
+ projectName: "create-krispya"
596
+ });
597
+ function getPreferredEditor() {
598
+ return config.get("preferredEditor");
599
+ }
600
+ function setPreferredEditor(editor) {
601
+ config.set("preferredEditor", editor);
602
+ }
603
+ function getReuseWindow() {
604
+ return config.get("reuseWindow") ?? false;
605
+ }
606
+ function setReuseWindow(reuse) {
607
+ config.set("reuseWindow", reuse);
608
+ }
609
+ function clearConfig() {
610
+ config.clear();
611
+ }
612
+
613
+ 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
+ const pkg = require$1("../package.json");
615
+ async function detectMonorepoRoot() {
616
+ let currentDir = process$1.cwd();
617
+ const root = path.resolve("/");
618
+ while (currentDir !== root) {
619
+ const workspaceFile = path.join(currentDir, "pnpm-workspace.yaml");
620
+ try {
621
+ await promises.access(workspaceFile, fs.constants.F_OK);
622
+ const content = await promises.readFile(workspaceFile, "utf-8");
623
+ if (content.includes("packages:")) {
624
+ return currentDir;
625
+ }
626
+ } catch {
627
+ }
628
+ currentDir = path.dirname(currentDir);
629
+ }
630
+ return null;
401
631
  }
402
632
  async function main() {
403
- const program = new commander.Command().name("create-krispya").description(
404
- "CLI for creating Vanilla, React, and React Three Fiber projects"
405
- ).argument("[name]", "name for the project").option(
406
- "--type <type>",
407
- "project type: app or library (default: app)"
408
- ).option(
633
+ const program = new commander.Command().name("create-krispya").description("CLI for creating Vanilla, React, and React Three Fiber projects").argument("[name]", "name for the project").option("--type <type>", "project type: app or library (default: app)").option(
409
634
  "--bundler <bundler>",
410
635
  "library bundler: unbuild or tsdown (default: unbuild, only for libraries)"
411
636
  ).option(
412
637
  "--template <type>",
413
638
  "project template: vanilla, vanilla-js, react, react-js, r3f, r3f-js (default: vanilla)"
414
- ).option(
415
- "--linter <type>",
416
- "linter: eslint, oxlint, or biome (default: oxlint)"
417
- ).option(
418
- "--formatter <type>",
419
- "formatter: prettier, oxfmt, or biome (default: oxfmt)"
420
- ).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(
421
- "--package-manager <manager>",
422
- "specify package manager (e.g. npm, yarn, pnpm)"
423
- ).option(
639
+ ).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(
424
640
  "--pnpm-manage-versions",
425
641
  "enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
426
642
  ).option(
@@ -429,9 +645,157 @@ async function main() {
429
645
  ).option(
430
646
  "--node-version <version>",
431
647
  'set Node.js version for engines.node field (default: "latest")'
432
- ).option("-y, --yes", "Skip prompts and use default values").action(async (name, options) => {
648
+ ).option("-y, --yes", "Skip prompts and use default values").option("--clear-config", "Clear saved preferences (e.g. editor choice)").action(async (name, options) => {
649
+ if (options.clearConfig) {
650
+ clearConfig();
651
+ console.log("Configuration cleared.");
652
+ process.exit(0);
653
+ }
433
654
  console.clear();
434
655
  p__namespace.intro(color__default.bgCyan(color__default.black(` create-krispya v${pkg.version} `)));
656
+ const monorepoRoot = await detectMonorepoRoot();
657
+ if (monorepoRoot && Object.keys(options).length === 0) {
658
+ const choice = await p__namespace.select({
659
+ message: "Detected monorepo workspace",
660
+ options: [
661
+ { value: "add", label: "Add new package to this workspace" },
662
+ { value: "standalone", label: "Create standalone project" }
663
+ ],
664
+ initialValue: "add"
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...");
764
+ try {
765
+ const files = index.generate(packageOptions);
766
+ const filePaths = Object.keys(files).sort();
767
+ for (const filePath of filePaths) {
768
+ const fullFilePath = path.join(basePath2, filePath);
769
+ await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
770
+ const file = files[filePath];
771
+ if (file.type === "text") {
772
+ await promises.writeFile(fullFilePath, file.content);
773
+ } else {
774
+ const response = await undici.fetch(file.url);
775
+ await promises.writeFile(fullFilePath, response.body);
776
+ }
777
+ }
778
+ s2.stop("Package created!");
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);
796
+ }
797
+ }
798
+ }
435
799
  let generateOptions;
436
800
  if (Object.keys(options).length > 0) {
437
801
  const template = options.template ?? "vanilla";
@@ -466,6 +830,107 @@ async function main() {
466
830
  } else {
467
831
  generateOptions = await promptForOptions(name);
468
832
  }
833
+ if (generateOptions.projectType === "monorepo") {
834
+ const { generateMonorepo } = await import('./chunks/index.cjs').then(function (n) { return n.monorepo; });
835
+ const packageManager2 = generateOptions.packageManager || "pnpm";
836
+ if (packageManager2 === "pnpm") {
837
+ generateOptions.pnpmVersion = await index.getLatestPnpmVersion();
838
+ }
839
+ const nodeVersion2 = generateOptions.nodeVersion ?? "latest";
840
+ if (nodeVersion2 === "latest") {
841
+ generateOptions.nodeVersion = await index.getLatestNodeVersion();
842
+ }
843
+ const basePath2 = path.join(process$1.cwd(), generateOptions.name);
844
+ const s2 = p__namespace.spinner();
845
+ s2.start("Creating monorepo workspace...");
846
+ try {
847
+ const { files } = generateMonorepo({
848
+ name: generateOptions.name,
849
+ linter: generateOptions.linter ?? "oxlint",
850
+ formatter: generateOptions.formatter ?? "oxfmt",
851
+ packageManager: packageManager2,
852
+ pnpmVersion: generateOptions.pnpmVersion,
853
+ pnpmManageVersions: generateOptions.pnpmManageVersions,
854
+ nodeVersion: generateOptions.nodeVersion
855
+ });
856
+ const filePaths = Object.keys(files).sort();
857
+ for (const filePath of filePaths) {
858
+ const fullFilePath = path.join(basePath2, filePath);
859
+ await promises.mkdir(path.dirname(fullFilePath), { recursive: true });
860
+ const file = files[filePath];
861
+ if (file.type === "text") {
862
+ await promises.writeFile(fullFilePath, file.content);
863
+ }
864
+ }
865
+ s2.stop("Monorepo workspace created!");
866
+ const initialPackage = await promptForInitialPackage();
867
+ if (initialPackage !== "skip") {
868
+ const packageName = await p__namespace.text({
869
+ message: "Package name?",
870
+ placeholder: initialPackage === "app" ? "my-app" : "my-package",
871
+ validate: (value) => {
872
+ if (!value.length) return "Package name is required";
873
+ }
874
+ });
875
+ if (!p__namespace.isCancel(packageName)) {
876
+ const targetDir = initialPackage === "app" ? "apps" : "packages";
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!");
918
+ }
919
+ }
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
+ }
933
+ }
469
934
  const base = generateOptions.template ? index.getBaseTemplate(generateOptions.template) : "vanilla";
470
935
  const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
471
936
  generateOptions.name ??= defaultFallbackName;
@@ -478,12 +943,17 @@ async function main() {
478
943
  generateOptions.nodeVersion = await index.getLatestNodeVersion();
479
944
  }
480
945
  const versions = {};
481
- const versionPromises = [
482
- index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
483
- versions.vitest = v;
484
- })
485
- ];
486
- if (generateOptions.projectType !== "library") {
946
+ const versionPromises = [];
947
+ const isLibrary = generateOptions.projectType === "library";
948
+ const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
949
+ if (testing === "vitest") {
950
+ versionPromises.push(
951
+ index.getLatestNpmVersion("vitest", "4.0.0").then((v) => {
952
+ versions.vitest = v;
953
+ })
954
+ );
955
+ }
956
+ if (!isLibrary) {
487
957
  versionPromises.push(
488
958
  index.getLatestNpmVersion("vite", "6.3.4").then((v) => {
489
959
  versions.vite = v;
@@ -550,8 +1020,8 @@ async function main() {
550
1020
  }
551
1021
  }
552
1022
  s.stop("Project created!");
553
- const isLibrary = generateOptions.projectType === "library";
554
- const nextSteps = isLibrary ? [
1023
+ const isLibrary2 = generateOptions.projectType === "library";
1024
+ const nextSteps = isLibrary2 ? [
555
1025
  `cd ${generateOptions.name}`,
556
1026
  `${packageManager} install`,
557
1027
  `${packageManager} run build`
@@ -561,33 +1031,62 @@ async function main() {
561
1031
  `${packageManager} run dev`
562
1032
  ].join("\n");
563
1033
  p__namespace.note(nextSteps, "Next steps");
564
- const openEditor = await p__namespace.select({
565
- message: "Open project in editor?",
566
- options: [
567
- { value: "skip", label: "Skip" },
568
- { value: "cursor", label: "Cursor" },
569
- { value: "code", label: "VS Code" },
570
- { value: "webstorm", label: "WebStorm" }
571
- ],
572
- initialValue: "skip"
573
- });
574
- if (!p__namespace.isCancel(openEditor) && openEditor !== "skip") {
575
- const editorNames = {
576
- cursor: "Cursor",
577
- code: "VS Code",
578
- webstorm: "WebStorm"
579
- };
1034
+ const savedEditor = getPreferredEditor();
1035
+ let selectedEditor;
1036
+ if (savedEditor && savedEditor !== "skip") {
1037
+ const useDefault = await p__namespace.confirm({
1038
+ message: `Open in editor? ${color__default.dim(`(${editorNames[savedEditor]})`)}`,
1039
+ initialValue: true
1040
+ });
1041
+ if (p__namespace.isCancel(useDefault)) {
1042
+ selectedEditor = void 0;
1043
+ } else if (useDefault) {
1044
+ selectedEditor = savedEditor;
1045
+ } else {
1046
+ selectedEditor = "skip";
1047
+ }
1048
+ } else {
1049
+ const openEditor = await p__namespace.select({
1050
+ message: "Open project in editor?",
1051
+ options: [
1052
+ { value: "skip", label: "Skip" },
1053
+ { value: "cursor", label: "Cursor" },
1054
+ { value: "code", label: "VS Code" },
1055
+ { value: "webstorm", label: "WebStorm" }
1056
+ ],
1057
+ initialValue: "skip"
1058
+ });
1059
+ if (!p__namespace.isCancel(openEditor)) {
1060
+ selectedEditor = openEditor;
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
+ }
1077
+ }
1078
+ }
1079
+ if (selectedEditor && selectedEditor !== "skip") {
580
1080
  try {
581
1081
  await openInEditor(
582
- openEditor,
583
- basePath
584
- );
585
- p__namespace.log.success(
586
- `Opening in ${editorNames[openEditor]}...`
1082
+ selectedEditor,
1083
+ basePath,
1084
+ getReuseWindow()
587
1085
  );
1086
+ p__namespace.log.success(`Opening in ${editorNames[selectedEditor]}...`);
588
1087
  } catch {
589
1088
  p__namespace.log.warn(
590
- `Could not open ${editorNames[openEditor]}. Make sure the CLI command is in your PATH.`
1089
+ `Could not open ${editorNames[selectedEditor]}. Make sure the CLI command is in your PATH.`
591
1090
  );
592
1091
  }
593
1092
  }