mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 19:08:50 +00:00
Fix bun pm pack to use workspace package.json version instead of lockfile version
Fixes #20477 When packing a workspace package that depends on another workspace package, `bun pm pack` now reads the version from the workspace dependency's package.json instead of using the cached version in bun.lock. This ensures that if a workspace package version is updated without running `bun install`, the pack command will use the current version from package.json. Changes: - Modified editRootPackageJSON to read workspace package.json files directly - Added detailed error messages for read/parse errors and missing version fields - Added test to verify correct behavior when workspace versions are updated 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1235,7 +1235,7 @@ pub const PackCommand = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const edited_package_json = try editRootPackageJSON(ctx.allocator, ctx.lockfile, json);
|
||||
const edited_package_json = try editRootPackageJSON(ctx.allocator, ctx.lockfile, json, manager, abs_package_json_path);
|
||||
|
||||
var this_transpiler: bun.transpiler.Transpiler = undefined;
|
||||
|
||||
@@ -2073,6 +2073,8 @@ pub const PackCommand = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
maybe_lockfile: ?*Lockfile,
|
||||
json: *PackageManager.WorkspacePackageJSONCache.MapEntry,
|
||||
manager: *PackageManager,
|
||||
abs_package_json_path: stringZ,
|
||||
) OOM!string {
|
||||
for ([_]string{
|
||||
"dependencies",
|
||||
@@ -2116,23 +2118,76 @@ pub const PackCommand = struct {
|
||||
};
|
||||
|
||||
failed_to_resolve: {
|
||||
// find the current workspace version and append to package spec without `workspace:`
|
||||
// find the current workspace version from its package.json and append to package spec without `workspace:`
|
||||
const lockfile = maybe_lockfile orelse break :failed_to_resolve;
|
||||
|
||||
const workspace_version = lockfile.workspace_versions.get(Semver.String.Builder.stringHash(dependency_name)) orelse break :failed_to_resolve;
|
||||
const dependency_name_hash = Semver.String.Builder.stringHash(dependency_name);
|
||||
const workspace_rel_path = lockfile.workspace_paths.get(dependency_name_hash) orelse break :failed_to_resolve;
|
||||
|
||||
// Get the project root directory (where lockfile lives)
|
||||
const project_root = std.fs.path.dirname(abs_package_json_path) orelse break :failed_to_resolve;
|
||||
|
||||
// Build absolute path to workspace package.json
|
||||
const workspace_package_json_path = try std.fs.path.join(allocator, &.{
|
||||
project_root,
|
||||
workspace_rel_path.slice(lockfile.buffers.string_bytes.items),
|
||||
"package.json",
|
||||
});
|
||||
defer allocator.free(workspace_package_json_path);
|
||||
|
||||
// Read the workspace package.json
|
||||
const workspace_json = switch (manager.workspace_package_json_cache.getWithPath(
|
||||
allocator,
|
||||
manager.log,
|
||||
workspace_package_json_path,
|
||||
.{ .guess_indentation = false },
|
||||
)) {
|
||||
.entry => |entry| entry,
|
||||
.read_err => |err| {
|
||||
Output.err(err, "Failed to read workspace package.json for \"{s}\" at \"{s}\"", .{
|
||||
dependency_name,
|
||||
workspace_package_json_path,
|
||||
});
|
||||
Global.crash();
|
||||
},
|
||||
.parse_err => |err| {
|
||||
Output.err(err, "Failed to parse workspace package.json for \"{s}\" at \"{s}\"", .{
|
||||
dependency_name,
|
||||
workspace_package_json_path,
|
||||
});
|
||||
manager.log.print(Output.errorWriter()) catch {};
|
||||
Global.crash();
|
||||
},
|
||||
};
|
||||
|
||||
// Extract version from workspace package.json
|
||||
const version_expr = workspace_json.root.get("version") orelse {
|
||||
Output.errGeneric("Workspace package \"{s}\" at \"{s}\" is missing a `version` field", .{
|
||||
dependency_name,
|
||||
workspace_package_json_path,
|
||||
});
|
||||
Global.crash();
|
||||
};
|
||||
const version_str = version_expr.asString(allocator) orelse {
|
||||
Output.errGeneric("Workspace package \"{s}\" at \"{s}\" has an invalid `version` field", .{
|
||||
dependency_name,
|
||||
workspace_package_json_path,
|
||||
});
|
||||
Global.crash();
|
||||
};
|
||||
|
||||
dependency.value = Expr.allocate(
|
||||
allocator,
|
||||
E.String,
|
||||
.{
|
||||
.data = try std.fmt.allocPrint(allocator, "{s}{}", .{
|
||||
.data = try std.fmt.allocPrint(allocator, "{s}{s}", .{
|
||||
switch (c) {
|
||||
'^' => "^",
|
||||
'~' => "~",
|
||||
'*' => "",
|
||||
else => unreachable,
|
||||
},
|
||||
workspace_version.fmt(lockfile.buffers.string_bytes.items),
|
||||
version_str,
|
||||
}),
|
||||
},
|
||||
.{},
|
||||
|
||||
@@ -615,6 +615,45 @@ describe("workspaces", () => {
|
||||
{ "pathname": "package/root.js" },
|
||||
]);
|
||||
});
|
||||
|
||||
test("uses package.json version not lockfile version when workspace version changes", async () => {
|
||||
await Promise.all([
|
||||
write(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "pack-workspace-version-update",
|
||||
version: "3.0.0",
|
||||
workspaces: ["pkgs/*"],
|
||||
dependencies: {
|
||||
"pkg1": "workspace:*",
|
||||
},
|
||||
}),
|
||||
),
|
||||
write(join(packageDir, "root.js"), "console.log('hello ./root.js')"),
|
||||
write(join(packageDir, "pkgs", "pkg1", "package.json"), JSON.stringify({ name: "pkg1", version: "1.0.0" })),
|
||||
write(join(packageDir, "pkgs", "pkg1", "index.js"), "console.log('pkg1')"),
|
||||
]);
|
||||
|
||||
// Install with version 1.0.0 to create lockfile
|
||||
await runBunInstall(bunEnv, packageDir);
|
||||
|
||||
// Update workspace package version to 2.0.0 without running install
|
||||
await write(join(packageDir, "pkgs", "pkg1", "package.json"), JSON.stringify({ name: "pkg1", version: "2.0.0" }));
|
||||
|
||||
// Pack should use 2.0.0 from package.json, not 1.0.0 from lockfile
|
||||
await pack(packageDir, bunEnv);
|
||||
|
||||
const tarball = readTarball(join(packageDir, "pack-workspace-version-update-3.0.0.tgz"));
|
||||
const packageJson = JSON.parse(tarball.entries[0].contents);
|
||||
expect(packageJson).toEqual({
|
||||
name: "pack-workspace-version-update",
|
||||
version: "3.0.0",
|
||||
workspaces: ["pkgs/*"],
|
||||
dependencies: {
|
||||
"pkg1": "2.0.0", // Should be 2.0.0 from package.json, not 1.0.0 from lockfile
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test("lifecycle scripts execution order", async () => {
|
||||
|
||||
Reference in New Issue
Block a user