Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/pm/workspaces.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ With the hoisted linker, dependencies shared by several workspaces are hoisted t
`node_modules`. Some tools cannot follow that: Electron packagers and serverless bundlers walk,
prune and repackage _one workspace's_ `node_modules` and expect every dependency to be physically
present under it. Mark such a workspace as self-contained, either in its own `package.json`
(the same key Yarn uses; only the `"workspaces"` value is recognized):
(the same key Yarn uses; only the `"workspaces"` value changes the layout):

```json title="apps/desktop/package.json" icon="file-json"
{
Expand Down
2 changes: 1 addition & 1 deletion src/install/lockfile/Package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2901,7 +2901,7 @@ impl Package<u64> {
Some(source),
bun_ast::Loc::EMPTY,
format_args!(
"workspace \"{}\": installConfig.hoistingLimits \"{}\" is not supported (only \"workspaces\" is); ignoring",
"workspace \"{}\": installConfig.hoistingLimits \"{}\" is not supported (only \"workspaces\" and \"none\" are); ignoring",
bstr::BStr::new(&entry.name),
bstr::BStr::new(v),
),
Expand Down
7 changes: 4 additions & 3 deletions src/install/lockfile/Package/WorkspaceMap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ pub struct Entry {
/// `"installConfig": { "hoistingLimits": "workspaces" }` — this workspace's
/// node_modules must be self-contained (see `Lockfile::self_contained_workspaces`).
pub(crate) hoisting_limits: bool,
/// An `installConfig.hoistingLimits` value other than "workspaces", kept so the
/// caller can warn about it (outside `process_names_array`'s log window).
/// An `installConfig.hoistingLimits` value other than "workspaces" or "none", kept so
/// the caller can warn about it (outside `process_names_array`'s log window).
pub(crate) unsupported_hoisting_limits: Option<Box<[u8]>>,
}

Expand Down Expand Up @@ -201,7 +201,8 @@ fn process_workspace_name(
name_loc: name_expr.loc,
hoisting_limits: hoisting_limits.as_deref() == Some(b"workspaces".as_slice()),
unsupported_hoisting_limits: match hoisting_limits {
Some(v) if &*v != b"workspaces" => Some(v),
// "none" is yarn's default: no limit, which is how every workspace hoists
Some(v) if !matches!(&*v, b"workspaces" | b"none") => Some(v),
_ => None,
},
version: 'brk: {
Expand Down
22 changes: 21 additions & 1 deletion test/cli/install/bun-workspaces-self-contained.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,22 @@ describe.each([
{ installConfig: { hoistingLimits: "dependencies" } },
{ selfContained: ["desktop"] },
],
// yarn's default value is the same as no value; the root list still applies
[
'hoistingLimits "none" plus the root list',
{ installConfig: { hoistingLimits: "none" } },
{ selfContained: ["desktop"] },
],
] as const)("self-contained workspace via %s", (_label, desktopExtra, workspacesExtra) => {
it("gets a complete, physical node_modules while other workspaces still hoist", async () => {
await writeProject(desktopExtra, workspacesExtra);
const r = await install(package_dir);
expect(r.err).not.toContain("error:");
if ("installConfig" in desktopExtra && (desktopExtra as any).installConfig.hoistingLimits !== "workspaces") {
if ((desktopExtra as any).installConfig?.hoistingLimits === "dependencies") {
// the unsupported value is reported (and otherwise ignored)
expect(r.err).toContain('installConfig.hoistingLimits "dependencies" is not supported');
} else {
expect(r.err).not.toContain("hoistingLimits");
}
expect(r.code).toBe(0);

Expand Down Expand Up @@ -167,6 +175,18 @@ it("an entry that matches no workspace warns and the rest still applies", async
]);
});

// "none" is yarn's default: no hoisting limit, so nothing to warn about
it('hoistingLimits "none" hoists the workspace normally and does not warn', async () => {
await writeProject({ installConfig: { hoistingLimits: "none" } }, {});
const r = await install(package_dir);
expect(r.err).not.toContain("error:");
expect(r.err).not.toContain("hoistingLimits");
expect(r.code).toBe(0);
expect(existsSync(join(package_dir, "apps", "desktop", "node_modules"))).toBeFalse();
expect(existsSync(join(package_dir, "node_modules", "@barn", "moo", "package.json"))).toBeTrue();
expect(await Bun.file(join(package_dir, "bun.lock")).text()).not.toContain("hoistingLimits");
});

it("without either setting the workspace is hoisted normally", async () => {
await writeProject({}, {});
const r = await install(package_dir);
Expand Down
Loading