create-krispya 0.4.8 → 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.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 { getBaseTemplate, getLatestPnpmVersion, getLatestNodeVersion, getLatestNpmVersion, generate, generateRandomName, getLanguageFromTemplate } from './index.mjs';
5
- import { join, dirname } from 'path';
6
- import { mkdir, writeFile } from 'fs/promises';
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 config = new Conf({
15
- projectName: "create-krispya"
16
- });
17
- function getPreferredEditor() {
18
- return config.get("preferredEditor");
19
- }
20
- function setPreferredEditor(editor) {
21
- config.set("preferredEditor", editor);
22
- }
23
- function getReuseWindow() {
24
- return config.get("reuseWindow") ?? false;
25
- }
26
- function setReuseWindow(reuse) {
27
- config.set("reuseWindow", reuse);
28
- }
29
- function clearConfig() {
30
- config.clear();
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
- lines.push(formatRow("Testing", "vitest"));
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,6 +103,71 @@ function formatConfigSummary(options) {
134
103
  }
135
104
  return lines.join("\n");
136
105
  }
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
+ function getDefaultOptions(template, name, projectType = "app", libraryBundler) {
127
+ const baseTemplate = getBaseTemplate(template);
128
+ const base = {
129
+ name,
130
+ template,
131
+ projectType,
132
+ libraryBundler: projectType === "library" ? libraryBundler ?? "unbuild" : void 0,
133
+ packageManager: "pnpm",
134
+ pnpmManageVersions: true,
135
+ nodeVersion: "latest",
136
+ linter: "oxlint",
137
+ formatter: "oxfmt",
138
+ // Libraries get vitest by default, apps don't
139
+ testing: projectType === "library" ? "vitest" : "none"
140
+ };
141
+ if (baseTemplate === "r3f") {
142
+ return {
143
+ ...base,
144
+ drei: {},
145
+ handle: {},
146
+ leva: {},
147
+ postprocessing: {},
148
+ rapier: {},
149
+ xr: {},
150
+ uikit: {},
151
+ offscreen: {},
152
+ zustand: {},
153
+ koota: {},
154
+ triplex: {},
155
+ viverse: {}
156
+ };
157
+ }
158
+ return base;
159
+ }
160
+ function getDefaultProjectName(template) {
161
+ const base = getBaseTemplate(template);
162
+ switch (base) {
163
+ case "vanilla":
164
+ return `vanilla-${generateRandomName()}`;
165
+ case "react":
166
+ return `react-${generateRandomName()}`;
167
+ case "r3f":
168
+ return `react-three-${generateRandomName()}`;
169
+ }
170
+ }
137
171
  async function promptForCustomization(template, name, projectType) {
138
172
  let libraryBundler;
139
173
  if (projectType === "library") {
@@ -232,6 +266,18 @@ async function promptForCustomization(template, name, projectType) {
232
266
  p.cancel("Operation cancelled.");
233
267
  process.exit(0);
234
268
  }
269
+ const testing = await p.select({
270
+ message: "Testing",
271
+ options: [
272
+ { value: "vitest", label: "Vitest", hint: "fast, Vite-native" },
273
+ { value: "none", label: "None" }
274
+ ],
275
+ initialValue: projectType === "library" ? "vitest" : "none"
276
+ });
277
+ if (p.isCancel(testing)) {
278
+ p.cancel("Operation cancelled.");
279
+ process.exit(0);
280
+ }
235
281
  const language = await p.select({
236
282
  message: "Language",
237
283
  options: [
@@ -296,6 +342,7 @@ async function promptForCustomization(template, name, projectType) {
296
342
  pnpmManageVersions,
297
343
  linter,
298
344
  formatter,
345
+ testing,
299
346
  ...baseTemplate === "r3f" && {
300
347
  drei: integrations.includes("drei") ? {} : void 0,
301
348
  handle: integrations.includes("handle") ? {} : void 0,
@@ -312,6 +359,140 @@ async function promptForCustomization(template, name, projectType) {
312
359
  }
313
360
  };
314
361
  }
362
+ async function promptForInitialPackage() {
363
+ const choice = await p.select({
364
+ message: "Add an initial package?",
365
+ options: [
366
+ { value: "app", label: "Application" },
367
+ { value: "library", label: "Library" },
368
+ { value: "skip", label: "Skip" }
369
+ ],
370
+ initialValue: "app"
371
+ });
372
+ if (p.isCancel(choice)) {
373
+ p.cancel("Operation cancelled.");
374
+ process.exit(0);
375
+ }
376
+ return choice;
377
+ }
378
+ function getDefaultMonorepoOptions(name) {
379
+ return {
380
+ name,
381
+ projectType: "monorepo",
382
+ packageManager: "pnpm",
383
+ pnpmManageVersions: true,
384
+ nodeVersion: "latest",
385
+ linter: "oxlint",
386
+ formatter: "oxfmt"
387
+ };
388
+ }
389
+ async function promptForMonorepoCustomization(name) {
390
+ const nodeVersion = await p.text({
391
+ message: "Node.js version",
392
+ placeholder: "latest",
393
+ defaultValue: "latest",
394
+ validate: (value) => {
395
+ if (!value.length) return "Required";
396
+ if (value !== "latest" && !/^\d+(\.\d+(\.\d+)?)?$/.test(value)) {
397
+ return 'Must be "latest" or a valid semver (e.g., "22" or "22.13.0")';
398
+ }
399
+ }
400
+ });
401
+ if (p.isCancel(nodeVersion)) {
402
+ p.cancel("Operation cancelled.");
403
+ process.exit(0);
404
+ }
405
+ const packageManager = await p.select({
406
+ message: "Package manager",
407
+ options: [
408
+ { value: "pnpm", label: "pnpm" },
409
+ { value: "npm", label: "npm" },
410
+ { value: "yarn", label: "yarn" }
411
+ ],
412
+ initialValue: "pnpm"
413
+ });
414
+ if (p.isCancel(packageManager)) {
415
+ p.cancel("Operation cancelled.");
416
+ process.exit(0);
417
+ }
418
+ let pnpmManageVersions = true;
419
+ if (packageManager === "pnpm") {
420
+ const managePnpm = await p.confirm({
421
+ message: "Enable manage-package-manager-versions?",
422
+ initialValue: true
423
+ });
424
+ if (p.isCancel(managePnpm)) {
425
+ p.cancel("Operation cancelled.");
426
+ process.exit(0);
427
+ }
428
+ pnpmManageVersions = managePnpm;
429
+ }
430
+ const linter = await p.select({
431
+ message: "Linter",
432
+ options: [
433
+ { value: "oxlint", label: "Oxlint", hint: "fast, from OXC" },
434
+ { value: "eslint", label: "ESLint", hint: "classic" },
435
+ { value: "biome", label: "Biome", hint: "all-in-one" }
436
+ ],
437
+ initialValue: "oxlint"
438
+ });
439
+ if (p.isCancel(linter)) {
440
+ p.cancel("Operation cancelled.");
441
+ process.exit(0);
442
+ }
443
+ const formatter = await p.select({
444
+ message: "Formatter",
445
+ options: [
446
+ { value: "oxfmt", label: "Oxfmt", hint: "fast, Prettier-compatible" },
447
+ { value: "prettier", label: "Prettier", hint: "classic" },
448
+ { value: "biome", label: "Biome", hint: "all-in-one" }
449
+ ],
450
+ initialValue: "oxfmt"
451
+ });
452
+ if (p.isCancel(formatter)) {
453
+ p.cancel("Operation cancelled.");
454
+ process.exit(0);
455
+ }
456
+ return {
457
+ name,
458
+ projectType: "monorepo",
459
+ nodeVersion,
460
+ packageManager,
461
+ pnpmManageVersions,
462
+ linter,
463
+ formatter
464
+ };
465
+ }
466
+ async function promptForMonorepo(workspaceName) {
467
+ const defaultOptions = getDefaultMonorepoOptions(workspaceName);
468
+ p.note(
469
+ formatMonorepoConfigSummary({
470
+ name: defaultOptions.name,
471
+ nodeVersion: defaultOptions.nodeVersion ?? "latest",
472
+ packageManager: defaultOptions.packageManager ?? "pnpm",
473
+ pnpmManageVersions: defaultOptions.pnpmManageVersions,
474
+ linter: defaultOptions.linter ?? "oxlint",
475
+ formatter: defaultOptions.formatter ?? "oxfmt"
476
+ }),
477
+ "Workspace Configuration"
478
+ );
479
+ const action = await p.select({
480
+ message: "Proceed with these settings?",
481
+ options: [
482
+ { value: "confirm", label: "Yes, create workspace" },
483
+ { value: "customize", label: "No, let me customize" }
484
+ ],
485
+ initialValue: "confirm"
486
+ });
487
+ if (p.isCancel(action)) {
488
+ p.cancel("Operation cancelled.");
489
+ process.exit(0);
490
+ }
491
+ if (action === "confirm") {
492
+ return defaultOptions;
493
+ }
494
+ return promptForMonorepoCustomization(workspaceName);
495
+ }
315
496
  async function promptForOptions(name) {
316
497
  let projectName = name;
317
498
  if (!projectName) {
@@ -333,7 +514,8 @@ async function promptForOptions(name) {
333
514
  message: "Project type",
334
515
  options: [
335
516
  { value: "app", label: "Application" },
336
- { value: "library", label: "Library" }
517
+ { value: "library", label: "Library" },
518
+ { value: "monorepo", label: "Monorepo" }
337
519
  ],
338
520
  initialValue: "app"
339
521
  });
@@ -341,6 +523,12 @@ async function promptForOptions(name) {
341
523
  p.cancel("Operation cancelled.");
342
524
  process.exit(0);
343
525
  }
526
+ if (projectType === "monorepo") {
527
+ return promptForMonorepo(projectName);
528
+ }
529
+ return promptForPackageOptions(projectName, projectType);
530
+ }
531
+ async function promptForPackageOptions(projectName, projectType) {
344
532
  const template = await p.select({
345
533
  message: "Select a template",
346
534
  options: [
@@ -381,51 +569,53 @@ async function promptForOptions(name) {
381
569
  projectType
382
570
  );
383
571
  }
384
- const editorNames = {
385
- cursor: "Cursor",
386
- code: "VS Code",
387
- webstorm: "WebStorm"
388
- };
389
- function openInEditor(editor, path, reuseWindow) {
390
- return new Promise((resolve, reject) => {
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"
401
- });
402
- child.on("error", reject);
403
- child.unref();
404
- setTimeout(resolve, 100);
405
- });
572
+
573
+ const config = new Conf({
574
+ projectName: "create-krispya"
575
+ });
576
+ function getPreferredEditor() {
577
+ return config.get("preferredEditor");
578
+ }
579
+ function setPreferredEditor(editor) {
580
+ config.set("preferredEditor", editor);
581
+ }
582
+ function getReuseWindow() {
583
+ return config.get("reuseWindow") ?? false;
584
+ }
585
+ function setReuseWindow(reuse) {
586
+ config.set("reuseWindow", reuse);
587
+ }
588
+ function clearConfig() {
589
+ config.clear();
590
+ }
591
+
592
+ const require$1 = createRequire(import.meta.url);
593
+ const pkg = require$1("../package.json");
594
+ async function detectMonorepoRoot() {
595
+ let currentDir = cwd();
596
+ const root = resolve("/");
597
+ while (currentDir !== root) {
598
+ const workspaceFile = join(currentDir, "pnpm-workspace.yaml");
599
+ try {
600
+ await access(workspaceFile, constants.F_OK);
601
+ const content = await readFile(workspaceFile, "utf-8");
602
+ if (content.includes("packages:")) {
603
+ return currentDir;
604
+ }
605
+ } catch {
606
+ }
607
+ currentDir = dirname(currentDir);
608
+ }
609
+ return null;
406
610
  }
407
611
  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(
612
+ 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
613
  "--bundler <bundler>",
415
614
  "library bundler: unbuild or tsdown (default: unbuild, only for libraries)"
416
615
  ).option(
417
616
  "--template <type>",
418
617
  "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(
618
+ ).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
619
  "--pnpm-manage-versions",
430
620
  "enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
431
621
  ).option(
@@ -442,6 +632,149 @@ async function main() {
442
632
  }
443
633
  console.clear();
444
634
  p.intro(color.bgCyan(color.black(` create-krispya v${pkg.version} `)));
635
+ const monorepoRoot = await detectMonorepoRoot();
636
+ if (monorepoRoot && Object.keys(options).length === 0) {
637
+ const choice = await p.select({
638
+ message: "Detected monorepo workspace",
639
+ options: [
640
+ { value: "add", label: "Add new package to this workspace" },
641
+ { value: "standalone", label: "Create standalone project" }
642
+ ],
643
+ initialValue: "add"
644
+ });
645
+ if (p.isCancel(choice)) {
646
+ p.cancel("Operation cancelled.");
647
+ process.exit(0);
648
+ }
649
+ if (choice === "add") {
650
+ const packageType = await promptForInitialPackage();
651
+ if (packageType === "skip") {
652
+ p.cancel("Operation cancelled.");
653
+ process.exit(0);
654
+ }
655
+ const packageName = await p.text({
656
+ message: "Package name?",
657
+ placeholder: packageType === "app" ? "my-app" : "my-package",
658
+ validate: (value) => {
659
+ if (!value.length) return "Package name is required";
660
+ }
661
+ });
662
+ if (p.isCancel(packageName)) {
663
+ p.cancel("Operation cancelled.");
664
+ process.exit(0);
665
+ }
666
+ const targetDir = packageType === "app" ? "apps" : "packages";
667
+ const packagePath = join(targetDir, packageName);
668
+ const workspaceRoot = "../..";
669
+ const packageOptions = await promptForPackageOptions(packageName, packageType);
670
+ packageOptions.workspaceRoot = workspaceRoot;
671
+ packageOptions.name = packageName;
672
+ const packageManager2 = packageOptions.packageManager || "pnpm";
673
+ if (packageManager2 === "pnpm") {
674
+ packageOptions.pnpmVersion = await getLatestPnpmVersion();
675
+ }
676
+ const nodeVersion2 = packageOptions.nodeVersion ?? "latest";
677
+ if (nodeVersion2 === "latest") {
678
+ packageOptions.nodeVersion = await getLatestNodeVersion();
679
+ }
680
+ const versions2 = {};
681
+ const versionPromises2 = [];
682
+ const pkgIsLibrary = packageOptions.projectType === "library";
683
+ const pkgTesting = packageOptions.testing ?? (pkgIsLibrary ? "vitest" : "none");
684
+ if (pkgTesting === "vitest") {
685
+ versionPromises2.push(
686
+ getLatestNpmVersion("vitest", "4.0.0").then((v) => {
687
+ versions2.vitest = v;
688
+ })
689
+ );
690
+ }
691
+ if (!pkgIsLibrary) {
692
+ versionPromises2.push(
693
+ getLatestNpmVersion("vite", "6.3.4").then((v) => {
694
+ versions2.vite = v;
695
+ })
696
+ );
697
+ }
698
+ const linter2 = packageOptions.linter ?? "oxlint";
699
+ if (linter2 === "eslint") {
700
+ versionPromises2.push(
701
+ getLatestNpmVersion("eslint", "9.17.0").then((v) => {
702
+ versions2.eslint = v;
703
+ })
704
+ );
705
+ } else if (linter2 === "oxlint") {
706
+ versionPromises2.push(
707
+ getLatestNpmVersion("oxlint", "0.16.0").then((v) => {
708
+ versions2.oxlint = v;
709
+ })
710
+ );
711
+ } else if (linter2 === "biome") {
712
+ versionPromises2.push(
713
+ getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
714
+ versions2.biome = v;
715
+ })
716
+ );
717
+ }
718
+ const formatter2 = packageOptions.formatter ?? "oxfmt";
719
+ if (formatter2 === "prettier") {
720
+ versionPromises2.push(
721
+ getLatestNpmVersion("prettier", "3.4.2").then((v) => {
722
+ versions2.prettier = v;
723
+ })
724
+ );
725
+ } else if (formatter2 === "oxfmt") {
726
+ versionPromises2.push(
727
+ getLatestNpmVersion("oxfmt", "0.1.0").then((v) => {
728
+ versions2.oxfmt = v;
729
+ })
730
+ );
731
+ } else if (formatter2 === "biome" && linter2 !== "biome") {
732
+ versionPromises2.push(
733
+ getLatestNpmVersion("@biomejs/biome", "1.9.4").then((v) => {
734
+ versions2.biome = v;
735
+ })
736
+ );
737
+ }
738
+ await Promise.all(versionPromises2);
739
+ packageOptions.versions = versions2;
740
+ const basePath2 = join(monorepoRoot, packagePath);
741
+ const s2 = p.spinner();
742
+ s2.start("Creating package...");
743
+ try {
744
+ const files = generate(packageOptions);
745
+ const filePaths = Object.keys(files).sort();
746
+ for (const filePath of filePaths) {
747
+ const fullFilePath = join(basePath2, filePath);
748
+ await mkdir(dirname(fullFilePath), { recursive: true });
749
+ const file = files[filePath];
750
+ if (file.type === "text") {
751
+ await writeFile(fullFilePath, file.content);
752
+ } else {
753
+ const response = await fetch(file.url);
754
+ await writeFile(fullFilePath, response.body);
755
+ }
756
+ }
757
+ s2.stop("Package created!");
758
+ const isLibrary2 = packageOptions.projectType === "library";
759
+ const nextSteps = isLibrary2 ? [
760
+ `cd ${packagePath}`,
761
+ `${packageManager2} install`,
762
+ `${packageManager2} run build`
763
+ ].join("\n") : [
764
+ `cd ${packagePath}`,
765
+ `${packageManager2} install`,
766
+ `${packageManager2} run dev`
767
+ ].join("\n");
768
+ p.note(nextSteps, "Next steps");
769
+ p.outro(color.green("Happy coding! \u2728"));
770
+ process.exit(0);
771
+ } catch (error) {
772
+ s2.stop("Failed to create package");
773
+ p.log.error(String(error));
774
+ process.exit(1);
775
+ }
776
+ }
777
+ }
445
778
  let generateOptions;
446
779
  if (Object.keys(options).length > 0) {
447
780
  const template = options.template ?? "vanilla";
@@ -476,6 +809,107 @@ async function main() {
476
809
  } else {
477
810
  generateOptions = await promptForOptions(name);
478
811
  }
812
+ if (generateOptions.projectType === "monorepo") {
813
+ const { generateMonorepo } = await import('./chunks/index.mjs').then(function (n) { return n.m; });
814
+ const packageManager2 = generateOptions.packageManager || "pnpm";
815
+ if (packageManager2 === "pnpm") {
816
+ generateOptions.pnpmVersion = await getLatestPnpmVersion();
817
+ }
818
+ const nodeVersion2 = generateOptions.nodeVersion ?? "latest";
819
+ if (nodeVersion2 === "latest") {
820
+ generateOptions.nodeVersion = await getLatestNodeVersion();
821
+ }
822
+ const basePath2 = join(cwd(), generateOptions.name);
823
+ const s2 = p.spinner();
824
+ s2.start("Creating monorepo workspace...");
825
+ try {
826
+ const { files } = generateMonorepo({
827
+ name: generateOptions.name,
828
+ linter: generateOptions.linter ?? "oxlint",
829
+ formatter: generateOptions.formatter ?? "oxfmt",
830
+ packageManager: packageManager2,
831
+ pnpmVersion: generateOptions.pnpmVersion,
832
+ pnpmManageVersions: generateOptions.pnpmManageVersions,
833
+ nodeVersion: generateOptions.nodeVersion
834
+ });
835
+ const filePaths = Object.keys(files).sort();
836
+ for (const filePath of filePaths) {
837
+ const fullFilePath = join(basePath2, filePath);
838
+ await mkdir(dirname(fullFilePath), { recursive: true });
839
+ const file = files[filePath];
840
+ if (file.type === "text") {
841
+ await writeFile(fullFilePath, file.content);
842
+ }
843
+ }
844
+ s2.stop("Monorepo workspace created!");
845
+ const initialPackage = await promptForInitialPackage();
846
+ if (initialPackage !== "skip") {
847
+ const packageName = await p.text({
848
+ message: "Package name?",
849
+ placeholder: initialPackage === "app" ? "my-app" : "my-package",
850
+ validate: (value) => {
851
+ if (!value.length) return "Package name is required";
852
+ }
853
+ });
854
+ if (!p.isCancel(packageName)) {
855
+ const targetDir = initialPackage === "app" ? "apps" : "packages";
856
+ const packagePath = join(targetDir, packageName);
857
+ const packageOptions = await promptForPackageOptions(packageName, initialPackage);
858
+ packageOptions.workspaceRoot = "../..";
859
+ packageOptions.name = packageName;
860
+ const pkgManager = packageOptions.packageManager || "pnpm";
861
+ const versions2 = {};
862
+ const versionPromises2 = [];
863
+ const initPkgIsLibrary = packageOptions.projectType === "library";
864
+ const initPkgTesting = packageOptions.testing ?? (initPkgIsLibrary ? "vitest" : "none");
865
+ if (initPkgTesting === "vitest") {
866
+ versionPromises2.push(
867
+ getLatestNpmVersion("vitest", "4.0.0").then((v) => {
868
+ versions2.vitest = v;
869
+ })
870
+ );
871
+ }
872
+ if (!initPkgIsLibrary) {
873
+ versionPromises2.push(
874
+ getLatestNpmVersion("vite", "6.3.4").then((v) => {
875
+ versions2.vite = v;
876
+ })
877
+ );
878
+ }
879
+ await Promise.all(versionPromises2);
880
+ packageOptions.versions = versions2;
881
+ s2.start("Creating initial package...");
882
+ const packageFiles = generate(packageOptions);
883
+ const packageFilePaths = Object.keys(packageFiles).sort();
884
+ const packageBasePath = join(basePath2, packagePath);
885
+ for (const filePath of packageFilePaths) {
886
+ const fullFilePath = join(packageBasePath, filePath);
887
+ await mkdir(dirname(fullFilePath), { recursive: true });
888
+ const file = packageFiles[filePath];
889
+ if (file.type === "text") {
890
+ await writeFile(fullFilePath, file.content);
891
+ } else {
892
+ const response = await fetch(file.url);
893
+ await writeFile(fullFilePath, response.body);
894
+ }
895
+ }
896
+ s2.stop("Initial package created!");
897
+ }
898
+ }
899
+ const nextSteps = [
900
+ `cd ${generateOptions.name}`,
901
+ `${packageManager2} install`,
902
+ `${packageManager2} run dev`
903
+ ].join("\n");
904
+ p.note(nextSteps, "Next steps");
905
+ p.outro(color.green("Happy coding! \u2728"));
906
+ process.exit(0);
907
+ } catch (error) {
908
+ s2.stop("Failed to create monorepo workspace");
909
+ p.log.error(String(error));
910
+ process.exit(1);
911
+ }
912
+ }
479
913
  const base = generateOptions.template ? getBaseTemplate(generateOptions.template) : "vanilla";
480
914
  const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
481
915
  generateOptions.name ??= defaultFallbackName;
@@ -488,12 +922,17 @@ async function main() {
488
922
  generateOptions.nodeVersion = await getLatestNodeVersion();
489
923
  }
490
924
  const versions = {};
491
- const versionPromises = [
492
- getLatestNpmVersion("vitest", "4.0.0").then((v) => {
493
- versions.vitest = v;
494
- })
495
- ];
496
- if (generateOptions.projectType !== "library") {
925
+ const versionPromises = [];
926
+ const isLibrary = generateOptions.projectType === "library";
927
+ const testing = generateOptions.testing ?? (isLibrary ? "vitest" : "none");
928
+ if (testing === "vitest") {
929
+ versionPromises.push(
930
+ getLatestNpmVersion("vitest", "4.0.0").then((v) => {
931
+ versions.vitest = v;
932
+ })
933
+ );
934
+ }
935
+ if (!isLibrary) {
497
936
  versionPromises.push(
498
937
  getLatestNpmVersion("vite", "6.3.4").then((v) => {
499
938
  versions.vite = v;
@@ -560,8 +999,8 @@ async function main() {
560
999
  }
561
1000
  }
562
1001
  s.stop("Project created!");
563
- const isLibrary = generateOptions.projectType === "library";
564
- const nextSteps = isLibrary ? [
1002
+ const isLibrary2 = generateOptions.projectType === "library";
1003
+ const nextSteps = isLibrary2 ? [
565
1004
  `cd ${generateOptions.name}`,
566
1005
  `${packageManager} install`,
567
1006
  `${packageManager} run build`