aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--src/install/install.zig7
-rw-r--r--src/install/lockfile.zig70
-rw-r--r--test/cli/install/bun-install.test.ts33
4 files changed, 97 insertions, 17 deletions
diff --git a/Makefile b/Makefile
index ea7a6a1e7..b1543ee15 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ CPU_TARGET ?= native
MARCH_NATIVE = -mtune=$(CPU_TARGET)
NATIVE_OR_OLD_MARCH =
-MMD_IF_LOCAL =
+MMD_IF_LOCAL =
DEFAULT_MIN_MACOS_VERSION=
ARCH_NAME :=
DOCKER_BUILDARCH =
@@ -1907,7 +1907,7 @@ regenerate-bindings: ## compile src/js/builtins + all c++ code, does not link
@make bindings -j$(CPU_COUNT)
.PHONY: setup
-setup: vendor-dev identifier-cache clean-bindings
+setup: vendor-dev identifier-cache clean-bindings js
make jsc-check
make bindings -j$(CPU_COUNT)
@echo ""
diff --git a/src/install/install.zig b/src/install/install.zig
index 2d5edacde..0a0c21637 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -4320,6 +4320,7 @@ pub const PackageManager = struct {
},
local_package_features: Features = .{
.dev_dependencies = true,
+ .workspaces = true,
},
// The idea here is:
// 1. package has a platform-specific binary to install
@@ -7502,6 +7503,7 @@ pub const PackageManager = struct {
manager.summary = try Package.Diff.generate(
ctx.allocator,
+ ctx.log,
manager.lockfile,
&lockfile,
&root,
@@ -7519,13 +7521,12 @@ pub const PackageManager = struct {
Global.crash();
}
- // If you changed packages, we will copy over the new package from the new lockfile
- const new_dependencies = maybe_root.dependencies.get(lockfile.buffers.dependencies.items);
-
if (had_any_diffs) {
var builder_ = manager.lockfile.stringBuilder();
// ensure we use one pointer to reference it instead of creating new ones and potentially aliasing
var builder = &builder_;
+ // If you changed packages, we will copy over the new package from the new lockfile
+ const new_dependencies = maybe_root.dependencies.get(lockfile.buffers.dependencies.items);
for (new_dependencies) |new_dep| {
new_dep.count(lockfile.buffers.string_bytes.items, *Lockfile.StringBuilder, builder);
diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig
index 7263a2c63..ff4f82400 100644
--- a/src/install/lockfile.zig
+++ b/src/install/lockfile.zig
@@ -2385,25 +2385,35 @@ pub const Package = extern struct {
};
pub fn generate(
- _: Allocator,
+ allocator: Allocator,
+ log: *logger.Log,
from_lockfile: *Lockfile,
to_lockfile: *Lockfile,
from: *Lockfile.Package,
to: *Lockfile.Package,
- mapping: []PackageID,
+ id_mapping: ?[]PackageID,
) !Summary {
var summary = Summary{};
const to_deps = to.dependencies.get(to_lockfile.buffers.dependencies.items);
const from_deps = from.dependencies.get(from_lockfile.buffers.dependencies.items);
+ const from_resolutions = from.resolutions.get(from_lockfile.buffers.resolutions.items);
+ var to_i: usize = 0;
for (from_deps, 0..) |*from_dep, i| {
- // common case: dependency is present in both versions and in the same position
- const to_i = if (to_deps.len > i and to_deps[i].name_hash == from_dep.name_hash)
- i
- else brk: {
+ found: {
+ const prev_i = to_i;
+
+ // common case, dependency is present in both versions:
+ // - in the same position
+ // - shifted by a constant offset
+ while (to_i < to_deps.len) : (to_i += 1) {
+ if (from_dep.name_hash == to_deps[to_i].name_hash) break :found;
+ }
+
// less common, o(n^2) case
- for (to_deps, 0..) |to_dep, j| {
- if (from_dep.name_hash == to_dep.name_hash) break :brk j;
+ to_i = 0;
+ while (to_i < prev_i) : (to_i += 1) {
+ if (from_dep.name_hash == to_deps[to_i].name_hash) break :found;
}
// We found a removed dependency!
@@ -2411,11 +2421,47 @@ pub const Package = extern struct {
// It will be cleaned up later
summary.remove += 1;
continue;
- };
+ }
if (to_deps[to_i].eql(from_dep, to_lockfile.buffers.string_bytes.items, from_lockfile.buffers.string_bytes.items)) {
- mapping[to_i] = @as(PackageID, @truncate(i));
- continue;
+ if (id_mapping) |mapping| {
+ const version = to_deps[to_i].version;
+ if (switch (version.tag) {
+ .workspace => if (to_lockfile.workspace_paths.getPtr(@truncate(from_dep.name_hash))) |path_ptr| brk: {
+ const path = to_lockfile.str(path_ptr);
+ var file = std.fs.cwd().openFile(Path.join(
+ &[_]string{ path, "package.json" },
+ .auto,
+ ), .{ .mode = .read_only }) catch break :brk false;
+ defer file.close();
+ const bytes = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
+ defer allocator.free(bytes);
+ const source = logger.Source.initPathString(path, bytes);
+
+ var workspace = Package{};
+ try workspace.parseMain(to_lockfile, allocator, log, source, Features.workspace);
+
+ var from_pkg = from_lockfile.packages.get(from_resolutions[i]);
+ const diff = try generate(
+ allocator,
+ log,
+ from_lockfile,
+ to_lockfile,
+ &from_pkg,
+ &workspace,
+ null,
+ );
+
+ break :brk diff.add == 0 and diff.remove == 0 and diff.update == 0;
+ } else false,
+ else => true,
+ }) {
+ mapping[to_i] = @as(PackageID, @truncate(i));
+ continue;
+ }
+ } else {
+ continue;
+ }
}
// We found a changed dependency!
@@ -2654,7 +2700,7 @@ pub const Package = extern struct {
}
const this_dep = Dependency{
- .behavior = group.behavior.setWorkspace(in_workspace),
+ .behavior = if (in_workspace) group.behavior.setWorkspace(in_workspace) else group.behavior,
.name = external_alias.value,
.name_hash = external_alias.hash,
.version = dependency_version,
diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts
index bdd098126..b8f2508c4 100644
--- a/test/cli/install/bun-install.test.ts
+++ b/test/cli/install/bun-install.test.ts
@@ -724,6 +724,7 @@ it("should handle life-cycle scripts during re-installation", async () => {
});
expect(stderr2).toBeDefined();
const err2 = await new Response(stderr2).text();
+ expect(err2).not.toContain("error:");
expect(err2).not.toContain("Saved lockfile");
expect(stdout2).toBeDefined();
const out2 = await new Response(stdout2).text();
@@ -739,6 +740,38 @@ it("should handle life-cycle scripts during re-installation", async () => {
expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual(["Bar"]);
expect(await readlink(join(package_dir, "node_modules", "Bar"))).toBe(join("..", "bar"));
await access(join(package_dir, "bun.lockb"));
+ // Perform `bun install --production` with lockfile from before
+ await rm(join(package_dir, "node_modules"), { force: true, recursive: true });
+ const {
+ stdout: stdout3,
+ stderr: stderr3,
+ exited: exited3,
+ } = spawn({
+ cmd: [bunExe(), "install", "--production"],
+ cwd: package_dir,
+ stdout: null,
+ stdin: "pipe",
+ stderr: "pipe",
+ env,
+ });
+ expect(stderr3).toBeDefined();
+ const err3 = await new Response(stderr3).text();
+ expect(err3).not.toContain("error:");
+ expect(err3).not.toContain("Saved lockfile");
+ expect(stdout3).toBeDefined();
+ const out3 = await new Response(stdout3).text();
+ expect(out3.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
+ "[scripts:run] Bar",
+ " + Bar@workspace:bar",
+ "[scripts:run] Foo",
+ "",
+ " 1 packages installed",
+ ]);
+ expect(await exited3).toBe(0);
+ expect(requested).toBe(0);
+ expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual(["Bar"]);
+ expect(await readlink(join(package_dir, "node_modules", "Bar"))).toBe(join("..", "bar"));
+ await access(join(package_dir, "bun.lockb"));
});
it("should ignore workspaces within workspaces", async () => {