diff options
Diffstat (limited to 'src/install/lockfile.zig')
-rw-r--r-- | src/install/lockfile.zig | 138 |
1 files changed, 72 insertions, 66 deletions
diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 83c3b9ffb..70e14fb82 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -362,16 +362,12 @@ pub const Tree = struct { /// Flatten the multi-dimensional ArrayList of package IDs into a single easily serializable array pub fn clean(this: *Builder) !DependencyIDList { - var end = @truncate(Id, this.list.len); + const end = @truncate(Id, this.list.len); var i: Id = 0; var total: u32 = 0; - var trees = this.list.items(.tree); var dependencies = this.list.items(.dependencies); - // var real_end: Id = 0; - - // TODO: can we cull empty trees here? while (i < end) : (i += 1) { total += trees[i].dependencies.len; } @@ -401,6 +397,14 @@ pub const Tree = struct { dependency_id: DependencyID, builder: *Builder, ) SubtreeError!void { + const package_id = switch (dependency_id) { + root_dep_id => 0, + else => |id| builder.resolutions[id], + }; + const resolution_list = builder.resolution_lists[package_id]; + + if (resolution_list.len == 0) return; + try builder.list.append(builder.allocator, .{ .tree = .{ .parent = this.id, @@ -414,15 +418,6 @@ pub const Tree = struct { const trees = list_slice.items(.tree); const dependency_lists = list_slice.items(.dependencies); const next: *Tree = &trees[builder.list.len - 1]; - - const package_id = switch (dependency_id) { - root_dep_id => 0, - else => |id| builder.resolutions[id], - }; - const resolution_list = builder.resolution_lists[package_id]; - - if (resolution_list.len == 0) return; - const name_hashes: []const PackageNameHash = builder.name_hashes; const max_package_id = @truncate(PackageID, name_hashes.len); var dep_id = resolution_list.off; @@ -430,44 +425,49 @@ pub const Tree = struct { while (dep_id < end) : (dep_id += 1) { const pid = builder.resolutions[dep_id]; + // Skip unresolved packages, e.g. "peerDependencies" if (pid >= max_package_id) continue; - const dependency = builder.dependencies[dep_id]; - - // Do not download/install "peerDependencies" - if (dependency.behavior.isPeer()) continue; + const dependency = builder.dependencies[dep_id]; // Do not hoist aliased packages - const destination = if (dependency.name_hash == name_hashes[pid]) next.addDependency( - true, - pid, - dep_id, - &dependency, - dependency_lists, - trees, - builder, - ) else brk: { - dependency_lists[next.id].append(builder.allocator, dep_id) catch unreachable; - next.dependencies.len += 1; - break :brk next.id; - }; + const destination = if (dependency.name_hash != name_hashes[pid]) + next.id + else + next.hoistDependency( + true, + pid, + dep_id, + &dependency, + dependency_lists, + trees, + builder, + ) catch |err| return err; switch (destination) { - Tree.dependency_loop => return error.DependencyLoop, - Tree.hoisted => continue, - else => if (builder.resolution_lists[pid].len > 0) { - try builder.queue.writeItem(.{ - .tree_id = destination, - .dependency_id = dep_id, - }); + Tree.dependency_loop, Tree.hoisted => continue, + else => { + dependency_lists[destination].append(builder.allocator, dep_id) catch unreachable; + trees[destination].dependencies.len += 1; + if (builder.resolution_lists[pid].len > 0) { + try builder.queue.writeItem(.{ + .tree_id = destination, + .dependency_id = dep_id, + }); + } }, } } + + if (next.dependencies.len == 0) { + if (comptime Environment.allow_assert) std.debug.assert(builder.list.len == next.id + 1); + _ = builder.list.pop(); + } } // This function does one of three things: // - de-duplicate (skip) the package // - move the package to the top directory // - leave the package at the same (relative) directory - fn addDependency( + fn hoistDependency( this: *Tree, comptime as_defined: bool, package_id: PackageID, @@ -476,16 +476,21 @@ pub const Tree = struct { dependency_lists: []Lockfile.DependencyIDList, trees: []Tree, builder: *Builder, - ) Id { + ) !Id { const this_dependencies = this.dependencies.get(dependency_lists[this.id].items); for (this_dependencies) |dep_id| { - if (builder.dependencies[dep_id].name_hash != dependency.name_hash) continue; - if (builder.resolutions[dep_id] != package_id) return dependency_loop; + const dep = builder.dependencies[dep_id]; + if (dep.name_hash != dependency.name_hash) continue; + if (builder.resolutions[dep_id] != package_id) { + if (as_defined and !dep.behavior.isPeer()) return error.DependencyLoop; + // ignore versioning conflicts caused by peer dependencies + return dependency_loop; + } return hoisted; } if (this.parent < error_id) { - const id = trees[this.parent].addDependency( + const id = trees[this.parent].hoistDependency( false, package_id, dependency_id, @@ -493,12 +498,10 @@ pub const Tree = struct { dependency_lists, trees, builder, - ); + ) catch unreachable; if (!as_defined or id != dependency_loop) return id; } - dependency_lists[this.id].append(builder.allocator, dependency_id) catch unreachable; - this.dependencies.len += 1; return this.id; } }; @@ -1279,20 +1282,20 @@ pub const Printer = struct { if (dep.behavior != behavior) { if (dep.behavior.isOptional()) { try writer.writeAll(" optionalDependencies:\n"); - if (comptime Environment.isDebug or Environment.isTest) dependency_behavior_change_count += 1; + if (comptime Environment.allow_assert) dependency_behavior_change_count += 1; } else if (dep.behavior.isNormal()) { try writer.writeAll(" dependencies:\n"); - if (comptime Environment.isDebug or Environment.isTest) dependency_behavior_change_count += 1; + if (comptime Environment.allow_assert) dependency_behavior_change_count += 1; } else if (dep.behavior.isDev()) { try writer.writeAll(" devDependencies:\n"); - if (comptime Environment.isDebug or Environment.isTest) dependency_behavior_change_count += 1; + if (comptime Environment.allow_assert) dependency_behavior_change_count += 1; } else { continue; } behavior = dep.behavior; // assert its sorted - if (comptime Environment.isDebug or Environment.isTest) std.debug.assert(dependency_behavior_change_count < 3); + if (comptime Environment.allow_assert) std.debug.assert(dependency_behavior_change_count < 3); } try writer.writeAll(" "); @@ -1351,10 +1354,12 @@ pub fn verifyResolutions(this: *Lockfile, local_features: Features, remote_featu for (list.get(resolutions_buffer)) |package_id, j| { if (package_id >= end) { const failed_dep: Dependency = dependency_lists[parent_id].get(dependencies_buffer)[j]; - if (!failed_dep.behavior.isEnabled(if (root_list.contains(@truncate(PackageID, parent_id))) - local_features - else - remote_features)) continue; + if (failed_dep.behavior.isPeer() or !failed_dep.behavior.isEnabled( + if (root_list.contains(@truncate(PackageID, parent_id))) + local_features + else + remote_features, + )) continue; if (log_level != .silent) Output.prettyErrorln( "<r><red>error<r><d>:<r> <b>{s}<r><d>@<b>{}<r><d> failed to resolve<r>\n", @@ -1844,7 +1849,7 @@ pub const Package = extern struct { const id = @truncate(PackageID, new.packages.len); const new_package = try new.appendPackageWithID( - Lockfile.Package{ + .{ .name = builder.appendWithHash( String, this.name.slice(old_string_buf), @@ -2378,15 +2383,16 @@ pub const Package = extern struct { .workspace => if (workspace_path) |path| { dependency_version.value.workspace = path; } else { + const workspace = dependency_version.value.workspace.slice(buf); const path = string_builder.append( String, - Path.relative( + if (strings.eqlComptime(workspace, "*")) "*" else Path.relative( FileSystem.instance.top_level_dir, Path.joinAbsString( FileSystem.instance.top_level_dir, &[_]string{ source.path.name.dir, - dependency_version.value.workspace.slice(buf), + workspace, }, .posix, ), @@ -2401,7 +2407,7 @@ pub const Package = extern struct { } const this_dep = Dependency{ - .behavior = if (group.behavior.isPeer()) group.behavior else group.behavior.setWorkspace(in_workspace), + .behavior = group.behavior.setWorkspace(in_workspace), .name = external_name.value, .name_hash = external_name.hash, .version = dependency_version, @@ -2736,8 +2742,8 @@ pub const Package = extern struct { const bin_name = obj.properties.ptr[0].key.?.asString(allocator) orelse break :bin; const value = obj.properties.ptr[0].value.?.asString(allocator) orelse break :bin; - package.bin = Bin{ - .tag = Bin.Tag.named_file, + package.bin = .{ + .tag = .named_file, .value = .{ .named_file = .{ string_builder.append(String, bin_name), @@ -2764,8 +2770,8 @@ pub const Package = extern struct { i += 1; } std.debug.assert(i == extern_strings.len); - package.bin = Bin{ - .tag = Bin.Tag.map, + package.bin = .{ + .tag = .map, .value = .{ .map = @import("./install.zig").ExternalStringList.init(lockfile.buffers.extern_strings.items, extern_strings) }, }; }, @@ -2775,8 +2781,8 @@ pub const Package = extern struct { }, .e_string => |stri| { if (stri.data.len > 0) { - package.bin = Bin{ - .tag = Bin.Tag.file, + package.bin = .{ + .tag = .file, .value = .{ .file = string_builder.append(String, stri.data), }, @@ -2799,8 +2805,8 @@ pub const Package = extern struct { if (dirs.expr.asProperty("bin")) |bin_prop| { if (bin_prop.expr.asString(allocator)) |str_| { if (str_.len > 0) { - package.bin = Bin{ - .tag = Bin.Tag.dir, + package.bin = .{ + .tag = .dir, .value = .{ .dir = string_builder.append(String, str_), }, |