diff options
-rw-r--r-- | src/cli/package_manager_command.zig | 182 | ||||
-rw-r--r-- | src/install/lockfile.zig | 2 |
2 files changed, 183 insertions, 1 deletions
diff --git a/src/cli/package_manager_command.zig b/src/cli/package_manager_command.zig index 8908a80eb..d67ab1040 100644 --- a/src/cli/package_manager_command.zig +++ b/src/cli/package_manager_command.zig @@ -3,11 +3,18 @@ const PackageManager = @import("../install/install.zig").PackageManager; const ComamndLineArguments = PackageManager.CommandLineArguments; const std = @import("std"); const strings = @import("bun").strings; +const Lockfile = @import("../install/lockfile.zig"); +const NodeModulesFolder = Lockfile.Tree.NodeModulesFolder; +const PackageID = @import("../install/install.zig").PackageID; +const PackageInstaller = @import("../install/install.zig").PackageInstaller; const Global = @import("bun").Global; const Output = @import("bun").Output; const Fs = @import("../fs.zig"); const Path = @import("../resolver/resolve_path.zig"); const bun = @import("bun"); +const StringBuilder = bun.StringBuilder; +const string = bun.string; +const stringZ = bun.stringZ; pub const PackageManagerCommand = struct { pub fn printHelp(_: std.mem.Allocator) void {} pub fn printHash(ctx: Command.Context, lockfile_: []const u8) !void { @@ -157,6 +164,59 @@ pub const PackageManagerCommand = struct { } Output.writer().writeAll(outpath) catch {}; Global.exit(0); + } else if (strings.eqlComptime(first, "ls")) { + const load_lockfile = pm.lockfile.loadFromDisk(ctx.allocator, ctx.log, "bun.lockb"); + if (load_lockfile == .not_found) { + if (pm.options.log_level != .silent) + Output.prettyError("Lockfile not found", .{}); + Global.exit(1); + } + + if (load_lockfile == .err) { + if (pm.options.log_level != .silent) + Output.prettyError("Error loading lockfile: {s}", .{@errorName(load_lockfile.err.value)}); + Global.exit(1); + } + + Output.flush(); + Output.disableBuffering(); + const lockfile = load_lockfile.ok; + + const parts = lockfile.packages.slice(); + const names = parts.items(.name); + + var iterator = Lockfile.Tree.Iterator.init( + lockfile.buffers.trees.items, + lockfile.buffers.hoisted_packages.items, + names, + lockfile.buffers.string_bytes.items, + ); + + var directories = std.ArrayList(NodeModulesFolder).init(ctx.allocator); + defer directories.deinit(); + while (iterator.nextNodeModulesFolder()) |node_modules| { + const path = try ctx.allocator.alloc(u8, node_modules.relative_path.len); + std.mem.copy(u8, path, node_modules.relative_path); + + const packages = try ctx.allocator.alloc(PackageID, node_modules.packages.len); + std.mem.copy(PackageID, packages, node_modules.packages); + + const folder: NodeModulesFolder = .{ + .relative_path = @ptrCast(stringZ, path), + .in = node_modules.in, + .packages = packages, + }; + directories.append(folder) catch unreachable; + } + + const first_directory = directories.orderedRemove(0); + + // TODO: find max depth beforehand + var more_packages = [_]bool{false} ** 16; + if (first_directory.packages.len > 1) more_packages[0] = true; + printNodeModulesFolderStructure(&first_directory, null, 0, &directories, lockfile, more_packages); + Output.enableBuffering(); + Global.exit(0); } Output.prettyln( @@ -169,6 +229,7 @@ pub const PackageManagerCommand = struct { \\ bun pm <b>hash-print<r> print the hash stored in the current lockfile \\ bun pm <b>cache<r> print the path to the cache folder \\ bun pm <b>cache rm<r> clear the cache + \\ bun pm <b>ls<r> list the directory structure of the current lockfile \\ , .{}); @@ -182,3 +243,124 @@ pub const PackageManagerCommand = struct { } } }; + +fn printNodeModulesFolderStructure( + directory: *const NodeModulesFolder, + directory_package_id: ?PackageID, + depth: usize, + directories: *std.ArrayList(NodeModulesFolder), + lockfile: *Lockfile, + more_packages_: [16]bool, +) void { + const allocator = lockfile.allocator; + var more_packages = more_packages_; + const parts = lockfile.packages.slice(); + const names = parts.items(.name); + const resolutions = parts.items(.resolution); + const string_bytes = lockfile.buffers.string_bytes.items; + + { + var i: usize = 0; + while (i < depth) : (i += 1) { + if (i == depth - 1) { + if (more_packages[i]) { + Output.pretty("<d>├──<r>", .{}); + } else if (directories.items.len == 1) { + Output.pretty("<d>└──<r>", .{}); + } else { + Output.pretty("<d>┬──<r>", .{}); + } + } else { + if (more_packages[i]) { + Output.pretty("<d>│<r> ", .{}); + } else { + Output.pretty(" ", .{}); + } + } + } + + var resolution_buf: [512]u8 = undefined; + if (directory_package_id) |id| { + var path = directory.relative_path; + + if (depth != 0) { + Output.pretty(" ", .{}); + var temp_depth = depth; + while (temp_depth > 0) : (temp_depth -= 1) { + if (std.mem.indexOf(u8, path, "node_modules")) |j| { + path = path[j + "node_modules".len + 1 ..]; + } + } + } + const directory_version = std.fmt.bufPrint(&resolution_buf, "{}", .{resolutions[id].fmt(string_bytes)}) catch unreachable; + if (std.mem.indexOf(u8, path, "node_modules")) |j| { + Output.prettyln("{s}<d>@{s}<r>", .{ path[0 .. j - 1], directory_version }); + } else { + Output.prettyln("{s}<d>@{s}<r>", .{ path, directory_version }); + } + } else { + var cwd_buf: [bun.MAX_PATH_BYTES]u8 = undefined; + const path = std.os.getcwd(&cwd_buf) catch { + Output.prettyErrorln("<r><red>error<r>: Could not get current working directory", .{}); + Global.exit(1); + }; + Output.println("{s} node_modules", .{path}); + } + } + + for (directory.packages) |package_id, package_index| { + const package_name_ = names[package_id].slice(string_bytes); + const package_name = allocator.alloc(u8, package_name_.len) catch unreachable; + defer allocator.free(package_name); + std.mem.copy(u8, package_name, package_name_); + + var possible_path = std.fmt.allocPrint(allocator, "{s}/{s}/node_modules", .{ directory.relative_path, package_name }) catch unreachable; + defer allocator.free(possible_path); + + if (package_index + 1 == directory.packages.len) { + more_packages[depth] = false; + } + + var dir_index: usize = 0; + var found_node_modules = false; + while (dir_index < directories.items.len) : (dir_index += 1) { + // Recursively print node_modules. node_modules is removed from + // the directories list before traversal. + if (strings.eql(possible_path, directories.items[dir_index].relative_path)) { + found_node_modules = true; + const next = directories.orderedRemove(dir_index); + + var new_depth: usize = 0; + var temp_path = possible_path; + while (std.mem.indexOf(u8, temp_path["node_modules".len..], "node_modules")) |j| { + new_depth += 1; + temp_path = temp_path[j + "node_modules".len ..]; + } + + if (package_index + 1 < directory.packages.len) more_packages[new_depth] = true; + printNodeModulesFolderStructure(&next, package_id, new_depth, directories, lockfile, more_packages); + } + } + + if (found_node_modules) continue; + + var i: usize = 0; + while (i < depth) : (i += 1) { + if (more_packages[i]) { + Output.pretty("<d>│<r> ", .{}); + } else { + Output.pretty(" ", .{}); + } + } + + if (more_packages[depth]) { + Output.pretty("<d>├──<r> ", .{}); + } else { + Output.pretty("<d>└──<r> ", .{}); + } + + var resolution_buf: [512]u8 = undefined; + const package_version = std.fmt.bufPrint(&resolution_buf, "{}", .{resolutions[package_id].fmt(string_bytes)}) catch unreachable; + Output.prettyln("{s}<d>@{s}<r>", .{ package_name, package_version }); + } +} diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 710cec8d2..4b60260e2 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -245,7 +245,7 @@ pub const Tree = struct { const SubtreeError = error{ OutOfMemory, DependencyLoop }; - const NodeModulesFolder = struct { + pub const NodeModulesFolder = struct { relative_path: stringZ, in: PackageID, packages: []const PackageID, |