[bun install] Print linked bin names and improve output

This commit is contained in:
Jarred Sumner
2022-02-12 01:23:19 -08:00
parent 7c6386d81e
commit 89c99700f6
2 changed files with 366 additions and 189 deletions

View File

@@ -9,6 +9,7 @@ const Path = @import("../resolver/resolve_path.zig");
const C = @import("../c.zig");
const Fs = @import("../fs.zig");
const stringZ = @import("../global.zig").stringZ;
const Resolution = @import("./resolution.zig").Resolution;
/// Normalized `bin` field in [package.json](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#bin)
/// Can be a:
@@ -124,6 +125,70 @@ pub const Bin = extern struct {
map = 4,
};
pub const NamesIterator = struct {
bin: Bin,
i: usize = 0,
done: bool = false,
dir_iterator: ?std.fs.Dir.Iterator = null,
package_name: String,
package_installed_node_modules: std.fs.Dir = std.fs.Dir{ .fd = std.math.maxInt(std.os.fd_t) },
buf: [std.fs.MAX_PATH_BYTES]u8 = undefined,
string_buffer: []const u8,
fn nextInDir(this: *NamesIterator) !?[]const u8 {
if (this.done) return null;
if (this.dir_iterator == null) {
var target = this.bin.value.dir.slice(this.string_buffer);
var parts = [_][]const u8{ this.package_name.slice(this.string_buffer), target };
if (strings.hasPrefix(target, "./")) {
target = target[2..];
}
var dir = this.package_installed_node_modules;
var joined = Path.joinStringBuf(&this.buf, &parts, .auto);
this.buf[joined.len] = 0;
var joined_: [:0]u8 = this.buf[0..joined.len :0];
var child_dir = try dir.openDirZ(joined_, .{ .iterate = true });
this.dir_iterator = child_dir.iterate();
}
var iter = &this.dir_iterator.?;
if (iter.next() catch null) |entry| {
this.i += 1;
return entry.name;
} else {
this.done = true;
this.dir_iterator.?.dir.close();
this.dir_iterator = null;
return null;
}
}
/// next filename, e.g. "babel" instead of "cli.js"
pub fn next(this: *NamesIterator) !?[]const u8 {
switch (this.bin.tag) {
.file => {
if (this.i > 0) return null;
this.i += 1;
this.done = true;
const base = std.fs.path.basename(this.bin.value.file.slice(this.string_buffer));
if (strings.hasPrefix(base, "./")) return base[2..];
return base;
},
.named_file => {
if (this.i > 0) return null;
this.i += 1;
this.done = true;
const base = std.fs.path.basename(this.bin.value.named_file[0].slice(this.string_buffer));
if (strings.hasPrefix(base, "./")) return base[2..];
return base;
},
.dir => return try this.nextInDir(),
else => return null,
}
}
};
pub const Linker = struct {
bin: Bin,
@@ -133,7 +198,8 @@ pub const Bin = extern struct {
/// Used for generating relative paths
package_name: strings.StringOrTinyString,
global_bin_dir: stringZ = "",
global_bin_dir: std.fs.Dir,
global_bin_path: stringZ = "",
string_buf: []const u8,
@@ -152,9 +218,6 @@ pub const Bin = extern struct {
return name_[(std.mem.indexOfScalar(u8, name_, '/') orelse return name) + 1 ..];
}
// Sometimes, packages set "bin" to a file not marked as executable in the tarball
// They want it to be executable though
// so we make it executable
fn setPermissions(this: *const Linker, target: [:0]const u8) void {
// we use fchmodat to avoid any issues with current working directory
_ = C.fchmodat(this.root_node_modules_folder, target, umask | 0o777, 0);
@@ -163,15 +226,38 @@ pub const Bin = extern struct {
// It is important that we use symlinkat(2) with relative paths instead of symlink()
// 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 {
pub fn link(this: *Linker, link_global: bool) void {
var target_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var dest_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var from_remain: []u8 = &target_buf;
var remain: []u8 = &dest_buf;
target_buf[0..".bin/".len].* = ".bin/".*;
var from_remain: []u8 = target_buf[".bin/".len..];
dest_buf[0.."../".len].* = "../".*;
if (!link_global) {
target_buf[0..".bin/".len].* = ".bin/".*;
from_remain = target_buf[".bin/".len..];
dest_buf[0.."../".len].* = "../".*;
remain = dest_buf["../".len..];
} else {
if (this.global_bin_dir.fd >= std.math.maxInt(std.os.fd_t)) {
this.err = error.MissingGlobalBinDir;
return;
}
@memcpy(&target_buf, this.global_bin_path.ptr, this.global_bin_path.len);
from_remain = target_buf[this.global_bin_path.len..];
from_remain[0] = std.fs.path.sep;
from_remain = from_remain[1..];
const abs = std.os.getFdPath(this.root_node_modules_folder, &dest_buf) catch |err| {
this.err = err;
return;
};
remain = remain[abs.len..];
remain[0] = std.fs.path.sep;
remain = remain[1..];
this.root_node_modules_folder = this.global_bin_dir.fd;
}
var remain: []u8 = dest_buf["../".len..];
const name = this.package_name.slice();
std.mem.copy(u8, remain, name);
remain = remain[name.len..];
@@ -288,7 +374,10 @@ pub const Bin = extern struct {
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;
var to_path = if (!link_global)
std.fmt.bufPrintZ(&dest_buf, ".bin/{s}", .{entry.name}) catch unreachable
else
std.fmt.bufPrintZ(&dest_buf, "{s}", .{entry.name}) catch unreachable;
std.os.symlinkatZ(
from_path,
@@ -317,131 +406,5 @@ 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;
// },
// }
// }
};
};

View File

@@ -400,7 +400,7 @@ pub const Lockfile = struct {
// Serialized data
/// The version of the lockfile format, intended to prevent data corruption for format changes.
format: FormatVersion = .v0,
format: FormatVersion = .v1,
///
packages: Lockfile.Package.List = Lockfile.Package.List{},
@@ -1074,6 +1074,8 @@ pub const Lockfile = struct {
options: PackageManager.Options,
successfully_installed: ?Bitset = null,
updates: []const PackageManager.UpdateRequest = &[_]PackageManager.UpdateRequest{},
pub const Format = enum { yarn };
var lockfile_path_buf1: [std.fs.MAX_PATH_BYTES]u8 = undefined;
@@ -1202,21 +1204,42 @@ pub const Lockfile = struct {
var slice = this.lockfile.packages.slice();
const names: []const String = slice.items(.name);
const names_hashes: []const PackageNameHash = slice.items(.name_hash);
const bins: []const Bin = slice.items(.bin);
const resolved: []const Resolution = slice.items(.resolution);
if (names.len == 0) return;
const resolutions_list = slice.items(.resolutions);
const resolutions_buffer = this.lockfile.buffers.resolutions.items;
const string_buf = this.lockfile.buffers.string_bytes.items;
var id_map = try default_allocator.alloc(PackageID, this.updates.len);
std.mem.set(PackageID, id_map, std.math.maxInt(PackageID));
defer if (id_map.len > 0) default_allocator.free(id_map);
visited.set(0);
const end = @truncate(PackageID, names.len);
if (this.successfully_installed) |installed| {
for (resolutions_list[0].get(resolutions_buffer)) |package_id| {
if (package_id > end or !installed.isSet(package_id)) continue;
outer: for (resolutions_list[0].get(resolutions_buffer)) |package_id| {
if (package_id > end) continue;
const is_new = installed.isSet(package_id);
const package_name = names[package_id].slice(string_buf);
if (this.updates.len > 0) {
const name_hash = names_hashes[package_id];
for (this.updates) |update, update_id| {
if (update.name.len == package_name.len and name_hash == update.name_hash) {
if (id_map[update_id] == std.math.maxInt(PackageID)) {
id_map[update_id] = @truncate(PackageID, package_id);
}
continue :outer;
}
}
}
if (!is_new) continue;
const fmt = comptime brk: {
if (enable_ansi_colors) {
break :brk Output.prettyFmt("<r> <green>+<r> <b>{s}<r><d>@{}<r>\n", enable_ansi_colors);
@@ -1234,9 +1257,22 @@ pub const Lockfile = struct {
);
}
} else {
for (names) |name, package_id| {
outer: for (names) |name, package_id| {
const package_name = name.slice(string_buf);
if (this.updates.len > 0) {
const name_hash = names_hashes[package_id];
for (this.updates) |update, update_id| {
if (update.name.len == package_name.len and name_hash == update.name_hash) {
if (id_map[update_id] == std.math.maxInt(PackageID)) {
id_map[update_id] = @truncate(PackageID, package_id);
}
continue :outer;
}
}
}
try writer.print(
comptime Output.prettyFmt(" <r><b>{s}<r><d>@<b>{}<r>\n", enable_ansi_colors),
.{
@@ -1246,6 +1282,71 @@ pub const Lockfile = struct {
);
}
}
if (this.updates.len > 0) {
try writer.writeAll("\n");
}
for (this.updates) |_, update_id| {
const package_id = id_map[update_id];
if (package_id == std.math.maxInt(PackageID)) continue;
const name = names[package_id];
const bin = bins[package_id];
const package_name = name.slice(string_buf);
switch (bin.tag) {
.none, .map, .dir => {
const fmt = comptime brk: {
if (enable_ansi_colors) {
break :brk Output.prettyFmt("<r> <green>installed<r> <b>{s}<r><d>@{}<r>\n", enable_ansi_colors);
} else {
break :brk Output.prettyFmt("<r> installed {s}<r><d>@{}<r>\n", enable_ansi_colors);
}
};
try writer.print(
comptime Output.prettyFmt(fmt, enable_ansi_colors),
.{
package_name,
resolved[package_id].fmt(string_buf),
},
);
},
.file, .named_file => {
var iterator = Bin.NamesIterator{ .bin = bin, .package_name = name, .string_buffer = string_buf };
const fmt = comptime brk: {
if (enable_ansi_colors) {
break :brk Output.prettyFmt("<r> <green>installed<r> {s}<r><d>@{}<r> with binaries:\n", enable_ansi_colors);
} else {
break :brk Output.prettyFmt("<r> installed {s}<r><d>@{}<r> with binaries:\n", enable_ansi_colors);
}
};
try writer.print(
comptime Output.prettyFmt(fmt, enable_ansi_colors),
.{
package_name,
resolved[package_id].fmt(string_buf),
},
);
while (iterator.next() catch null) |bin_name| {
try writer.print(
comptime Output.prettyFmt("<r> <d>- <r><b>{s}<r>\n", enable_ansi_colors),
.{
bin_name,
},
);
}
},
}
}
if (this.updates.len > 0) {
try writer.writeAll("\n");
}
}
};
@@ -1430,7 +1531,7 @@ pub const Lockfile = struct {
};
pub fn verifyData(this: *Lockfile) !void {
std.debug.assert(this.format == .v0);
std.debug.assert(this.format == Lockfile.FormatVersion.current);
{
var i: usize = 0;
while (i < this.packages.len) : (i += 1) {
@@ -1552,7 +1653,7 @@ pub const Lockfile = struct {
pub fn initEmpty(this: *Lockfile, allocator: std.mem.Allocator) !void {
this.* = Lockfile{
.format = .v0,
.format = Lockfile.FormatVersion.current,
.packages = Lockfile.Package.List{},
.buffers = Buffers{},
.package_index = PackageIndex.Map.initContext(allocator, .{}),
@@ -1865,7 +1966,9 @@ pub const Lockfile = struct {
pub const FormatVersion = enum(u32) {
v0,
v1,
_,
pub const current = FormatVersion.v1;
};
pub const DependencySlice = ExternalSlice(Dependency);
@@ -2927,6 +3030,7 @@ pub const Lockfile = struct {
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());
}
pub fn load(
lockfile: *Lockfile,
@@ -2943,10 +3047,10 @@ pub const Lockfile = struct {
}
var format = try reader.readIntLittle(u32);
if (format != @enumToInt(Lockfile.FormatVersion.v0)) {
return error.InvalidLockfileVersion;
if (format != @enumToInt(Lockfile.FormatVersion.current)) {
return error.@"Outdated lockfile version";
}
lockfile.format = .v0;
lockfile.format = Lockfile.FormatVersion.current;
lockfile.allocator = allocator;
const total_buffer_size = try reader.readIntLittle(u64);
if (total_buffer_size > stream.buffer.len) {
@@ -4909,6 +5013,7 @@ pub const PackageManager = struct {
log_level: LogLevel = LogLevel.default,
global: bool = false,
global_bin_dir: std.fs.Dir = std.fs.Dir{ .fd = std.math.maxInt(std.os.fd_t) },
/// destination directory to link bins into
// must be a variable due to global installs and bunx
bin_path: stringZ = "node_modules/.bin",
@@ -4943,6 +5048,17 @@ pub const PackageManager = struct {
native_bin_link_allowlist: []const PackageNameHash = &default_native_bin_link_allowlist,
max_retry_count: u16 = 5,
pub fn isBinPathInPATH(this: *const Options) bool {
// must be absolute
if (this.bin_path[0] != std.fs.path.sep) return false;
var tokenizer = std.mem.split(std.os.getenvZ("PATH") orelse "", ":");
const spanned = std.mem.span(this.bin_path);
while (tokenizer.next()) |token| {
if (strings.eql(token, spanned)) return true;
}
return false;
}
const default_native_bin_link_allowlist = [_]PackageNameHash{
String.Builder.stringHash("esbuild"),
String.Builder.stringHash("turbo"),
@@ -5009,7 +5125,7 @@ pub const PackageManager = struct {
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 parts = [_]string{ ".bun", "install", "global" };
var path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto);
return try std.fs.cwd().makeOpenPath(path, .{ .iterate = true });
}
@@ -5017,6 +5133,41 @@ pub const PackageManager = struct {
return error.@"No global directory found";
}
pub fn openGlobalBinDir(opts_: ?*const Api.BunInstall) !std.fs.Dir {
if (std.os.getenvZ("BUN_INSTALL_BIN")) |home_dir| {
return try std.fs.cwd().makeOpenPath(home_dir, .{ .iterate = true });
}
if (opts_) |opts| {
if (opts.global_bin_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{
"bin",
};
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",
"bin",
};
var path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto);
return try std.fs.cwd().makeOpenPath(path, .{ .iterate = true });
}
return error.@"Missing global bin directory: try setting $BUN_INSTALL";
}
pub fn load(
this: *Options,
allocator: std.mem.Allocator,
@@ -5532,7 +5683,7 @@ pub const PackageManager = struct {
}
};
fn init(
pub fn init(
ctx: Command.Context,
package_json_file_: ?std.fs.File,
comptime params: []const ParamType,
@@ -5605,7 +5756,9 @@ pub const PackageManager = struct {
std.mem.copy(u8, package_json_cwd_buf[fs.top_level_dir.len..], "package.json");
var entries_option = try fs.fs.readDirectory(fs.top_level_dir, null);
var options = Options{};
var options = Options{
.global = cli.global,
};
var env_loader: *DotEnv.Loader = brk: {
var map = try ctx.allocator.create(DotEnv.Map);
@@ -5737,7 +5890,7 @@ pub const PackageManager = struct {
clap.parseParam("<POS> ... \"name\" of packages to remove from package.json") catch unreachable,
};
const CommandLineArguments = struct {
pub const CommandLineArguments = struct {
registry: string = "",
cache_dir: string = "",
lockfile: string = "",
@@ -5890,6 +6043,7 @@ pub const PackageManager = struct {
const UpdateRequest = struct {
name: string = "",
name_hash: PackageNameHash = 0,
resolved_version_buf: string = "",
version: Dependency.Version = Dependency.Version{},
version_buf: []const u8 = "",
@@ -5975,7 +6129,7 @@ pub const PackageManager = struct {
const sliced = SlicedString.init(request.version_buf, request.version_buf);
request.version = Dependency.parse(allocator, request.version_buf, &sliced, log) orelse Dependency.Version{};
}
request.name_hash = String.Builder.stringHash(request.name);
update_requests.append(request) catch break;
}
@@ -6067,10 +6221,23 @@ pub const PackageManager = struct {
\\ bun add {s}
\\ bun add {s}
\\ bun add {s}
\\
\\<d>Shorthand: <b>bun a<r>
\\ bun add -g git-peek
\\
, .{ examples_to_print[0], examples_to_print[1], examples_to_print[2] });
if (manager.options.global) {
Output.prettyErrorln(
\\
\\<d>Shorthand: <b>bun a -g<r>
\\
, .{});
} else {
Output.prettyErrorln(
\\
\\<d>Shorthand: <b>bun a<r>
\\
, .{});
}
Output.flush();
Global.exit(0);
},
@@ -6093,13 +6260,25 @@ pub const PackageManager = struct {
\\ bun remove {s} {s}
\\ bun remove {s}
\\
\\<d>Shorthand: <b>bun rm<r>
\\
, .{
examples_to_print[0],
examples_to_print[1],
examples_to_print[2],
});
if (manager.options.global) {
Output.prettyErrorln(
\\
\\<d>Shorthand: <b>bun rm -g<r>
\\
, .{});
} else {
Output.prettyErrorln(
\\
\\<d>Shorthand: <b>bun rm<r>
\\
, .{});
}
Output.flush();
Global.exit(0);
@@ -6331,20 +6510,19 @@ pub const PackageManager = struct {
iterator: while (iter.next() catch null) |entry| {
switch (entry.kind) {
std.fs.Dir.Entry.Kind.SymLink => {
if (std.fs.path.extension(entry.name).len == 0) {
// any symlinks which we are unable to open are assumed to be dangling
// note that using access won't work here, because access doesn't resolve symlinks
std.mem.copy(u8, &node_modules_buf, entry.name);
node_modules_buf[entry.name.len] = 0;
var buf: [:0]u8 = node_modules_buf[0..entry.name.len :0];
var file = node_modules_bin.openFileZ(buf, .{ .read = true }) catch {
node_modules_bin.deleteFileZ(buf) catch {};
continue :iterator;
};
// any symlinks which we are unable to open are assumed to be dangling
// note that using access won't work here, because access doesn't resolve symlinks
std.mem.copy(u8, &node_modules_buf, entry.name);
node_modules_buf[entry.name.len] = 0;
var buf: [:0]u8 = node_modules_buf[0..entry.name.len :0];
file.close();
}
var file = node_modules_bin.openFileZ(buf, .{ .read = true }) catch {
node_modules_bin.deleteFileZ(buf) catch {};
continue :iterator;
};
file.close();
},
else => {},
}
@@ -6401,6 +6579,7 @@ pub const PackageManager = struct {
resolutions: []Resolution,
node: *Progress.Node,
has_created_bin: bool = false,
global_bin_dir: std.fs.Dir,
destination_dir_subpath_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined,
install_count: usize = 0,
successfully_installed: Bitset,
@@ -6487,8 +6666,10 @@ pub const PackageManager = struct {
const bin = this.bins[package_id];
if (bin.tag != .none) {
if (!this.has_created_bin) {
this.node_modules_folder.makeDirZ(".bin") catch {};
Bin.Linker.umask = C.umask(0);
if (!this.options.global)
this.node_modules_folder.makeDirZ(".bin") catch {};
this.has_created_bin = true;
}
@@ -6507,15 +6688,15 @@ 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,
.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,
};
bin_linker.link();
bin_linker.link(this.manager.options.global);
if (comptime log_level != .silent) {
if (bin_linker.err) |err| {
const fmt = "\n<r><red>error:<r> linking <b>{s}<r>: {s}\n";
@@ -6727,6 +6908,7 @@ pub const PackageManager = struct {
.skip_verify = skip_verify,
.skip_delete = skip_delete,
.summary = &summary,
.global_bin_dir = this.options.global_bin_dir,
.force_install = force_install,
.install_count = lockfile.buffers.hoisted_packages.items.len,
.successfully_installed = try Bitset.initEmpty(lockfile.packages.len, this.allocator),
@@ -6818,7 +7000,9 @@ pub const PackageManager = struct {
const name: string = installer.names[resolved_id].slice(lockfile.buffers.string_bytes.items);
if (!installer.has_created_bin) {
node_modules_folder.makeDirZ(".bin") catch {};
if (!this.options.global) {
node_modules_folder.makeDirZ(".bin") catch {};
}
Bin.Linker.umask = C.umask(0);
installer.has_created_bin = true;
}
@@ -6827,12 +7011,14 @@ 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,
.global_bin_path = this.options.bin_path,
.global_bin_dir = this.options.global_bin_dir,
.package_name = strings.StringOrTinyString.init(name),
.string_buf = lockfile.buffers.string_bytes.items,
};
bin_linker.link();
bin_linker.link(this.options.global);
if (comptime log_level != .silent) {
if (bin_linker.err) |err| {
@@ -6874,6 +7060,15 @@ pub const PackageManager = struct {
return summary;
}
pub fn setupGlobalDir(manager: *PackageManager, ctx: *const Command.Context) !void {
manager.options.global_bin_dir = try Options.openGlobalBinDir(ctx.install);
var out_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var result = try std.os.getFdPath(manager.options.global_bin_dir.fd, &out_buffer);
out_buffer[result.len] = 0;
var result_: [:0]u8 = out_buffer[0..result.len :0];
manager.options.bin_path = std.meta.assumeSentinel(try FileSystem.instance.dirname_store.append([:0]u8, result_), 0);
}
fn installWithManager(
ctx: Command.Context,
manager: *PackageManager,
@@ -6925,9 +7120,9 @@ pub const PackageManager = struct {
}
if (manager.options.enable.fail_early) {
Output.prettyError("<b>Failed to load lockfile<r>\n", .{});
Output.prettyError("<b><red>failed to load lockfile<r>\n", .{});
} else {
Output.prettyError("<b>Ignoring lockfile<r>\n", .{});
Output.prettyError("<b><red>ignoring lockfile<r>\n", .{});
}
if (ctx.log.errors > 0) {
@@ -6991,7 +7186,7 @@ pub const PackageManager = struct {
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", .{});
Output.prettyErrorln("<r><red>error<r>: lockfile had changes, but lockfile is frozen", .{});
}
Global.exit(1);
@@ -7077,7 +7272,7 @@ pub const PackageManager = struct {
if (manager.options.enable.frozen_lockfile) {
if (log_level != .silent) {
Output.prettyErrorln("<r><red>error<r>: Lockfile had changes, but lockfile is frozen", .{});
Output.prettyErrorln("<r><red>error<r>: lockfile had changes, but lockfile is frozen", .{});
}
Global.exit(1);
@@ -7170,6 +7365,10 @@ pub const PackageManager = struct {
manager.lockfile.verifyResolutions(manager.options.local_package_features, manager.options.remote_package_features, log_level);
}
if (manager.options.global) {
try manager.setupGlobalDir(&ctx);
}
if (manager.options.do.save_lockfile) {
save: {
if (manager.lockfile.isEmpty()) {
@@ -7185,7 +7384,10 @@ pub const PackageManager = struct {
break :save;
};
}
if (log_level != .silent) Output.prettyErrorln("No packages! Deleted empty lockfile", .{});
if (!manager.options.global) {
if (log_level != .silent) Output.prettyErrorln("No packages! Deleted empty lockfile", .{});
}
break :save;
}
@@ -7241,6 +7443,7 @@ pub const PackageManager = struct {
var printer = Lockfile.Printer{
.lockfile = manager.lockfile,
.options = manager.options,
.updates = manager.package_json_updates,
.successfully_installed = install_summary.successfully_installed,
};
if (Output.enable_ansi_colors) {
@@ -7248,16 +7451,20 @@ pub const PackageManager = struct {
} else {
try Lockfile.Printer.Tree.print(&printer, Output.WriterType, Output.writer(), false);
}
var printed_timestamp = false;
if (install_summary.success > 0) {
Output.pretty("\n <green>{d}<r> packages<r> installed ", .{install_summary.success});
// it's confusing when it shows 3 packages and says it installed 1
Output.pretty("\n <green>{d}<r> packages<r> installed ", .{@maximum(
install_summary.success,
@truncate(
u32,
manager.package_json_updates.len,
),
)});
Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp());
printed_timestamp = true;
Output.pretty("<r>\n", .{});
if (manager.summary.update > 0) {
Output.pretty(" Updated: <cyan>{d}<r>\n", .{manager.summary.update});
}
if (manager.summary.remove > 0) {
Output.pretty(" Removed: <cyan>{d}<r>\n", .{manager.summary.remove});
}
@@ -7270,8 +7477,9 @@ pub const PackageManager = struct {
Output.pretty("\n <r><b>{d}<r> packages removed ", .{manager.summary.remove});
Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp());
printed_timestamp = true;
Output.pretty("<r>\n", .{});
} else if (install_summary.skipped > 0 and install_summary.fail == 0) {
} else if (install_summary.skipped > 0 and install_summary.fail == 0 and manager.package_json_updates.len == 0) {
Output.pretty("\n", .{});
const count = @truncate(PackageID, manager.lockfile.packages.len);
@@ -7281,20 +7489,26 @@ pub const PackageManager = struct {
count,
});
Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp());
printed_timestamp = true;
Output.pretty("<r>\n", .{});
} else {
Output.pretty("<r> Done! Checked <green>{d} packages<r> <d>(no changes)<r> ", .{
Output.pretty("<r> <green>Done<r>! Checked {d} packages<r> <d>(no changes)<r> ", .{
install_summary.skipped,
});
Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp());
printed_timestamp = true;
Output.pretty("<r>\n", .{});
}
} else if (manager.summary.update > 0) {
Output.prettyln(" Updated: <cyan>{d}<r>\n", .{manager.summary.update});
}
if (install_summary.fail > 0) {
Output.prettyln("<r> Failed to install <red><b>{d}<r> packages", .{install_summary.fail});
Output.prettyln("<r> Failed to install <red><b>{d}<r> packages\n", .{install_summary.fail});
}
if (!printed_timestamp) {
Output.printStartEndStdout(ctx.start_time, std.time.nanoTimestamp());
Output.prettyln("<d> done<r>", .{});
printed_timestamp = true;
}
}
Output.flush();