mirror of
https://github.com/oven-sh/bun
synced 2026-02-17 06:12:08 +00:00
Compare commits
7 Commits
claude/opt
...
claude/fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8da5652b69 | ||
|
|
070833d208 | ||
|
|
ca157907bb | ||
|
|
92927644ab | ||
|
|
d436f54eda | ||
|
|
fb99767588 | ||
|
|
70e38e8146 |
@@ -24,6 +24,7 @@ const Npm = Install.Npm;
|
||||
const PmViewCommand = @import("./pm_view_command.zig");
|
||||
const PmVersionCommand = @import("./pm_version_command.zig").PmVersionCommand;
|
||||
const File = bun.sys.File;
|
||||
const DotEnv = bun.DotEnv;
|
||||
|
||||
const ByName = struct {
|
||||
dependencies: []const Dependency,
|
||||
@@ -98,6 +99,211 @@ pub const PackageManagerCommand = struct {
|
||||
return subcommand;
|
||||
}
|
||||
|
||||
fn requiresPackageJson(subcommand: []const u8, global: bool) bool {
|
||||
// Commands that work globally and don't need package.json
|
||||
if (strings.eqlComptime(subcommand, "cache")) return false;
|
||||
if (strings.eqlComptime(subcommand, "whoami")) return false;
|
||||
if (strings.eqlComptime(subcommand, "default-trusted")) return false;
|
||||
if (strings.eqlComptime(subcommand, "bin") and global) return false;
|
||||
|
||||
// All other commands require package.json
|
||||
return true;
|
||||
}
|
||||
|
||||
fn execWithoutPackageJson(ctx: Command.Context, cli: PackageManager.CommandLineArguments, subcommand: []const u8) !void {
|
||||
if (strings.eqlComptime(subcommand, "whoami")) {
|
||||
// Create a minimal environment for npm registry access
|
||||
var env: *DotEnv.Loader = brk: {
|
||||
const map = try ctx.allocator.create(DotEnv.Map);
|
||||
map.* = DotEnv.Map.init(ctx.allocator);
|
||||
const loader = try ctx.allocator.create(DotEnv.Loader);
|
||||
loader.* = DotEnv.Loader.init(map, ctx.allocator);
|
||||
break :brk loader;
|
||||
};
|
||||
env.loadProcess();
|
||||
|
||||
const pm = try createMinimalPackageManager(ctx, cli, env);
|
||||
|
||||
const username = Npm.whoami(ctx.allocator, pm) catch |err| {
|
||||
switch (err) {
|
||||
error.OutOfMemory => bun.outOfMemory(),
|
||||
error.NeedAuth => {
|
||||
Output.errGeneric("missing authentication (run <cyan>`bunx npm login`<r>)", .{});
|
||||
},
|
||||
error.ProbablyInvalidAuth => {
|
||||
Output.errGeneric("failed to authenticate with registry '{}'", .{
|
||||
bun.fmt.redactedNpmUrl(pm.options.scope.url.href),
|
||||
});
|
||||
},
|
||||
}
|
||||
Global.crash();
|
||||
};
|
||||
Output.println("{s}", .{username});
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "cache")) {
|
||||
const pm = try createMinimalPackageManager(ctx, cli, null);
|
||||
|
||||
var dir: bun.PathBuffer = undefined;
|
||||
var fd = pm.getCacheDirectory();
|
||||
const outpath = bun.getFdPath(.fromStdDir(fd), &dir) catch |err| {
|
||||
Output.prettyErrorln("{s} getting cache directory", .{@errorName(err)});
|
||||
Global.crash();
|
||||
};
|
||||
|
||||
if (cli.positionals.len > 1 and strings.eqlComptime(cli.positionals[1], "rm")) {
|
||||
fd.close();
|
||||
|
||||
var had_err = false;
|
||||
|
||||
std.fs.deleteTreeAbsolute(outpath) catch |err| {
|
||||
Output.err(err, "Could not delete {s}", .{outpath});
|
||||
had_err = true;
|
||||
};
|
||||
Output.prettyln("Cleared 'bun install' cache", .{});
|
||||
|
||||
bunx: {
|
||||
const tmp = bun.fs.FileSystem.RealFS.platformTempDir();
|
||||
const tmp_dir = std.fs.openDirAbsolute(tmp, .{ .iterate = true }) catch |err| {
|
||||
Output.err(err, "Could not open {s}", .{tmp});
|
||||
had_err = true;
|
||||
break :bunx;
|
||||
};
|
||||
var iter = tmp_dir.iterate();
|
||||
|
||||
// This is to match 'bunx_command.BunxCommand.exec's logic
|
||||
const prefix = try std.fmt.allocPrint(ctx.allocator, "bunx-{d}-", .{
|
||||
if (bun.Environment.isPosix) bun.c.getuid() else bun.windows.userUniqueId(),
|
||||
});
|
||||
|
||||
var deleted: usize = 0;
|
||||
while (iter.next() catch |err| {
|
||||
Output.err(err, "Could not read {s}", .{tmp});
|
||||
had_err = true;
|
||||
break :bunx;
|
||||
}) |entry| {
|
||||
if (std.mem.startsWith(u8, entry.name, prefix)) {
|
||||
tmp_dir.deleteTree(entry.name) catch |err| {
|
||||
Output.err(err, "Could not delete {s}", .{entry.name});
|
||||
had_err = true;
|
||||
continue;
|
||||
};
|
||||
|
||||
deleted += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Output.prettyln("Cleared {d} cached 'bunx' packages", .{deleted});
|
||||
}
|
||||
|
||||
Global.exit(if (had_err) 1 else 0);
|
||||
}
|
||||
|
||||
Output.writer().writeAll(outpath) catch {};
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "default-trusted")) {
|
||||
try DefaultTrustedCommand.exec();
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "bin") and cli.global) {
|
||||
const pm = try createMinimalPackageManager(ctx, cli, null);
|
||||
const output_path = Path.joinAbs(Fs.FileSystem.instance.top_level_dir, .auto, bun.asByteSlice(pm.options.bin_path));
|
||||
Output.prettyln("{s}", .{output_path});
|
||||
if (Output.stdout_descriptor_type == .terminal) {
|
||||
Output.prettyln("\n", .{});
|
||||
}
|
||||
|
||||
warner: {
|
||||
if (Output.enable_ansi_colors_stderr) {
|
||||
if (bun.getenvZ("PATH")) |path| {
|
||||
var path_iter = std.mem.tokenizeScalar(u8, path, std.fs.path.delimiter);
|
||||
while (path_iter.next()) |entry| {
|
||||
if (strings.eql(entry, output_path)) {
|
||||
break :warner;
|
||||
}
|
||||
}
|
||||
|
||||
Output.prettyErrorln("\n<r><yellow>warn<r>: not in $PATH\n", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Output.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unknown command or shouldn't reach here
|
||||
Global.exit(1);
|
||||
}
|
||||
|
||||
fn createMinimalPackageManager(ctx: Command.Context, cli: PackageManager.CommandLineArguments, env_loader: ?*DotEnv.Loader) !*PackageManager {
|
||||
const env: *DotEnv.Loader = env_loader orelse brk: {
|
||||
const map = try ctx.allocator.create(DotEnv.Map);
|
||||
map.* = DotEnv.Map.init(ctx.allocator);
|
||||
const loader = try ctx.allocator.create(DotEnv.Loader);
|
||||
loader.* = DotEnv.Loader.init(map, ctx.allocator);
|
||||
loader.loadProcess();
|
||||
break :brk loader;
|
||||
};
|
||||
|
||||
PackageManager.allocatePackageManager();
|
||||
const manager = PackageManager.get();
|
||||
const cpu_count = bun.getThreadCount();
|
||||
|
||||
const options = PackageManager.Options{
|
||||
.global = cli.global,
|
||||
.max_concurrent_lifecycle_scripts = cpu_count * 2,
|
||||
};
|
||||
|
||||
// Get current directory
|
||||
var cwd_buf: bun.PathBuffer = undefined;
|
||||
const cwd = try bun.getcwd(&cwd_buf);
|
||||
var entries = try Fs.FileSystem.init(null);
|
||||
const entries_option = try entries.fs.readDirectory(cwd, null, 0, true);
|
||||
|
||||
manager.* = PackageManager{
|
||||
.preallocated_network_tasks = .init(bun.default_allocator),
|
||||
.preallocated_resolve_tasks = .init(bun.default_allocator),
|
||||
.options = options,
|
||||
.active_lifecycle_scripts = .{
|
||||
.context = manager,
|
||||
},
|
||||
.network_task_fifo = std.fifo.LinearFifo(*Install.NetworkTask, .{ .Static = 32 }).init(),
|
||||
.patch_task_fifo = std.fifo.LinearFifo(*Install.PatchTask, .{ .Static = 32 }).init(),
|
||||
.allocator = ctx.allocator,
|
||||
.log = ctx.log,
|
||||
.root_dir = entries_option.entries,
|
||||
.env = env,
|
||||
.cpu_count = cpu_count,
|
||||
.thread_pool = bun.ThreadPool.init(.{
|
||||
.max_threads = cpu_count,
|
||||
}),
|
||||
.resolve_tasks = .{},
|
||||
.lockfile = undefined,
|
||||
.root_package_json_file = undefined,
|
||||
.event_loop = .{
|
||||
.mini = bun.JSC.MiniEventLoop.init(bun.default_allocator),
|
||||
},
|
||||
.original_package_json_path = try ctx.allocator.dupeZ(u8, cwd),
|
||||
.workspace_package_json_cache = .{},
|
||||
.workspace_name_hash = null,
|
||||
.subcommand = .pm,
|
||||
.root_package_json_name_at_time_of_init = "",
|
||||
};
|
||||
|
||||
manager.lockfile = try ctx.allocator.create(Lockfile);
|
||||
manager.lockfile.initEmpty(ctx.allocator);
|
||||
|
||||
try manager.options.load(
|
||||
ctx.allocator,
|
||||
ctx.log,
|
||||
env,
|
||||
cli,
|
||||
ctx.install,
|
||||
.pm,
|
||||
);
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
pub fn printHelp() void {
|
||||
|
||||
// the output of --help uses the following syntax highlighting
|
||||
@@ -155,30 +361,41 @@ pub const PackageManagerCommand = struct {
|
||||
var args = try std.process.argsAlloc(ctx.allocator);
|
||||
args = args[1..];
|
||||
const cli = try PackageManager.CommandLineArguments.parse(ctx.allocator, .pm);
|
||||
|
||||
// Get subcommand early to determine if package.json is needed
|
||||
var positionals_copy = cli.positionals;
|
||||
const subcommand = getSubcommand(&positionals_copy);
|
||||
|
||||
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| {
|
||||
Output.errGeneric("No package.json was found for directory \"{s}\"", .{cwd});
|
||||
} else |_| {
|
||||
Output.errGeneric("No package.json was found", .{});
|
||||
if (requiresPackageJson(subcommand, cli.global)) {
|
||||
var cwd_buf: bun.PathBuffer = undefined;
|
||||
if (bun.getcwd(&cwd_buf)) |cwd| {
|
||||
Output.errGeneric("No package.json was found for directory \"{s}\"", .{cwd});
|
||||
} else |_| {
|
||||
Output.errGeneric("No package.json was found", .{});
|
||||
}
|
||||
Output.note("Run \"bun init\" to initialize a project", .{});
|
||||
Global.exit(1);
|
||||
} else {
|
||||
// For commands that don't require package.json, create a minimal PackageManager
|
||||
return execWithoutPackageJson(ctx, cli, subcommand);
|
||||
}
|
||||
Output.note("Run \"bun init\" to initialize a project", .{});
|
||||
Global.exit(1);
|
||||
}
|
||||
return err;
|
||||
};
|
||||
defer ctx.allocator.free(cwd);
|
||||
|
||||
const subcommand = getSubcommand(&pm.options.positionals);
|
||||
// Get the subcommand for the normal flow
|
||||
const subcommand_final = getSubcommand(&pm.options.positionals);
|
||||
if (pm.options.global) {
|
||||
try pm.setupGlobalDir(ctx);
|
||||
}
|
||||
|
||||
if (strings.eqlComptime(subcommand, "pack")) {
|
||||
if (strings.eqlComptime(subcommand_final, "pack")) {
|
||||
try PackCommand.execWithManager(ctx, pm);
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "whoami")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "whoami")) {
|
||||
const username = Npm.whoami(ctx.allocator, pm) catch |err| {
|
||||
switch (err) {
|
||||
error.OutOfMemory => bun.outOfMemory(),
|
||||
@@ -195,11 +412,11 @@ pub const PackageManagerCommand = struct {
|
||||
};
|
||||
Output.println("{s}", .{username});
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "view")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "view")) {
|
||||
const property_path = if (pm.options.positionals.len > 2) pm.options.positionals[2] else null;
|
||||
try PmViewCommand.view(ctx.allocator, pm, if (pm.options.positionals.len > 1) pm.options.positionals[1] else "", property_path, pm.options.json_output);
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "bin")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "bin")) {
|
||||
const output_path = Path.joinAbs(Fs.FileSystem.instance.top_level_dir, .auto, bun.asByteSlice(pm.options.bin_path));
|
||||
Output.prettyln("{s}", .{output_path});
|
||||
if (Output.stdout_descriptor_type == .terminal) {
|
||||
@@ -225,7 +442,7 @@ pub const PackageManagerCommand = struct {
|
||||
|
||||
Output.flush();
|
||||
return;
|
||||
} else if (strings.eqlComptime(subcommand, "hash")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "hash")) {
|
||||
const load_lockfile = pm.lockfile.loadFromCwd(pm, ctx.allocator, ctx.log, true);
|
||||
handleLoadLockfileErrors(load_lockfile, pm);
|
||||
|
||||
@@ -236,7 +453,7 @@ pub const PackageManagerCommand = struct {
|
||||
try Output.writer().print("{}", .{load_lockfile.ok.lockfile.fmtMetaHash()});
|
||||
Output.enableBuffering();
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "hash-print")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "hash-print")) {
|
||||
const load_lockfile = pm.lockfile.loadFromCwd(pm, ctx.allocator, ctx.log, true);
|
||||
handleLoadLockfileErrors(load_lockfile, pm);
|
||||
|
||||
@@ -245,13 +462,13 @@ pub const PackageManagerCommand = struct {
|
||||
try Output.writer().print("{}", .{load_lockfile.ok.lockfile.fmtMetaHash()});
|
||||
Output.enableBuffering();
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "hash-string")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "hash-string")) {
|
||||
const load_lockfile = pm.lockfile.loadFromCwd(pm, ctx.allocator, ctx.log, true);
|
||||
handleLoadLockfileErrors(load_lockfile, pm);
|
||||
|
||||
_ = try pm.lockfile.hasMetaHashChanged(true, pm.lockfile.packages.len);
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "cache")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "cache")) {
|
||||
var dir: bun.PathBuffer = undefined;
|
||||
var fd = pm.getCacheDirectory();
|
||||
const outpath = bun.getFdPath(.fromStdDir(fd), &dir) catch |err| {
|
||||
@@ -309,16 +526,16 @@ pub const PackageManagerCommand = struct {
|
||||
|
||||
Output.writer().writeAll(outpath) catch {};
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "default-trusted")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "default-trusted")) {
|
||||
try DefaultTrustedCommand.exec();
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "untrusted")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "untrusted")) {
|
||||
try UntrustedCommand.exec(ctx, pm, args);
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "trust")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "trust")) {
|
||||
try TrustCommand.exec(ctx, pm, args);
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "ls")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "ls")) {
|
||||
const load_lockfile = pm.lockfile.loadFromCwd(pm, ctx.allocator, ctx.log, true);
|
||||
handleLoadLockfileErrors(load_lockfile, pm);
|
||||
|
||||
@@ -396,7 +613,7 @@ pub const PackageManagerCommand = struct {
|
||||
}
|
||||
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "migrate")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "migrate")) {
|
||||
if (!pm.options.enable.force_save_lockfile) {
|
||||
if (bun.sys.existsZ("bun.lock")) {
|
||||
Output.prettyErrorln(
|
||||
@@ -432,15 +649,15 @@ pub const PackageManagerCommand = struct {
|
||||
|
||||
lockfile.saveToDisk(&load_lockfile, &pm.options);
|
||||
Global.exit(0);
|
||||
} else if (strings.eqlComptime(subcommand, "version")) {
|
||||
} else if (strings.eqlComptime(subcommand_final, "version")) {
|
||||
try PmVersionCommand.exec(ctx, pm, pm.options.positionals, cwd);
|
||||
Global.exit(0);
|
||||
}
|
||||
|
||||
printHelp();
|
||||
|
||||
if (subcommand.len > 0) {
|
||||
Output.prettyErrorln("\n<red>error<r>: \"{s}\" unknown command\n", .{subcommand});
|
||||
if (subcommand_final.len > 0) {
|
||||
Output.prettyErrorln("\n<red>error<r>: \"{s}\" unknown command\n", .{subcommand_final});
|
||||
Output.flush();
|
||||
|
||||
Global.exit(1);
|
||||
|
||||
@@ -372,3 +372,131 @@ it("bun pm migrate", async () => {
|
||||
|
||||
expect(hash).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should work without package.json for global commands", async () => {
|
||||
const test_dir = tmpdirSync();
|
||||
const cache_dir = join(test_dir, ".cache");
|
||||
|
||||
// Test pm cache without package.json
|
||||
const {
|
||||
stdout: cacheOut,
|
||||
stderr: cacheErr,
|
||||
exitCode: cacheCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "cache"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...bunEnv,
|
||||
BUN_INSTALL_CACHE_DIR: cache_dir,
|
||||
},
|
||||
});
|
||||
expect(cacheCode).toBe(0);
|
||||
expect(cacheErr.toString("utf-8")).toBe("");
|
||||
expect(cacheOut.toString("utf-8")).toBe(cache_dir);
|
||||
|
||||
// Test pm whoami without package.json (will fail auth but shouldn't fail for missing package.json)
|
||||
const {
|
||||
stdout: whoamiOut,
|
||||
stderr: whoamiErr,
|
||||
exitCode: whoamiCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "whoami"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(whoamiCode).toBe(1); // Expected to fail due to missing auth
|
||||
expect(whoamiErr.toString("utf-8")).toContain("missing authentication");
|
||||
expect(whoamiErr.toString("utf-8")).not.toContain("No package.json");
|
||||
|
||||
// Test pm bin -g without package.json
|
||||
const {
|
||||
stdout: binOut,
|
||||
stderr: binErr,
|
||||
exitCode: binCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "bin", "-g"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(binCode).toBe(0);
|
||||
expect(binErr.toString("utf-8")).toBe("");
|
||||
expect(binOut.toString("utf-8")).toMatch(/bin/);
|
||||
|
||||
// Test pm default-trusted without package.json
|
||||
const {
|
||||
stdout: trustedOut,
|
||||
stderr: trustedErr,
|
||||
exitCode: trustedCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "default-trusted"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(trustedCode).toBe(0);
|
||||
expect(trustedErr.toString("utf-8")).toBe("");
|
||||
expect(trustedOut.toString("utf-8")).toContain("esbuild");
|
||||
});
|
||||
|
||||
it("should require package.json for project-specific commands", async () => {
|
||||
const test_dir = tmpdirSync();
|
||||
|
||||
// Test pm ls without package.json (should fail)
|
||||
const {
|
||||
stdout: lsOut,
|
||||
stderr: lsErr,
|
||||
exitCode: lsCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "ls"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(lsCode).toBe(1);
|
||||
expect(lsErr.toString("utf-8")).toContain("No package.json");
|
||||
|
||||
// Test pm version without package.json (should fail)
|
||||
const {
|
||||
stdout: versionOut,
|
||||
stderr: versionErr,
|
||||
exitCode: versionCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "version"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(versionCode).toBe(1);
|
||||
expect(versionErr.toString("utf-8")).toContain("No package.json");
|
||||
|
||||
// Test pm bin (without -g) without package.json (should fail)
|
||||
const {
|
||||
stdout: binOut,
|
||||
stderr: binErr,
|
||||
exitCode: binCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "bin"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(binCode).toBe(1);
|
||||
expect(binErr.toString("utf-8")).toContain("No package.json");
|
||||
});
|
||||
|
||||
52
test/regression/issue/18733.test.ts
Normal file
52
test/regression/issue/18733.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
// Regression test for https://github.com/oven-sh/bun/issues/18733
|
||||
// bun pm cache and bun pm cache rm should work without package.json
|
||||
|
||||
import { expect, it } from "bun:test";
|
||||
import { bunEnv, bunExe, tmpdirSync } from "harness";
|
||||
import { join } from "path";
|
||||
|
||||
it("pm cache commands work without package.json (#18733)", async () => {
|
||||
const test_dir = tmpdirSync();
|
||||
const cache_dir = join(test_dir, ".cache");
|
||||
|
||||
// Test pm cache without package.json
|
||||
const {
|
||||
stdout: cacheOut,
|
||||
stderr: cacheErr,
|
||||
exitCode: cacheCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "cache"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...bunEnv,
|
||||
BUN_INSTALL_CACHE_DIR: cache_dir,
|
||||
},
|
||||
});
|
||||
expect(cacheCode).toBe(0);
|
||||
expect(cacheErr.toString("utf-8")).toBe("");
|
||||
expect(cacheOut.toString("utf-8")).toBe(cache_dir);
|
||||
|
||||
// Test pm cache rm without package.json
|
||||
const {
|
||||
stdout: cacheRmOut,
|
||||
stderr: cacheRmErr,
|
||||
exitCode: cacheRmCode,
|
||||
} = Bun.spawnSync({
|
||||
cmd: [bunExe(), "pm", "cache", "rm"],
|
||||
cwd: test_dir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...bunEnv,
|
||||
BUN_INSTALL_CACHE_DIR: cache_dir,
|
||||
},
|
||||
});
|
||||
expect(cacheRmCode).toBe(0);
|
||||
expect(cacheRmErr.toString("utf-8")).toBe("");
|
||||
// The important thing is that it doesn't error with "No package.json"
|
||||
expect(cacheRmErr.toString("utf-8")).not.toContain("No package.json");
|
||||
});
|
||||
Reference in New Issue
Block a user