isolate-package 1.29.0 → 1.30.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.
Files changed (29) hide show
  1. package/dist/index.d.mts +7 -1
  2. package/dist/index.mjs +2 -3
  3. package/dist/index.mjs.map +1 -1
  4. package/dist/{isolate-3GcdAuUK.mjs → isolate-CJy3YyKG.mjs} +261 -64
  5. package/dist/isolate-CJy3YyKG.mjs.map +1 -0
  6. package/dist/isolate-bin.mjs +3 -5
  7. package/dist/isolate-bin.mjs.map +1 -1
  8. package/package.json +21 -20
  9. package/src/get-internal-package-names.test.ts +0 -10
  10. package/src/index.ts +6 -0
  11. package/src/isolate.ts +38 -8
  12. package/src/lib/lockfile/helpers/generate-bun-lockfile.test.ts +619 -0
  13. package/src/lib/lockfile/helpers/generate-bun-lockfile.ts +354 -0
  14. package/src/lib/lockfile/helpers/generate-pnpm-lockfile.test.ts +387 -0
  15. package/src/lib/lockfile/helpers/index.ts +1 -0
  16. package/src/lib/lockfile/helpers/pnpm-map-importer.test.ts +183 -0
  17. package/src/lib/lockfile/process-lockfile.test.ts +238 -0
  18. package/src/lib/lockfile/process-lockfile.ts +6 -6
  19. package/src/lib/manifest/adapt-target-package-manifest.ts +6 -4
  20. package/src/lib/manifest/helpers/adapt-internal-package-manifests.test.ts +49 -0
  21. package/src/lib/manifest/helpers/adapt-internal-package-manifests.ts +15 -3
  22. package/src/lib/manifest/helpers/adopt-pnpm-fields-from-root.test.ts +61 -0
  23. package/src/lib/manifest/helpers/adopt-pnpm-fields-from-root.ts +42 -3
  24. package/src/lib/patches/copy-patches.test.ts +49 -11
  25. package/src/lib/patches/copy-patches.ts +38 -17
  26. package/src/lib/types.ts +5 -0
  27. package/src/lib/utils/filter-patched-dependencies.test.ts +1 -11
  28. package/src/testing/setup.ts +13 -0
  29. package/dist/isolate-3GcdAuUK.mjs.map +0 -1
@@ -24,7 +24,6 @@ import path$1 from "path";
24
24
  import { getTsconfig } from "get-tsconfig";
25
25
  import outdent$1 from "outdent";
26
26
  import { globSync } from "glob";
27
-
28
27
  //#region src/lib/logger.ts
29
28
  /**
30
29
  * Map our log levels to consola's numeric levels. Consola levels:
@@ -56,13 +55,11 @@ function setLogLevel(logLevel) {
56
55
  function useLogger() {
57
56
  return _logger;
58
57
  }
59
-
60
58
  //#endregion
61
59
  //#region src/lib/utils/filter-object-undefined.ts
62
60
  function filterObjectUndefined(object) {
63
61
  return Object.fromEntries(Object.entries(object).filter(([_, value]) => value !== void 0));
64
62
  }
65
-
66
63
  //#endregion
67
64
  //#region src/lib/utils/get-package-name.ts
68
65
  /**
@@ -74,7 +71,6 @@ function getPackageName(packageSpec) {
74
71
  /** Regular packages: package@version -> package */
75
72
  return packageSpec.split("@")[0] ?? "";
76
73
  }
77
-
78
74
  //#endregion
79
75
  //#region src/lib/utils/filter-patched-dependencies.ts
80
76
  /**
@@ -115,7 +111,6 @@ function filterPatchedDependencies({ patchedDependencies, targetPackageManifest,
115
111
  log.debug(`Filtered patches: ${includedCount} included, ${excludedCount} excluded`);
116
112
  return Object.keys(filteredPatches).length > 0 ? filteredPatches : void 0;
117
113
  }
118
-
119
114
  //#endregion
120
115
  //#region src/lib/utils/get-dirname.ts
121
116
  /**
@@ -125,7 +120,6 @@ function filterPatchedDependencies({ patchedDependencies, targetPackageManifest,
125
120
  function getDirname(importMetaUrl) {
126
121
  return fileURLToPath(new URL(".", importMetaUrl));
127
122
  }
128
-
129
123
  //#endregion
130
124
  //#region src/lib/utils/get-error-message.ts
131
125
  function getErrorMessage(error) {
@@ -146,13 +140,11 @@ function toErrorWithMessage(maybeError) {
146
140
  return new Error(String(maybeError));
147
141
  }
148
142
  }
149
-
150
143
  //#endregion
151
144
  //#region src/lib/utils/inspect-value.ts
152
145
  function inspectValue(value) {
153
146
  return inspect(value, false, 16, true);
154
147
  }
155
-
156
148
  //#endregion
157
149
  //#region src/lib/utils/is-rush-workspace.ts
158
150
  /**
@@ -162,7 +154,6 @@ function inspectValue(value) {
162
154
  function isRushWorkspace(workspaceRootDir) {
163
155
  return fs$1.existsSync(path.join(workspaceRootDir, "rush.json"));
164
156
  }
165
-
166
157
  //#endregion
167
158
  //#region src/lib/utils/json.ts
168
159
  /** @todo Pass in zod schema and validate */
