bun-nested-workspace 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,7 +113,7 @@ A package version patched by both the host and the nested repository is an error
113
113
  ### Lockfile
114
114
 
115
115
  The nested lockfile entries replace the host entries, so the nested packages keep their versions.
116
- When a replaced entry no longer satisfies a dependency of a host package, the host package keeps its previous resolution as a nested lockfile entry.
116
+ When a replaced entry no longer satisfies a dependency of a host package, the host package keeps its previous resolution, and the nested packages keep theirs.
117
117
  When neither satisfies the range, which Bun lockfiles occasionally contain, the dependency gets the highest satisfying version recorded in either lockfile.
118
118
  Ranges are checked as `bun install` applies them: an override replaces the declared range, and an `npm:` alias requires the package it names.
119
119
  `bun install` then resolves new dependencies of host packages and drops unused entries.
@@ -121,7 +121,7 @@ Ranges are checked as `bun install` applies them: an override replaces the decla
121
121
  After the install, the sync compares the direct dependencies of host packages with the original host lockfile.
122
122
  A dependency may only change to the version that the nested lockfile hoists to its top level, and only within the host range.
123
123
  Any other change means either that the host lockfile did not pin the host dependencies, for example after an edit of a host `package.json` without `bun install`, or that the lockfile cannot record the host version next to the nested one.
124
- The latter happens to a host workspace without a name, because Bun records workspace-specific versions under the workspace name; name the workspace or align its range with the nested version.
124
+ The latter happens to a host workspace other than the root without a name, because Bun records workspace-specific versions under the workspace name; name the workspace or align its range with the nested version.
125
125
 
126
126
  Only `bun.lock` with `lockfileVersion` 1 or 2 is supported, because the lockfile format is internal to Bun.
127
127
 
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { a as syncNestedWorkspace, i as loadNestedWorkspace, n as checkNestedWorkspace, r as isInSync, t as BunInstallError, y as getErrorMessage } from "./workspace-BzA7ccrb.mjs";
2
+ import { a as syncNestedWorkspace, i as loadNestedWorkspace, n as checkNestedWorkspace, r as isInSync, t as BunInstallError, y as getErrorMessage } from "./workspace-ChlHn5I8.mjs";
3
3
  // src/cli.ts
