create-krispya 0.12.0 → 0.14.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
@@ -4,17 +4,18 @@
4
4
  const p = require('@clack/prompts');
5
5
  const color = require('chalk');
6
6
  const commander = require('commander');
7
- const promises$1 = require('node:fs/promises');
7
+ const promises = require('node:fs/promises');
8
8
  const node_module = require('node:module');
9
9
  const node_path = require('node:path');
10
10
  const node_process = require('node:process');
11
11
  const undici = require('undici');
12
- const workspace = require('./shared/create-krispya.FNrYi_5V.cjs');
13
- const Conf = require('conf');
14
- const promises = require('fs/promises');
12
+ const workspace = require('./shared/create-krispya.Wf4wp5cQ.cjs');
13
+ const promises$1 = require('fs/promises');
15
14
  const fs = require('fs');
16
15
  const path = require('path');
17
16
  const node_fs = require('node:fs');
17
+ const node_child_process = require('node:child_process');
18
+ require('conf');
18
19
 
19
20
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
20
21
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -33,7 +34,6 @@ function _interopNamespaceCompat(e) {
33
34
 
34
35
  const p__namespace = /*#__PURE__*/_interopNamespaceCompat(p);
35
36
  const color__default = /*#__PURE__*/_interopDefaultCompat(color);
36
- const Conf__default = /*#__PURE__*/_interopDefaultCompat(Conf);
37
37
 
38
38
  function formatConfigSummary(options, inherited) {
39
39
  const lines = [];
@@ -51,8 +51,9 @@ function formatConfigSummary(options, inherited) {
51
51
  const projectType = options.projectType ?? "app";
52
52
  const baseTemplate = options.template ? workspace.getBaseTemplate(options.template) : "vanilla";
53
53
  if (baseTemplate === "react") {
54
- lines.push(formatRow("Framework", "React"));
55
- lines.push(formatRow("\u21B3 React compiler", workspace.shouldEnableReactCompiler(options) ? "yes" : "no"));
54
+ lines.push(
55
+ formatRow("Framework", workspace.shouldEnableReactCompiler(options) ? "React + compiler" : "React")
56
+ );
56
57
  } else if (baseTemplate === "r3f") {
57
58
  lines.push(formatRow("Framework", "React Three Fiber"));
58
59
  }
@@ -133,8 +134,9 @@ function formatMonorepoConfigSummary(options) {
133
134
  lines.push(
134
135
  formatRow("Engine", `${workspace.getEngineName(options.engine)}@${options.engine.version ?? "latest"}`)
135
136
  );
136
- lines.push(formatRow("Package manager", options.packageManager));
137
- if (options.packageManager === "pnpm") {
137
+ const packageManagerName = workspace.getPackageManagerName(options.packageManager);
138
+ lines.push(formatRow("Package manager", packageManagerName));
139
+ if (packageManagerName === "pnpm") {
138
140
  const versionManaged = options.pnpmManageVersions ? "yes" : "no";
139
141
  lines.push(formatRow("\u21B3 Version managed", versionManaged, ""));
140
142
  }
@@ -144,22 +146,6 @@ function formatMonorepoConfigSummary(options) {
144
146
  return lines.join("\n");
145
147
  }
146
148
 
147
- const config = new Conf__default({
148
- projectName: "create-krispya"
149
- });
150
- function getAiPlatforms() {
151
- return config.get("aiPlatforms");
152
- }
153
- function getConfigStrategy() {
154
- return config.get("configStrategy") ?? "stealth";
155
- }
156
- function clearConfig() {
157
- config.clear();
158
- }
159
- function getConfigPath() {
160
- return config.path;
161
- }
162
-
163
149
  const R3F_INTEGRATION_OPTIONS = [
164
150
  { value: "drei", label: "Drei" },
165
151
  { value: "handle", label: "Handle" },
@@ -223,7 +209,7 @@ function getDefaultOptions(template, name, projectType = "app", libraryBundler,
223
209
  formatter: inheritedSettings?.formatter ?? "prettier",
224
210
  // Libraries get vitest by default, apps don't
225
211
  testing: projectType === "library" ? "vitest" : "none",
226
- configStrategy: getConfigStrategy(),
212
+ configStrategy: workspace.getConfigStrategy(),
227
213
  ide: "vscode"
228
214
  };
229
215
  return {
@@ -273,7 +259,7 @@ async function promptForCustomization(template, name, projectType, features, inh
273
259
  libraryBundler = bundler;
274
260
  }
275
261
  let engine = inheritedSettings?.engine ?? presets?.engine ?? { name: "node", version: "latest" };
276
- let finalPackageManager = inheritedSettings?.packageManager?.name ?? presets?.packageManager ?? "pnpm";
262
+ let finalPackageManager = inheritedSettings?.packageManager?.name ?? presets?.packageManager?.name ?? "pnpm";
277
263
  let pnpmManageVersions = inheritedSettings?.pnpmManageVersions ?? presets?.pnpmManageVersions ?? true;
278
264
  if (!inheritedSettings?.engine?.version) {
279
265
  const nodeVersionInput = await p__namespace.text({
@@ -281,7 +267,7 @@ async function promptForCustomization(template, name, projectType, features, inh
281
267
  placeholder: presets?.engine?.version ?? "latest",
282
268
  defaultValue: presets?.engine?.version ?? "latest",
283
269
  validate: (value) => {
284
- if (!value.length) return "Required";
270
+ if (!value?.length) return "Required";
285
271
  if (value !== "latest" && !/^\d+(\.\d+(\.\d+)?)?$/.test(value)) {
286
272
  return 'Must be "latest" or a valid semver (e.g., "22" or "22.13.0")';
287
273
  }
@@ -301,7 +287,7 @@ async function promptForCustomization(template, name, projectType, features, inh
301
287
  { value: "npm", label: "npm" },
302
288
  { value: "yarn", label: "yarn" }
303
289
  ],
304
- initialValue: presets?.packageManager ?? "pnpm"
290
+ initialValue: presets?.packageManager?.name ?? "pnpm"
305
291
  });
306
292
  if (p__namespace.isCancel(packageManager)) {
307
293
  p__namespace.cancel("Operation cancelled.");
@@ -384,7 +370,7 @@ async function promptForCustomization(template, name, projectType, features, inh
384
370
  { value: "stealth", label: "stealth", hint: "configs in .config/" },
385
371
  { value: "root", label: "root", hint: "configs at project root" }
386
372
  ],
387
- initialValue: getConfigStrategy()
373
+ initialValue: workspace.getConfigStrategy()
388
374
  });
389
375
  if (p__namespace.isCancel(configStrategyChoice)) {
390
376
  p__namespace.cancel("Operation cancelled.");
@@ -410,7 +396,7 @@ async function promptForCustomization(template, name, projectType, features, inh
410
396
  projectType,
411
397
  libraryBundler: projectType === "library" ? libraryBundler : void 0,
412
398
  engine,
413
- packageManager: { name: finalPackageManager },
399
+ packageManager: presets?.packageManager?.name === finalPackageManager ? presets.packageManager : { name: finalPackageManager },
414
400
  pnpmManageVersions,
415
401
  linter,
416
402
  formatter,
@@ -457,7 +443,7 @@ async function promptForMonorepoCustomization(name, presets) {
457
443
  placeholder: presets?.engine?.version ?? "latest",
458
444
  defaultValue: presets?.engine?.version ?? "latest",
459
445
  validate: (value) => {
460
- if (!value.length) return "Required";
446
+ if (!value?.length) return "Required";
461
447
  if (value !== "latest" && !/^\d+(\.\d+(\.\d+)?)?$/.test(value)) {
462
448
  return 'Must be "latest" or a valid semver (e.g., "22" or "22.13.0")';
463
449
  }
@@ -538,7 +524,7 @@ async function promptForMonorepo(workspaceName, presets) {
538
524
  formatMonorepoConfigSummary({
539
525
  name: defaultOptions.name,
540
526
  engine: defaultOptions.engine ?? { name: "node", version: "latest" },
541
- packageManager: workspace.getPackageManagerName(defaultOptions.packageManager),
527
+ packageManager: defaultOptions.packageManager ?? { name: "pnpm" },
542
528
  pnpmManageVersions: defaultOptions.pnpmManageVersions,
543
529
  linter: defaultOptions.linter ?? "oxlint",
544
530
  formatter: defaultOptions.formatter ?? "prettier",
@@ -559,7 +545,7 @@ async function promptForOptions(name, presets) {
559
545
  placeholder: workspace.generateRandomName(),
560
546
  defaultValue: workspace.generateRandomName(),
561
547
  validate: (value) => {
562
- if (!value.length) return "Project name is required";
548
+ if (!value?.length) return "Project name is required";
563
549
  }
564
550
  });
565
551
  if (p__namespace.isCancel(nameResult)) {
@@ -591,7 +577,7 @@ function presetsToInheritedSettings(presets) {
591
577
  return {
592
578
  linter: presets.linter,
593
579
  formatter: presets.formatter,
594
- packageManager: presets.packageManager ? { name: presets.packageManager } : void 0,
580
+ packageManager: presets.packageManager,
595
581
  engine: presets.engine,
596
582
  pnpmManageVersions: presets.pnpmManageVersions
597
583
  };
@@ -643,7 +629,7 @@ async function promptForPackageOptions(projectName, projectType, inheritedSettin
643
629
  }
644
630
 
645
631
  async function promptForAiAgentPlatforms(isNonInteractive) {
646
- const savedPlatforms = getAiPlatforms();
632
+ const savedPlatforms = workspace.getAiPlatforms();
647
633
  if (isNonInteractive) {
648
634
  return savedPlatforms ?? workspace.ALL_AI_PLATFORMS;
649
635
  }
@@ -676,54 +662,9 @@ async function promptForAiAgentPlatforms(isNonInteractive) {
676
662
  return selected;
677
663
  }
678
664
 
679
- async function checkAnyExists(paths) {
680
- for (const path of paths) {
681
- try {
682
- await promises.access(path, promises.constants.F_OK);
683
- return true;
684
- } catch {
685
- }
686
- }
687
- return false;
688
- }
689
- async function validateWorkspace(monorepoRoot) {
690
- const errors = [];
691
- const tsConfigPath = path.join(monorepoRoot, ".config/typescript/package.json");
692
- try {
693
- await promises.access(tsConfigPath, promises.constants.F_OK);
694
- } catch {
695
- errors.push("Missing .config/typescript package");
696
- }
697
- const linterPaths = [
698
- path.join(monorepoRoot, ".config/oxlint/package.json"),
699
- path.join(monorepoRoot, ".config/eslint/package.json"),
700
- path.join(monorepoRoot, "eslint.config.js"),
701
- path.join(monorepoRoot, "biome.json")
702
- ];
703
- const hasLinter = await checkAnyExists(linterPaths);
704
- if (!hasLinter) {
705
- errors.push(
706
- "Missing linter config (.config/oxlint, .config/eslint, eslint.config.js, or biome.json)"
707
- );
708
- }
709
- const formatterPaths = [
710
- path.join(monorepoRoot, ".config/oxfmt/package.json"),
711
- path.join(monorepoRoot, ".config/prettier/package.json"),
712
- path.join(monorepoRoot, ".prettierrc.json"),
713
- path.join(monorepoRoot, "biome.json")
714
- ];
715
- const hasFormatter = await checkAnyExists(formatterPaths);
716
- if (!hasFormatter) {
717
- errors.push(
718
- "Missing formatter config (.config/oxfmt, .config/prettier, .prettierrc.json, or biome.json)"
719
- );
720
- }
721
- return { valid: errors.length === 0, errors };
722
- }
723
-
724
665
  async function fileExists$1(path) {
725
666
  try {
726
- await promises$1.access(path, node_fs.constants.F_OK);
667
+ await promises.access(path, node_fs.constants.F_OK);
727
668
  return true;
728
669
  } catch {
729
670
  return false;
@@ -739,8 +680,8 @@ async function detectMonorepoRoot() {
739
680
  while (currentDir !== root) {
740
681
  const workspaceFile = node_path.join(currentDir, "pnpm-workspace.yaml");
741
682
  try {
742
- await promises$1.access(workspaceFile, node_fs.constants.F_OK);
743
- const content = await promises$1.readFile(workspaceFile, "utf-8");
683
+ await promises.access(workspaceFile, node_fs.constants.F_OK);
684
+ const content = await promises.readFile(workspaceFile, "utf-8");
744
685
  if (content.includes("packages:")) {
745
686
  return currentDir;
746
687
  }
@@ -764,7 +705,7 @@ async function detectPackageRoot() {
764
705
  async function parseWorkspaceDirectories(monorepoRoot) {
765
706
  try {
766
707
  const workspaceFile = node_path.join(monorepoRoot, "pnpm-workspace.yaml");
767
- const content = await promises$1.readFile(workspaceFile, "utf-8");
708
+ const content = await promises.readFile(workspaceFile, "utf-8");
768
709
  return workspace.parseWorkspaceYamlContent(content);
769
710
  } catch {
770
711
  return [];
@@ -774,14 +715,14 @@ async function detectWorkspaceSettings(monorepoRoot) {
774
715
  try {
775
716
  const tooling = await workspace.detectTooling(monorepoRoot);
776
717
  const pkgPath = node_path.join(monorepoRoot, "package.json");
777
- const content = await promises$1.readFile(pkgPath, "utf-8");
718
+ const content = await promises.readFile(pkgPath, "utf-8");
778
719
  const pkgJson = JSON.parse(content);
779
- const packageManager = workspace.parsePackageManager(pkgJson.packageManager);
720
+ const packageManager = workspace.parsePackageManagerSpec(pkgJson.packageManager);
780
721
  const engine = workspace.parseEngine(pkgJson.engines);
781
722
  let pnpmManageVersions;
782
723
  try {
783
724
  const workspaceFile = node_path.join(monorepoRoot, "pnpm-workspace.yaml");
784
- const workspaceContent = await promises$1.readFile(workspaceFile, "utf-8");
725
+ const workspaceContent = await promises.readFile(workspaceFile, "utf-8");
785
726
  pnpmManageVersions = workspaceContent.includes("manage-package-manager-versions: true");
786
727
  } catch {
787
728
  }
@@ -819,7 +760,7 @@ async function detectExistingConfigs(monorepoRoot) {
819
760
  async function getMonorepoScope(monorepoRoot) {
820
761
  try {
821
762
  const pkgPath = node_path.join(monorepoRoot, "package.json");
822
- const content = await promises$1.readFile(pkgPath, "utf-8");
763
+ const content = await promises.readFile(pkgPath, "utf-8");
823
764
  const pkgJson = JSON.parse(content);
824
765
  if (pkgJson.name) {
825
766
  return pkgJson.name.replace(/^@/, "").replace(/\/.*$/, "");
@@ -831,12 +772,12 @@ async function getMonorepoScope(monorepoRoot) {
831
772
  async function getWorkspacePackages(monorepoRoot) {
832
773
  const packagesDir = node_path.join(monorepoRoot, "packages");
833
774
  try {
834
- const entries = await promises$1.readdir(packagesDir, { withFileTypes: true });
775
+ const entries = await promises.readdir(packagesDir, { withFileTypes: true });
835
776
  const names = [];
836
777
  for (const entry of entries) {
837
778
  if (!entry.isDirectory()) continue;
838
779
  try {
839
- const content = await promises$1.readFile(node_path.join(packagesDir, entry.name, "package.json"), "utf-8");
780
+ const content = await promises.readFile(node_path.join(packagesDir, entry.name, "package.json"), "utf-8");
840
781
  const pkg = JSON.parse(content);
841
782
  if (pkg.name) names.push(pkg.name);
842
783
  } catch {
@@ -851,13 +792,13 @@ async function ensureConfigInWorkspace(monorepoRoot) {
851
792
  const workspacePath = node_path.join(monorepoRoot, "pnpm-workspace.yaml");
852
793
  let content;
853
794
  try {
854
- content = await promises$1.readFile(workspacePath, "utf-8");
795
+ content = await promises.readFile(workspacePath, "utf-8");
855
796
  } catch {
856
797
  content = `packages:
857
798
  - '.config/*'
858
799
  - 'packages/*'
859
800
  `;
860
- await promises$1.writeFile(workspacePath, content);
801
+ await promises.writeFile(workspacePath, content);
861
802
  return;
862
803
  }
863
804
  if (content.includes(".config/*")) {
@@ -873,7 +814,7 @@ ${content}`;
873
814
  lines.splice(packagesIndex + 1, 0, " - '.config/*'");
874
815
  content = lines.join("\n");
875
816
  }
876
- await promises$1.writeFile(workspacePath, content);
817
+ await promises.writeFile(workspacePath, content);
877
818
  }
878
819
 
879
820
  async function handleCheckCommand() {
@@ -882,7 +823,7 @@ async function handleCheckCommand() {
882
823
  console.log(color__default.red("\u2717") + " Not a monorepo workspace");
883
824
  process.exit(1);
884
825
  }
885
- const { valid, errors } = await validateWorkspace(monorepoRoot);
826
+ const { valid, errors } = await workspace.validateWorkspace(monorepoRoot);
886
827
  if (valid) {
887
828
  console.log(color__default.green("\u2713") + " Valid monorepo workspace");
888
829
  console.log(color__default.dim(` ${monorepoRoot}`));
@@ -901,7 +842,7 @@ async function migrateEslintConfig(monorepoRoot, files) {
901
842
  const existingConfigPath = node_path.join(monorepoRoot, "eslint.config.js");
902
843
  let existingContent;
903
844
  try {
904
- existingContent = await promises$1.readFile(existingConfigPath, "utf-8");
845
+ existingContent = await promises.readFile(existingConfigPath, "utf-8");
905
846
  } catch {
906
847
  workspace.renderEslintConfigPackage(files);
907
848
  return;
@@ -980,7 +921,7 @@ async function migratePrettierConfig(monorepoRoot, files) {
980
921
  const existingConfigPath = node_path.join(monorepoRoot, ".prettierrc.json");
981
922
  let existingContent;
982
923
  try {
983
- existingContent = await promises$1.readFile(existingConfigPath, "utf-8");
924
+ existingContent = await promises.readFile(existingConfigPath, "utf-8");
984
925
  } catch {
985
926
  workspace.renderPrettierConfigPackage(files);
986
927
  return;
@@ -1039,7 +980,7 @@ async function handleFixCommand(options) {
1039
980
  console.log(color__default.dim(" Run this command from within a monorepo"));
1040
981
  process.exit(1);
1041
982
  }
1042
- const { valid, errors } = await validateWorkspace(monorepoRoot);
983
+ const { valid, errors } = await workspace.validateWorkspace(monorepoRoot);
1043
984
  if (valid) {
1044
985
  console.log(color__default.green("\u2713") + " Workspace is already valid");
1045
986
  console.log(color__default.dim(` ${monorepoRoot}`));
@@ -1173,19 +1114,19 @@ async function handleFixCommand(options) {
1173
1114
  }
1174
1115
  for (const [filePath, file] of Object.entries(files)) {
1175
1116
  const fullPath = node_path.join(monorepoRoot, filePath);
1176
- await promises$1.mkdir(node_path.dirname(fullPath), { recursive: true });
1177
- await promises$1.writeFile(fullPath, file.content);
1117
+ await promises.mkdir(node_path.dirname(fullPath), { recursive: true });
1118
+ await promises.writeFile(fullPath, file.content);
1178
1119
  }
1179
1120
  await ensureConfigInWorkspace(monorepoRoot);
1180
1121
  if (existingConfigs.eslintConfigPath && linter === "eslint") {
1181
1122
  try {
1182
- await promises$1.unlink(existingConfigs.eslintConfigPath);
1123
+ await promises.unlink(existingConfigs.eslintConfigPath);
1183
1124
  } catch {
1184
1125
  }
1185
1126
  }
1186
1127
  if (existingConfigs.prettierConfigPath && formatter === "prettier") {
1187
1128
  try {
1188
- await promises$1.unlink(existingConfigs.prettierConfigPath);
1129
+ await promises.unlink(existingConfigs.prettierConfigPath);
1189
1130
  } catch {
1190
1131
  }
1191
1132
  }
@@ -1214,8 +1155,8 @@ async function handleFixCommand(options) {
1214
1155
  workspace.renderVscodeFiles(vscodeFiles, linter, formatter);
1215
1156
  for (const [filePath, file] of Object.entries(vscodeFiles)) {
1216
1157
  const fullPath = node_path.join(monorepoRoot, filePath);
1217
- await promises$1.mkdir(node_path.dirname(fullPath), { recursive: true });
1218
- await promises$1.writeFile(fullPath, file.content);
1158
+ await promises.mkdir(node_path.dirname(fullPath), { recursive: true });
1159
+ await promises.writeFile(fullPath, file.content);
1219
1160
  }
1220
1161
  console.log(color__default.dim(" Generated .vscode/settings.json"));
1221
1162
  console.log(color__default.dim(" Generated .vscode/extensions.json"));
@@ -1238,8 +1179,8 @@ async function handleFixCommand(options) {
1238
1179
  });
1239
1180
  for (const [filePath, file] of Object.entries(aiFilesOutput)) {
1240
1181
  const fullPath = node_path.join(monorepoRoot, filePath);
1241
- await promises$1.mkdir(node_path.dirname(fullPath), { recursive: true });
1242
- await promises$1.writeFile(fullPath, file.content);
1182
+ await promises.mkdir(node_path.dirname(fullPath), { recursive: true });
1183
+ await promises.writeFile(fullPath, file.content);
1243
1184
  console.log(color__default.dim(` Generated ${filePath}`));
1244
1185
  }
1245
1186
  }
@@ -1281,18 +1222,22 @@ function renderExpectedViteConfig(template) {
1281
1222
  async function detectCurrentConfig(root, isMonorepo = true) {
1282
1223
  let name = root.split(/[/\\]/).pop() ?? "workspace";
1283
1224
  let packageManager = "pnpm";
1225
+ let packageManagerSpec = { name: "pnpm" };
1226
+ let engine;
1284
1227
  let hasTypecheck = false;
1285
1228
  let viteTemplate;
1286
1229
  try {
1287
1230
  const pkgPath = path.join(root, "package.json");
1288
- const content = await promises.readFile(pkgPath, "utf-8");
1231
+ const content = await promises$1.readFile(pkgPath, "utf-8");
1289
1232
  const pkgJson = JSON.parse(content);
1290
1233
  if (pkgJson.name) {
1291
1234
  name = pkgJson.name.replace(/^@/, "").replace(/\/.*$/, "");
1292
1235
  }
1293
1236
  if (pkgJson.packageManager) {
1294
- packageManager = pkgJson.packageManager.split("@")[0] ?? packageManager;
1237
+ packageManagerSpec = workspace.parsePackageManagerSpec(pkgJson.packageManager) ?? packageManagerSpec;
1238
+ packageManager = packageManagerSpec.name;
1295
1239
  }
1240
+ engine = workspace.parseEngine(pkgJson.engines);
1296
1241
  hasTypecheck = pkgJson.scripts?.typecheck != null;
1297
1242
  viteTemplate = detectViteTemplate(pkgJson);
1298
1243
  } catch {
@@ -1304,6 +1249,8 @@ async function detectCurrentConfig(root, isMonorepo = true) {
1304
1249
  linter: tooling.linter ?? "oxlint",
1305
1250
  formatter: tooling.formatter ?? "prettier",
1306
1251
  packageManager,
1252
+ packageManagerSpec,
1253
+ engine,
1307
1254
  isMonorepo,
1308
1255
  configStrategy,
1309
1256
  hasTypecheck,
@@ -1410,7 +1357,7 @@ async function planExpectedFiles(config) {
1410
1357
  }
1411
1358
  async function fileExists(path) {
1412
1359
  try {
1413
- await promises.access(path, fs.constants.F_OK);
1360
+ await promises$1.access(path, fs.constants.F_OK);
1414
1361
  return true;
1415
1362
  } catch {
1416
1363
  return false;
@@ -1552,7 +1499,7 @@ async function compareWithDisk(expected, root) {
1552
1499
  const fullPath = path.join(root, filePath);
1553
1500
  const newContent = file.content;
1554
1501
  if (await fileExists(fullPath)) {
1555
- const currentContent = await promises.readFile(fullPath, "utf-8");
1502
+ const currentContent = await promises$1.readFile(fullPath, "utf-8");
1556
1503
  if (fileContentsEqual(filePath, currentContent, newContent)) {
1557
1504
  changes.push({
1558
1505
  path: filePath,
@@ -1630,7 +1577,7 @@ function detectLibraryPackage(pkg) {
1630
1577
  return pkg.exports != null || pkg.main?.includes("dist") === true || pkg.module?.includes("dist") === true || Array.isArray(pkg.files) && pkg.files.includes("dist");
1631
1578
  }
1632
1579
  function getPackageManagerForScripts(config, pkg) {
1633
- const packageManager = pkg.packageManager?.split("@")[0] ?? config.packageManager;
1580
+ const packageManager = workspace.parsePackageManagerSpec(pkg.packageManager)?.name ?? config.packageManager;
1634
1581
  return isPackageManagerName(packageManager) ? packageManager : "pnpm";
1635
1582
  }
1636
1583
  function getSinglePackageToolScripts(config) {
@@ -1660,6 +1607,35 @@ function scriptsEqual(left, right) {
1660
1607
  if (leftEntries.length !== Object.keys(right).length) return false;
1661
1608
  return leftEntries.every(([key, value]) => right[key] === value);
1662
1609
  }
1610
+ function packageManagerFieldsEqual(pkg, packageManagerSpec, targetNodeVersion) {
1611
+ if (packageManagerSpec == null || packageManagerSpec.version == null) {
1612
+ return targetNodeVersion == null || pkg.engines?.node === `>=${targetNodeVersion}`;
1613
+ }
1614
+ const majorVersion = workspace.getSemverMajorString(packageManagerSpec.version);
1615
+ return pkg.packageManager === workspace.formatPackageManager(packageManagerSpec) && pkg.engines?.[packageManagerSpec.name] === `>=${majorVersion}.0.0` && (targetNodeVersion == null || pkg.engines?.node === `>=${targetNodeVersion}`);
1616
+ }
1617
+ function applyPackageManagerFields(pkg, packageManagerSpec, targetNodeVersion) {
1618
+ if (packageManagerSpec == null || packageManagerSpec.version == null) {
1619
+ if (targetNodeVersion == null) return pkg;
1620
+ return {
1621
+ ...pkg,
1622
+ engines: {
1623
+ ...pkg.engines,
1624
+ node: `>=${targetNodeVersion}`
1625
+ }
1626
+ };
1627
+ }
1628
+ const majorVersion = workspace.getSemverMajorString(packageManagerSpec.version);
1629
+ return {
1630
+ ...pkg,
1631
+ packageManager: workspace.formatPackageManager(packageManagerSpec),
1632
+ engines: {
1633
+ ...pkg.engines,
1634
+ [packageManagerSpec.name]: `>=${majorVersion}.0.0`,
1635
+ ...targetNodeVersion == null ? {} : { node: `>=${targetNodeVersion}` }
1636
+ }
1637
+ };
1638
+ }
1663
1639
  async function getExpectedPackageScripts(root, config, pkg) {
1664
1640
  if (config.isMonorepo) {
1665
1641
  return workspace.packageJsonScripts.monorepoRoot(config.linter, config.formatter);
@@ -1688,6 +1664,9 @@ async function getExpectedPackageDevDependencies(root, config, pkg) {
1688
1664
  addMissingDevDependency(pkg, nextDevDependencies, "@babel/core");
1689
1665
  addMissingDevDependency(pkg, nextDevDependencies, "@rolldown/plugin-babel");
1690
1666
  addMissingDevDependency(pkg, nextDevDependencies, "babel-plugin-react-compiler");
1667
+ if (config.linter === "oxlint") {
1668
+ addMissingDevDependency(pkg, nextDevDependencies, "eslint-plugin-react-hooks");
1669
+ }
1691
1670
  if (await detectTypeScriptPackage(root, pkg)) {
1692
1671
  addMissingDevDependency(pkg, nextDevDependencies, "@types/babel__core");
1693
1672
  }
@@ -1698,7 +1677,7 @@ async function getPackageJsonScriptUpdates(root, config) {
1698
1677
  const packageJsonPath = path.join(root, "package.json");
1699
1678
  let currentContent;
1700
1679
  try {
1701
- currentContent = await promises.readFile(packageJsonPath, "utf-8");
1680
+ currentContent = await promises$1.readFile(packageJsonPath, "utf-8");
1702
1681
  } catch {
1703
1682
  return [];
1704
1683
  }
@@ -1708,7 +1687,9 @@ async function getPackageJsonScriptUpdates(root, config) {
1708
1687
  const nextScripts = workspace.mergePackageJsonScripts(currentScripts, expectedScripts);
1709
1688
  const currentDevDependencies = pkg.devDependencies ?? {};
1710
1689
  const nextDevDependencies = await getExpectedPackageDevDependencies(root, config, pkg);
1711
- if (scriptsEqual(currentScripts, nextScripts) && scriptsEqual(currentDevDependencies, nextDevDependencies)) {
1690
+ const targetPackageManagerSpec = config.targetPackageManagerSpec;
1691
+ const targetNodeVersion = config.targetNodeVersion;
1692
+ if (scriptsEqual(currentScripts, nextScripts) && scriptsEqual(currentDevDependencies, nextDevDependencies) && packageManagerFieldsEqual(pkg, targetPackageManagerSpec, targetNodeVersion)) {
1712
1693
  return [
1713
1694
  {
1714
1695
  path: "package.json",
@@ -1718,10 +1699,14 @@ async function getPackageJsonScriptUpdates(root, config) {
1718
1699
  }
1719
1700
  ];
1720
1701
  }
1721
- const nextPackageJson = {
1722
- ...pkg,
1723
- scripts: nextScripts
1724
- };
1702
+ const nextPackageJson = applyPackageManagerFields(
1703
+ {
1704
+ ...pkg,
1705
+ scripts: nextScripts
1706
+ },
1707
+ targetPackageManagerSpec,
1708
+ targetNodeVersion
1709
+ );
1725
1710
  if (Object.keys(nextDevDependencies).length > 0 || pkg.devDependencies != null) {
1726
1711
  nextPackageJson.devDependencies = nextDevDependencies;
1727
1712
  }
@@ -1736,12 +1721,49 @@ async function getPackageJsonScriptUpdates(root, config) {
1736
1721
  }
1737
1722
  ];
1738
1723
  }
1724
+ async function getPackageManagerConfigUpdates(root, config) {
1725
+ if (config.targetPackageManagerSpec == null && config.targetNodeVersion == null) return [];
1726
+ const packageJsonPath = path.join(root, "package.json");
1727
+ let currentContent;
1728
+ try {
1729
+ currentContent = await promises$1.readFile(packageJsonPath, "utf-8");
1730
+ } catch {
1731
+ return [];
1732
+ }
1733
+ const pkg = JSON.parse(currentContent);
1734
+ if (packageManagerFieldsEqual(pkg, config.targetPackageManagerSpec, config.targetNodeVersion)) {
1735
+ return [
1736
+ {
1737
+ path: "package.json",
1738
+ status: "unchanged",
1739
+ currentContent,
1740
+ newContent: currentContent
1741
+ }
1742
+ ];
1743
+ }
1744
+ const nextPackageJson = applyPackageManagerFields(
1745
+ pkg,
1746
+ config.targetPackageManagerSpec,
1747
+ config.targetNodeVersion
1748
+ );
1749
+ return [
1750
+ {
1751
+ path: "package.json",
1752
+ status: "modified",
1753
+ currentContent,
1754
+ newContent: `${JSON.stringify(nextPackageJson, null, 2)}
1755
+ `
1756
+ }
1757
+ ];
1758
+ }
1739
1759
  function planSinglePackageOxlintConfig(config) {
1740
1760
  if (config.linter !== "oxlint" || config.isMonorepo) return void 0;
1741
1761
  const isStealth = (config.configStrategy ?? "stealth") === "stealth";
1742
1762
  const path = isStealth ? ".config/oxlint.json" : "oxlint.json";
1743
1763
  const oxlintConfig = workspace.renderOxlintConfig({
1744
1764
  schemaPath: isStealth ? "../node_modules/oxlint/configuration_schema.json" : "./node_modules/oxlint/configuration_schema.json",
1765
+ react: config.viteTemplate === "react" || config.viteTemplate === "r3f",
1766
+ reactCompiler: config.viteTemplate === "react",
1745
1767
  typescript: true
1746
1768
  });
1747
1769
  return {
@@ -1757,7 +1779,7 @@ async function getOxlintConfigReplacementUpdates(root, config) {
1757
1779
  const fullPath = path.join(root, expected.path);
1758
1780
  let currentContent;
1759
1781
  try {
1760
- currentContent = await promises.readFile(fullPath, "utf-8");
1782
+ currentContent = await promises$1.readFile(fullPath, "utf-8");
1761
1783
  } catch {
1762
1784
  return [expected];
1763
1785
  }
@@ -1779,26 +1801,118 @@ async function getOxlintConfigReplacementUpdates(root, config) {
1779
1801
  }
1780
1802
  ];
1781
1803
  }
1782
- async function getWorkspaceConfigUpdates(root) {
1804
+ function extractBuildDependencies(content) {
1805
+ const buildDependencies = {};
1806
+ let section;
1807
+ for (const line of content.split("\n")) {
1808
+ const trimmed = line.trim();
1809
+ if (trimmed === "onlyBuiltDependencies:") {
1810
+ section = "onlyBuiltDependencies";
1811
+ continue;
1812
+ }
1813
+ if (trimmed === "allowBuilds:") {
1814
+ section = "allowBuilds";
1815
+ continue;
1816
+ }
1817
+ if (section != null && trimmed && !line.startsWith(" ") && !line.startsWith(" ")) {
1818
+ section = void 0;
1819
+ }
1820
+ if (section === "onlyBuiltDependencies" && trimmed.startsWith("-")) {
1821
+ const dependency = trimmed.slice(1).trim().replace(/^["']|["']$/g, "");
1822
+ if (dependency.length > 0) {
1823
+ buildDependencies[dependency] = true;
1824
+ }
1825
+ }
1826
+ if (section === "allowBuilds") {
1827
+ const match = trimmed.match(/^([^:]+):\s*(true|false)$/);
1828
+ if (match == null) continue;
1829
+ const dependency = match[1].trim().replace(/^["']|["']$/g, "");
1830
+ if (dependency.length > 0) {
1831
+ buildDependencies[dependency] = match[2] === "true";
1832
+ }
1833
+ }
1834
+ }
1835
+ return buildDependencies;
1836
+ }
1837
+ function withDefaultBuildDependencies(buildDependencies) {
1838
+ return Object.keys(buildDependencies).length > 0 ? buildDependencies : { esbuild: true };
1839
+ }
1840
+ function isPnpmWorkspaceManagedScalarKey(trimmed) {
1841
+ return trimmed.startsWith("manage-package-manager-versions:") || trimmed.startsWith("pmOnFail:");
1842
+ }
1843
+ function isPnpmWorkspaceManagedBlockKey(trimmed) {
1844
+ return trimmed === "onlyBuiltDependencies:" || trimmed === "allowBuilds:";
1845
+ }
1846
+ function isIndentedWorkspaceLine(line) {
1847
+ return line.startsWith(" ") || line.startsWith(" ");
1848
+ }
1849
+ function removePnpmWorkspaceManagedKeys(content) {
1850
+ const sourceLines = content.split("\n");
1851
+ const lines = [];
1852
+ let insertionIndex = 0;
1853
+ let foundManagedKey = false;
1854
+ for (let index = 0; index < sourceLines.length; index++) {
1855
+ const line = sourceLines[index];
1856
+ const trimmed = line.trim();
1857
+ if (isPnpmWorkspaceManagedScalarKey(trimmed)) {
1858
+ if (!foundManagedKey) {
1859
+ insertionIndex = lines.length;
1860
+ foundManagedKey = true;
1861
+ }
1862
+ continue;
1863
+ }
1864
+ if (isPnpmWorkspaceManagedBlockKey(trimmed)) {
1865
+ if (!foundManagedKey) {
1866
+ insertionIndex = lines.length;
1867
+ foundManagedKey = true;
1868
+ }
1869
+ while (index + 1 < sourceLines.length) {
1870
+ const nextLine = sourceLines[index + 1];
1871
+ const nextTrimmed = nextLine.trim();
1872
+ if (nextTrimmed !== "" && !isIndentedWorkspaceLine(nextLine)) break;
1873
+ index++;
1874
+ }
1875
+ continue;
1876
+ }
1877
+ lines.push(line);
1878
+ }
1879
+ return {
1880
+ content: lines.join("\n").trim(),
1881
+ insertionIndex
1882
+ };
1883
+ }
1884
+ function patchPnpmWorkspaceManagedKeys(content, profile, buildDependencies) {
1885
+ const managedContent = workspace.renderPnpmWorkspaceConfig({
1886
+ profile,
1887
+ manageVersions: true,
1888
+ buildDependencies
1889
+ });
1890
+ const existing = removePnpmWorkspaceManagedKeys(content);
1891
+ const preservedLines = existing.content.length > 0 ? existing.content.split("\n") : [];
1892
+ const insertionIndex = Math.min(existing.insertionIndex, preservedLines.length);
1893
+ const managedLines = managedContent.split("\n");
1894
+ preservedLines.splice(insertionIndex, 0, ...managedLines);
1895
+ return preservedLines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
1896
+ }
1897
+ async function getWorkspaceConfigUpdates(root, config) {
1783
1898
  const workspacePath = path.join(root, "pnpm-workspace.yaml");
1784
1899
  const changes = [];
1900
+ const packageManagerSpec = config?.targetPackageManagerSpec ?? config?.packageManagerSpec ?? { name: "pnpm" };
1901
+ const profile = workspace.getPackageManagerProfile(packageManagerSpec);
1902
+ const defaultPackages = [".config/*", "apps/*", "packages/*"];
1785
1903
  let currentContent = "";
1786
1904
  let exists = false;
1787
1905
  try {
1788
- currentContent = await promises.readFile(workspacePath, "utf-8");
1906
+ currentContent = await promises$1.readFile(workspacePath, "utf-8");
1789
1907
  exists = true;
1790
1908
  } catch {
1791
1909
  }
1792
1910
  if (!exists) {
1793
- const newContent = `manage-package-manager-versions: true
1794
-
1795
- packages:
1796
- - '.config/*'
1797
- - 'apps/*'
1798
- - 'packages/*'
1799
-
1800
- onlyBuiltDependencies:
1801
- - esbuild
1911
+ const newContent = `${workspace.renderPnpmWorkspaceConfig({
1912
+ profile,
1913
+ manageVersions: true,
1914
+ packages: defaultPackages
1915
+ })}
1802
1916
  `;
1803
1917
  changes.push({
1804
1918
  path: "pnpm-workspace.yaml",
@@ -1807,32 +1921,14 @@ onlyBuiltDependencies:
1807
1921
  });
1808
1922
  return changes;
1809
1923
  }
1810
- let updatedContent = currentContent;
1811
- let needsUpdate = false;
1812
- if (!currentContent.includes("manage-package-manager-versions")) {
1813
- updatedContent = `manage-package-manager-versions: true
1814
-
1815
- ${updatedContent}`;
1816
- needsUpdate = true;
1817
- }
1818
- if (!currentContent.includes("onlyBuiltDependencies")) {
1819
- updatedContent = `${updatedContent.trimEnd()}
1820
-
1821
- onlyBuiltDependencies:
1822
- - esbuild
1924
+ const buildDependencies = withDefaultBuildDependencies(extractBuildDependencies(currentContent));
1925
+ const updatedContent = `${patchPnpmWorkspaceManagedKeys(
1926
+ currentContent,
1927
+ profile,
1928
+ buildDependencies
1929
+ )}
1823
1930
  `;
1824
- needsUpdate = true;
1825
- }
1826
- if (!currentContent.includes(".config/*")) {
1827
- const lines = updatedContent.split("\n");
1828
- const packagesIndex = lines.findIndex((line) => line.trim().startsWith("packages:"));
1829
- if (packagesIndex !== -1) {
1830
- lines.splice(packagesIndex + 1, 0, " - '.config/*'");
1831
- updatedContent = lines.join("\n");
1832
- needsUpdate = true;
1833
- }
1834
- }
1835
- if (needsUpdate) {
1931
+ if (updatedContent !== currentContent) {
1836
1932
  changes.push({
1837
1933
  path: "pnpm-workspace.yaml",
1838
1934
  status: "modified",
@@ -1853,8 +1949,8 @@ async function applyUpdates(changes, root) {
1853
1949
  for (const change of changes) {
1854
1950
  if (change.status === "unchanged") continue;
1855
1951
  const fullPath = path.join(root, change.path);
1856
- await promises.mkdir(path.dirname(fullPath), { recursive: true });
1857
- await promises.writeFile(fullPath, change.newContent);
1952
+ await promises$1.mkdir(path.dirname(fullPath), { recursive: true });
1953
+ await promises$1.writeFile(fullPath, change.newContent);
1858
1954
  }
1859
1955
  }
1860
1956
  function formatFileChange(change) {
@@ -1930,6 +2026,96 @@ function orderUpdateCategories(categories) {
1930
2026
  (left, right) => getCategoryOrder(left.category) - getCategoryOrder(right.category)
1931
2027
  );
1932
2028
  }
2029
+ function isPnpmMajorMigration(config) {
2030
+ const currentPackageManager = config.packageManagerSpec;
2031
+ const targetPackageManager = config.targetPackageManagerSpec;
2032
+ if (currentPackageManager?.name !== "pnpm" || targetPackageManager?.name !== "pnpm") {
2033
+ return false;
2034
+ }
2035
+ const currentMajor = workspace.getSemverMajor(currentPackageManager.version);
2036
+ const targetMajor = workspace.getSemverMajor(targetPackageManager.version);
2037
+ return currentMajor != null && targetMajor != null && currentMajor !== targetMajor;
2038
+ }
2039
+ function getPackageUpdateCommand(config) {
2040
+ const packageManagerName = config.packageManager.split("@")[0] ?? config.packageManager;
2041
+ if (packageManagerName === "pnpm") {
2042
+ if (isPnpmMajorMigration(config)) {
2043
+ return {
2044
+ command: "pnpm",
2045
+ args: ["install"],
2046
+ displayCommand: "pnpm install",
2047
+ promptMessage: "Install dependencies?",
2048
+ successMessage: "Dependencies installed",
2049
+ failureLabel: "Dependency install"
2050
+ };
2051
+ }
2052
+ return {
2053
+ command: "pnpm",
2054
+ args: ["update"],
2055
+ displayCommand: "pnpm update",
2056
+ promptMessage: "Update packages?",
2057
+ successMessage: "Packages updated",
2058
+ failureLabel: "Package update"
2059
+ };
2060
+ }
2061
+ if (packageManagerName === "npm") {
2062
+ return {
2063
+ command: "npm",
2064
+ args: ["update"],
2065
+ displayCommand: "npm update",
2066
+ promptMessage: "Update packages?",
2067
+ successMessage: "Packages updated",
2068
+ failureLabel: "Package update"
2069
+ };
2070
+ }
2071
+ return void 0;
2072
+ }
2073
+ async function runPackageUpdate(projectRoot, updateCommand) {
2074
+ await new Promise((resolve, reject) => {
2075
+ const child = node_child_process.spawn(updateCommand.command, updateCommand.args, {
2076
+ cwd: projectRoot,
2077
+ shell: process.platform === "win32",
2078
+ stdio: "inherit"
2079
+ });
2080
+ child.on("error", reject);
2081
+ child.on("exit", (code) => {
2082
+ if (code === 0) {
2083
+ resolve();
2084
+ return;
2085
+ }
2086
+ reject(new Error(`${updateCommand.displayCommand} exited with code ${code ?? "unknown"}`));
2087
+ });
2088
+ });
2089
+ }
2090
+ async function promptForPackageUpdate(projectRoot, config, options) {
2091
+ const updateCommand = getPackageUpdateCommand(config);
2092
+ if (!updateCommand) {
2093
+ console.log(color__default.dim(` Package updates are not supported for ${config.packageManager}`));
2094
+ return;
2095
+ }
2096
+ const shouldUpdatePackages = options.yes || await p__namespace.confirm({
2097
+ message: updateCommand.promptMessage,
2098
+ initialValue: true
2099
+ });
2100
+ if (p__namespace.isCancel(shouldUpdatePackages)) {
2101
+ p__namespace.cancel("Operation cancelled.");
2102
+ process.exit(0);
2103
+ }
2104
+ if (!shouldUpdatePackages) {
2105
+ console.log(color__default.dim(" Skipped package updates"));
2106
+ return;
2107
+ }
2108
+ console.log();
2109
+ console.log(color__default.cyan(`Running ${updateCommand.displayCommand}...`));
2110
+ try {
2111
+ await runPackageUpdate(projectRoot, updateCommand);
2112
+ console.log(color__default.green("\u2713") + ` ${updateCommand.successMessage}`);
2113
+ } catch (error) {
2114
+ const message = error instanceof Error ? error.message : String(error);
2115
+ console.log(color__default.red("\u2717") + ` ${updateCommand.failureLabel} failed: ${message}`);
2116
+ process.exit(1);
2117
+ }
2118
+ }
1933
2119
  async function collectUpdateCategories(projectRoot, config, isMonorepo) {
1934
2120
  const expected = await planExpectedFiles(config);
1935
2121
  const categories = await compareWithDisk(expected, projectRoot);
@@ -1953,7 +2139,7 @@ async function collectUpdateCategories(projectRoot, config, isMonorepo) {
1953
2139
  });
1954
2140
  }
1955
2141
  if (isMonorepo) {
1956
- const workspaceConfigChanges = await getWorkspaceConfigUpdates(projectRoot);
2142
+ const workspaceConfigChanges = await getWorkspaceConfigUpdates(projectRoot, config);
1957
2143
  if (workspaceConfigChanges.length > 0) {
1958
2144
  allCategories.push({
1959
2145
  category: "workspace-config",
@@ -1965,6 +2151,110 @@ async function collectUpdateCategories(projectRoot, config, isMonorepo) {
1965
2151
  }
1966
2152
  return orderUpdateCategories(allCategories);
1967
2153
  }
2154
+ async function resolveTargetPackageManagerSpec(options, config) {
2155
+ if (options.packageManager == null) return void 0;
2156
+ const packageManager = workspace.parsePackageManagerSpec(options.packageManager);
2157
+ if (packageManager == null) {
2158
+ console.log(color__default.red("\u2717") + ` Unsupported package manager: ${options.packageManager}`);
2159
+ process.exit(1);
2160
+ }
2161
+ return workspace.resolvePackageManager({
2162
+ name: config.name,
2163
+ packageManager
2164
+ });
2165
+ }
2166
+ function getPackageManagerMajorUpdateTarget(currentPackageManager, latestPackageManager) {
2167
+ if (currentPackageManager?.version == null) return void 0;
2168
+ const currentMajor = workspace.getSemverMajor(currentPackageManager.version);
2169
+ const latestMajor = workspace.getSemverMajor(latestPackageManager.version);
2170
+ if (currentMajor == null || latestMajor == null || latestMajor <= currentMajor) {
2171
+ return void 0;
2172
+ }
2173
+ return latestPackageManager;
2174
+ }
2175
+ function getRequiredNodeUpdateTarget(currentNodeVersion, requiredNodeVersion) {
2176
+ if (requiredNodeVersion == null) return void 0;
2177
+ if (currentNodeVersion == null) return requiredNodeVersion;
2178
+ return workspace.compareNumericSemver(currentNodeVersion, requiredNodeVersion) < 0 ? requiredNodeVersion : void 0;
2179
+ }
2180
+ function formatPackageManagerMajor(packageManager) {
2181
+ const major = workspace.getSemverMajor(packageManager.version);
2182
+ return major == null ? packageManager.name : `${packageManager.name}@${major}`;
2183
+ }
2184
+ async function promptForNodeRequirementUpdate(options, config, targetPackageManagerSpec) {
2185
+ if (targetPackageManagerSpec == null) return void 0;
2186
+ const profile = workspace.getPackageManagerProfile(targetPackageManagerSpec);
2187
+ const requiredNodeVersion = profile.requirements.node;
2188
+ const currentNodeVersion = config.engine?.version;
2189
+ const targetNodeVersion = getRequiredNodeUpdateTarget(currentNodeVersion, requiredNodeVersion);
2190
+ if (targetNodeVersion == null) return void 0;
2191
+ const currentNodeLabel = currentNodeVersion == null ? "not set" : `>=${currentNodeVersion}`;
2192
+ p__namespace.log.warn(
2193
+ `${workspace.formatPackageManager(targetPackageManagerSpec)} requires Node >=${targetNodeVersion}; current engines.node is ${currentNodeLabel}.`
2194
+ );
2195
+ const shouldUpdate = options.yes || await p__namespace.confirm({
2196
+ message: `Update engines.node to >=${targetNodeVersion} too?`,
2197
+ initialValue: true
2198
+ });
2199
+ if (p__namespace.isCancel(shouldUpdate)) {
2200
+ p__namespace.cancel("Operation cancelled.");
2201
+ process.exit(0);
2202
+ }
2203
+ return shouldUpdate ? targetNodeVersion : void 0;
2204
+ }
2205
+ async function promptForPackageManagerMajorUpdate(options, config) {
2206
+ if (options.packageManager != null || config.packageManagerSpec == null) return void 0;
2207
+ const currentPackageManager = config.packageManagerSpec;
2208
+ if (currentPackageManager.version == null) return void 0;
2209
+ const latestPackageManager = await workspace.resolvePackageManager({
2210
+ name: config.name,
2211
+ packageManager: { name: currentPackageManager.name }
2212
+ });
2213
+ const updateTarget = getPackageManagerMajorUpdateTarget(
2214
+ currentPackageManager,
2215
+ latestPackageManager
2216
+ );
2217
+ if (updateTarget == null) return void 0;
2218
+ const shouldUpdate = options.yes || await p__namespace.confirm({
2219
+ message: `Update ${currentPackageManager.name} from ${formatPackageManagerMajor(
2220
+ currentPackageManager
2221
+ )} to ${formatPackageManagerMajor(latestPackageManager)}?`,
2222
+ initialValue: true
2223
+ });
2224
+ if (p__namespace.isCancel(shouldUpdate)) {
2225
+ p__namespace.cancel("Operation cancelled.");
2226
+ process.exit(0);
2227
+ }
2228
+ return shouldUpdate ? updateTarget : void 0;
2229
+ }
2230
+ async function applyPackageManagerMigration(projectRoot, isMonorepo, options, detectedConfig) {
2231
+ const targetPackageManagerSpec = await resolveTargetPackageManagerSpec(options, detectedConfig) ?? await promptForPackageManagerMajorUpdate(options, detectedConfig);
2232
+ if (targetPackageManagerSpec == null) return detectedConfig;
2233
+ const targetNodeVersion = await promptForNodeRequirementUpdate(
2234
+ options,
2235
+ detectedConfig,
2236
+ targetPackageManagerSpec
2237
+ );
2238
+ const migrationConfig = {
2239
+ ...detectedConfig,
2240
+ packageManager: targetPackageManagerSpec.name,
2241
+ targetPackageManagerSpec,
2242
+ targetNodeVersion
2243
+ };
2244
+ const changes = [
2245
+ ...await getPackageManagerConfigUpdates(projectRoot, migrationConfig),
2246
+ ...isMonorepo || targetPackageManagerSpec.name === "pnpm" ? await getWorkspaceConfigUpdates(projectRoot, migrationConfig) : []
2247
+ ].filter((change) => change.status === "added" || change.status === "modified");
2248
+ if (changes.length === 0) return migrationConfig;
2249
+ console.log();
2250
+ console.log(color__default.cyan("Package Manager:"));
2251
+ for (const change of changes) {
2252
+ console.log(formatFileChange(change));
2253
+ }
2254
+ await applyUpdates(changes, projectRoot);
2255
+ console.log(color__default.green("\u2713") + ` Package Manager: updated ${changes.length}`);
2256
+ return migrationConfig;
2257
+ }
1968
2258
  async function processUpdateCategory(category, projectRoot, options) {
1969
2259
  const newChanges = category.changes.filter((change) => change.status === "added");
1970
2260
  const modifiedChanges = category.changes.filter((change) => change.status === "modified");
@@ -2004,11 +2294,8 @@ async function processUpdateCategory(category, projectRoot, options) {
2004
2294
  if (addedCount > 0) parts.push(`added ${addedCount}`);
2005
2295
  if (updatedFilesCount > 0) parts.push(`updated ${updatedFilesCount}`);
2006
2296
  console.log(color__default.green("\u2713") + ` ${category.label}: ${parts.join(", ")}`);
2007
- console.log();
2008
2297
  return "updated";
2009
2298
  }
2010
- console.log(color__default.dim(` Skipped ${category.label}`));
2011
- console.log();
2012
2299
  return "skipped";
2013
2300
  }
2014
2301
  async function handleUpdateCommand(options, handleFixCommand) {
@@ -2021,7 +2308,7 @@ async function handleUpdateCommand(options, handleFixCommand) {
2021
2308
  }
2022
2309
  const isMonorepo = monorepoRoot != null;
2023
2310
  if (isMonorepo) {
2024
- const { valid, errors } = await validateWorkspace(projectRoot);
2311
+ const { valid, errors } = await workspace.validateWorkspace(projectRoot);
2025
2312
  if (!valid) {
2026
2313
  console.log(color__default.yellow("!") + " Workspace has issues:");
2027
2314
  for (const error of errors) {
@@ -2074,6 +2361,8 @@ async function handleUpdateCommand(options, handleFixCommand) {
2074
2361
  console.log(color__default.dim(` Skipped ${skippedCount}`));
2075
2362
  }
2076
2363
  }
2364
+ const finalConfig = await applyPackageManagerMigration(projectRoot, isMonorepo, options, config);
2365
+ await promptForPackageUpdate(projectRoot, finalConfig, options);
2077
2366
  process.exit(0);
2078
2367
  }
2079
2368
 
@@ -2091,9 +2380,10 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedS
2091
2380
  message: "Package name?",
2092
2381
  initialValue: `@${scope}/`,
2093
2382
  validate: (value) => {
2094
- const validationError = workspace.validatePackageName(value);
2383
+ const packageName = value ?? "";
2384
+ const validationError = workspace.validatePackageName(packageName);
2095
2385
  if (validationError) return validationError;
2096
- const dirName = value.includes("/") ? value.split("/").pop() : value;
2386
+ const dirName = workspace.getPackageDirectoryName(packageName);
2097
2387
  if (!dirName) return "Package name is required";
2098
2388
  if (!hasCustomDirectories) {
2099
2389
  const targetPath = node_path.join(monorepoRoot, defaultDir, dirName);
@@ -2110,7 +2400,7 @@ async function createPackageInWorkspace(monorepoRoot, packageManager, inheritedS
2110
2400
  return false;
2111
2401
  }
2112
2402
  const scopedName = packageNameInput;
2113
- const shortName = scopedName.includes("/") ? scopedName.split("/").pop() : scopedName;
2403
+ const shortName = workspace.getPackageDirectoryName(scopedName);
2114
2404
  const packageOptions = await promptForPackageOptions(scopedName, packageType, inheritedSettings);
2115
2405
  let targetDir = defaultDir;
2116
2406
  if (hasCustomDirectories && workspaceDirectories.length > 0) {
@@ -2191,10 +2481,11 @@ async function handleWorkspaceCommand(name, options, writeGeneratedFiles) {
2191
2481
  const template = options.template ?? "vanilla";
2192
2482
  const baseTemplate = workspace.getBaseTemplate(template);
2193
2483
  const scopedName = name.startsWith("@") ? name : `@${scope}/${name}`;
2194
- const fullPackagePath = node_path.join(monorepoRoot, targetDir, name);
2484
+ const packageDirName = workspace.getPackageDirectoryName(name);
2485
+ const fullPackagePath = node_path.join(monorepoRoot, targetDir, packageDirName);
2195
2486
  try {
2196
- await promises$1.access(fullPackagePath, node_fs.constants.F_OK);
2197
- console.error(color__default.red("Error:") + ` Directory ${targetDir}/${name} already exists`);
2487
+ await promises.access(fullPackagePath, node_fs.constants.F_OK);
2488
+ console.error(color__default.red("Error:") + ` Directory ${targetDir}/${packageDirName} already exists`);
2198
2489
  process.exit(1);
2199
2490
  } catch {
2200
2491
  }
@@ -2207,7 +2498,7 @@ async function handleWorkspaceCommand(name, options, writeGeneratedFiles) {
2207
2498
  };
2208
2499
  const pnpmManageVersions = inheritedSettings.pnpmManageVersions ?? true;
2209
2500
  const isLibrary = projectType === "library";
2210
- const relativePkgPath = node_path.join(targetDir, name);
2501
+ const relativePkgPath = node_path.join(targetDir, packageDirName);
2211
2502
  const workspaceRoot = calculateWorkspaceRoot(relativePkgPath);
2212
2503
  const projectOptions = {
2213
2504
  name: scopedName,
@@ -2235,11 +2526,11 @@ async function handleWorkspaceCommand(name, options, writeGeneratedFiles) {
2235
2526
  triplex: options.triplex ? {} : void 0
2236
2527
  }
2237
2528
  };
2238
- console.log(color__default.cyan("Creating") + ` ${scopedName} in ${targetDir}/${name}...`);
2529
+ console.log(color__default.cyan("Creating") + ` ${scopedName} in ${targetDir}/${packageDirName}...`);
2239
2530
  try {
2240
2531
  const { files } = await workspace.planProject(workspace.resolveProjectPlanInput(projectOptions));
2241
2532
  await writeGeneratedFiles(fullPackagePath, files);
2242
- console.log(color__default.green("\u2713") + ` Created ${scopedName} at ${targetDir}/${name}`);
2533
+ console.log(color__default.green("\u2713") + ` Created ${scopedName} at ${targetDir}/${packageDirName}`);
2243
2534
  process.exit(0);
2244
2535
  } catch (error) {
2245
2536
  console.error(color__default.red("Error:") + " Failed to create package");
@@ -2310,13 +2601,13 @@ async function writeGeneratedFiles(basePath, files) {
2310
2601
  const filePaths = Object.keys(files).sort();
2311
2602
  for (const filePath of filePaths) {
2312
2603
  const fullFilePath = node_path.join(basePath, filePath);
2313
- await promises$1.mkdir(node_path.dirname(fullFilePath), { recursive: true });
2604
+ await promises.mkdir(node_path.dirname(fullFilePath), { recursive: true });
2314
2605
  const file = files[filePath];
2315
2606
  if (file.type === "text") {
2316
- await promises$1.writeFile(fullFilePath, file.content);
2607
+ await promises.writeFile(fullFilePath, file.content);
2317
2608
  } else {
2318
2609
  const response = await undici.fetch(file.url);
2319
- await promises$1.writeFile(fullFilePath, response.body);
2610
+ await promises.writeFile(fullFilePath, response.body);
2320
2611
  }
2321
2612
  }
2322
2613
  }
@@ -2384,7 +2675,8 @@ async function handleSingleWorkspaceCreation(projectOptions, isNonInteractive) {
2384
2675
  const defaultFallbackName = base === "vanilla" ? "vanilla-app" : base === "react" ? "react-app" : "react-three-app";
2385
2676
  projectOptions.name ??= defaultFallbackName;
2386
2677
  const packageManager = workspace.getPackageManagerName(projectOptions.packageManager);
2387
- const projectPath = node_path.join(node_process.cwd(), projectOptions.name);
2678
+ const projectDirName = workspace.getPackageDirectoryName(projectOptions.name);
2679
+ const projectPath = node_path.join(node_process.cwd(), projectDirName);
2388
2680
  const spinner = p__namespace.spinner();
2389
2681
  spinner.start("Creating project...");
2390
2682
  try {
@@ -2393,15 +2685,11 @@ async function handleSingleWorkspaceCreation(projectOptions, isNonInteractive) {
2393
2685
  await writeGeneratedFiles(projectPath, files);
2394
2686
  spinner.stop(color__default.green.inverse(" \u2713 Project created! "));
2395
2687
  if (isNonInteractive) process.exit(0);
2396
- const nextSteps = projectOptions.projectType === "library" ? [
2397
- `cd ${projectOptions.name}`,
2398
- `${packageManager} install`,
2399
- `${packageManager} run build`
2400
- ].join("\n") : [
2401
- `cd ${projectOptions.name}`,
2402
- `${packageManager} install`,
2403
- `${packageManager} run dev`
2404
- ].join("\n");
2688
+ const nextSteps = projectOptions.projectType === "library" ? [`cd ${projectDirName}`, `${packageManager} install`, `${packageManager} run build`].join(
2689
+ "\n"
2690
+ ) : [`cd ${projectDirName}`, `${packageManager} install`, `${packageManager} run dev`].join(
2691
+ "\n"
2692
+ );
2405
2693
  p__namespace.note(nextSteps, "Next steps");
2406
2694
  p__namespace.outro(color__default.green("Happy coding! \u2728"));
2407
2695
  } catch (error) {
@@ -2417,7 +2705,7 @@ async function main() {
2417
2705
  ).option(
2418
2706
  "--template <type>",
2419
2707
  "project template: vanilla, vanilla-js, react, react-js, r3f, r3f-js (default: vanilla)"
2420
- ).option("--linter <type>", "linter: eslint, oxlint, or biome (default: oxlint)").option("--formatter <type>", "formatter: prettier, oxfmt, or biome (default: prettier)").option("--drei", "add @react-three/drei (r3f only)").option("--handle", "add @react-three/handle (r3f only)").option("--leva", "add leva (r3f only)").option("--postprocessing", "add @react-three/postprocessing (r3f only)").option("--rapier", "add @react-three/rapier (r3f only)").option("--xr", "add @react-three/xr (r3f only)").option("--uikit", "add @react-three/uikit (r3f only)").option("--offscreen", "add @react-three/offscreen (r3f only)").option("--zustand", "add zustand (r3f only)").option("--koota", "add koota (r3f only)").option("--triplex", "set up triplex development environment (r3f only)").option("--viverse", "set up viverse deployment (r3f only)").option("--package-manager <manager>", "specify package manager (e.g. npm, yarn, pnpm)").option("--ide <ide>", "IDE files: vscode or none (default: vscode)").option(
2708
+ ).option("--linter <type>", "linter: eslint, oxlint, or biome (default: oxlint)").option("--formatter <type>", "formatter: prettier, oxfmt, or biome (default: prettier)").option("--drei", "add @react-three/drei (r3f only)").option("--handle", "add @react-three/handle (r3f only)").option("--leva", "add leva (r3f only)").option("--postprocessing", "add @react-three/postprocessing (r3f only)").option("--rapier", "add @react-three/rapier (r3f only)").option("--xr", "add @react-three/xr (r3f only)").option("--uikit", "add @react-three/uikit (r3f only)").option("--offscreen", "add @react-three/offscreen (r3f only)").option("--zustand", "add zustand (r3f only)").option("--koota", "add koota (r3f only)").option("--triplex", "set up triplex development environment (r3f only)").option("--viverse", "set up viverse deployment (r3f only)").option("--package-manager <manager>", "specify package manager (e.g. npm, yarn, pnpm@11)").option("--ide <ide>", "IDE files: vscode or none (default: vscode)").option(
2421
2709
  "--pnpm-manage-versions",
2422
2710
  "enable manage-package-manager-versions in pnpm-workspace.yaml (default: true)"
2423
2711
  ).option(
@@ -2431,18 +2719,24 @@ async function main() {
2431
2719
  process.chdir(options.path);
2432
2720
  }
2433
2721
  if (options.clearConfig) {
2434
- clearConfig();
2722
+ workspace.clearConfig();
2435
2723
  console.log("Configuration cleared.");
2436
2724
  process.exit(0);
2437
2725
  }
2438
2726
  if (options.configPath) {
2439
- console.log(getConfigPath());
2727
+ console.log(workspace.getConfigPath());
2440
2728
  process.exit(0);
2441
2729
  }
2442
2730
  if (options.ide && !["vscode", "none"].includes(options.ide)) {
2443
2731
  console.error(color__default.red("Error:") + ' --ide must be "vscode" or "none"');
2444
2732
  process.exit(1);
2445
2733
  }
2734
+ const packageManagerSpec = workspace.parsePackageManagerSpec(options.packageManager);
2735
+ if (options.packageManager && packageManagerSpec == null) {
2736
+ console.error(color__default.red("Error:") + " --package-manager must be npm, yarn, or pnpm");
2737
+ console.log(color__default.dim(" Version specs are allowed, e.g. pnpm@10 or pnpm@11"));
2738
+ process.exit(1);
2739
+ }
2446
2740
  if (name?.startsWith("-")) {
2447
2741
  switch (name) {
2448
2742
  case "--version":
@@ -2454,11 +2748,11 @@ async function main() {
2454
2748
  program.help();
2455
2749
  break;
2456
2750
  case "--clear-config":
2457
- clearConfig();
2751
+ workspace.clearConfig();
2458
2752
  console.log("Configuration cleared.");
2459
2753
  process.exit(0);
2460
2754
  case "--config-path":
2461
- console.log(getConfigPath());
2755
+ console.log(workspace.getConfigPath());
2462
2756
  process.exit(0);
2463
2757
  case "--check":
2464
2758
  await handleCheckCommand();
@@ -2528,7 +2822,7 @@ async function main() {
2528
2822
  viverse: options.viverse ? {} : void 0,
2529
2823
  triplex: options.triplex ? {} : void 0
2530
2824
  },
2531
- packageManager: options.packageManager ? { name: options.packageManager } : void 0,
2825
+ packageManager: packageManagerSpec,
2532
2826
  pnpmManageVersions: options.pnpmManageVersions,
2533
2827
  engine: { name: "node", version: options.nodeVersion ?? "latest" }
2534
2828
  };
@@ -2539,7 +2833,7 @@ async function main() {
2539
2833
  bundler: options.bundler,
2540
2834
  linter: options.linter,
2541
2835
  formatter: options.formatter,
2542
- packageManager: options.packageManager,
2836
+ packageManager: packageManagerSpec,
2543
2837
  ide: options.ide,
2544
2838
  engine: options.nodeVersion ? { name: "node", version: options.nodeVersion } : void 0,
2545
2839
  pnpmManageVersions: options.pnpmManageVersions,