mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
fix(install): update overrides/resolutions when running bun add
When `bun add pkg@version` is used to update a package that has an existing entry in the `overrides` (npm) or `resolutions` (yarn) section of package.json, the override/resolution entry was not being updated. This caused the old version from the override to be installed instead of the newly requested version. This fix adds an `editOverrides` function that updates any matching overrides/resolutions entries when a package is added or updated. The function is called twice: 1. Before install: Uses the literal version from CLI to ensure the install uses the correct version 2. After install: Uses the resolved version from the lockfile to ensure the final package.json has resolved versions (e.g., when using dist-tags like "latest") Fixes #25843 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -775,6 +775,57 @@ pub fn edit(
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the overrides (npm) or resolutions (yarn) section of package.json
|
||||
/// when a dependency is being added/updated. This ensures overrides don't
|
||||
/// conflict with the new dependency version.
|
||||
/// When before_install is true, uses the literal version from the CLI.
|
||||
/// When before_install is false, uses the resolved version from e_string (set by edit()).
|
||||
pub fn editOverrides(
|
||||
allocator: std.mem.Allocator,
|
||||
updates: []const UpdateRequest,
|
||||
current_package_json: *Expr,
|
||||
before_install: bool,
|
||||
) !void {
|
||||
// Try both "overrides" (npm) and "resolutions" (yarn)
|
||||
inline for ([_]string{ "overrides", "resolutions" }) |override_key| {
|
||||
if (current_package_json.asProperty(override_key)) |query| {
|
||||
if (query.expr.data == .e_object) {
|
||||
for (query.expr.data.e_object.properties.slice()) |*prop| {
|
||||
const key = prop.key orelse continue;
|
||||
if (key.data != .e_string) continue;
|
||||
|
||||
const override_name = key.data.e_string.slice(allocator);
|
||||
|
||||
// Check if this override matches any of our update requests
|
||||
for (updates) |request| {
|
||||
const name = request.getName();
|
||||
if (!strings.eqlLong(override_name, name, true)) continue;
|
||||
|
||||
// Found a matching override - update it with the new version
|
||||
const value = prop.value orelse continue;
|
||||
if (value.data != .e_string) continue;
|
||||
|
||||
// Get the version - either from literal (before install) or resolved (after)
|
||||
const new_version = if (before_install)
|
||||
request.version.literal.slice(request.version_buf)
|
||||
else if (request.e_string) |e_string|
|
||||
e_string.data
|
||||
else
|
||||
continue;
|
||||
if (new_version.len == 0) continue;
|
||||
|
||||
// Update the override value
|
||||
prop.value = JSAst.Expr.allocate(allocator, JSAst.E.String, .{
|
||||
.data = try allocator.dupe(u8, new_version),
|
||||
}, logger.Loc.Empty);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const trusted_dependencies_string = "trustedDependencies";
|
||||
|
||||
const string = []const u8;
|
||||
|
||||
@@ -196,6 +196,14 @@ fn updatePackageJSONAndInstallWithManagerWithUpdates(
|
||||
.before_install = true,
|
||||
},
|
||||
);
|
||||
// Also update any matching overrides/resolutions with the literal version
|
||||
// This ensures the install uses the correct version
|
||||
try PackageJSONEditor.editOverrides(
|
||||
manager.allocator,
|
||||
updates.*,
|
||||
¤t_package_json.root,
|
||||
true, // before_install = use literal version
|
||||
);
|
||||
} else if (subcommand == .update) {
|
||||
try PackageJSONEditor.editUpdateNoArgs(
|
||||
manager,
|
||||
@@ -375,6 +383,13 @@ fn updatePackageJSONAndInstallWithManagerWithUpdates(
|
||||
.add_trusted_dependencies = manager.options.do.trust_dependencies_from_args,
|
||||
},
|
||||
);
|
||||
// Update any matching overrides/resolutions with the resolved versions
|
||||
try PackageJSONEditor.editOverrides(
|
||||
manager.allocator,
|
||||
updates.*,
|
||||
&new_package_json,
|
||||
false, // before_install = use resolved version from e_string
|
||||
);
|
||||
}
|
||||
var buffer_writer_two = JSPrinter.BufferWriter.init(manager.allocator);
|
||||
try buffer_writer_two.buffer.list.ensureTotalCapacity(manager.allocator, source.contents.len + 1);
|
||||
|
||||
@@ -247,3 +247,71 @@ test("overrides do not apply to workspaces", async () => {
|
||||
expect(await exited).toBe(0);
|
||||
expect(await stderr.text()).not.toContain("Saved lockfile");
|
||||
});
|
||||
|
||||
test("bun add updates overrides when adding new version of overridden package", async () => {
|
||||
// Regression test for https://github.com/oven-sh/bun/issues/25843
|
||||
// When running `bun add pkg@newversion`, if there's an override for that package,
|
||||
// the override should also be updated to prevent conflicts.
|
||||
const tmp = tmpdirSync();
|
||||
writeFileSync(
|
||||
join(tmp, "package.json"),
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
lodash: "4.0.0",
|
||||
},
|
||||
overrides: {
|
||||
lodash: "4.0.0",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// First install to set up the lockfile with the old version
|
||||
install(tmp, ["install"]);
|
||||
expect(versionOf(tmp, "node_modules/lodash/package.json")).toBe("4.0.0");
|
||||
|
||||
// Now use bun add to update to a new version
|
||||
install(tmp, ["add", "lodash@4.17.21"]);
|
||||
|
||||
// Verify the new version is installed
|
||||
expect(versionOf(tmp, "node_modules/lodash/package.json")).toBe("4.17.21");
|
||||
|
||||
// Verify the package.json was updated correctly (both dependencies and overrides)
|
||||
const packageJson = JSON.parse(readFileSync(join(tmp, "package.json")).toString());
|
||||
expect(packageJson.dependencies.lodash).toBe("4.17.21");
|
||||
expect(packageJson.overrides.lodash).toBe("4.17.21");
|
||||
|
||||
ensureLockfileDoesntChangeOnBunI(tmp);
|
||||
});
|
||||
|
||||
test("bun add updates resolutions when adding new version of package with resolution", async () => {
|
||||
// Similar to overrides test but for yarn-style resolutions
|
||||
const tmp = tmpdirSync();
|
||||
writeFileSync(
|
||||
join(tmp, "package.json"),
|
||||
JSON.stringify({
|
||||
dependencies: {
|
||||
lodash: "4.0.0",
|
||||
},
|
||||
resolutions: {
|
||||
lodash: "4.0.0",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// First install to set up the lockfile with the old version
|
||||
install(tmp, ["install"]);
|
||||
expect(versionOf(tmp, "node_modules/lodash/package.json")).toBe("4.0.0");
|
||||
|
||||
// Now use bun add to update to a new version
|
||||
install(tmp, ["add", "lodash@4.17.21"]);
|
||||
|
||||
// Verify the new version is installed
|
||||
expect(versionOf(tmp, "node_modules/lodash/package.json")).toBe("4.17.21");
|
||||
|
||||
// Verify the package.json was updated correctly (both dependencies and resolutions)
|
||||
const packageJson = JSON.parse(readFileSync(join(tmp, "package.json")).toString());
|
||||
expect(packageJson.dependencies.lodash).toBe("4.17.21");
|
||||
expect(packageJson.resolutions.lodash).toBe("4.17.21");
|
||||
|
||||
ensureLockfileDoesntChangeOnBunI(tmp);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user