diff options
-rwxr-xr-x | bun.lockb | bin | 7340 -> 7340 bytes | |||
-rw-r--r-- | src/api/schema.d.ts | 1 | ||||
-rw-r--r-- | src/api/schema.js | 10 | ||||
-rw-r--r-- | src/api/schema.peechy | 1 | ||||
-rw-r--r-- | src/api/schema.zig | 10 | ||||
-rw-r--r-- | src/bunfig.zig | 10 | ||||
-rw-r--r-- | src/cli.zig | 12 | ||||
-rw-r--r-- | src/install/bin.zig | 177 | ||||
-rw-r--r-- | src/install/install.zig | 86 |
9 files changed, 273 insertions, 34 deletions
Binary files differ diff --git a/src/api/schema.d.ts b/src/api/schema.d.ts index d896bc160..6c667e455 100644 --- a/src/api/schema.d.ts +++ b/src/api/schema.d.ts @@ -632,6 +632,7 @@ export interface BunInstall { native_bin_links?: string[]; disable_cache?: boolean; disable_manifest_cache?: boolean; + global_dir?: string; } export declare function encodeStackFrame( diff --git a/src/api/schema.js b/src/api/schema.js index 67852d027..2a1fd8c1d 100644 --- a/src/api/schema.js +++ b/src/api/schema.js @@ -2902,6 +2902,10 @@ function decodeBunInstall(bb) { result["disable_manifest_cache"] = !!bb.readByte(); break; + case 17: + result["global_dir"] = bb.readString(); + break; + default: throw new Error("Attempted to parse invalid message"); } @@ -3010,6 +3014,12 @@ function encodeBunInstall(message, bb) { bb.writeByte(16); bb.writeByte(value); } + + var value = message["global_dir"]; + if (value != null) { + bb.writeByte(17); + bb.writeString(value); + } bb.writeByte(0); } diff --git a/src/api/schema.peechy b/src/api/schema.peechy index a7284f713..59c239aa8 100644 --- a/src/api/schema.peechy +++ b/src/api/schema.peechy @@ -547,4 +547,5 @@ message BunInstall { bool disable_cache = 15; bool disable_manifest_cache = 16; + string global_dir = 17; }
\ No newline at end of file diff --git a/src/api/schema.zig b/src/api/schema.zig index 8879b34cb..e12eda9d5 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -2719,6 +2719,9 @@ pub const Api = struct { /// disable_manifest_cache disable_manifest_cache: ?bool = null, + /// global_dir + global_dir: ?[]const u8 = null, + pub fn decode(reader: anytype) anyerror!BunInstall { var this = std.mem.zeroes(BunInstall); @@ -2776,6 +2779,9 @@ pub const Api = struct { 16 => { this.disable_manifest_cache = try reader.readValue(bool); }, + 17 => { + this.global_dir = try reader.readValue([]const u8); + }, else => { return error.InvalidMessage; }, @@ -2849,6 +2855,10 @@ pub const Api = struct { try writer.writeFieldID(16); try writer.writeInt(@as(u8, @boolToInt(disable_manifest_cache))); } + if (this.global_dir) |global_dir| { + try writer.writeFieldID(17); + try writer.writeValue(@TypeOf(global_dir), global_dir); + } try writer.endMessage(); } }; diff --git a/src/bunfig.zig b/src/bunfig.zig index 65e200098..78aed4761 100644 --- a/src/bunfig.zig +++ b/src/bunfig.zig @@ -237,7 +237,7 @@ pub const Bunfig = struct { } if (bun.get("lockfile")) |lockfile_expr| { - if (lockfile_expr.get("outputFormat")) |lockfile| { + if (lockfile_expr.get("print")) |lockfile| { try this.expect(lockfile, .e_string); if (lockfile.asString(this.allocator)) |value| { if (!(strings.eqlComptime(value, "bun"))) { @@ -287,6 +287,12 @@ pub const Bunfig = struct { } } + if (bun.get("globalDir")) |dir| { + if (dir.asString()) |value| { + install.global_dir = value; + } + } + if (bun.get("logLevel")) |expr| { try this.loadLogLevel(expr); } @@ -320,7 +326,7 @@ pub const Bunfig = struct { } } - if (cache.get("directory")) |directory| { + if (cache.get("dir")) |directory| { if (directory.asString(allocator)) |value| { install.cache_directory = value; } diff --git a/src/cli.zig b/src/cli.zig index ed588a2cd..f00c0b5a8 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -795,7 +795,16 @@ pub const Command = struct { RootCommandMatcher.case("completions") => .InstallCompletionsCommand, RootCommandMatcher.case("getcompletes") => .GetCompletionsCommand, - RootCommandMatcher.case("i"), RootCommandMatcher.case("install") => .InstallCommand, + RootCommandMatcher.case("i"), RootCommandMatcher.case("install") => brk: { + for (args_iter.buf) |arg| { + const span = std.mem.span(arg); + if (span.len > 0 and (strings.eqlComptime(span, "-g") or strings.eqlComptime(span, "--global"))) { + break :brk Command.Tag.AddCommand; + } + } + + break :brk Command.Tag.InstallCommand; + }, RootCommandMatcher.case("c"), RootCommandMatcher.case("create") => .CreateCommand, RootCommandMatcher.case(TestCommand.name) => .TestCommand, @@ -836,6 +845,7 @@ pub const Command = struct { pub fn start(allocator: std.mem.Allocator, log: *logger.Log) !void { const tag = which(); + switch (tag) { .DiscordCommand => return try DiscordCommand.exec(allocator), .HelpCommand => return try HelpCommand.exec(allocator), diff --git a/src/install/bin.zig b/src/install/bin.zig index 810009be3..15e768bca 100644 --- a/src/install/bin.zig +++ b/src/install/bin.zig @@ -7,6 +7,9 @@ const strings = @import("strings"); const Environment = @import("../env.zig"); const Path = @import("../resolver/resolve_path.zig"); const C = @import("../c.zig"); +const Fs = @import("../fs.zig"); +const stringZ = @import("../global.zig").stringZ; + /// Normalized `bin` field in [package.json](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#bin) /// Can be a: /// - file path (relative to the package root) @@ -130,6 +133,8 @@ pub const Bin = extern struct { /// Used for generating relative paths package_name: strings.StringOrTinyString, + global_bin_dir: stringZ = "", + string_buf: []const u8, err: ?anyerror = null, @@ -159,14 +164,14 @@ pub const Bin = extern struct { // That way, if you move your node_modules folder around, the symlinks in .bin still work // If we used absolute paths for the symlinks, you'd end up with broken symlinks pub fn link(this: *Linker) void { - var from_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; - var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var target_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var dest_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; - from_buf[0..".bin/".len].* = ".bin/".*; - var from_remain: []u8 = from_buf[".bin/".len..]; - path_buf[0.."../".len].* = "../".*; + target_buf[0..".bin/".len].* = ".bin/".*; + var from_remain: []u8 = target_buf[".bin/".len..]; + dest_buf[0.."../".len].* = "../".*; - var remain: []u8 = path_buf["../".len..]; + var remain: []u8 = dest_buf["../".len..]; const name = this.package_name.slice(); std.mem.copy(u8, remain, name); remain = remain[name.len..]; @@ -192,17 +197,17 @@ pub const Bin = extern struct { std.mem.copy(u8, remain, target); remain = remain[target.len..]; remain[0] = 0; - const target_len = @ptrToInt(remain.ptr) - @ptrToInt(&path_buf); + const target_len = @ptrToInt(remain.ptr) - @ptrToInt(&dest_buf); remain = remain[1..]; - var target_path: [:0]u8 = path_buf[0..target_len :0]; + var target_path: [:0]u8 = dest_buf[0..target_len :0]; // we need to use the unscoped package name here // this is why @babel/parser would fail to link const unscoped_name = unscopedPackageName(name); std.mem.copy(u8, from_remain, unscoped_name); from_remain = from_remain[unscoped_name.len..]; from_remain[0] = 0; - var dest_path: [:0]u8 = from_buf[0 .. @ptrToInt(from_remain.ptr) - @ptrToInt(&from_buf) :0]; + var dest_path: [:0]u8 = target_buf[0 .. @ptrToInt(from_remain.ptr) - @ptrToInt(&target_buf) :0]; std.os.symlinkatZ(target_path, this.root_node_modules_folder, dest_path) catch |err| { // Silently ignore PathAlreadyExists @@ -224,15 +229,15 @@ pub const Bin = extern struct { std.mem.copy(u8, remain, target); remain = remain[target.len..]; remain[0] = 0; - const target_len = @ptrToInt(remain.ptr) - @ptrToInt(&path_buf); + const target_len = @ptrToInt(remain.ptr) - @ptrToInt(&dest_buf); remain = remain[1..]; - var target_path: [:0]u8 = path_buf[0..target_len :0]; + var target_path: [:0]u8 = dest_buf[0..target_len :0]; var name_to_use = this.bin.value.named_file[0].slice(this.string_buf); std.mem.copy(u8, from_remain, name_to_use); from_remain = from_remain[name_to_use.len..]; from_remain[0] = 0; - var dest_path: [:0]u8 = from_buf[0 .. @ptrToInt(from_remain.ptr) - @ptrToInt(&from_buf) :0]; + var dest_path: [:0]u8 = target_buf[0 .. @ptrToInt(from_remain.ptr) - @ptrToInt(&target_buf) :0]; std.os.symlinkatZ(target_path, this.root_node_modules_folder, dest_path) catch |err| { // Silently ignore PathAlreadyExists @@ -257,9 +262,9 @@ pub const Bin = extern struct { remain[0] = 0; var dir = std.fs.Dir{ .fd = this.package_installed_node_modules }; - var joined = Path.joinStringBuf(&from_buf, &parts, .auto); - from_buf[joined.len] = 0; - var joined_: [:0]u8 = from_buf[0..joined.len :0]; + var joined = Path.joinStringBuf(&target_buf, &parts, .auto); + target_buf[joined.len] = 0; + var joined_: [:0]u8 = target_buf[0..joined.len :0]; var child_dir = dir.openDirZ(joined_, .{ .iterate = true }) catch |err| { this.err = err; return; @@ -268,22 +273,22 @@ pub const Bin = extern struct { var iter = child_dir.iterate(); - var basedir_path = std.os.getFdPath(child_dir.fd, &from_buf) catch |err| { + var basedir_path = std.os.getFdPath(child_dir.fd, &target_buf) catch |err| { this.err = err; return; }; - from_buf[basedir_path.len] = std.fs.path.sep; - var from_buf_remain = from_buf[basedir_path.len + 1 ..]; + target_buf[basedir_path.len] = std.fs.path.sep; + var target_buf_remain = target_buf[basedir_path.len + 1 ..]; while (iter.next() catch null) |entry_| { const entry: std.fs.Dir.Entry = entry_; switch (entry.kind) { std.fs.Dir.Entry.Kind.SymLink, std.fs.Dir.Entry.Kind.File => { - std.mem.copy(u8, from_buf_remain, entry.name); - from_buf_remain = from_buf_remain[entry.name.len..]; - from_buf_remain[0] = 0; - var from_path: [:0]u8 = from_buf[0 .. @ptrToInt(from_buf_remain.ptr) - @ptrToInt(&from_buf) :0]; - var to_path = std.fmt.bufPrintZ(&path_buf, ".bin/{s}", .{entry.name}) catch unreachable; + std.mem.copy(u8, target_buf_remain, entry.name); + target_buf_remain = target_buf_remain[entry.name.len..]; + target_buf_remain[0] = 0; + var from_path: [:0]u8 = target_buf[0 .. @ptrToInt(target_buf_remain.ptr) - @ptrToInt(&target_buf) :0]; + var to_path = std.fmt.bufPrintZ(&dest_buf, ".bin/{s}", .{entry.name}) catch unreachable; std.os.symlinkatZ( from_path, @@ -312,5 +317,131 @@ pub const Bin = extern struct { }, } } + + // fn linkGlobalSymlink(this: *Linker, realpath: string, filename_in_terminal: string) void {} + + // pub fn linkGlobal(this: *Linker) void { + // var target_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + // const name = this.package_name.slice(); + + // if (comptime Environment.isWindows) { + // @compileError("Bin.Linker.link() needs to be updated to generate .cmd files on Windows"); + // } + + // switch (this.bin.tag) { + // .none => { + // if (comptime Environment.isDebug) { + // unreachable; + // } + // }, + // .file => { + // var target = this.bin.value.file.slice(this.string_buf); + + // if (strings.hasPrefix(target, "./")) { + // target = target[2..]; + // } + // @memcpy(&target_buf, target.ptr, target.len); + + // // we need to use the unscoped package name here + // // this is why @babel/parser would fail to link + // const unscoped_name = unscopedPackageName(name); + + // }, + // .named_file => { + // var target = this.bin.value.named_file[1].slice(this.string_buf); + // if (strings.hasPrefix(target, "./")) { + // target = target[2..]; + // } + // std.mem.copy(u8, remain, target); + // remain = remain[target.len..]; + // remain[0] = 0; + // const target_len = @ptrToInt(remain.ptr) - @ptrToInt(&dest_buf); + // remain = remain[1..]; + + // var target_path: [:0]u8 = dest_buf[0..target_len :0]; + // var name_to_use = this.bin.value.named_file[0].slice(this.string_buf); + // std.mem.copy(u8, from_remain, name_to_use); + // from_remain = from_remain[name_to_use.len..]; + // from_remain[0] = 0; + // var dest_path: [:0]u8 = target_buf[0 .. @ptrToInt(from_remain.ptr) - @ptrToInt(&target_buf) :0]; + + // std.os.symlinkatZ(target_path, this.root_node_modules_folder, dest_path) catch |err| { + // // Silently ignore PathAlreadyExists + // // Most likely, the symlink was already created by another package + // if (err == error.PathAlreadyExists) { + // this.setPermissions(dest_path); + // return; + // } + + // this.err = err; + // }; + // this.setPermissions(dest_path); + // }, + // .dir => { + // var target = this.bin.value.dir.slice(this.string_buf); + // var parts = [_][]const u8{ name, target }; + // if (strings.hasPrefix(target, "./")) { + // target = target[2..]; + // } + // std.mem.copy(u8, remain, target); + // remain = remain[target.len..]; + // remain[0] = 0; + // var dir = std.fs.Dir{ .fd = this.package_installed_node_modules }; + + // var joined = Path.joinStringBuf(&target_buf, &parts, .auto); + // target_buf[joined.len] = 0; + // var joined_: [:0]u8 = target_buf[0..joined.len :0]; + // var child_dir = dir.openDirZ(joined_, .{ .iterate = true }) catch |err| { + // this.err = err; + // return; + // }; + // defer child_dir.close(); + + // var iter = child_dir.iterate(); + + // var basedir_path = std.os.getFdPath(child_dir.fd, &target_buf) catch |err| { + // this.err = err; + // return; + // }; + // target_buf[basedir_path.len] = std.fs.path.sep; + // var target_buf_remain = target_buf[basedir_path.len + 1 ..]; + + // while (iter.next() catch null) |entry_| { + // const entry: std.fs.Dir.Entry = entry_; + // switch (entry.kind) { + // std.fs.Dir.Entry.Kind.SymLink, std.fs.Dir.Entry.Kind.File => { + // std.mem.copy(u8, target_buf_remain, entry.name); + // target_buf_remain = target_buf_remain[entry.name.len..]; + // target_buf_remain[0] = 0; + // var from_path: [:0]u8 = target_buf[0 .. @ptrToInt(target_buf_remain.ptr) - @ptrToInt(&target_buf) :0]; + // var to_path = std.fmt.bufPrintZ(&dest_buf, ".bin/{s}", .{entry.name}) catch unreachable; + + // std.os.symlinkatZ( + // from_path, + // this.root_node_modules_folder, + // to_path, + // ) catch |err| { + + // // Silently ignore PathAlreadyExists + // // Most likely, the symlink was already created by another package + // if (err == error.PathAlreadyExists) { + // this.setPermissions(to_path); + // continue; + // } + + // this.err = err; + // continue; + // }; + // this.setPermissions(to_path); + // }, + // else => {}, + // } + // } + // }, + // .map => { + // this.err = error.NotImplementedYet; + // }, + // } + // } }; }; diff --git a/src/install/install.zig b/src/install/install.zig index facdf9faf..8eeebb752 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -21,7 +21,7 @@ const linker = @import("../linker.zig"); const panicky = @import("../panic_handler.zig"); const sync = @import("../sync.zig"); const Api = @import("../api/schema.zig").Api; -const resolve_path = @import("../resolver/resolve_path.zig"); +const Path = @import("../resolver/resolve_path.zig"); const configureTransformOptionsForBun = @import("../javascript/jsc/config.zig").configureTransformOptionsForBun; const Command = @import("../cli.zig").Command; const bundler = @import("../bundler.zig"); @@ -1090,7 +1090,7 @@ pub const Lockfile = struct { if (!std.fs.path.isAbsolute(lockfile_path_)) { var cwd = try std.os.getcwd(&lockfile_path_buf1); var parts = [_]string{lockfile_path_}; - var lockfile_path__ = resolve_path.joinAbsStringBuf(cwd, &lockfile_path_buf2, &parts, .auto); + var lockfile_path__ = Path.joinAbsStringBuf(cwd, &lockfile_path_buf2, &parts, .auto); lockfile_path_buf2[lockfile_path__.len] = 0; lockfile_path = lockfile_path_buf2[0..lockfile_path__.len :0]; } else { @@ -3874,7 +3874,7 @@ pub const PackageManager = struct { }; continue :brk; } - Output.prettyErrorln("<r><red>error<r>: {s} accessing temporary directory. Please set <b>$BUN_TMPDIR<r> or <b>$BUN_INSTALL_DIR<r>", .{ + Output.prettyErrorln("<r><red>error<r>: {s} accessing temporary directory. Please set <b>$BUN_TMPDIR<r> or <b>$BUN_INSTALL<r>", .{ @errorName(err2), }); Output.flush(); @@ -3892,7 +3892,7 @@ pub const PackageManager = struct { continue :brk; } - Output.prettyErrorln("<r><red>error<r>: {s} accessing temporary directory. Please set <b>$BUN_TMPDIR<r> or <b>$BUN_INSTALL_DIR<r>", .{ + Output.prettyErrorln("<r><red>error<r>: {s} accessing temporary directory. Please set <b>$BUN_TMPDIR<r> or <b>$BUN_INSTALL<r>", .{ @errorName(err), }); Output.flush(); @@ -4907,6 +4907,11 @@ pub const PackageManager = struct { pub const Options = struct { log_level: LogLevel = LogLevel.default, + global: bool = false, + + /// destination directory to link bins into + // must be a variable due to global installs and bunx + bin_path: stringZ = "node_modules/.bin", lockfile_path: stringZ = Lockfile.default_filename, save_lockfile_path: stringZ = Lockfile.default_filename, @@ -4982,6 +4987,36 @@ pub const PackageManager = struct { optional: bool = false, }; + pub fn openGlobalDir(opts_: ?*Api.BunInstall) !std.fs.Dir { + if (std.os.getenvZ("BUN_INSTALL_GLOBAL_DIR")) |home_dir| { + return try std.fs.cwd().makeOpenPath(home_dir, .{ .iterate = true }); + } + + if (opts_) |opts| { + if (opts.global_dir) |home_dir| { + if (home_dir.len > 0) { + return try std.fs.cwd().makeOpenPath(home_dir, .{ .iterate = true }); + } + } + } + + if (std.os.getenvZ("BUN_INSTALL")) |home_dir| { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var parts = [_]string{ "install", "global" }; + var path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto); + return try std.fs.cwd().makeOpenPath(path, .{ .iterate = true }); + } + + if (std.os.getenvZ("XDG_CACHE_HOME") orelse std.os.getenvZ("HOME")) |home_dir| { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var parts = [_]string{ "./bun", "install", "global" }; + var path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto); + return try std.fs.cwd().makeOpenPath(path, .{ .iterate = true }); + } + + return error.@"No global directory found"; + } + pub fn load( this: *Options, allocator: std.mem.Allocator, @@ -5502,10 +5537,22 @@ pub const PackageManager = struct { package_json_file_: ?std.fs.File, comptime params: []const ParamType, ) !*PackageManager { + var cli = try CommandLineArguments.parse(ctx.allocator, params); + return try initWithCLI(ctx, package_json_file_, cli); + } + + fn initWithCLI( + ctx: Command.Context, + package_json_file_: ?std.fs.File, + cli: CommandLineArguments, + ) !*PackageManager { // assume that spawning a thread will take a lil so we do that asap try NetworkThread.warmup(); - var cli = try CommandLineArguments.parse(ctx.allocator, params); + if (cli.global) { + var global_dir = try Options.openGlobalDir(ctx.install); + try global_dir.setAsCwd(); + } var fs = try Fs.FileSystem.init1(ctx.allocator, null); var original_cwd = std.mem.trimRight(u8, fs.top_level_dir, "/"); @@ -5521,6 +5568,7 @@ pub const PackageManager = struct { if (package_json_file_) |file| { package_json_file = file; } else { + // can't use orelse due to a stage1 bug package_json_file = std.fs.cwd().openFileZ("package.json", .{ .read = true, .write = true }) catch brk: { var this_cwd = original_cwd; @@ -5664,6 +5712,7 @@ pub const PackageManager = struct { clap.parseParam("--no-cache Ignore manifest cache entirely") catch unreachable, clap.parseParam("--silent Don't log anything") catch unreachable, clap.parseParam("--verbose Excessively verbose logging") catch unreachable, + clap.parseParam("-g, --global Install globally") catch unreachable, clap.parseParam("--cwd <STR> Set a specific cwd") catch unreachable, clap.parseParam("--backend <STR> Platform-specific optimizations for installing dependencies. For macOS, \"clonefile\" (default), \"copyfile\"") catch unreachable, clap.parseParam("--link-native-bins <STR>... Link \"bin\" from a matching platform-specific \"optionalDependencies\" instead. Default: esbuild, turbo") catch unreachable, @@ -5693,6 +5742,7 @@ pub const PackageManager = struct { cache_dir: string = "", lockfile: string = "", token: string = "", + global: bool = false, backend: ?PackageInstall.Method = null, @@ -5760,6 +5810,7 @@ pub const PackageManager = struct { cli.production = args.flag("--production"); cli.no_save = args.flag("--no-save"); cli.dry_run = args.flag("--dry-run"); + cli.global = args.flag("--global"); cli.force = args.flag("--force"); // cli.no_dedupe = args.flag("--no-dedupe"); cli.no_cache = args.flag("--no-cache"); @@ -5798,7 +5849,7 @@ pub const PackageManager = struct { if (cwd_.len > 0 and cwd_[0] == '.') { var cwd = try std.os.getcwd(&buf); var parts = [_]string{cwd_}; - var path_ = resolve_path.joinAbsStringBuf(cwd, &buf2, &parts, .auto); + var path_ = Path.joinAbsStringBuf(cwd, &buf2, &parts, .auto); buf2[path_.len] = 0; final_path = buf2[0..path_.len :0]; } else { @@ -6272,7 +6323,7 @@ pub const PackageManager = struct { // This is where we clean dangling symlinks // This could be slow if there are a lot of symlinks - if (cwd.openDirZ("node_modules/.bin", .{ + if (cwd.openDirZ(manager.options.bin_path, .{ .iterate = true, })) |node_modules_bin_| { var node_modules_bin: std.fs.Dir = node_modules_bin_; @@ -6456,6 +6507,8 @@ pub const PackageManager = struct { var bin_linker = Bin.Linker{ .bin = bin, .package_installed_node_modules = this.node_modules_folder.fd, + .global_bin_dir = this.manager.options.bin_path, + // .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, @@ -6500,7 +6553,7 @@ pub const PackageManager = struct { 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 > 6) { + if (this.manager.network_tarball_batch.len > 0) { _ = this.manager.scheduleNetworkTasks(); } } @@ -6774,6 +6827,7 @@ pub const PackageManager = struct { .bin = original_bin, .package_installed_node_modules = folder.fd, .root_node_modules_folder = node_modules_folder.fd, + .global_bin_dir = installer.manager.options.bin_path, .package_name = strings.StringOrTinyString.init(name), .string_buf = lockfile.buffers.string_bytes.items, }; @@ -6935,6 +6989,14 @@ pub const PackageManager = struct { const sum = manager.summary.add + manager.summary.remove + manager.summary.update; had_any_diffs = had_any_diffs or sum > 0; + if (manager.options.enable.frozen_lockfile and had_any_diffs) { + if (log_level != .silent) { + Output.prettyErrorln("<r><red>error<r>: Lockfile had changes, but lockfile is frozen", .{}); + } + + Global.exit(1); + } + // 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); @@ -7013,6 +7075,14 @@ pub const PackageManager = struct { root = Lockfile.Package{}; try manager.lockfile.initEmpty(ctx.allocator); + if (manager.options.enable.frozen_lockfile) { + if (log_level != .silent) { + Output.prettyErrorln("<r><red>error<r>: Lockfile had changes, but lockfile is frozen", .{}); + } + + Global.exit(1); + } + try Lockfile.Package.parseMain( manager.lockfile, &root, |