4
4
  const usage = `Usage:
5
5
  bun-nested-workspace [bun install options] Sync package.json and bun.lock, then run bun install
package/dist/index.d.mts CHANGED
@@ -194,7 +194,9 @@ export declare function applyManifestToLockfile(lock: Lockfile, manifest: Packag
194
194
  * A root entry overridden by the nested lockfile might not satisfy a dependency of a host package:
195
195
  * such a dependency gets back its previous host resolution as a nested entry, so `bun install` keeps the host version
196
196
  * instead of resolving the range again.
197
- * A host workspace without a name cannot have nested entries, so `bun install` resolves its dependency again.
197
+ * A dependency of the host root package gets it back at the top level instead, as `keepHostRootResolutions` describes.
198
+ * A host workspace other than the root without a name cannot have nested entries,
199
+ * so `bun install` resolves its dependency again.
198
200
  * When the previous host resolution does not satisfy the range either, which Bun lockfiles occasionally contain,
199
201
  * the dependency gets the highest satisfying version recorded in either lockfile.
200
202
  * Catalog dependencies are checked against the catalogs of the host lockfile, so apply the synced root fields
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export { t as BunInstallError, d as applyManifestToLockfile, n as checkNestedWorkspace, g as diffManifests, o as findDirectDependencyMismatches, s as findOwnDependencyChanges, c as findTransitiveMismatches, l as findUncoveredWorkspaces, u as findWorkspaceMismatches, r as isInSync, i as loadNestedWorkspace, f as mergeLockfiles, p as parseLockfile, m as relocateLockfile, h as supportedLockfileVersions, _ as syncManifest, a as syncNestedWorkspace, v as syncedFields } from "./workspace-BzA7ccrb.mjs";
1
+ export { t as BunInstallError, d as applyManifestToLockfile, n as checkNestedWorkspace, g as diffManifests, o as findDirectDependencyMismatches, s as findOwnDependencyChanges, c as findTransitiveMismatches, l as findUncoveredWorkspaces, u as findWorkspaceMismatches, r as isInSync, i as loadNestedWorkspace, f as mergeLockfiles, p as parseLockfile, m as relocateLockfile, h as supportedLockfileVersions, _ as syncManifest, a as syncNestedWorkspace, v as syncedFields } from "./workspace-ChlHn5I8.mjs";
@@ -557,7 +557,9 @@ function applyManifestToLockfile(lock, manifest) {
557
557
  * A root entry overridden by the nested lockfile might not satisfy a dependency of a host package:
558
558
  * such a dependency gets back its previous host resolution as a nested entry, so `bun install` keeps the host version
559
559
  * instead of resolving the range again.
560
- * A host workspace without a name cannot have nested entries, so `bun install` resolves its dependency again.
560
+ * A dependency of the host root package gets it back at the top level instead, as `keepHostRootResolutions` describes.
561
+ * A host workspace other than the root without a name cannot have nested entries,
562
+ * so `bun install` resolves its dependency again.
561
563
  * When the previous host resolution does not satisfy the range either, which Bun lockfiles occasionally contain,
562
564
  * the dependency gets the highest satisfying version recorded in either lockfile.
563
565
  * Catalog dependencies are checked against the catalogs of the host lockfile, so apply the synced root fields
@@ -571,16 +573,16 @@ function mergeLockfiles(lock, nestedLock, dir) {
571
573
  ...nestedLock.packages
572
574
  };
573
575
  const hostWorkspaces = Object.entries(lock.workspaces).filter(([workspacePath]) => !isNestedPath(workspacePath, dir));
574
- const dependents = [...hostWorkspaces.map(([, workspace]) => ({
576
+ if (lock.workspaces[""]) keepHostRootResolutions(lock, nestedLock, packages);
577
+ const dependents = [...hostWorkspaces.flatMap(([workspacePath, workspace]) => workspacePath && workspace.name ? [{
575
578
  key: workspace.name,
576
579
  dependencies: getWorkspaceDependencies(workspace)
577
- })), ...Object.keys(packages).map((key) => ({
580
+ }] : []), ...Object.keys(packages).map((key) => ({
578
581
  key,
579
582
  dependencies: getPackageDependencies(packages[key])
580
583
  }))];
581
584
  for (let index = 0; index < dependents.length; index++) {
582
585
  const { key, dependencies } = dependents[index];
583
- if (key === void 0) continue;
584
586
  for (const [dependency, specifier] of Object.entries(dependencies)) {
585
587
  const requirement = getRequirement(lock, dependency, specifier);
586
588
  const resolved = resolveDependencyLockKey(key, dependency, packages);
@@ -606,6 +608,63 @@ function mergeLockfiles(lock, nestedLock, dir) {
606
608
  packages
607
609
  };
608
610
  }
611
+ /**
612
+ * Gives the dependencies of the host root package back their previous host resolutions where top-level nested
613
+ * entries do not satisfy them.
614
+ *
615
+ * Bun resolves the dependencies of the root package at the top level only, so placing the host resolution under the
616
+ * root name does not pin the root dependency: the host resolution returns to the top level with its host chain instead.
617
+ * Then every dependency of a nested workspace or package that no longer resolves to its nested entry or a copy of it,
618
+ * such as one that resolved the replaced entry, gets a copy of its nested entry under the dependent,
619
+ * so the copied entries keep their whole nested dependency trees.
620
+ */
621
+ function keepHostRootResolutions(lock, nestedLock, packages) {
622
+ const replaced = /* @__PURE__ */ new Set();
623
+ for (const [dependency, specifier] of Object.entries(getWorkspaceDependencies(lock.workspaces[""]))) {
624
+ const requirement = getRequirement(lock, dependency, specifier);
625
+ const nestedEntry = Object.hasOwn(nestedLock.packages, dependency) ? nestedLock.packages[dependency] : void 0;
626
+ if (!requirement || !nestedEntry || satisfies(nestedEntry, requirement)) continue;
627
+ const previous = Object.hasOwn(lock.packages, dependency) ? lock.packages[dependency] : void 0;
628
+ const replacement = previous && satisfies(previous, requirement) ? previous : findSatisfyingEntry([nestedLock.packages, lock.packages], requirement);
629
+ if (!replacement) continue;
630
+ replaced.add(dependency);
631
+ const prefix = `${dependency}/`;
632
+ for (const key of Object.keys(packages)) if (key.startsWith(prefix)) delete packages[key];
633
+ packages[dependency] = replacement;
634
+ if (replacement === previous) {
635
+ for (const [key, entry] of Object.entries(lock.packages)) if (key.startsWith(prefix)) packages[key] = entry;
636
+ }
637
+ }
638
+ if (!replaced.size) return;
639
+ const nestedKeys = new Map(Object.keys(nestedLock.packages).filter((key) => !replaced.has(splitPackageKey(key)[0])).map((key) => [key, key]));
640
+ const queue = [...Object.values(nestedLock.workspaces).flatMap((workspace) => workspace.name ? [{
641
+ key: workspace.name,
642
+ nestedKey: workspace.name,
643
+ dependencies: getWorkspaceDependencies(workspace)
644
+ }] : []), ...[...nestedKeys.keys()].map((key) => ({
645
+ key,
646
+ nestedKey: key,
647
+ dependencies: getPackageDependencies(nestedLock.packages[key])
648
+ }))];
649
+ for (let index = 0; index < queue.length; index++) {
650
+ const { key, nestedKey, dependencies } = queue[index];
651
+ for (const dependency of Object.keys(dependencies)) {
652
+ const expected = resolveDependencyLockKey(nestedKey, dependency, nestedLock.packages);
653
+ if (expected === void 0) continue;
654
+ const actual = resolveDependencyLockKey(key, dependency, packages);
655
+ if (actual !== void 0 && nestedKeys.get(actual) === expected) continue;
656
+ const entry = nestedLock.packages[expected];
657
+ const pinnedKey = `${key}/${dependency}`;
658
+ packages[pinnedKey] = entry;
659
+ nestedKeys.set(pinnedKey, expected);
660
+ queue.push({
661
+ key: pinnedKey,
662
+ nestedKey: expected,
663
+ dependencies: getPackageDependencies(entry)
664
+ });
665
+ }
666
+ }
667
+ }
609
668
  /** Returns the installed dependencies declared by a lockfile workspace. */
610
669
  function getWorkspaceDependencies(workspace) {
611
670
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-nested-workspace",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Embed a checkout of another Bun workspace repository into a Bun workspace and keep the root package.json and bun.lock in sync with it",
5
5
  "keywords": [
6
6
  "bun",