@@ -182,7 +173,6 @@ async function readTypedJson(filePath) {
182
173
  throw new Error(`Failed to read JSON from ${filePath}: ${getErrorMessage(err)}`, { cause: err });
183
174
  }
184
175
  }
185
-
186
176
  //#endregion
187
177
  //#region src/lib/utils/log-paths.ts
188
178
  function getRootRelativeLogPath(path, rootPath) {
@@ -191,13 +181,11 @@ function getRootRelativeLogPath(path, rootPath) {
191
181
  function getIsolateRelativeLogPath(path, isolatePath) {
192
182
  return join("(isolate)", path.replace(isolatePath, ""));
193
183
  }
194
-
195
184
  //#endregion
196
185
  //#region src/lib/utils/get-major-version.ts
197
186
  function getMajorVersion(version) {
198
187
  return parseInt(version.split(".").at(0) ?? "0", 10);
199
188
  }
200
-
201
189
  //#endregion
202
190
  //#region src/lib/package-manager/names.ts
203
191
  const supportedPackageManagerNames = [
@@ -214,7 +202,6 @@ function getLockfileFileName(name) {
214
202
  case "npm": return "package-lock.json";
215
203
  }
216
204
  }
217
-
218
205
  //#endregion
219
206
  //#region src/lib/package-manager/helpers/infer-from-files.ts
220
207
  function inferFromFiles(workspaceRoot) {
@@ -245,7 +232,6 @@ function inferFromFiles(workspaceRoot) {
245
232
  function getVersion(packageManagerName) {
246
233
  return execSync(`${packageManagerName} --version`).toString().trim();
247
234
  }
248
-
249
235
  //#endregion
250
236
  //#region src/lib/package-manager/helpers/infer-from-manifest.ts
251
237
  function inferFromManifest(workspaceRoot) {
@@ -266,7 +252,6 @@ function inferFromManifest(workspaceRoot) {
266
252
  packageManagerString
267
253
  };
268
254
  }
269
-
270
255
  //#endregion
271
256
  //#region src/lib/package-manager/index.ts
272
257
  let packageManager;
@@ -293,7 +278,6 @@ function shouldUsePnpmPack() {
293
278
  const { name, majorVersion } = usePackageManager();
294
279
  return name === "pnpm" && majorVersion >= 8;
295
280
  }
296
-
297
281
  //#endregion
298
282
  //#region src/lib/utils/pack.ts
299
283
  async function pack(srcDir, dstDir) {
@@ -334,7 +318,6 @@ async function pack(srcDir, dstDir) {
334
318
  */
335
319
  return filePath;
336
320
  }
337
-
338
321
  //#endregion
339
322
  //#region src/lib/utils/unpack.ts
340
323
  async function unpack(filePath, unpackDir) {
@@ -342,7 +325,6 @@ async function unpack(filePath, unpackDir) {
342
325
  fs.createReadStream(filePath).pipe(createGunzip()).pipe(tar.extract(unpackDir)).on("finish", () => resolve()).on("error", (err) => reject(err));
343
326
  });
344
327
  }
345
-
346
328
  //#endregion
347
329
  //#region src/lib/utils/yaml.ts
348
330
  function readTypedYamlSync(filePath) {
@@ -358,7 +340,6 @@ function writeTypedYamlSync(filePath, content) {
358
340
  /** @todo Add some zod validation maybe */
359
341
  fs.writeFileSync(filePath, yaml.stringify(content), "utf-8");
360
342
  }
361
-
362
343
  //#endregion
363
344
  //#region src/lib/config.ts
364
345
  const configDefaults = {
@@ -474,7 +455,186 @@ function resolveConfig(initialConfig) {
474
455
  log.debug("Using configuration:", inspectValue(config));
475
456
  return config;
476
457
  }
477
-
458
+ //#endregion
459
+ //#region src/lib/lockfile/helpers/generate-bun-lockfile.ts
460
+ /**
461
+ * Serialize a value to JSON with trailing commas after every array element and
462
+ * object property, matching Bun's native bun.lock output format.
463
+ */
464
+ function serializeWithTrailingCommas(value, indent = 2) {
465
+ /**
466
+ * Add trailing commas after values that precede a closing bracket/brace.
467
+ * Apply repeatedly because consecutive closing brackets (e.g. ]\n}) need
468
+ * multiple passes — the first pass adds a comma after the inner value, and
469
+ * subsequent passes handle the outer brackets.
470
+ */
471
+ let result = JSON.stringify(value, null, indent);
472
+ let previous;
473
+ do {
474
+ previous = result;
475
+ result = result.replace(/(["\d\w\]}-])\n(\s*[\]}])/g, "$1,\n$2");
476
+ } while (result !== previous);
477
+ return result;
478
+ }
479
+ /**
480
+ * Extract dependency names from a workspace entry, optionally including
481
+ * devDependencies.
482
+ */
483
+ function collectDependencyNames(entry, includeDevDependencies) {
484
+ const names = /* @__PURE__ */ new Set();
485
+ for (const name of Object.keys(entry.dependencies ?? {})) names.add(name);
486
+ for (const name of Object.keys(entry.optionalDependencies ?? {})) names.add(name);
487
+ for (const name of Object.keys(entry.peerDependencies ?? {})) names.add(name);
488
+ if (includeDevDependencies) for (const name of Object.keys(entry.devDependencies ?? {})) names.add(name);
489
+ return [...names];
490
+ }
491
+ /**
492
+ * Check whether a package entry represents a workspace package by examining its
493
+ * identifier string (first element in the entry array).
494
+ */
495
+ function isWorkspacePackageEntry(entry) {
496
+ const ident = entry[0];
497
+ return typeof ident === "string" && ident.includes("@workspace:");
498
+ }
499
+ /**
500
+ * Extract the info object from a packages entry. The position varies by type:
501
+ * - npm packages: [ident, registry, info, checksum] -> index 2
502
+ * - workspace packages: [ident, info] -> index 1
503
+ * - git/github packages: [ident, info, checksum] -> index 1
504
+ *
505
+ * Detection: if the second element is a string (registry URL or checksum), the
506
+ * info object is deeper. Workspace entries have only 2 elements.
507
+ */
508
+ function getPackageInfoObject(entry) {
509
+ if (entry.length <= 1) return void 0;
510
+ /** Workspace entries: [ident, info] */
511
+ if (isWorkspacePackageEntry(entry)) return typeof entry[1] === "object" ? entry[1] : void 0;
512
+ /**
513
+ * npm entries with registry URL: [ident, registryUrl, info, checksum].
514
+ * The second element is a string (the registry URL).
515
+ */
516
+ if (typeof entry[1] === "string") return typeof entry[2] === "object" ? entry[2] : void 0;
517
+ /** git/tarball entries: [ident, info, checksum] */
518
+ return typeof entry[1] === "object" ? entry[1] : void 0;
519
+ }
520
+ /**
521
+ * Recursively collect all package keys that are required, starting from a set
522
+ * of direct dependency names and walking through their transitive dependencies
523
+ * in the packages section.
524
+ */
525
+ function collectRequiredPackages(directDependencyNames, packages) {
526
+ const required = /* @__PURE__ */ new Set();
527
+ const queue = [...directDependencyNames];
528
+ while (queue.length > 0) {
529
+ const name = queue.pop();
530
+ if (required.has(name)) continue;
531
+ const entry = packages[name];
532
+ if (!entry) continue;
533
+ required.add(name);
534
+ const info = getPackageInfoObject(entry);
535
+ if (!info) continue;
536
+ /** Walk transitive dependencies from the info object */
537
+ for (const depField of [
538
+ "dependencies",
539
+ "optionalDependencies",
540
+ "peerDependencies"
541
+ ]) {
542
+ const deps = info[depField];
543
+ if (deps && typeof deps === "object") {
544
+ for (const depName of Object.keys(deps)) if (!required.has(depName)) queue.push(depName);
545
+ }
546
+ }
547
+ }
548
+ return required;
549
+ }
550
+ async function generateBunLockfile({ workspaceRootDir, targetPackageDir, isolateDir, internalDepPackageNames, packagesRegistry, includeDevDependencies }) {
551
+ const log = useLogger();
552
+ log.debug("Generating Bun lockfile...");
553
+ const lockfilePath = path.join(workspaceRootDir, "bun.lock");
554
+ try {
555
+ if (!fs.existsSync(lockfilePath)) throw new Error(`Failed to find bun.lock at ${lockfilePath}`);
556
+ const lockfile = readTypedJsonSync(lockfilePath);
557
+ /** Compute workspace keys for the target and internal deps */
558
+ const targetWorkspaceKey = path.relative(workspaceRootDir, targetPackageDir).split(path.sep).join(path.posix.sep);
559
+ const internalDepWorkspaceKeys = /* @__PURE__ */ new Map();
560
+ for (const name of internalDepPackageNames) {
561
+ /** Normalize to POSIX separators for matching bun.lock workspace keys */
562
+ const workspaceKey = got(packagesRegistry, name).rootRelativeDir.split(path.sep).join(path.posix.sep);
563
+ internalDepWorkspaceKeys.set(name, workspaceKey);
564
+ }
565
+ /** Build the filtered workspaces object */
566
+ const filteredWorkspaces = {};
567
+ /** Remap the target workspace to root ("") */
568
+ const targetEntry = lockfile.workspaces[targetWorkspaceKey];
569
+ if (!targetEntry) throw new Error(`Target workspace "${targetWorkspaceKey}" not found in bun.lock. Available workspaces: ${Object.keys(lockfile.workspaces).join(", ")}`);
570
+ {
571
+ const entry = { ...targetEntry };
572
+ if (!includeDevDependencies) delete entry.devDependencies;
573
+ filteredWorkspaces[""] = entry;
574
+ }
575
+ /** Add internal dependency workspaces */
576
+ for (const [, workspaceKey] of internalDepWorkspaceKeys) {
577
+ const entry = lockfile.workspaces[workspaceKey];
578
+ if (entry) {
579
+ /** Strip devDependencies from internal deps */
580
+ const filtered = { ...entry };
581
+ delete filtered.devDependencies;
582
+ filteredWorkspaces[workspaceKey] = filtered;
583
+ }
584
+ }
585
+ /**
586
+ * Collect all dependency names from filtered workspace entries, then
587
+ * recursively walk through the packages section to find all transitive
588
+ * dependencies.
589
+ */
590
+ const directDependencyNames = /* @__PURE__ */ new Set();
591
+ for (const [workspaceKey, entry] of Object.entries(filteredWorkspaces)) {
592
+ const names = collectDependencyNames(entry, workspaceKey === "" && includeDevDependencies);
593
+ for (const name of names) directDependencyNames.add(name);
594
+ }
595
+ const requiredPackages = collectRequiredPackages(directDependencyNames, lockfile.packages);
596
+ /** Also include workspace package entries for kept internal deps */
597
+ const keptInternalDepNames = new Set(internalDepPackageNames);
598
+ /** Filter the packages section */
599
+ const filteredPackages = {};
600
+ for (const [key, entry] of Object.entries(lockfile.packages)) if (requiredPackages.has(key)) {
601
+ /**
602
+ * Skip workspace entries for packages that are not in our kept internal
603
+ * deps. This removes workspace references to packages outside the
604
+ * isolate.
605
+ */
606
+ if (isWorkspacePackageEntry(entry) && !keptInternalDepNames.has(key)) continue;
607
+ filteredPackages[key] = entry;
608
+ }
609
+ /** Also make sure workspace entries for kept internal deps are included */
610
+ for (const name of keptInternalDepNames) if (!filteredPackages[name] && lockfile.packages[name]) filteredPackages[name] = lockfile.packages[name];
611
+ /** Build the output lockfile preserving metadata */
612
+ const outputLockfile = {
613
+ lockfileVersion: lockfile.lockfileVersion,
614
+ workspaces: filteredWorkspaces,
615
+ packages: filteredPackages
616
+ };
617
+ if (lockfile.overrides && Object.keys(lockfile.overrides).length > 0) outputLockfile.overrides = lockfile.overrides;
618
+ if (lockfile.trustedDependencies && lockfile.trustedDependencies.length > 0) {
619
+ /** Filter to only include trusted dependencies that are in the output */
620
+ const outputTrusted = lockfile.trustedDependencies.filter((name) => filteredPackages[name] !== void 0);
621
+ if (outputTrusted.length > 0) outputLockfile.trustedDependencies = outputTrusted;
622
+ }
623
+ if (lockfile.patchedDependencies && Object.keys(lockfile.patchedDependencies).length > 0) {
624
+ /** Filter to only include patches for packages in the output */
625
+ const outputPatches = {};
626
+ for (const [spec, patchPath] of Object.entries(lockfile.patchedDependencies)) if (filteredPackages[getPackageName(spec)] !== void 0) outputPatches[spec] = patchPath;
627
+ if (Object.keys(outputPatches).length > 0) outputLockfile.patchedDependencies = outputPatches;
628
+ }
629
+ const outputPath = path.join(isolateDir, "bun.lock");
630
+ /** Append trailing newline to match Bun's native output format */
631
+ await fs.writeFile(outputPath, serializeWithTrailingCommas(outputLockfile) + "\n");
632
+ log.debug("Created lockfile at", outputPath);
633
+ } catch (err) {
634
+ log.error(`Failed to generate lockfile: ${getErrorMessage(err)}`);
635
+ throw err;
636
+ }
637
+ }
478
638
  //#endregion
479
639
  //#region src/lib/lockfile/helpers/load-npm-config.ts
480
640
  async function loadNpmConfig({ npmPath }) {
@@ -487,7 +647,6 @@ async function loadNpmConfig({ npmPath }) {
487
647
  await config.load();
488
648
  return config;
489
649
  }
490
-
491
650
  //#endregion
492
651
  //#region src/lib/lockfile/helpers/generate-npm-lockfile.ts
493
652
  /**
@@ -514,7 +673,6 @@ async function generateNpmLockfile({ workspaceRootDir, isolateDir }) {
514
673
  throw err;
515
674
  }
516
675
  }
517
-
518
676
  //#endregion
519
677
  //#region src/lib/lockfile/helpers/pnpm-map-importer.ts
520
678
  /** Convert dependency links */
@@ -545,7 +703,6 @@ function pnpmMapDependenciesLinks(importerPath, def, directoryByPackageName) {
545
703
  return [[key, relativePath.startsWith(".") ? `link:${relativePath}` : `link:./${relativePath}`]];
546
704
  }));
547
705
  }
548
-
549
706
  //#endregion
550
707
  //#region src/lib/lockfile/helpers/generate-pnpm-lockfile.ts
551
708
  async function generatePnpmLockfile({ workspaceRootDir, targetPackageDir, isolateDir, internalDepPackageNames, packagesRegistry, targetPackageManifest, majorVersion, includeDevDependencies, patchedDependencies }) {
@@ -616,7 +773,6 @@ async function generatePnpmLockfile({ workspaceRootDir, targetPackageDir, isolat
616
773
  throw err;
617
774
  }
618
775
  }
619
-
620
776
  //#endregion
621
777
  //#region src/lib/lockfile/helpers/generate-yarn-lockfile.ts
622
778
  /**
@@ -645,7 +801,6 @@ async function generateYarnLockfile({ workspaceRootDir, isolateDir }) {
645
801
  throw err;
646
802
  }
647
803
  }
648
-
649
804
  //#endregion
650
805
  //#region src/lib/lockfile/process-lockfile.ts
651
806
  /**
@@ -702,12 +857,14 @@ async function processLockfile({ workspaceRootDir, packagesRegistry, isolateDir,
702
857
  });
703
858
  break;
704
859
  case "bun":
705
- log.warn(`Ouput lockfiles for Bun are not yet supported. Using NPM for output`);
706
- await generateNpmLockfile({
860
+ await generateBunLockfile({
707
861
  workspaceRootDir,
708
- isolateDir
862
+ targetPackageDir,
863
+ isolateDir,
864
+ internalDepPackageNames,
865
+ packagesRegistry,
866
+ includeDevDependencies: config.includeDevDependencies
709
867
  });
710
- usedFallbackToNpm = true;
711
868
  break;
712
869
  default:
713
870
  log.warn(`Unexpected package manager ${name}. Using NPM for output`);
@@ -719,7 +876,6 @@ async function processLockfile({ workspaceRootDir, packagesRegistry, isolateDir,
719
876
  }
720
877
  return usedFallbackToNpm;
721
878
  }
722
-
723
879
  //#endregion
724
880
  //#region src/lib/manifest/io.ts
725
881
  async function readManifest(packageDir) {
@@ -728,7 +884,6 @@ async function readManifest(packageDir) {
728
884
  async function writeManifest(outputDir, manifest) {
729
885
  await fs.writeFile(path.join(outputDir, "package.json"), JSON.stringify(manifest, null, 2));
730
886
  }
731
-
732
887
  //#endregion
733
888
  //#region src/lib/manifest/helpers/patch-internal-entries.ts
734
889
  function patchInternalEntries(dependencies, packagesRegistry, parentRootRelativeDir) {
@@ -743,7 +898,6 @@ function patchInternalEntries(dependencies, packagesRegistry, parentRootRelative
743
898
  } else return [key, value];
744
899
  }));
745
900
  }
746
-
747
901
  //#endregion
748
902
  //#region src/lib/manifest/helpers/adapt-manifest-internal-deps.ts
749
903
  /**
@@ -759,7 +913,6 @@ function adaptManifestInternalDeps({ manifest, packagesRegistry, parentRootRelat
759
913
  devDependencies: devDependencies ? patchInternalEntries(devDependencies, packagesRegistry, parentRootRelativeDir) : void 0
760
914
  };
761
915
  }
762
-
763
916
  //#endregion
764
917
  //#region src/lib/manifest/helpers/resolve-catalog-dependencies.ts
765
918
  /**
@@ -793,7 +946,6 @@ async function resolveCatalogDependencies(dependencies, workspaceRootDir) {
793
946
  }
794
947
  return resolved;
795
948
  }
796
-
797
949
  //#endregion
798
950
  //#region src/lib/manifest/helpers/adapt-internal-package-manifests.ts
799
951
  /**
@@ -807,12 +959,20 @@ async function adaptInternalPackageManifests({ internalPackageNames, packagesReg
807
959
  const { manifest, rootRelativeDir } = got(packagesRegistry, packageName);
808
960
  /** Dev dependencies are never included for internal deps */
809
961
  const strippedManifest = omit(manifest, ["devDependencies"]);
962
+ /**
963
+ * Strip the `prepare` script because it runs during `pnpm install` and
964
+ * typically depends on devDependency binaries (e.g. tsdown, del-cli)
965
+ * which are not available in the isolated output. Other lifecycle
966
+ * scripts like `postinstall` are preserved because they handle runtime
967
+ * setup (e.g. Prisma client generation).
968
+ */
969
+ if (strippedManifest.scripts) strippedManifest.scripts = omit(strippedManifest.scripts, ["prepare"]);
810
970
  /** Resolve catalog dependencies before adapting internal deps */
811
971
  const manifestWithResolvedCatalogs = {
812
972
  ...strippedManifest,
813
973
  dependencies: await resolveCatalogDependencies(strippedManifest.dependencies, workspaceRootDir)
814
974
  };
815
- const outputManifest = packageManager.name === "pnpm" && !forceNpm ? manifestWithResolvedCatalogs : adaptManifestInternalDeps({
975
+ const outputManifest = (packageManager.name === "pnpm" || packageManager.name === "bun") && !forceNpm ? manifestWithResolvedCatalogs : adaptManifestInternalDeps({
816
976
  manifest: manifestWithResolvedCatalogs,
817
977
  packagesRegistry,
818
978
  parentRootRelativeDir: rootRelativeDir
@@ -820,17 +980,36 @@ async function adaptInternalPackageManifests({ internalPackageNames, packagesReg
820
980
  await writeManifest(path.join(isolateDir, rootRelativeDir), outputManifest);
821
981
  }));
822
982
  }
823
-
824
983
  //#endregion
825
984
  //#region src/lib/manifest/helpers/adopt-pnpm-fields-from-root.ts
826
985
  /**
827
- * Adopts the `pnpm` fields from the root package manifest. Currently it takes
828
- * overrides, onlyBuiltDependencies, and ignoredBuiltDependencies, because these
829
- * are typically workspace-level configuration settings.
986
+ * Adopts workspace-level fields from the root package manifest. For pnpm this
987
+ * reads overrides, onlyBuiltDependencies, and ignoredBuiltDependencies from the
988
+ * `pnpm` key. For Bun it reads `overrides` from the top level.
830
989
  */
831
990
  async function adoptPnpmFieldsFromRoot(targetPackageManifest, workspaceRootDir) {
832
991
  if (isRushWorkspace(workspaceRootDir)) return targetPackageManifest;
833
- const { overrides, onlyBuiltDependencies, ignoredBuiltDependencies } = (await readTypedJson(path$1.join(workspaceRootDir, "package.json"))).pnpm || {};
992
+ const rootPackageManifest = await readTypedJson(path$1.join(workspaceRootDir, "package.json"));
993
+ if (usePackageManager().name === "bun") return adoptBunFieldsFromRoot(targetPackageManifest, rootPackageManifest);
994
+ return adoptPnpmFieldsOnly(targetPackageManifest, rootPackageManifest);
995
+ }
996
+ /** Adopt Bun's top-level overrides from the root manifest */
997
+ function adoptBunFieldsFromRoot(targetPackageManifest, rootPackageManifest) {
998
+ /**
999
+ * Bun supports `overrides` at the top level of package.json (same as npm).
1000
+ * Read from the root manifest and set them on the output manifest so that
1001
+ * `bun install --frozen-lockfile` succeeds.
1002
+ */
1003
+ const overrides = rootPackageManifest["overrides"];
1004
+ if (!overrides) return targetPackageManifest;
1005
+ return {
1006
+ ...targetPackageManifest,
1007
+ overrides
1008
+ };
1009
+ }
1010
+ /** Adopt pnpm-specific fields from the root manifest */
1011
+ function adoptPnpmFieldsOnly(targetPackageManifest, rootPackageManifest) {
1012
+ const { overrides, onlyBuiltDependencies, ignoredBuiltDependencies } = rootPackageManifest.pnpm || {};
834
1013
  /** If no pnpm fields are present, return the original manifest */
835
1014
  if (!overrides && !onlyBuiltDependencies && !ignoredBuiltDependencies) return targetPackageManifest;
836
1015
  const pnpmConfig = {};
@@ -842,7 +1021,6 @@ async function adoptPnpmFieldsFromRoot(targetPackageManifest, workspaceRootDir)
842
1021
  pnpm: pnpmConfig
843
1022
  };
844
1023
  }
845
-
846
1024
  //#endregion
847
1025
  //#region src/lib/manifest/adapt-target-package-manifest.ts
848
1026
  /**
@@ -863,7 +1041,7 @@ async function adaptTargetPackageManifest({ manifest, packagesRegistry, workspac
863
1041
  dependencies: await resolveCatalogDependencies(inputManifest.dependencies, workspaceRootDir)
864
1042
  };
865
1043
  return {
866
- ...packageManager.name === "pnpm" && !forceNpm ? await adoptPnpmFieldsFromRoot(manifestWithResolvedCatalogs, workspaceRootDir) : adaptManifestInternalDeps({
1044
+ ...(packageManager.name === "pnpm" || packageManager.name === "bun") && !forceNpm ? await adoptPnpmFieldsFromRoot(manifestWithResolvedCatalogs, workspaceRootDir) : adaptManifestInternalDeps({
867
1045
  manifest: manifestWithResolvedCatalogs,
868
1046
  packagesRegistry
869
1047
  }),
@@ -871,7 +1049,6 @@ async function adaptTargetPackageManifest({ manifest, packagesRegistry, workspac
871
1049
  scripts: pickFromScripts ? pick(manifest.scripts ?? {}, pickFromScripts) : omitFromScripts ? omit(manifest.scripts ?? {}, omitFromScripts) : {}
872
1050
  };
873
1051
  }
874
-
875
1052
  //#endregion
876
1053
  //#region src/lib/manifest/validate-manifest.ts
877
1054
  /** Maps field names to their documentation URLs */
@@ -907,7 +1084,6 @@ function validateManifestMandatoryFields(manifest, packagePath, requireFilesFiel
907
1084
  }
908
1085
  log.debug(`Validated mandatory fields for package at ${packagePath}`);
909
1086
  }
910
-
911
1087
  //#endregion
912
1088
  //#region src/lib/output/get-build-output-dir.ts
913
1089
  async function getBuildOutputDir({ targetPackageDir, buildDirName, tsconfigPath }) {
@@ -932,7 +1108,6 @@ async function getBuildOutputDir({ targetPackageDir, buildDirName, tsconfigPath
932
1108
  `);
933
1109
  }
934
1110
  }
935
-
936
1111
  //#endregion
937
1112
  //#region src/lib/output/pack-dependencies.ts
938
1113
  /**
@@ -960,7 +1135,6 @@ async function packDependencies({ packagesRegistry, internalPackageNames, packDe
960
1135
  }
961
1136
  return packedFileByName;
962
1137
  }
963
-
964
1138
  //#endregion
965
1139
  //#region src/lib/output/process-build-output-files.ts
966
1140
  const TIMEOUT_MS = 5e3;
@@ -978,7 +1152,6 @@ async function processBuildOutputFiles({ targetPackageDir, tmpDir, isolateDir })
978
1152
  await unpack(packedFilePath, unpackDir);
979
1153
  await fs.copy(path.join(unpackDir, "package"), isolateDir);
980
1154
  }
981
-
982
1155
  //#endregion
983
1156
  //#region src/lib/output/unpack-dependencies.ts
984
1157
  async function unpackDependencies(packedFilesByName, packagesRegistry, tmpDir, isolateDir) {
@@ -994,21 +1167,28 @@ async function unpackDependencies(packedFilesByName, packagesRegistry, tmpDir, i
994
1167
  log.debug(`Moved package files to ${getIsolateRelativeLogPath(destinationDir, isolateDir)}`);
995
1168
  }));
996
1169
  }
997
-
998
1170
  //#endregion
999
1171
  //#region src/lib/patches/copy-patches.ts
1000
1172
  async function copyPatches({ workspaceRootDir, targetPackageManifest, isolateDir, includeDevDependencies }) {
1001
1173
  const log = useLogger();
1174
+ const { name: packageManagerName } = usePackageManager();
1002
1175
  let patchedDependencies;
1003
- try {
1176
+ /**
1177
+ * Only try reading pnpm-workspace.yaml for pnpm workspaces. Bun workspaces
1178
+ * don't have this file and the warning would be noisy.
1179
+ */
1180
+ if (packageManagerName === "pnpm") try {
1004
1181
  patchedDependencies = readTypedYamlSync(path.join(workspaceRootDir, "pnpm-workspace.yaml"))?.patchedDependencies;
1005
1182
  } catch (error) {
1006
1183
  log.warn(`Could not read pnpm-workspace.yaml: ${error instanceof Error ? error.message : String(error)}`);
1007
1184
  }
1008
1185
  if (!patchedDependencies || Object.keys(patchedDependencies).length === 0) {
1009
- log.debug("No patched dependencies found in pnpm-workspace.yaml; Falling back to workspace root package.json");
1186
+ if (packageManagerName === "pnpm") log.debug("No patched dependencies found in pnpm-workspace.yaml; Falling back to workspace root package.json");
1187
+ else log.debug("Reading patched dependencies from workspace root package.json");
1010
1188
  try {
1011
- patchedDependencies = (await readTypedJson(path.join(workspaceRootDir, "package.json")))?.pnpm?.patchedDependencies;
1189
+ const workspaceRootManifest = await readTypedJson(path.join(workspaceRootDir, "package.json"));
1190
+ /** PNPM stores patches under pnpm.patchedDependencies, Bun at the top level */
1191
+ patchedDependencies = workspaceRootManifest?.pnpm?.patchedDependencies ?? workspaceRootManifest?.patchedDependencies;
1012
1192
  } catch (error) {
1013
1193
  log.warn(`Could not read workspace root package.json: ${error instanceof Error ? error.message : String(error)}`);
1014
1194
  }
@@ -1024,8 +1204,11 @@ async function copyPatches({ workspaceRootDir, targetPackageManifest, isolateDir
1024
1204
  includeDevDependencies
1025
1205
  });
1026
1206
  if (!filteredPatches) return {};
1027
- /** Read the lockfile to get the hashes for each patch */
1028
- const lockfilePatchedDependencies = await readLockfilePatchedDependencies(workspaceRootDir);
1207
+ /**
1208
+ * Read the pnpm lockfile to get patch hashes. Bun doesn't store hashes in
1209
+ * its lockfile so we skip this for Bun.
1210
+ */
1211
+ const lockfilePatchedDependencies = packageManagerName === "pnpm" ? await readLockfilePatchedDependencies(workspaceRootDir) : void 0;
1029
1212
  const copiedPatches = {};
1030
1213
  for (const [packageSpec, patchPath] of Object.entries(filteredPatches)) {
1031
1214
  const sourcePatchPath = path.resolve(workspaceRootDir, patchPath);
@@ -1039,7 +1222,7 @@ async function copyPatches({ workspaceRootDir, targetPackageManifest, isolateDir
1039
1222
  await fs.copy(sourcePatchPath, targetPatchPath);
1040
1223
  log.debug(`Copied patch for ${packageSpec}: ${patchPath}`);
1041
1224
  const hash = (lockfilePatchedDependencies?.[packageSpec])?.hash ?? "";
1042
- if (!hash) log.warn(`No hash found for patch ${packageSpec} in lockfile`);
1225
+ if (packageManagerName === "pnpm" && !hash) log.warn(`No hash found for patch ${packageSpec} in lockfile`);
1043
1226
  copiedPatches[packageSpec] = {
1044
1227
  path: patchPath,
1045
1228
  hash
@@ -1063,7 +1246,6 @@ async function readLockfilePatchedDependencies(workspaceRootDir) {
1063
1246
  return;
1064
1247
  }
1065
1248
  }
1066
-
1067
1249
  //#endregion
1068
1250
  //#region src/lib/registry/helpers/find-packages-globs.ts
1069
1251
  /**
@@ -1102,7 +1284,6 @@ function findPackagesGlobs(workspaceRootDir) {
1102
1284
  }
1103
1285
  }
1104
1286
  }
1105
-
1106
1287
  //#endregion
1107
1288
  //#region src/lib/registry/create-packages-registry.ts
1108
1289
  /**
@@ -1137,7 +1318,6 @@ function listWorkspacePackages(workspacePackagesOverride, workspaceRootDir) {
1137
1318
  if (isRushWorkspace(workspaceRootDir)) return readTypedJsonSync(path.join(workspaceRootDir, "rush.json")).projects.map(({ projectFolder }) => projectFolder);
1138
1319
  else return (workspacePackagesOverride ?? findPackagesGlobs(workspaceRootDir)).flatMap((glob) => globSync(glob, { cwd: workspaceRootDir })).filter((dir) => fs.lstatSync(path.join(workspaceRootDir, dir)).isDirectory());
1139
1320
  }
1140
-
1141
1321
  //#endregion
1142
1322
  //#region src/lib/registry/list-internal-packages.ts
1143
1323
  /**
@@ -1182,7 +1362,6 @@ function listInternalPackages(manifest, packagesRegistry, { includeDevDependenci
1182
1362
  const result = collectInternalPackages(manifest, packagesRegistry, includeDevDependencies, /* @__PURE__ */ new Set(), new Set(manifest.name ? [manifest.name] : []));
1183
1363
  return [...new Set(result)];
1184
1364
  }
1185
-
1186
1365
  //#endregion
1187
1366
  //#region src/isolate.ts
1188
1367
  const __dirname = getDirname(import.meta.url);
@@ -1265,7 +1444,7 @@ function createIsolator(config) {
1265
1444
  config
1266
1445
  });
1267
1446
  await writeManifest(isolateDir, outputManifest);
1268
- const copiedPatches = packageManager.name === "pnpm" && !config.forceNpm ? await copyPatches({
1447
+ const copiedPatches = (packageManager.name === "pnpm" || packageManager.name === "bun") && !config.forceNpm ? await copyPatches({
1269
1448
  workspaceRootDir,
1270
1449
  targetPackageManifest: outputManifest,
1271
1450
  isolateDir,
@@ -1288,12 +1467,17 @@ function createIsolator(config) {
1288
1467
  if (hasCopiedPatches || usedFallbackToNpm) {
1289
1468
  const manifest = await readManifest(isolateDir);
1290
1469
  if (hasCopiedPatches) {
1291
- if (!manifest.pnpm) manifest.pnpm = {};
1292
1470
  /**
1293
1471
  * Extract just the paths for the manifest (lockfile needs full
1294
- * PatchFile)
1472
+ * PatchFile). PNPM stores patches under pnpm.patchedDependencies, Bun
1473
+ * at the top level.
1295
1474
  */
1296
- manifest.pnpm.patchedDependencies = Object.fromEntries(Object.entries(copiedPatches).map(([spec, patchFile]) => [spec, patchFile.path]));
1475
+ const patchEntries = Object.fromEntries(Object.entries(copiedPatches).map(([spec, patchFile]) => [spec, patchFile.path]));
1476
+ if (packageManager.name === "bun") manifest.patchedDependencies = patchEntries;
1477
+ else {
1478
+ if (!manifest.pnpm) manifest.pnpm = {};
1479
+ manifest.pnpm.patchedDependencies = patchEntries;
1480
+ }
1297
1481
  log.debug(`Added ${Object.keys(copiedPatches).length} patches to isolated package.json`);
1298
1482
  }
1299
1483
  if (usedFallbackToNpm) manifest.packageManager = `npm@${getVersion("npm")}`;
@@ -1314,6 +1498,12 @@ function createIsolator(config) {
1314
1498
  const packages = packagesFolderNames.map((x) => path.join(x, "/*"));
1315
1499
  writeTypedYamlSync(path.join(isolateDir, "pnpm-workspace.yaml"), { packages });
1316
1500
  } else fs.copyFileSync(path.join(workspaceRootDir, "pnpm-workspace.yaml"), path.join(isolateDir, "pnpm-workspace.yaml"));
1501
+ if (packageManager.name === "bun" && !config.forceNpm) {
1502
+ /** Add workspaces field to the manifest so Bun treats the isolate as a workspace */
1503
+ const manifest = await readManifest(isolateDir);
1504
+ manifest.workspaces = unique(internalPackageNames.map((name) => path.parse(got(packagesRegistry, name).rootRelativeDir).dir)).map((x) => path.join(x, "/*"));
1505
+ await writeManifest(isolateDir, manifest);
1506
+ }
1317
1507
  /**
1318
1508
  * If there is an .npmrc file in the workspace root, copy it to the isolate
1319
1509
  * because the settings there could affect how the lockfile is resolved.
@@ -1326,6 +1516,13 @@ function createIsolator(config) {
1326
1516
  fs.copyFileSync(npmrcPath, path.join(isolateDir, ".npmrc"));
1327
1517
  log.debug("Copied .npmrc file to the isolate output");
1328
1518
  }
1519
+ if (packageManager.name === "bun" && !config.forceNpm) {
1520
+ const bunfigPath = path.join(workspaceRootDir, "bunfig.toml");
1521
+ if (fs.existsSync(bunfigPath)) {
1522
+ fs.copyFileSync(bunfigPath, path.join(isolateDir, "bunfig.toml"));
1523
+ log.debug("Copied bunfig.toml file to the isolate output");
1524
+ }
1525
+ }
1329
1526
  /**
1330
1527
  * Clean up. Only do this when things succeed, so we can look at the temp
1331
1528
  * folder in case something goes wrong.
@@ -1340,7 +1537,7 @@ function createIsolator(config) {
1340
1537
  async function isolate(config) {
1341
1538
  return createIsolator(config)();
1342
1539
  }
1343
-
1344
1540
  //#endregion
1345
1541
  export { loadConfigFromFile as a, detectPackageManager as c, defineConfig as i, readTypedJson as l, listInternalPackages as n, resolveConfig as o, createPackagesRegistry as r, resolveWorkspacePaths as s, isolate as t, filterObjectUndefined as u };
1346
- //# sourceMappingURL=isolate-3GcdAuUK.mjs.map
1542
+
1543
+ //# sourceMappingURL=isolate-CJy3YyKG.mjs.map