mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Compare commits
1 Commits
bun-v1.3.5
...
cursor/ren
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5bbf32601f |
@@ -34,37 +34,30 @@ $ bun pm bin -g
|
||||
<$HOME>/.bun/bin
|
||||
```
|
||||
|
||||
## ls
|
||||
## List packages
|
||||
|
||||
To print a list of installed dependencies in the current project and their resolved versions, excluding their dependencies.
|
||||
List installed packages in the current project and their dependencies.
|
||||
|
||||
```bash
|
||||
$ bun list
|
||||
```
|
||||
|
||||
This is the primary way to list packages. You can also use the alias:
|
||||
|
||||
```bash
|
||||
$ bun pm ls
|
||||
/path/to/project node_modules (135)
|
||||
├── eslint@8.38.0
|
||||
├── react@18.2.0
|
||||
├── react-dom@18.2.0
|
||||
├── typescript@5.0.4
|
||||
└── zod@3.21.4
|
||||
```
|
||||
|
||||
To print all installed dependencies, including nth-order dependencies.
|
||||
By default, this command only lists top-level dependencies. To list the entire dependency tree:
|
||||
|
||||
```bash
|
||||
$ bun list --all
|
||||
```
|
||||
|
||||
Alternative:
|
||||
|
||||
```bash
|
||||
$ bun pm ls --all
|
||||
/path/to/project node_modules (135)
|
||||
├── @eslint-community/eslint-utils@4.4.0
|
||||
├── @eslint-community/regexpp@4.5.0
|
||||
├── @eslint/eslintrc@2.0.2
|
||||
├── @eslint/js@8.38.0
|
||||
├── @nodelib/fs.scandir@2.1.5
|
||||
├── @nodelib/fs.stat@2.0.5
|
||||
├── @nodelib/fs.walk@1.2.8
|
||||
├── acorn@8.8.2
|
||||
├── acorn-jsx@5.3.2
|
||||
├── ajv@6.12.6
|
||||
├── ansi-regex@5.0.1
|
||||
├── ...
|
||||
```
|
||||
|
||||
## whoami
|
||||
|
||||
@@ -142,20 +142,13 @@ $ bun outdated
|
||||
|
||||
## List installed packages
|
||||
|
||||
To list installed packages, you can use `bun pm ls`. This will list all the packages that are installed in the `node_modules` folder using Bun's lockfile as the source of truth. You can pass the `-a` flag to list all installed packages, including transitive dependencies.
|
||||
To list installed packages, you can use `bun list`. This will list all the packages that are installed in the `node_modules` folder using Bun's lockfile as the source of truth. You can pass the `-a` flag to list all installed packages, including transitive dependencies. It's also available as an alias `bun pm ls`.
|
||||
|
||||
```sh
|
||||
# List top-level installed packages:
|
||||
$ bun pm ls
|
||||
my-pkg node_modules (781)
|
||||
├── @types/node@20.16.5
|
||||
├── @types/react@18.3.8
|
||||
├── @types/react-dom@18.3.0
|
||||
├── eslint@8.57.1
|
||||
├── eslint-config-next@14.2.8
|
||||
```bash
|
||||
$ bun list
|
||||
elysia node_modules (v1.0.27)
|
||||
|
||||
# List all installed packages:
|
||||
$ bun pm ls -a
|
||||
$ bun list -a
|
||||
my-pkg node_modules
|
||||
├── @alloc/quick-lru@5.2.0
|
||||
├── @isaacs/cliui@8.0.2
|
||||
|
||||
41
src/cli.zig
41
src/cli.zig
@@ -129,6 +129,7 @@ pub const PublishCommand = @import("./cli/publish_command.zig").PublishCommand;
|
||||
pub const PackCommand = @import("./cli/pack_command.zig").PackCommand;
|
||||
pub const AuditCommand = @import("./cli/audit_command.zig").AuditCommand;
|
||||
pub const InitCommand = @import("./cli/init_command.zig").InitCommand;
|
||||
pub const ListCommand = @import("./cli/list_command.zig").ListCommand;
|
||||
|
||||
pub const Arguments = struct {
|
||||
pub fn loader_resolver(in: string) !Api.Loader {
|
||||
@@ -1376,6 +1377,7 @@ pub const HelpCommand = struct {
|
||||
\\ <b><blue>update<r> <d>{s:<16}<r> Update outdated dependencies
|
||||
\\ <b><blue>audit<r> Check installed packages for vulnerabilities
|
||||
\\ <b><blue>outdated<r> Display latest versions of outdated dependencies
|
||||
\\ <b><blue>list<r> List installed packages in the current project
|
||||
\\ <b><blue>link<r> <d>[\<package\>]<r> Register or link a local npm package
|
||||
\\ <b><blue>unlink<r> Unregister a local npm package
|
||||
\\ <b><blue>publish<r> Publish a package to the npm registry
|
||||
@@ -1768,6 +1770,7 @@ pub const Command = struct {
|
||||
RootCommandMatcher.case("outdated") => .OutdatedCommand,
|
||||
RootCommandMatcher.case("publish") => .PublishCommand,
|
||||
RootCommandMatcher.case("audit") => .AuditCommand,
|
||||
RootCommandMatcher.case("list") => .ListCommand,
|
||||
|
||||
// These are reserved for future use by Bun, so that someone
|
||||
// doing `bun deploy` to run a script doesn't accidentally break
|
||||
@@ -1782,8 +1785,6 @@ pub const Command = struct {
|
||||
RootCommandMatcher.case("logout") => .ReservedCommand,
|
||||
RootCommandMatcher.case("whoami") => .ReservedCommand,
|
||||
RootCommandMatcher.case("prune") => .ReservedCommand,
|
||||
RootCommandMatcher.case("list") => .ReservedCommand,
|
||||
RootCommandMatcher.case("why") => .ReservedCommand,
|
||||
|
||||
RootCommandMatcher.case("-e") => .AutoCommand,
|
||||
|
||||
@@ -1808,6 +1809,7 @@ pub const Command = struct {
|
||||
"pm",
|
||||
"x",
|
||||
"repl",
|
||||
"list",
|
||||
};
|
||||
|
||||
const reject_list = default_completions_list ++ [_]string{
|
||||
@@ -1957,6 +1959,13 @@ pub const Command = struct {
|
||||
try LinkCommand.exec(ctx);
|
||||
return;
|
||||
},
|
||||
.ListCommand => {
|
||||
if (comptime bun.fast_debug_build_mode and bun.fast_debug_build_cmd != .ListCommand) unreachable;
|
||||
const ctx = try Command.init(allocator, log, .ListCommand);
|
||||
|
||||
try ListCommand.exec(ctx);
|
||||
return;
|
||||
},
|
||||
.UnlinkCommand => {
|
||||
if (comptime bun.fast_debug_build_mode and bun.fast_debug_build_cmd != .UnlinkCommand) unreachable;
|
||||
const ctx = try Command.init(allocator, log, .UnlinkCommand);
|
||||
@@ -2338,6 +2347,7 @@ pub const Command = struct {
|
||||
InstallCommand,
|
||||
InstallCompletionsCommand,
|
||||
LinkCommand,
|
||||
ListCommand,
|
||||
PackageManagerCommand,
|
||||
RemoveCommand,
|
||||
RunCommand,
|
||||
@@ -2372,6 +2382,7 @@ pub const Command = struct {
|
||||
.InstallCommand => 'i',
|
||||
.InstallCompletionsCommand => 'C',
|
||||
.LinkCommand => 'l',
|
||||
.ListCommand => 'L',
|
||||
.PackageManagerCommand => 'P',
|
||||
.RemoveCommand => 'R',
|
||||
.RunCommand => 'r',
|
||||
@@ -2609,7 +2620,28 @@ pub const Command = struct {
|
||||
Output.pretty(intro_text, .{});
|
||||
Output.flush();
|
||||
},
|
||||
Command.Tag.ListCommand => {
|
||||
const intro_text =
|
||||
\\<b>Usage<r>: <b><green>bun list<r> <cyan>[flags]<r>
|
||||
\\ List installed packages in the current project
|
||||
\\
|
||||
\\<b>Flags<r>:
|
||||
\\ <cyan>--all<r> List the entire dependency tree
|
||||
\\
|
||||
\\<b>Examples:<r>
|
||||
\\ <d>List top-level dependencies<r>
|
||||
\\ <b><green>bun list<r>
|
||||
\\
|
||||
\\ <d>List the entire dependency tree<r>
|
||||
\\ <b><green>bun list<r> <cyan>--all<r>
|
||||
\\
|
||||
\\<b>Alias:<r> <b><green>bun pm ls<r>
|
||||
\\
|
||||
;
|
||||
|
||||
Output.pretty(intro_text, .{});
|
||||
Output.flush();
|
||||
},
|
||||
Command.Tag.GetCompletionsCommand => {
|
||||
Output.pretty("<b>Usage<r>: <b><green>bun getcompletes<r>", .{});
|
||||
Output.flush();
|
||||
@@ -2665,6 +2697,7 @@ pub const Command = struct {
|
||||
.OutdatedCommand,
|
||||
.PublishCommand,
|
||||
.AuditCommand,
|
||||
.ListCommand,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
@@ -2685,6 +2718,7 @@ pub const Command = struct {
|
||||
.OutdatedCommand,
|
||||
.PublishCommand,
|
||||
.AuditCommand,
|
||||
.ListCommand,
|
||||
=> true,
|
||||
else => false,
|
||||
};
|
||||
@@ -2707,6 +2741,7 @@ pub const Command = struct {
|
||||
.OutdatedCommand = true,
|
||||
.PublishCommand = true,
|
||||
.AuditCommand = true,
|
||||
.ListCommand = true,
|
||||
});
|
||||
|
||||
pub const always_loads_config: std.EnumArray(Tag, bool) = std.EnumArray(Tag, bool).initDefault(false, .{
|
||||
@@ -2723,6 +2758,7 @@ pub const Command = struct {
|
||||
.OutdatedCommand = true,
|
||||
.PublishCommand = true,
|
||||
.AuditCommand = true,
|
||||
.ListCommand = true,
|
||||
});
|
||||
|
||||
pub const uses_global_options: std.EnumArray(Tag, bool) = std.EnumArray(Tag, bool).initDefault(true, .{
|
||||
@@ -2740,6 +2776,7 @@ pub const Command = struct {
|
||||
.OutdatedCommand = false,
|
||||
.PublishCommand = false,
|
||||
.AuditCommand = false,
|
||||
.ListCommand = false,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
243
src/cli/list_command.zig
Normal file
243
src/cli/list_command.zig
Normal file
@@ -0,0 +1,243 @@
|
||||
const std = @import("std");
|
||||
const bun = @import("bun");
|
||||
const Global = bun.Global;
|
||||
const Output = bun.Output;
|
||||
const string = bun.string;
|
||||
const strings = bun.strings;
|
||||
const Command = @import("../cli.zig").Command;
|
||||
const Install = @import("../install/install.zig");
|
||||
const PackageManager = Install.PackageManager;
|
||||
const PackageManagerCommand = @import("./package_manager_command.zig").PackageManagerCommand;
|
||||
const Lockfile = @import("../install/lockfile.zig");
|
||||
const NodeModulesFolder = Lockfile.Tree.Iterator(.node_modules).Next;
|
||||
const DependencyID = Install.DependencyID;
|
||||
|
||||
const ByName = struct {
|
||||
dependencies: []const Install.Dependency,
|
||||
buf: []const u8,
|
||||
|
||||
pub fn isLessThan(ctx: ByName, lhs: DependencyID, rhs: DependencyID) bool {
|
||||
return strings.cmpStringsAsc(
|
||||
{},
|
||||
ctx.dependencies[lhs].name.slice(ctx.buf),
|
||||
ctx.dependencies[rhs].name.slice(ctx.buf),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
pub const ListCommand = struct {
|
||||
pub fn exec(ctx: Command.Context) !void {
|
||||
const cli = try PackageManager.CommandLineArguments.parse(ctx.allocator, .pm);
|
||||
var pm, const cwd = PackageManager.init(ctx, cli, PackageManager.Subcommand.pm) catch |err| {
|
||||
if (err == error.MissingPackageJSON) {
|
||||
var cwd_buf: bun.PathBuffer = undefined;
|
||||
if (bun.getcwd(&cwd_buf)) |cwd_path| {
|
||||
Output.errGeneric("No package.json was found for directory \"{s}\"", .{cwd_path});
|
||||
} else |_| {
|
||||
Output.errGeneric("No package.json was found", .{});
|
||||
}
|
||||
Output.note("Run \"bun init\" to initialize a project", .{});
|
||||
Global.exit(1);
|
||||
}
|
||||
return err;
|
||||
};
|
||||
defer ctx.allocator.free(cwd);
|
||||
|
||||
if (pm.options.global) {
|
||||
try pm.setupGlobalDir(ctx);
|
||||
}
|
||||
|
||||
const load_lockfile = pm.lockfile.loadFromCwd(pm, ctx.allocator, ctx.log, true);
|
||||
PackageManagerCommand.handleLoadLockfileErrors(load_lockfile, pm);
|
||||
|
||||
Output.flush();
|
||||
Output.disableBuffering();
|
||||
const lockfile = load_lockfile.ok.lockfile;
|
||||
var iterator = Lockfile.Tree.Iterator(.node_modules).init(lockfile);
|
||||
|
||||
var max_depth: usize = 0;
|
||||
|
||||
var directories = std.ArrayList(NodeModulesFolder).init(ctx.allocator);
|
||||
defer directories.deinit();
|
||||
while (iterator.next(null)) |node_modules| {
|
||||
const path_len = node_modules.relative_path.len;
|
||||
const path = try ctx.allocator.alloc(u8, path_len + 1);
|
||||
bun.copy(u8, path, node_modules.relative_path);
|
||||
path[path_len] = 0;
|
||||
|
||||
const dependencies = try ctx.allocator.alloc(DependencyID, node_modules.dependencies.len);
|
||||
bun.copy(DependencyID, dependencies, node_modules.dependencies);
|
||||
|
||||
if (max_depth < node_modules.depth + 1) max_depth = node_modules.depth + 1;
|
||||
|
||||
try directories.append(.{
|
||||
.relative_path = path[0..path_len :0],
|
||||
.dependencies = dependencies,
|
||||
.tree_id = node_modules.tree_id,
|
||||
.depth = node_modules.depth,
|
||||
});
|
||||
}
|
||||
|
||||
const first_directory = directories.orderedRemove(0);
|
||||
|
||||
var more_packages = try ctx.allocator.alloc(bool, max_depth);
|
||||
@memset(more_packages, false);
|
||||
if (first_directory.dependencies.len > 1) more_packages[0] = true;
|
||||
|
||||
// Check for --all flag
|
||||
const args = try std.process.argsAlloc(ctx.allocator);
|
||||
if (strings.leftHasAnyInRight(args, &.{ "-A", "-a", "--all" })) {
|
||||
try printNodeModulesFolderStructure(&first_directory, null, 0, &directories, lockfile, more_packages);
|
||||
} else {
|
||||
var cwd_buf: bun.PathBuffer = undefined;
|
||||
const path = bun.getcwd(&cwd_buf) catch {
|
||||
Output.prettyErrorln("<r><red>error<r>: Could not get current working directory", .{});
|
||||
Global.exit(1);
|
||||
};
|
||||
const dependencies = lockfile.buffers.dependencies.items;
|
||||
const slice = lockfile.packages.slice();
|
||||
const resolutions = slice.items(.resolution);
|
||||
const root_deps = slice.items(.dependencies)[0];
|
||||
|
||||
Output.println("{s} node_modules ({d})", .{ path, lockfile.buffers.hoisted_dependencies.items.len });
|
||||
const string_bytes = lockfile.buffers.string_bytes.items;
|
||||
const sorted_dependencies = try ctx.allocator.alloc(DependencyID, root_deps.len);
|
||||
defer ctx.allocator.free(sorted_dependencies);
|
||||
for (sorted_dependencies, 0..) |*dep, i| {
|
||||
dep.* = @as(DependencyID, @truncate(root_deps.off + i));
|
||||
}
|
||||
std.sort.pdq(DependencyID, sorted_dependencies, ByName{
|
||||
.dependencies = dependencies,
|
||||
.buf = string_bytes,
|
||||
}, ByName.isLessThan);
|
||||
|
||||
for (sorted_dependencies, 0..) |dependency_id, index| {
|
||||
const package_id = lockfile.buffers.resolutions.items[dependency_id];
|
||||
if (package_id >= lockfile.packages.len) continue;
|
||||
const name = dependencies[dependency_id].name.slice(string_bytes);
|
||||
const resolution = resolutions[package_id].fmt(string_bytes, .auto);
|
||||
|
||||
if (index < sorted_dependencies.len - 1) {
|
||||
Output.prettyln("<d>├──<r> {s}<r><d>@{any}<r>\n", .{ name, resolution });
|
||||
} else {
|
||||
Output.prettyln("<d>└──<r> {s}<r><d>@{any}<r>\n", .{ name, resolution });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Global.exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
fn printNodeModulesFolderStructure(
|
||||
directory: *const NodeModulesFolder,
|
||||
directory_package_id: ?Install.PackageID,
|
||||
depth: usize,
|
||||
directories: *std.ArrayList(NodeModulesFolder),
|
||||
lockfile: *Lockfile,
|
||||
more_packages: []bool,
|
||||
) !void {
|
||||
const resolutions = lockfile.packages.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 {
|
||||
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 = try std.fmt.bufPrint(&resolution_buf, "{}", .{resolutions[id].fmt(string_bytes, .auto)});
|
||||
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.PathBuffer = undefined;
|
||||
const path = bun.getcwd(&cwd_buf) catch {
|
||||
Output.prettyErrorln("<r><red>error<r>: Could not get current working directory", .{});
|
||||
Global.exit(1);
|
||||
};
|
||||
Output.prettyln("{s} node_modules", .{path});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort IDs by name
|
||||
const sorted_deps = try lockfile.allocator.alloc(DependencyID, directory.dependencies.len);
|
||||
defer lockfile.allocator.free(sorted_deps);
|
||||
bun.copy(DependencyID, sorted_deps, directory.dependencies);
|
||||
const tree_id = directory.tree_id;
|
||||
const dependencies = lockfile.buffers.trees.items[tree_id].dependencies.items;
|
||||
const resolutions_list = lockfile.buffers.trees.items[tree_id].resolutions.items;
|
||||
const list = lockfile.buffers.trees.items[tree_id].list.items;
|
||||
|
||||
std.sort.pdq(DependencyID, sorted_deps, ByName{
|
||||
.dependencies = dependencies,
|
||||
.buf = string_bytes,
|
||||
}, ByName.isLessThan);
|
||||
|
||||
for (sorted_deps, 0..) |dependency_id, j| {
|
||||
const pkg_id = resolutions_list[dependency_id];
|
||||
const dependency = dependencies[dependency_id];
|
||||
const name = dependency.name.slice(string_bytes);
|
||||
const new_directory_i = std.mem.indexOfScalar(u8, list, @intFromEnum(dependency_id));
|
||||
|
||||
if (j < sorted_deps.len - 1) {
|
||||
more_packages[depth] = true;
|
||||
} else {
|
||||
more_packages[depth] = false;
|
||||
}
|
||||
|
||||
if (new_directory_i != null and directories.items.len > 0) {
|
||||
const new_directory = &directories.items[0];
|
||||
_ = directories.orderedRemove(0);
|
||||
try printNodeModulesFolderStructure(new_directory, pkg_id, depth + 1, directories, lockfile, more_packages);
|
||||
} else {
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < depth + 1) : (i += 1) {
|
||||
if (i == depth) {
|
||||
if (more_packages[i]) {
|
||||
Output.pretty("<d>├──<r>", .{});
|
||||
} else {
|
||||
Output.pretty("<d>└──<r>", .{});
|
||||
}
|
||||
} else {
|
||||
if (more_packages[i]) {
|
||||
Output.pretty("<d>│<r> ", .{});
|
||||
} else {
|
||||
Output.pretty(" ", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
const resolution = resolutions[pkg_id].fmt(string_bytes, .auto);
|
||||
Output.prettyln(" {s}<d>@{}<r>", .{ name, resolution });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ pub const PackageManagerCommand = struct {
|
||||
\\ <d>└<r> <cyan>--gzip-level<r> specify a custom compression level for gzip (0-9, default is 9)
|
||||
\\ <b><green>bun pm<r> <blue>bin<r> print the path to bin folder
|
||||
\\ <d>└<r> <cyan>-g<r> print the <b>global<r> path to bin folder
|
||||
\\ <b><green>bun pm<r> <blue>ls<r> list the dependency tree according to the current lockfile
|
||||
\\ <b><green>bun pm<r> <blue>ls<r> list the dependency tree according to the current lockfile <d>(alias for `bun list`)<r>
|
||||
\\ <d>└<r> <cyan>--all<r> list the entire dependency tree according to the current lockfile
|
||||
\\ <b><green>bun pm<r> <blue>whoami<r> print the current npm username
|
||||
\\ <b><green>bun pm<r> <blue>view<r> <d>name[@version]<r> view package metadata from the registry
|
||||
|
||||
Reference in New Issue
Block a user