bun-nested-workspace 0.3.0 → 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 +3 -2
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +9 -4
- package/dist/index.mjs +1 -1
- package/dist/{workspace-DfVz3A2J.mjs → workspace-ChlHn5I8.mjs} +75 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,6 +102,7 @@ Exclude `node_modules` and other large directories from the build context with `
|
|
|
102
102
|
An explicit list replaces the default trusted list of Bun, so when only one side defines the field, the packages of the other side lose the default list; list the ones whose lifecycle scripts they need.
|
|
103
103
|
The sync warns when it adds the field to the host.
|
|
104
104
|
- `patchedDependencies` paths are rewritten through the nested directory, such as `nested/patches/exceljs@4.4.0.patch`.
|
|
105
|
+
Bun warns on every install that it ignores `patchedDependencies` of the nested root package; keep them there, because the host applies the synced copies.
|
|
105
106
|
|
|
106
107
|
A nested entry replaces a host entry with the same name, while the other host entries stay.
|
|
107
108
|
The sync lists the host entries it replaced.
|
|
@@ -112,7 +113,7 @@ A package version patched by both the host and the nested repository is an error
|
|
|
112
113
|
### Lockfile
|
|
113
114
|
|
|
114
115
|
The nested lockfile entries replace the host entries, so the nested packages keep their versions.
|
|
115
|
-
When a replaced entry no longer satisfies a dependency of a host package, the host package keeps its previous resolution
|
|
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.
|
|
116
117
|
When neither satisfies the range, which Bun lockfiles occasionally contain, the dependency gets the highest satisfying version recorded in either lockfile.
|
|
117
118
|
Ranges are checked as `bun install` applies them: an override replaces the declared range, and an `npm:` alias requires the package it names.
|
|
118
119
|
`bun install` then resolves new dependencies of host packages and drops unused entries.
|
|
@@ -120,7 +121,7 @@ Ranges are checked as `bun install` applies them: an override replaces the decla
|
|
|
120
121
|
After the install, the sync compares the direct dependencies of host packages with the original host lockfile.
|
|
121
122
|
A dependency may only change to the version that the nested lockfile hoists to its top level, and only within the host range.
|
|
122
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.
|
|
123
|
-
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.
|
|
124
125
|
|
|
125
126
|
Only `bun.lock` with `lockfileVersion` 1 or 2 is supported, because the lockfile format is internal to Bun.
|
|
126
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-
|
|
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
|
@@ -173,9 +173,12 @@ export declare function parseLockfile(text: string, source?: string): Lockfile;
|
|
|
173
173
|
/**
|
|
174
174
|
* Returns the nested lockfile with workspace and local paths relative to the host root.
|
|
175
175
|
*
|
|
176
|
-
* The nested root workspace moves to the nested directory, where it is a workspace of the host
|
|
176
|
+
* The nested root workspace moves to the nested directory, where it is a workspace of the host,
|
|
177
|
+
* and takes `rootName`, the name of the nested root `package.json`, when it is given.
|
|
178
|
+
* Bun requires the name of a workspace package entry but keeps a root entry without the name
|
|
179
|
+
* when the name was added to `package.json` after the lockfile.
|
|
177
180
|
*/
|
|
178
|
-
export declare function relocateLockfile(lock: Lockfile, dir: string): Lockfile;
|
|
181
|
+
export declare function relocateLockfile(lock: Lockfile, dir: string, rootName?: string): Lockfile;
|
|
179
182
|
/**
|
|
180
183
|
* Returns the lockfile with root fields that affect resolution taken from the root `package.json`.
|
|
181
184
|
*
|
|
@@ -191,7 +194,9 @@ export declare function applyManifestToLockfile(lock: Lockfile, manifest: Packag
|
|
|
191
194
|
* A root entry overridden by the nested lockfile might not satisfy a dependency of a host package:
|
|
192
195
|
* such a dependency gets back its previous host resolution as a nested entry, so `bun install` keeps the host version
|
|
193
196
|
* instead of resolving the range again.
|
|
194
|
-
* A host
|
|
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.
|
|
195
200
|
* When the previous host resolution does not satisfy the range either, which Bun lockfiles occasionally contain,
|
|
196
201
|
* the dependency gets the highest satisfying version recorded in either lockfile.
|
|
197
202
|
* Catalog dependencies are checked against the catalogs of the host lockfile, so apply the synced root fields
|
|
@@ -257,7 +262,7 @@ interface NestedWorkspace {
|
|
|
257
262
|
manifestText: string;
|
|
258
263
|
nestedManifest: PackageManifest;
|
|
259
264
|
lock: Lockfile;
|
|
260
|
-
/** Nested lockfile relocated with `relocateLockfile`. */
|
|
265
|
+
/** Nested lockfile relocated with `relocateLockfile`, with the root name of `nestedManifest`. */
|
|
261
266
|
nestedLock: Lockfile;
|
|
262
267
|
}
|
|
263
268
|
/** Loads the root `package.json` and `bun.lock` files of the host and of the nested checkout. */
|
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-
|
|
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";
|
|
@@ -506,12 +506,18 @@ async function readLockfile(file) {
|
|
|
506
506
|
/**
|
|
507
507
|
* Returns the nested lockfile with workspace and local paths relative to the host root.
|
|
508
508
|
*
|
|
509
|
-
* The nested root workspace moves to the nested directory, where it is a workspace of the host
|
|
509
|
+
* The nested root workspace moves to the nested directory, where it is a workspace of the host,
|
|
510
|
+
* and takes `rootName`, the name of the nested root `package.json`, when it is given.
|
|
511
|
+
* Bun requires the name of a workspace package entry but keeps a root entry without the name
|
|
512
|
+
* when the name was added to `package.json` after the lockfile.
|
|
510
513
|
*/
|
|
511
|
-
function relocateLockfile(lock, dir) {
|
|
514
|
+
function relocateLockfile(lock, dir, rootName) {
|
|
512
515
|
return {
|
|
513
516
|
...lock,
|
|
514
|
-
workspaces: Object.fromEntries(Object.entries(lock.workspaces).map(([workspacePath, workspace]) => [path.posix.join(dir, workspacePath),
|
|
517
|
+
workspaces: Object.fromEntries(Object.entries(lock.workspaces).map(([workspacePath, workspace]) => [path.posix.join(dir, workspacePath), workspacePath === "" && rootName !== void 0 ? {
|
|
518
|
+
...workspace,
|
|
519
|
+
name: rootName
|
|
520
|
+
} : workspace])),
|
|
515
521
|
packages: Object.fromEntries(Object.entries(lock.packages).map(([key, [resolution, ...rest]]) => [key, [relocateResolution(resolution, dir), ...rest]]))
|
|
516
522
|
};
|
|
517
523
|
}
|
|
@@ -551,7 +557,9 @@ function applyManifestToLockfile(lock, manifest) {
|
|
|
551
557
|
* A root entry overridden by the nested lockfile might not satisfy a dependency of a host package:
|
|
552
558
|
* such a dependency gets back its previous host resolution as a nested entry, so `bun install` keeps the host version
|
|
553
559
|
* instead of resolving the range again.
|
|
554
|
-
* A host
|
|
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.
|
|
555
563
|
* When the previous host resolution does not satisfy the range either, which Bun lockfiles occasionally contain,
|
|
556
564
|
* the dependency gets the highest satisfying version recorded in either lockfile.
|
|
557
565
|
* Catalog dependencies are checked against the catalogs of the host lockfile, so apply the synced root fields
|
|
@@ -565,16 +573,16 @@ function mergeLockfiles(lock, nestedLock, dir) {
|
|
|
565
573
|
...nestedLock.packages
|
|
566
574
|
};
|
|
567
575
|
const hostWorkspaces = Object.entries(lock.workspaces).filter(([workspacePath]) => !isNestedPath(workspacePath, dir));
|
|
568
|
-
|
|
576
|
+
if (lock.workspaces[""]) keepHostRootResolutions(lock, nestedLock, packages);
|
|
577
|
+
const dependents = [...hostWorkspaces.flatMap(([workspacePath, workspace]) => workspacePath && workspace.name ? [{
|
|
569
578
|
key: workspace.name,
|
|
570
579
|
dependencies: getWorkspaceDependencies(workspace)
|
|
571
|
-
})
|
|
580
|
+
}] : []), ...Object.keys(packages).map((key) => ({
|
|
572
581
|
key,
|
|
573
582
|
dependencies: getPackageDependencies(packages[key])
|
|
574
583
|
}))];
|
|
575
584
|
for (let index = 0; index < dependents.length; index++) {
|
|
576
585
|
const { key, dependencies } = dependents[index];
|
|
577
|
-
if (key === void 0) continue;
|
|
578
586
|
for (const [dependency, specifier] of Object.entries(dependencies)) {
|
|
579
587
|
const requirement = getRequirement(lock, dependency, specifier);
|
|
580
588
|
const resolved = resolveDependencyLockKey(key, dependency, packages);
|
|
@@ -600,6 +608,63 @@ function mergeLockfiles(lock, nestedLock, dir) {
|
|
|
600
608
|
packages
|
|
601
609
|
};
|
|
602
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
|
+
}
|
|
603
668
|
/** Returns the installed dependencies declared by a lockfile workspace. */
|
|
604
669
|
function getWorkspaceDependencies(workspace) {
|
|
605
670
|
return {
|
|
@@ -815,14 +880,15 @@ async function loadNestedWorkspace(root) {
|
|
|
815
880
|
const manifest = JSON.parse(manifestText);
|
|
816
881
|
const config = readConfig(manifest);
|
|
817
882
|
const nestedRoot = path.join(root, config.dir);
|
|
883
|
+
const nestedManifest = await Bun.file(path.join(nestedRoot, "package.json")).json();
|
|
818
884
|
return {
|
|
819
885
|
root,
|
|
820
886
|
config,
|
|
821
887
|
manifest,
|
|
822
888
|
manifestText,
|
|
823
|
-
nestedManifest
|
|
889
|
+
nestedManifest,
|
|
824
890
|
lock: await readLockfile(path.join(root, "bun.lock")),
|
|
825
|
-
nestedLock: relocateLockfile(await readLockfile(path.join(nestedRoot, "bun.lock")), config.dir)
|
|
891
|
+
nestedLock: relocateLockfile(await readLockfile(path.join(nestedRoot, "bun.lock")), config.dir, typeof nestedManifest.name === "string" ? nestedManifest.name : void 0)
|
|
826
892
|
};
|
|
827
893
|
}
|
|
828
894
|
/** Returns whether a check found no differences. */
|
package/package.json
CHANGED