mirror of
https://github.com/oven-sh/bun
synced 2026-02-13 04:18:58 +00:00
481 lines
17 KiB
Zig
481 lines
17 KiB
Zig
fn printInstalledWorkspaceSection(
|
|
this: *const Printer,
|
|
manager: *PackageManager,
|
|
comptime Writer: type,
|
|
writer: Writer,
|
|
comptime enable_ansi_colors: bool,
|
|
workspace_package_id: PackageID,
|
|
installed: *const Bitset,
|
|
comptime print_section_header: enum(u1) { print_section_header, dont_print_section_header },
|
|
printed_new_install: *bool,
|
|
id_map: ?[]DependencyID,
|
|
) !void {
|
|
const lockfile = this.lockfile;
|
|
const string_buf = lockfile.buffers.string_bytes.items;
|
|
const packages_slice = lockfile.packages.slice();
|
|
const resolutions = lockfile.buffers.resolutions.items;
|
|
const dependencies = lockfile.buffers.dependencies.items;
|
|
const workspace_res = packages_slice.items(.resolution)[workspace_package_id];
|
|
const names = packages_slice.items(.name);
|
|
const pkg_metas = packages_slice.items(.meta);
|
|
bun.assert(workspace_res.tag == .workspace or workspace_res.tag == .root);
|
|
const resolutions_list = packages_slice.items(.resolutions);
|
|
var printed_section_header = false;
|
|
var printed_update = false;
|
|
|
|
// It's possible to have duplicate dependencies with the same version and resolution.
|
|
// While both are technically installed, only one was chosen and should be printed.
|
|
var dep_dedupe: std.AutoHashMap(install.PackageNameHash, void) = .init(manager.allocator);
|
|
defer dep_dedupe.deinit();
|
|
|
|
// find the updated packages
|
|
for (resolutions_list[workspace_package_id].begin()..resolutions_list[workspace_package_id].end()) |_dep_id| {
|
|
const dep_id: DependencyID = @intCast(_dep_id);
|
|
|
|
switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map, pkg_metas)) {
|
|
.yes, .no, .@"return" => {},
|
|
.update => |update_info| {
|
|
printed_new_install.* = true;
|
|
printed_update = true;
|
|
|
|
if (comptime print_section_header == .print_section_header) {
|
|
if (!printed_section_header) {
|
|
printed_section_header = true;
|
|
const workspace_name = names[workspace_package_id].slice(string_buf);
|
|
try writer.print(comptime Output.prettyFmt("<r>\n<cyan>{s}<r><d>:<r>\n", enable_ansi_colors), .{
|
|
workspace_name,
|
|
});
|
|
}
|
|
}
|
|
|
|
try printUpdatedPackage(this, update_info, enable_ansi_colors, Writer, writer);
|
|
},
|
|
}
|
|
}
|
|
|
|
for (resolutions_list[workspace_package_id].begin()..resolutions_list[workspace_package_id].end()) |_dep_id| {
|
|
const dep_id: DependencyID = @intCast(_dep_id);
|
|
|
|
switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map, pkg_metas)) {
|
|
.@"return" => return,
|
|
.yes => {},
|
|
.no, .update => continue,
|
|
}
|
|
|
|
const dep = dependencies[dep_id];
|
|
const package_id = resolutions[dep_id];
|
|
|
|
if ((try dep_dedupe.getOrPut(dep.name_hash)).found_existing) {
|
|
continue;
|
|
}
|
|
|
|
printed_new_install.* = true;
|
|
|
|
if (comptime print_section_header == .print_section_header) {
|
|
if (!printed_section_header) {
|
|
printed_section_header = true;
|
|
const workspace_name = names[workspace_package_id].slice(string_buf);
|
|
try writer.print(comptime Output.prettyFmt("<r>\n<cyan>{s}<r><d>:<r>\n", enable_ansi_colors), .{
|
|
workspace_name,
|
|
});
|
|
}
|
|
}
|
|
|
|
if (printed_update) {
|
|
printed_update = false;
|
|
try writer.writeAll("\n");
|
|
}
|
|
try printInstalledPackage(this, manager, &dep, package_id, enable_ansi_colors, Writer, writer);
|
|
}
|
|
}
|
|
|
|
const PackageUpdatePrintInfo = struct {
|
|
version: Semver.Version,
|
|
version_buf: string,
|
|
resolution: Resolution,
|
|
dependency_id: DependencyID,
|
|
};
|
|
|
|
const ShouldPrintPackageInstallResult = union(enum) {
|
|
yes,
|
|
no,
|
|
@"return",
|
|
update: PackageUpdatePrintInfo,
|
|
};
|
|
|
|
fn shouldPrintPackageInstall(
|
|
this: *const Printer,
|
|
manager: *PackageManager,
|
|
dep_id: DependencyID,
|
|
installed: *const Bitset,
|
|
id_map: ?[]DependencyID,
|
|
pkg_metas: []const Package.Meta,
|
|
) ShouldPrintPackageInstallResult {
|
|
const dependencies = this.lockfile.buffers.dependencies.items;
|
|
const resolutions = this.lockfile.buffers.resolutions.items;
|
|
const dependency = dependencies[dep_id];
|
|
const package_id = resolutions[dep_id];
|
|
|
|
if (dependency.behavior.isWorkspace() or package_id >= this.lockfile.packages.len) return .no;
|
|
|
|
if (id_map) |map| {
|
|
for (this.updates, map) |update, *update_dependency_id| {
|
|
if (update.failed) return .@"return";
|
|
if (update.matches(dependency, this.lockfile.buffers.string_bytes.items)) {
|
|
if (update_dependency_id.* == invalid_package_id) {
|
|
update_dependency_id.* = dep_id;
|
|
}
|
|
|
|
return .no;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!installed.isSet(package_id)) return .no;
|
|
|
|
// It's possible this package was installed but the dependency is disabled.
|
|
// Have "zod@1.0.0" in dependencies and `zod2@npm:zod@1.0.0` in devDependencies
|
|
// and install with --omit=dev.
|
|
if (this.lockfile.isResolvedDependencyDisabled(
|
|
dep_id,
|
|
this.options.local_package_features,
|
|
&pkg_metas[package_id],
|
|
)) {
|
|
return .no;
|
|
}
|
|
|
|
const resolution = this.lockfile.packages.items(.resolution)[package_id];
|
|
if (resolution.tag == .npm) {
|
|
const name = dependency.name.slice(this.lockfile.buffers.string_bytes.items);
|
|
if (manager.updating_packages.get(name)) |entry| {
|
|
if (entry.original_version) |original_version| {
|
|
if (!original_version.eql(resolution.value.npm.version)) {
|
|
return .{
|
|
.update = .{
|
|
.version = original_version,
|
|
.version_buf = entry.original_version_string_buf,
|
|
.resolution = resolution,
|
|
.dependency_id = dep_id,
|
|
},
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return .yes;
|
|
}
|
|
|
|
fn printUpdatedPackage(
|
|
this: *const Printer,
|
|
update_info: PackageUpdatePrintInfo,
|
|
comptime enable_ansi_colors: bool,
|
|
comptime Writer: type,
|
|
writer: Writer,
|
|
) !void {
|
|
const string_buf = this.lockfile.buffers.string_bytes.items;
|
|
const dependency = this.lockfile.buffers.dependencies.items[update_info.dependency_id];
|
|
|
|
const fmt = comptime brk: {
|
|
if (enable_ansi_colors) {
|
|
break :brk Output.prettyFmt("<r><cyan>↑<r> <b>{s}<r><d> <b>{} →<r> <b><cyan>{}<r>\n", enable_ansi_colors);
|
|
}
|
|
break :brk Output.prettyFmt("<r>^ <b>{s}<r><d> <b>{} -\\><r> <b>{}<r>\n", enable_ansi_colors);
|
|
};
|
|
|
|
try writer.print(
|
|
fmt,
|
|
.{
|
|
dependency.name.slice(string_buf),
|
|
update_info.version.fmt(update_info.version_buf),
|
|
update_info.resolution.value.npm.version.fmt(string_buf),
|
|
},
|
|
);
|
|
}
|
|
|
|
fn printInstalledPackage(
|
|
this: *const Printer,
|
|
manager: *PackageManager,
|
|
dependency: *const Dependency,
|
|
package_id: PackageID,
|
|
comptime enable_ansi_colors: bool,
|
|
comptime Writer: type,
|
|
writer: Writer,
|
|
) !void {
|
|
const string_buf = this.lockfile.buffers.string_bytes.items;
|
|
const packages_slice = this.lockfile.packages.slice();
|
|
const resolution: Resolution = packages_slice.items(.resolution)[package_id];
|
|
const name = dependency.name.slice(string_buf);
|
|
|
|
const package_name = packages_slice.items(.name)[package_id].slice(string_buf);
|
|
if (manager.formatLaterVersionInCache(package_name, dependency.name_hash, resolution)) |later_version_fmt| {
|
|
const fmt = comptime brk: {
|
|
if (enable_ansi_colors) {
|
|
break :brk Output.prettyFmt("<r><green>+<r> <b>{s}<r><d>@{}<r> <d>(<blue>v{} available<r><d>)<r>\n", enable_ansi_colors);
|
|
} else {
|
|
break :brk Output.prettyFmt("<r>+ {s}<r><d>@{}<r> <d>(v{} available)<r>\n", enable_ansi_colors);
|
|
}
|
|
};
|
|
try writer.print(
|
|
fmt,
|
|
.{
|
|
name,
|
|
resolution.fmt(string_buf, .posix),
|
|
later_version_fmt,
|
|
},
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
const fmt = comptime brk: {
|
|
if (enable_ansi_colors) {
|
|
break :brk Output.prettyFmt("<r><green>+<r> <b>{s}<r><d>@{}<r>\n", enable_ansi_colors);
|
|
} else {
|
|
break :brk Output.prettyFmt("<r>+ {s}<r><d>@{}<r>\n", enable_ansi_colors);
|
|
}
|
|
};
|
|
|
|
try writer.print(
|
|
fmt,
|
|
.{
|
|
name,
|
|
resolution.fmt(string_buf, .posix),
|
|
},
|
|
);
|
|
}
|
|
|
|
/// - Prints an empty newline with no diffs
|
|
/// - Prints a leading and trailing blank newline with diffs
|
|
pub fn print(
|
|
this: *const Printer,
|
|
manager: *PackageManager,
|
|
comptime Writer: type,
|
|
writer: Writer,
|
|
comptime enable_ansi_colors: bool,
|
|
log_level: PackageManager.Options.LogLevel,
|
|
) !void {
|
|
try writer.writeAll("\n");
|
|
const allocator = this.lockfile.allocator;
|
|
var slice = this.lockfile.packages.slice();
|
|
const bins: []const Bin = slice.items(.bin);
|
|
const resolved: []const Resolution = slice.items(.resolution);
|
|
if (resolved.len == 0) return;
|
|
const string_buf = this.lockfile.buffers.string_bytes.items;
|
|
const resolutions_list = slice.items(.resolutions);
|
|
const pkg_metas = slice.items(.meta);
|
|
const resolutions_buffer: []const PackageID = this.lockfile.buffers.resolutions.items;
|
|
const dependencies_buffer: []const Dependency = this.lockfile.buffers.dependencies.items;
|
|
if (dependencies_buffer.len == 0) return;
|
|
const id_map = try default_allocator.alloc(DependencyID, this.updates.len);
|
|
@memset(id_map, invalid_package_id);
|
|
defer if (id_map.len > 0) default_allocator.free(id_map);
|
|
|
|
const end = @as(PackageID, @truncate(resolved.len));
|
|
|
|
var had_printed_new_install = false;
|
|
if (this.successfully_installed) |*installed| {
|
|
if (log_level.isVerbose()) {
|
|
var workspaces_to_print: std.ArrayListUnmanaged(DependencyID) = .{};
|
|
defer workspaces_to_print.deinit(allocator);
|
|
|
|
for (resolutions_list[0].begin()..resolutions_list[0].end()) |dep_id| {
|
|
const dep = dependencies_buffer[dep_id];
|
|
if (dep.behavior.isWorkspace()) {
|
|
workspaces_to_print.append(allocator, @intCast(dep_id)) catch bun.outOfMemory();
|
|
}
|
|
}
|
|
|
|
var found_workspace_to_print = false;
|
|
for (workspaces_to_print.items) |workspace_dep_id| {
|
|
const workspace_package_id = resolutions_buffer[workspace_dep_id];
|
|
for (resolutions_list[workspace_package_id].begin()..resolutions_list[workspace_package_id].end()) |dep_id| {
|
|
switch (shouldPrintPackageInstall(this, manager, @intCast(dep_id), installed, id_map, pkg_metas)) {
|
|
.yes => found_workspace_to_print = true,
|
|
else => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
try printInstalledWorkspaceSection(
|
|
this,
|
|
manager,
|
|
Writer,
|
|
writer,
|
|
enable_ansi_colors,
|
|
0,
|
|
installed,
|
|
.dont_print_section_header,
|
|
&had_printed_new_install,
|
|
null,
|
|
);
|
|
|
|
for (workspaces_to_print.items) |workspace_dep_id| {
|
|
try printInstalledWorkspaceSection(
|
|
this,
|
|
manager,
|
|
Writer,
|
|
writer,
|
|
enable_ansi_colors,
|
|
resolutions_buffer[workspace_dep_id],
|
|
installed,
|
|
.print_section_header,
|
|
&had_printed_new_install,
|
|
null,
|
|
);
|
|
}
|
|
} else {
|
|
// just print installed packages for the current workspace
|
|
var workspace_package_id: DependencyID = 0;
|
|
if (manager.workspace_name_hash) |workspace_name_hash| {
|
|
for (resolutions_list[0].begin()..resolutions_list[0].end()) |dep_id| {
|
|
const dep = dependencies_buffer[dep_id];
|
|
if (dep.behavior.isWorkspace() and dep.name_hash == workspace_name_hash) {
|
|
workspace_package_id = resolutions_buffer[dep_id];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
try printInstalledWorkspaceSection(
|
|
this,
|
|
manager,
|
|
Writer,
|
|
writer,
|
|
enable_ansi_colors,
|
|
workspace_package_id,
|
|
installed,
|
|
.dont_print_section_header,
|
|
&had_printed_new_install,
|
|
id_map,
|
|
);
|
|
}
|
|
} else {
|
|
outer: for (dependencies_buffer, resolutions_buffer, 0..) |dependency, package_id, dep_id| {
|
|
if (package_id >= end) continue;
|
|
if (dependency.behavior.isPeer()) continue;
|
|
const package_name = dependency.name.slice(string_buf);
|
|
|
|
if (this.updates.len > 0) {
|
|
for (this.updates, id_map) |update, *dependency_id| {
|
|
if (update.failed) return;
|
|
if (update.matches(dependency, string_buf)) {
|
|
if (dependency_id.* == invalid_package_id) {
|
|
dependency_id.* = @as(DependencyID, @truncate(dep_id));
|
|
}
|
|
|
|
continue :outer;
|
|
}
|
|
}
|
|
}
|
|
|
|
try writer.print(
|
|
comptime Output.prettyFmt(" <r><b>{s}<r><d>@<b>{}<r>\n", enable_ansi_colors),
|
|
.{
|
|
package_name,
|
|
resolved[package_id].fmt(string_buf, .auto),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
if (had_printed_new_install) {
|
|
try writer.writeAll("\n");
|
|
}
|
|
|
|
if (bun.Environment.allow_assert) had_printed_new_install = false;
|
|
|
|
var printed_installed_update_request = false;
|
|
for (id_map) |dependency_id| {
|
|
if (dependency_id == invalid_package_id) continue;
|
|
if (bun.Environment.allow_assert) had_printed_new_install = true;
|
|
|
|
const name = dependencies_buffer[dependency_id].name;
|
|
const package_id = resolutions_buffer[dependency_id];
|
|
const bin = bins[package_id];
|
|
|
|
const package_name = name.slice(string_buf);
|
|
|
|
switch (bin.tag) {
|
|
.none, .dir => {
|
|
printed_installed_update_request = true;
|
|
|
|
const fmt = comptime Output.prettyFmt("<r><green>installed<r> <b>{s}<r><d>@{}<r>\n", enable_ansi_colors);
|
|
|
|
try writer.print(
|
|
fmt,
|
|
.{
|
|
package_name,
|
|
resolved[package_id].fmt(string_buf, .posix),
|
|
},
|
|
);
|
|
},
|
|
.map, .file, .named_file => {
|
|
printed_installed_update_request = true;
|
|
|
|
var iterator = Bin.NamesIterator{
|
|
.bin = bin,
|
|
.package_name = name,
|
|
.string_buffer = string_buf,
|
|
.extern_string_buf = this.lockfile.buffers.extern_strings.items,
|
|
};
|
|
|
|
{
|
|
const fmt = comptime Output.prettyFmt("<r><green>installed<r> {s}<r><d>@{}<r> with binaries:\n", enable_ansi_colors);
|
|
|
|
try writer.print(
|
|
fmt,
|
|
.{
|
|
package_name,
|
|
resolved[package_id].fmt(string_buf, .posix),
|
|
},
|
|
);
|
|
}
|
|
|
|
{
|
|
const fmt = comptime Output.prettyFmt("<r> <d>- <r><b>{s}<r>\n", enable_ansi_colors);
|
|
|
|
if (manager.track_installed_bin == .pending) {
|
|
if (iterator.next() catch null) |bin_name| {
|
|
manager.track_installed_bin = .{
|
|
.basename = bun.default_allocator.dupe(u8, bin_name) catch bun.outOfMemory(),
|
|
};
|
|
|
|
try writer.print(fmt, .{bin_name});
|
|
}
|
|
}
|
|
|
|
while (iterator.next() catch null) |bin_name| {
|
|
try writer.print(fmt, .{bin_name});
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
if (printed_installed_update_request) {
|
|
try writer.writeAll("\n");
|
|
}
|
|
}
|
|
|
|
const std = @import("std");
|
|
const bun = @import("bun");
|
|
const Environment = bun.Environment;
|
|
const assert = bun.assert;
|
|
const install = bun.install;
|
|
const PackageID = install.PackageID;
|
|
const Dependency = install.Dependency;
|
|
const Resolution = install.Resolution;
|
|
const Lockfile = install.Lockfile;
|
|
const Printer = Lockfile.Printer;
|
|
const Output = bun.Output;
|
|
const PackageManager = bun.install.PackageManager;
|
|
const Semver = bun.Semver;
|
|
const Bitset = bun.bit_set.DynamicBitSetUnmanaged;
|
|
const DependencyID = bun.install.DependencyID;
|
|
const invalid_package_id = bun.install.invalid_package_id;
|
|
const default_allocator = bun.default_allocator;
|
|
const Bin = bun.install.Bin;
|
|
const Package = Lockfile.Package;
|
|
const string = []const u8;
|