aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/install/dependency.zig110
-rw-r--r--src/install/extract_tarball.zig1
-rw-r--r--src/install/install.zig459
-rw-r--r--src/install/resolvers/folder_resolver.zig5
4 files changed, 341 insertions, 234 deletions
diff --git a/src/install/dependency.zig b/src/install/dependency.zig
index 9567b7cea..2f813b3cc 100644
--- a/src/install/dependency.zig
+++ b/src/install/dependency.zig
@@ -87,45 +87,49 @@ pub fn clone(this: Dependency, buf: []const u8, comptime StringBuilder: type, bu
};
}
-pub const External = extern struct {
- name: String = String{},
- name_hash: PackageNameHash = 0,
- behavior: Behavior = Behavior.uninitialized,
- version: Dependency.Version.External,
-
- pub const Context = struct {
- allocator: std.mem.Allocator,
- log: *logger.Log,
- buffer: []const u8,
- };
+pub const External = [size]u8;
- pub fn toDependency(
- this: Dependency.External,
- ctx: Context,
- ) Dependency {
- return Dependency{
- .name = this.name,
- .name_hash = this.name_hash,
- .behavior = this.behavior,
- .version = this.version.toVersion(ctx),
- };
- }
+const size = @sizeOf(Dependency.Version.External) +
+ @sizeOf(PackageNameHash) +
+ @sizeOf(Dependency.Behavior) +
+ @sizeOf(String);
+
+pub const Context = struct {
+ allocator: std.mem.Allocator,
+ log: *logger.Log,
+ buffer: []const u8,
};
-pub fn toExternal(this: Dependency) External {
- return External{
- .name = this.name,
- .name_hash = this.name_hash,
- .behavior = this.behavior,
- .version = this.version.toExternal(),
+pub fn toDependency(
+ this: External,
+ ctx: Context,
+) Dependency {
+ return Dependency{
+ .name = String{
+ .bytes = this[0..8].*,
+ },
+ .name_hash = @bitCast(u64, this[8..16].*),
+ .behavior = @intToEnum(Dependency.Behavior, this[16]),
+ .version = Dependency.Version.toVersion(this[17..this.len].*, ctx),
};
}
+pub fn toExternal(this: Dependency) External {
+ var bytes: External = undefined;
+ bytes[0..this.name.bytes.len].* = this.name.bytes;
+ bytes[8..16].* = @bitCast([8]u8, this.name_hash);
+ bytes[16] = @enumToInt(this.behavior);
+ bytes[17..bytes.len].* = this.version.toExternal();
+ return bytes;
+}
+
pub const Version = struct {
tag: Dependency.Version.Tag = Dependency.Version.Tag.uninitialized,
literal: String = String{},
value: Value = Value{ .uninitialized = void{} },
+ pub const zeroed = Version{};
+
pub fn clone(
this: Version,
buf: []const u8,
@@ -144,30 +148,29 @@ pub const Version = struct {
return strings.cmpStringsAsc(.{}, lhs.literal.slice(string_buf), rhs.literal.slice(string_buf));
}
- pub const External = extern struct {
- tag: Dependency.Version.Tag,
- literal: String,
-
- pub fn toVersion(
- this: Version.External,
- ctx: Dependency.External.Context,
- ) Dependency.Version {
- const sliced = &this.literal.sliced(ctx.buffer);
- return Dependency.parseWithTag(
- ctx.allocator,
- sliced.slice,
- this.tag,
- sliced,
- ctx.log,
- ) orelse Dependency.Version{};
- }
- };
+ pub const External = [9]u8;
+
+ pub fn toVersion(
+ bytes: Version.External,
+ ctx: Dependency.Context,
+ ) Dependency.Version {
+ const slice = String{ .bytes = bytes[1..9].* };
+ const tag = @intToEnum(Dependency.Version.Tag, bytes[0]);
+ const sliced = &slice.sliced(ctx.buffer);
+ return Dependency.parseWithTag(
+ ctx.allocator,
+ sliced.slice,
+ tag,
+ sliced,
+ ctx.log,
+ ) orelse Dependency.Version.zeroed;
+ }
pub inline fn toExternal(this: Version) Version.External {
- return Version.External{
- .tag = this.tag,
- .literal = this.literal,
- };
+ var bytes: Version.External = undefined;
+ bytes[0] = @enumToInt(this.tag);
+ bytes[1..9].* = this.literal.bytes;
+ return bytes;
}
pub inline fn eql(
@@ -473,7 +476,12 @@ pub fn eqlResolved(a: Dependency, b: Dependency) bool {
return @as(Dependency.Version.Tag, a.version) == @as(Dependency.Version.Tag, b.version) and a.resolution == b.resolution;
}
-pub fn parse(allocator: std.mem.Allocator, dependency_: string, sliced: *const SlicedString, log: ?*logger.Log) ?Version {
+pub fn parse(
+ allocator: std.mem.Allocator,
+ dependency_: string,
+ sliced: *const SlicedString,
+ log: ?*logger.Log,
+) ?Version {
var dependency = std.mem.trimLeft(u8, dependency_, " \t\n\r");
if (dependency.len == 0) return null;
diff --git a/src/install/extract_tarball.zig b/src/install/extract_tarball.zig
index 0f5b3b5df..1ece844b9 100644
--- a/src/install/extract_tarball.zig
+++ b/src/install/extract_tarball.zig
@@ -19,7 +19,6 @@ registry: string,
cache_dir: std.fs.Dir,
temp_dir: std.fs.Dir,
package_id: PackageID,
-extracted_file_count: usize = 0,
skip_verify: bool = false,
integrity: Integrity = Integrity{},
diff --git a/src/install/install.zig b/src/install/install.zig
index 897acd19d..f23ccae08 100644
--- a/src/install/install.zig
+++ b/src/install/install.zig
@@ -47,14 +47,14 @@ const clap = @import("clap");
const ExtractTarball = @import("./extract_tarball.zig");
const Npm = @import("./npm.zig");
const Bitset = @import("./bit_set.zig").DynamicBitSetUnmanaged;
+const z_allocator = @import("../memory_allocator.zig").z_allocator;
threadlocal var initialized_store = false;
// these bytes are skipped
// so we just make it repeat bun bun bun bun bun bun bun bun bun
// because why not
-const alignment_bytes_to_repeat = "bun";
-const alignment_bytes_to_repeat_buffer = "bunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbunbun";
+const alignment_bytes_to_repeat_buffer = [_]u8{0} ** 144;
const JSAst = @import("../js_ast.zig");
@@ -371,14 +371,13 @@ pub const Features = struct {
return @intToEnum(Behavior, out);
}
- // When it's a folder, we do not parse any of the dependencies
pub const folder = Features{
- .optional_dependencies = false,
- .dev_dependencies = false,
+ .optional_dependencies = true,
+ .dev_dependencies = true,
.scripts = false,
- .peer_dependencies = false,
+ .peer_dependencies = true,
.is_main = false,
- .dependencies = false,
+ .dependencies = true,
};
pub const npm = Features{
@@ -476,15 +475,41 @@ pub const Lockfile = struct {
summary: PackageInstall.Summary,
};
- pub const Tree = extern struct {
+ pub const Tree = struct {
id: Id = invalid_id,
package_id: PackageID = invalid_package_id,
parent: Id = invalid_id,
packages: Lockfile.PackageIDSlice = Lockfile.PackageIDSlice{},
+ pub const external_size = @sizeOf(Id) + @sizeOf(PackageID) + @sizeOf(Id) + @sizeOf(Lockfile.PackageIDSlice);
+ pub const External = [external_size]u8;
pub const Slice = ExternalSlice(Tree);
pub const List = std.ArrayListUnmanaged(Tree);
pub const Id = u32;
+
+ pub fn toExternal(this: Tree) External {
+ var out = External{};
+ out[0..4].* = @bitCast(Id, this.id);
+ out[4..8].* = @bitCast(Id, this.package_id);
+ out[8..12].* = @bitCast(Id, this.parent);
+ out[12..16].* = @bitCast(u32, this.packages.off);
+ out[16..20].* = @bitCast(u32, this.packages.len);
+ if (out.len != 20) @compileError("Tree.External is not 20 bytes");
+ return out;
+ }
+
+ pub fn toTree(out: External) Tree {
+ return Tree{
+ .id = @bitCast(Id, out[0..4].*),
+ .package_id = @bitCast(Id, out[4..8].*),
+ .parent = @bitCast(Id, out[8..12].*),
+ .packages = .{
+ .off = @bitCast(u32, out[12..16].*),
+ .len = @bitCast(u32, out[16..20].*),
+ },
+ };
+ }
+
const invalid_id: Id = std.math.maxInt(Id);
const dependency_loop = invalid_id - 1;
const hoisted = invalid_id - 2;
@@ -603,19 +628,30 @@ pub const Lockfile = struct {
/// Flatten the multi-dimensional ArrayList of package IDs into a single easily serializable array
pub fn clean(this: *Builder) ![]PackageID {
- const end = @truncate(Id, this.list.len);
+ var end = @truncate(Id, this.list.len);
var i: Id = 0;
var total_packages_count: u32 = 0;
var trees = this.list.items(.tree);
var packages = this.list.items(.packages);
+ var real_end: Id = 0;
+ const max_package_id = @truncate(PackageID, this.resolutions.len);
// TODO: can we cull empty trees here?
while (i < end) : (i += 1) {
+ const prev = total_packages_count;
total_packages_count += trees[i].packages.len;
+ if (!(prev == total_packages_count and trees[i].package_id >= max_package_id)) {
+ trees[real_end] = trees[i];
+ packages[real_end] = packages[i];
+ real_end += 1;
+ }
}
+ this.list.len = real_end;
+ trees = trees[0..real_end];
+ packages = packages[0..real_end];
- var package_ids = try this.allocator.alloc(PackageID, total_packages_count);
+ var package_ids = try z_allocator.alloc(PackageID, total_packages_count);
var next = PackageIDSlice{};
for (trees) |tree, id| {
@@ -2093,6 +2129,7 @@ pub const Lockfile = struct {
.bin = this.bin.clone(old_string_buf, old_extern_string_buf, new.buffers.extern_strings.items, new_extern_strings, *Lockfile.StringBuilder, builder),
.name_hash = this.name_hash,
.meta = this.meta.clone(
+ id,
old_string_buf,
*Lockfile.StringBuilder,
builder,
@@ -2514,8 +2551,16 @@ pub const Lockfile = struct {
if (json.asProperty(group.prop)) |dependencies_q| {
if (dependencies_q.expr.data == .e_object) {
for (dependencies_q.expr.data.e_object.properties.slice()) |item| {
- string_builder.count(item.key.?.asString(allocator) orelse "");
- string_builder.count(item.value.?.asString(allocator) orelse "");
+ const key = item.key.?.asString(allocator) orelse "";
+ const value = item.value.?.asString(allocator) orelse "";
+
+ string_builder.count(key);
+ string_builder.count(value);
+
+ // If it's a folder, pessimistically assume we will need a maximum path
+ if (Dependency.Version.Tag.infer(value) == .folder) {
+ string_builder.cap += std.fs.MAX_PATH_BYTES;
+ }
}
total_dependencies_count += @truncate(u32, dependencies_q.expr.data.e_object.properties.len);
}
@@ -2578,12 +2623,31 @@ pub const Lockfile = struct {
lockfile.buffers.string_bytes.items,
);
- const dependency_version = Dependency.parse(
+ var dependency_version = Dependency.parse(
allocator,
sliced.slice,
&sliced,
log,
) orelse Dependency.Version{};
+
+ if (dependency_version.tag == .folder) {
+ const folder_path = dependency_version.value.folder.slice(lockfile.buffers.string_bytes.items);
+ dependency_version.value.folder = string_builder.append(
+ String,
+ Path.relative(
+ FileSystem.instance.top_level_dir,
+ Path.joinAbsString(
+ FileSystem.instance.top_level_dir,
+ &[_]string{
+ source.path.name.dir,
+ folder_path,
+ },
+ .posix,
+ ),
+ ),
+ );
+ }
+
const this_dep = Dependency{
.behavior = group.behavior,
.name = external_name.value,
@@ -2652,6 +2716,8 @@ pub const Lockfile = struct {
lockfile.buffers.dependencies.items = lockfile.buffers.dependencies.items.ptr[0 .. lockfile.buffers.dependencies.items.len + total_dependencies_count];
lockfile.buffers.resolutions.items = lockfile.buffers.resolutions.items.ptr[0..lockfile.buffers.dependencies.items.len];
+
+ string_builder.clamp();
}
pub const List = std.MultiArrayList(Lockfile.Package);
@@ -2908,10 +2974,18 @@ pub const Lockfile = struct {
try writer.writeIntLittle(u64, 0xDEADBEEF);
try writer.writeIntLittle(u64, 0xDEADBEEF);
+ const prefix = comptime std.fmt.comptimePrint(
+ "\n<{s}> {d} sizeof, {d} alignof\n",
+ .{
+ @typeName(std.meta.Child(ArrayList)),
+ @sizeOf(std.meta.Child(ArrayList)),
+ @alignOf(std.meta.Child(ArrayList)),
+ },
+ );
+ try writer.writeAll(prefix);
+
if (bytes.len > 0) {
- if (std.meta.Child(ArrayList) != u8) {
- _ = try Aligner.write([*]std.meta.Child(ArrayList), Writer, writer, try stream.getPos());
- }
+ _ = try Aligner.write(sizes.types[0], Writer, writer, try stream.getPos());
const real_start_pos = try stream.getPos();
try writer.writeAll(bytes);
@@ -2944,9 +3018,10 @@ pub const Lockfile = struct {
// It would be faster to buffer these instead of one big allocation
var to_clone = try std.ArrayListUnmanaged(Dependency.External).initCapacity(allocator, remaining.len);
+
defer to_clone.deinit(allocator);
for (remaining) |dep| {
- to_clone.appendAssumeCapacity(dep.toExternal());
+ to_clone.appendAssumeCapacity(Dependency.toExternal(dep));
}
try writeArray(StreamType, stream, Writer, writer, []Dependency.External, to_clone.items);
@@ -2954,7 +3029,23 @@ pub const Lockfile = struct {
var list = @field(this, name);
const items = list.items;
const Type = @TypeOf(items);
- try writeArray(StreamType, stream, Writer, writer, Type, items);
+ if (comptime Type == Tree) {
+ // We duplicate it here so that alignment bytes are zeroed out
+ var clone = try std.ArrayListUnmanaged(Tree.External).initCapacity(allocator, list.items.len);
+ for (list.items) |item| {
+ clone.appendAssumeCapacity(Tree.toExternal(item));
+ }
+ defer clone.deinit(allocator);
+
+ try writeArray(StreamType, stream, Writer, writer, Tree.External, clone.items);
+ } else {
+ // We duplicate it here so that alignment bytes are zeroed out
+ var clone = try std.ArrayListUnmanaged(std.meta.Child(Type)).initCapacity(allocator, list.items.len);
+ clone.appendSliceAssumeCapacity(items);
+ defer clone.deinit(allocator);
+
+ try writeArray(StreamType, stream, Writer, writer, Type, clone.items);
+ }
}
if (comptime Environment.isDebug) {
@@ -2981,6 +3072,15 @@ pub const Lockfile = struct {
if (PackageManager.instance.options.log_level.isVerbose()) {
Output.prettyErrorln("Loaded {d} {s}", .{ external_dependency_list_.items.len, name });
}
+ } else if (comptime Type == @TypeOf(this.trees)) {
+ var tree_list = try readArray(stream, allocator, std.ArrayListUnmanaged(Tree.External));
+ defer tree_list.deinit(allocator);
+ this.trees = try Tree.List.initCapacity(allocator, tree_list.items.len);
+ this.trees.items.len = tree_list.items.len;
+
+ for (tree_list.items) |tree, j| {
+ this.trees.items[j] = Tree.toTree(tree);
+ }
} else {
@field(this, sizes.names[i]) = try readArray(stream, allocator, Type);
if (PackageManager.instance.options.log_level.isVerbose()) {
@@ -2997,7 +3097,7 @@ pub const Lockfile = struct {
// Dependencies are serialized separately.
// This is unfortunate. However, not using pointers for Semver Range's make the code a lot more complex.
this.dependencies = try DependencyList.initCapacity(allocator, external_dependency_list.len);
- const extern_context = Dependency.External.Context{
+ const extern_context = Dependency.Context{
.log = log,
.allocator = allocator,
.buffer = this.string_bytes.items,
@@ -3006,7 +3106,7 @@ pub const Lockfile = struct {
this.dependencies.expandToCapacity();
this.dependencies.items.len = external_dependency_list.len;
for (external_dependency_list) |dep, i| {
- this.dependencies.items[i] = dep.toDependency(extern_context);
+ this.dependencies.items[i] = Dependency.toDependency(dep, extern_context);
}
return this;
@@ -3018,6 +3118,10 @@ pub const Lockfile = struct {
const header_bytes: string = "#!/usr/bin/env bun\n" ++ version;
pub fn save(this: *Lockfile, comptime StreamType: type, stream: StreamType) !void {
+ var old_package_list = this.packages;
+ this.packages = try this.packages.clone(z_allocator);
+ old_package_list.deinit(this.allocator);
+
var writer = stream.writer();
try writer.writeAll(header_bytes);
try writer.writeIntLittle(u32, @enumToInt(this.format));
@@ -3025,10 +3129,10 @@ pub const Lockfile = struct {
try writer.writeIntLittle(u64, 0);
try Lockfile.Package.Serializer.save(this.packages, StreamType, stream, @TypeOf(&writer), &writer);
- try Lockfile.Buffers.save(this.buffers, this.allocator, StreamType, stream, @TypeOf(&writer), &writer);
+ try Lockfile.Buffers.save(this.buffers, z_allocator, StreamType, stream, @TypeOf(&writer), &writer);
try writer.writeIntLittle(u64, 0);
const end = try stream.getPos();
- try writer.writeAll(alignment_bytes_to_repeat_buffer);
+ try writer.writeAll(&alignment_bytes_to_repeat_buffer);
_ = try std.os.pwrite(stream.handle, std.mem.asBytes(&end), pos);
try std.os.ftruncate(stream.handle, try stream.getPos());
@@ -3285,29 +3389,42 @@ const PackageInstall = struct {
var resolution_buf: [512]u8 = undefined;
var resolution_label = std.fmt.bufPrint(&resolution_buf, "{}", .{resolution.fmt(ctx.string_buf)}) catch unreachable;
+ this.package_install = PackageInstall{
+ .cache_dir = undefined,
+ .cache_dir_subpath = undefined,
+ .progress = ctx.progress,
+
+ .destination_dir = this.destination_dir,
+ .destination_dir_subpath = destination_dir_subpath,
+ .destination_dir_subpath_buf = &destination_dir_subpath_buf,
+ .allocator = ctx.allocator,
+ .package_name = name,
+ .package_version = resolution_label,
+ };
+
switch (resolution.tag) {
.npm => {
- this.package_install = PackageInstall{
- .cache_dir = ctx.cache_dir,
- .progress = ctx.progress,
- .cache_dir_subpath = PackageManager.instance.cachedNPMPackageFolderNamePrint(&cache_dir_subpath_buf, name, resolution.value.npm),
- .destination_dir = this.destination_dir,
- .destination_dir_subpath = destination_dir_subpath,
- .destination_dir_subpath_buf = &destination_dir_subpath_buf,
- .allocator = ctx.allocator,
- .package_name = name,
- .package_version = resolution_label,
- };
+ this.package_install.cache_dir_subpath = this.manager.cachedNPMPackageFolderName(name, resolution.value.npm);
+ this.package_install.cache_dir = this.manager.getCacheDirectory();
+ },
+ .folder => {
+ var folder_buf = &cache_dir_subpath_buf;
+ const folder = resolution.value.folder.slice(ctx.string_buf);
+ std.mem.copy(u8, folder_buf, "../" ++ std.fs.path.sep_str);
+ std.mem.copy(u8, folder_buf["../".len..], folder);
+ folder_buf["../".len + folder.len] = 0;
+ this.package_install.cache_dir_subpath = folder_buf[0 .. "../".len + folder.len :0];
+ this.package_install.cache_dir = std.fs.cwd();
+ },
+ else => return,
+ }
- const needs_install = ctx.skip_verify or !this.package_install.verify();
+ const needs_install = ctx.skip_verify or !this.package_install.verify();
- if (needs_install) {
- this.result = this.package_install.install(ctx.skip_verify);
- } else {
- this.result = .{ .skip = .{} };
- }
- },
- else => {},
+ if (needs_install) {
+ this.result = this.package_install.install(ctx.skip_verify);
+ } else {
+ this.result = .{ .skip = .{} };
}
ctx.channel.writeItem(this) catch unreachable;
@@ -4386,7 +4503,12 @@ pub const PackageManager = struct {
.err => |err| return err,
.package_id => |package_id| {
this.lockfile.buffers.resolutions.items[dependency_id] = package_id;
- return ResolvedPackageResult{ .package = this.lockfile.packages.get(res.package_id) };
+ return ResolvedPackageResult{ .package = this.lockfile.packages.get(package_id) };
+ },
+
+ .new_package_id => |package_id| {
+ this.lockfile.buffers.resolutions.items[dependency_id] = package_id;
+ return ResolvedPackageResult{ .package = this.lockfile.packages.get(package_id), .is_first_time = true };
},
}
},
@@ -4500,7 +4622,11 @@ pub const PackageManager = struct {
if (comptime !is_main) {
// it might really be main
if (!this.root_dependency_list.contains(id))
- if (!dependency.behavior.isEnabled(Features.npm))
+ if (!dependency.behavior.isEnabled(switch (dependency.version.tag) {
+ .folder => this.options.remote_package_features,
+ .dist_tag, .npm => this.options.remote_package_features,
+ else => Features{},
+ }))
return;
}
@@ -6710,6 +6836,7 @@ pub const PackageManager = struct {
has_created_bin: bool = false,
global_bin_dir: std.fs.Dir,
destination_dir_subpath_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined,
+ folder_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined,
install_count: usize = 0,
successfully_installed: Bitset,
@@ -6764,167 +6891,139 @@ pub const PackageManager = struct {
const buf = this.lockfile.buffers.string_bytes.items;
const extern_string_buf = this.lockfile.buffers.extern_strings.items;
var resolution_label = std.fmt.bufPrint(&resolution_buf, "{}", .{resolution.fmt(buf)}) catch unreachable;
+ var installer = PackageInstall{
+ .progress = this.progress,
+ .cache_dir = undefined,
+ .cache_dir_subpath = undefined,
+ .destination_dir = this.node_modules_folder,
+ .destination_dir_subpath = destination_dir_subpath,
+ .destination_dir_subpath_buf = &this.destination_dir_subpath_buf,
+ .allocator = this.lockfile.allocator,
+ .package_name = name,
+ .package_version = resolution_label,
+ };
+
switch (resolution.tag) {
.npm => {
- var installer = PackageInstall{
- .cache_dir = this.manager.getCacheDirectory(),
- .progress = this.progress,
- .cache_dir_subpath = this.manager.cachedNPMPackageFolderName(name, resolution.value.npm),
- .destination_dir = this.node_modules_folder,
- .destination_dir_subpath = destination_dir_subpath,
- .destination_dir_subpath_buf = &this.destination_dir_subpath_buf,
- .allocator = this.lockfile.allocator,
- .package_name = name,
- .package_version = resolution_label,
- };
+ installer.cache_dir_subpath = this.manager.cachedNPMPackageFolderName(name, resolution.value.npm);
+ installer.cache_dir = this.manager.getCacheDirectory();
+ },
+ .folder => {
+ const folder = resolution.value.folder.slice(buf);
+ @memcpy(&this.folder_path_buf, folder.ptr, folder.len);
+ this.folder_path_buf[folder.len] = 0;
+ installer.cache_dir_subpath = std.meta.assumeSentinel(this.folder_path_buf[0..folder.len], 0);
+ installer.cache_dir = std.fs.cwd();
+ },
+ else => return,
+ }
+ const needs_install = this.force_install or this.skip_verify or !installer.verify();
+ this.summary.skipped += @as(u32, @boolToInt(!needs_install));
- const needs_install = this.force_install or this.skip_verify or !installer.verify();
- this.summary.skipped += @as(u32, @boolToInt(!needs_install));
+ if (needs_install) {
+ const result = installer.install(this.skip_delete);
+ switch (result) {
+ .success => {
+ const is_duplicate = this.successfully_installed.isSet(package_id);
+ this.summary.success += @as(u32, @boolToInt(!is_duplicate));
+ this.successfully_installed.set(package_id);
- if (needs_install) {
- const result = installer.install(this.skip_delete);
- switch (result) {
- .success => {
- const is_duplicate = this.successfully_installed.isSet(package_id);
- this.summary.success += @as(u32, @boolToInt(!is_duplicate));
- this.successfully_installed.set(package_id);
+ if (comptime log_level.showProgress()) {
+ this.node.completeOne();
+ }
- if (comptime log_level.showProgress()) {
- this.node.completeOne();
- }
+ const bin = this.bins[package_id];
+ if (bin.tag != .none) {
+ if (!this.has_created_bin) {
+ Bin.Linker.umask = C.umask(0);
+ if (!this.options.global)
+ this.node_modules_folder.makeDirZ(".bin") catch {};
- const bin = this.bins[package_id];
- if (bin.tag != .none) {
- if (!this.has_created_bin) {
- Bin.Linker.umask = C.umask(0);
- if (!this.options.global)
- this.node_modules_folder.makeDirZ(".bin") catch {};
+ this.has_created_bin = true;
+ }
- this.has_created_bin = true;
+ const bin_task_id = Task.Id.forBinLink(package_id);
+ var task_queue = this.manager.task_queue.getOrPut(this.manager.allocator, bin_task_id) catch unreachable;
+ if (!task_queue.found_existing) {
+ run_bin_link: {
+ if (std.mem.indexOfScalar(PackageNameHash, this.options.native_bin_link_allowlist, String.Builder.stringHash(name)) != null) {
+ this.platform_binlinks.append(this.lockfile.allocator, .{
+ .package_id = package_id,
+ .node_modules_folder = this.node_modules_folder,
+ }) catch unreachable;
+ break :run_bin_link;
}
- const bin_task_id = Task.Id.forBinLink(package_id);
- var task_queue = this.manager.task_queue.getOrPut(this.manager.allocator, bin_task_id) catch unreachable;
- if (!task_queue.found_existing) {
- run_bin_link: {
- if (std.mem.indexOfScalar(PackageNameHash, this.options.native_bin_link_allowlist, String.Builder.stringHash(name)) != null) {
- this.platform_binlinks.append(this.lockfile.allocator, .{
- .package_id = package_id,
- .node_modules_folder = this.node_modules_folder,
- }) catch unreachable;
- break :run_bin_link;
- }
-
- var bin_linker = Bin.Linker{
- .bin = bin,
- .package_installed_node_modules = this.node_modules_folder.fd,
- .global_bin_path = this.options.bin_path,
- .global_bin_dir = this.options.global_bin_dir,
-
- // .destination_dir_subpath = destination_dir_subpath,
- .root_node_modules_folder = this.root_node_modules_folder.fd,
- .package_name = strings.StringOrTinyString.init(name),
- .string_buf = buf,
- .extern_string_buf = extern_string_buf,
- };
-
- bin_linker.link(this.manager.options.global);
- if (bin_linker.err) |err| {
- if (comptime log_level != .silent) {
- const fmt = "\n<r><red>error:<r> linking <b>{s}<r>: {s}\n";
- const args = .{ name, @errorName(err) };
-
- if (comptime log_level.showProgress()) {
- if (Output.enable_ansi_colors) {
- this.progress.log(comptime Output.prettyFmt(fmt, true), args);
- } else {
- this.progress.log(comptime Output.prettyFmt(fmt, false), args);
- }
- } else {
- Output.prettyErrorln(fmt, args);
- }
- }
+ var bin_linker = Bin.Linker{
+ .bin = bin,
+ .package_installed_node_modules = this.node_modules_folder.fd,
+ .global_bin_path = this.options.bin_path,
+ .global_bin_dir = this.options.global_bin_dir,
+
+ // .destination_dir_subpath = destination_dir_subpath,
+ .root_node_modules_folder = this.root_node_modules_folder.fd,
+ .package_name = strings.StringOrTinyString.init(name),
+ .string_buf = buf,
+ .extern_string_buf = extern_string_buf,
+ };
- if (this.manager.options.enable.fail_early) {
- installer.uninstall() catch {};
- Global.exit(1);
+ bin_linker.link(this.manager.options.global);
+ if (bin_linker.err) |err| {
+ if (comptime log_level != .silent) {
+ const fmt = "\n<r><red>error:<r> linking <b>{s}<r>: {s}\n";
+ const args = .{ name, @errorName(err) };
+
+ if (comptime log_level.showProgress()) {
+ if (Output.enable_ansi_colors) {
+ this.progress.log(comptime Output.prettyFmt(fmt, true), args);
+ } else {
+ this.progress.log(comptime Output.prettyFmt(fmt, false), args);
}
+ } else {
+ Output.prettyErrorln(fmt, args);
}
}
- }
- }
- },
- .fail => |cause| {
- if (cause.isPackageMissingFromCache()) {
- const task_id = Task.Id.forNPMPackage(Task.Tag.extract, name, resolution.value.npm);
- var task_queue = this.manager.task_queue.getOrPut(this.manager.allocator, task_id) catch unreachable;
- if (!task_queue.found_existing) {
- task_queue.value_ptr.* = .{};
- }
- task_queue.value_ptr.append(
- this.manager.allocator,
- .{
- .node_modules_folder = @intCast(u32, this.node_modules_folder.fd),
- },
- ) catch unreachable;
-
- if (this.manager.generateNetworkTaskForTarball(task_id, this.lockfile.packages.get(package_id)) catch unreachable) |task| {
- task.schedule(&this.manager.network_tarball_batch);
- if (this.manager.network_tarball_batch.len > 0) {
- _ = this.manager.scheduleNetworkTasks();
+ if (this.manager.options.enable.fail_early) {
+ installer.uninstall() catch {};
+ Global.exit(1);
}
}
- } else {
- Output.prettyErrorln(
- "<r><red>error<r>: <b><red>{s}<r> installing <b>{s}<r>",
- .{ @errorName(cause.err), this.names[package_id].slice(buf) },
- );
- this.summary.fail += 1;
}
- },
- else => {},
+ }
}
- }
- },
- // TODO: support folder links deeper than node_modules folder
- // if node_modules/foo/package.json has a folder:, then this will not create a valid symlink
- .folder => {
- var folder_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
- const folder = resolution.value.folder.slice(buf);
- std.mem.copy(u8, &folder_buf, "../" ++ std.fs.path.sep_str);
- std.mem.copy(u8, folder_buf["../".len..], folder);
- folder_buf["../".len + folder.len] = 0;
- var folderZ: [:0]u8 = folder_buf[0 .. "../".len + folder.len :0];
-
- const needs_install = this.force_install or this.skip_verify or brk: {
- std.mem.copy(u8, this.destination_dir_subpath_buf[name.len..], std.fs.path.sep_str ++ "package.json");
- this.destination_dir_subpath_buf[name.len + "/package.json".len] = 0;
- var package_json_path = this.destination_dir_subpath_buf[0 .. name.len + "/package.json".len :0];
- defer this.destination_dir_subpath_buf[name.len] = 0;
- const file = this.node_modules_folder.openFileZ(package_json_path, .{ .read = true }) catch break :brk true;
- file.close();
-
- break :brk false;
- };
- this.summary.skipped += @as(u32, @boolToInt(!needs_install));
+ },
+ .fail => |cause| {
+ if (cause.isPackageMissingFromCache()) {
+ const task_id = Task.Id.forNPMPackage(Task.Tag.extract, name, resolution.value.npm);
+ var task_queue = this.manager.task_queue.getOrPut(this.manager.allocator, task_id) catch unreachable;
+ if (!task_queue.found_existing) {
+ task_queue.value_ptr.* = .{};
+ }
- if (needs_install) {
- if (!this.skip_delete) this.node_modules_folder.deleteFileZ(destination_dir_subpath) catch {};
+ task_queue.value_ptr.append(
+ this.manager.allocator,
+ .{
+ .node_modules_folder = @intCast(u32, this.node_modules_folder.fd),
+ },
+ ) catch unreachable;
- std.os.symlinkatZ(folderZ, this.node_modules_folder.fd, destination_dir_subpath) catch |err| {
+ if (this.manager.generateNetworkTaskForTarball(task_id, this.lockfile.packages.get(package_id)) catch unreachable) |task| {
+ task.schedule(&this.manager.network_tarball_batch);
+ if (this.manager.network_tarball_batch.len > 0) {
+ _ = this.manager.scheduleNetworkTasks();
+ }
+ }
+ } else {
Output.prettyErrorln(
"<r><red>error<r>: <b><red>{s}<r> installing <b>{s}<r>",
- .{ err, this.names[package_id].slice(buf) },
+ .{ @errorName(cause.err), this.names[package_id].slice(buf) },
);
this.summary.fail += 1;
- return;
- };
- this.summary.success += 1;
- this.successfully_installed.set(package_id);
- }
- },
- else => {},
+ }
+ },
+ else => {},
+ }
}
}
diff --git a/src/install/resolvers/folder_resolver.zig b/src/install/resolvers/folder_resolver.zig
index b7261c9f1..9d92a5b30 100644
--- a/src/install/resolvers/folder_resolver.zig
+++ b/src/install/resolvers/folder_resolver.zig
@@ -15,9 +15,10 @@ const String = @import("../semver.zig").String;
pub const FolderResolution = union(Tag) {
package_id: PackageID,
+ new_package_id: PackageID,
err: anyerror,
- pub const Tag = enum { package_id, err };
+ pub const Tag = enum { package_id, err, new_package_id };
pub const Map = std.HashMapUnmanaged(u64, FolderResolution, IdentityContext(u64), 80);
@@ -119,6 +120,6 @@ pub const FolderResolution = union(Tag) {
package = manager.lockfile.appendPackage(package) catch unreachable;
entry.value_ptr.* = .{ .package_id = package.meta.id };
- return FolderResolution{ .package_id = package.meta.id };
+ return FolderResolution{ .new_package_id = package.meta.id };
}
};