aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/api/bun.zig79
-rw-r--r--test/bun.js/which.test.ts52
2 files changed, 131 insertions, 0 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 8a75c3653..f36549485 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -76,6 +76,7 @@ const Transpiler = @import("./transpiler.zig");
const VirtualMachine = @import("../javascript.zig").VirtualMachine;
const IOTask = JSC.IOTask;
const zlib = @import("../../zlib.zig");
+const Which = @import("../../which.zig");
const is_bindgen = JSC.is_bindgen;
const max_addressible_memory = std.math.maxInt(u56);
@@ -129,6 +130,80 @@ pub fn getCSSImports() []ZigString {
return css_imports_list_strings[0..tail];
}
+pub fn which(
+ // this
+ _: void,
+ globalThis: js.JSContextRef,
+ // function
+ _: js.JSObjectRef,
+ // thisObject
+ _: js.JSObjectRef,
+ arguments_: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+) js.JSValueRef {
+ var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
+ var arguments = JSC.Node.ArgumentsSlice.from(globalThis.bunVM(), arguments_);
+ defer arguments.deinit();
+ const path_arg = arguments.nextEat() orelse {
+ JSC.throwInvalidArguments("which: expected 1 argument, got 0", .{}, globalThis, exception);
+ return JSC.JSValue.jsUndefined().asObjectRef();
+ };
+
+ var path_str: ZigString.Slice = ZigString.Slice.empty;
+ var bin_str: ZigString.Slice = ZigString.Slice.empty;
+ var cwd_str: ZigString.Slice = ZigString.Slice.empty;
+ defer {
+ path_str.deinit();
+ bin_str.deinit();
+ cwd_str.deinit();
+ }
+
+ if (path_arg.isEmptyOrUndefinedOrNull()) {
+ return JSC.JSValue.jsNull().asObjectRef();
+ }
+
+ bin_str = path_arg.toSlice(globalThis, globalThis.bunVM().allocator);
+
+ if (bin_str.len >= bun.MAX_PATH_BYTES) {
+ JSC.throwInvalidArguments("bin path is too long", .{}, globalThis, exception);
+ return JSC.JSValue.jsUndefined().asObjectRef();
+ }
+
+ if (bin_str.len == 0) {
+ return JSC.JSValue.jsNull().asObjectRef();
+ }
+
+ path_str = ZigString.Slice.fromUTF8(
+ globalThis.bunVM().bundler.env.map.get("PATH") orelse "",
+ );
+ cwd_str = ZigString.Slice.fromUTF8(
+ globalThis.bunVM().bundler.fs.top_level_dir,
+ );
+
+ if (arguments.nextEat()) |arg| {
+ if (!arg.isEmptyOrUndefinedOrNull() and arg.isObject()) {
+ if (arg.get(globalThis, "PATH")) |str_| {
+ path_str = str_.toSlice(globalThis, globalThis.bunVM().allocator);
+ }
+
+ if (arg.get(globalThis, "cwd")) |str_| {
+ cwd_str = str_.toSlice(globalThis, globalThis.bunVM().allocator);
+ }
+ }
+ }
+
+ if (Which.which(
+ &path_buf,
+ path_str.slice(),
+ cwd_str.slice(),
+ bin_str.slice(),
+ )) |bin_path| {
+ return ZigString.init(bin_path).withEncoding().toValueGC(globalThis).asObjectRef();
+ }
+
+ return JSC.JSValue.jsNull().asObjectRef();
+}
+
pub fn inspect(
// this
_: void,
@@ -1116,6 +1191,10 @@ pub const Class = NewClass(
.inflateSync = .{
.rfn = JSC.wrapWithHasContainer(JSZlib, "inflateSync", false, false, true),
},
+
+ .which = .{
+ .rfn = which,
+ },
},
.{
.main = .{
diff --git a/test/bun.js/which.test.ts b/test/bun.js/which.test.ts
new file mode 100644
index 000000000..7fd4b8400
--- /dev/null
+++ b/test/bun.js/which.test.ts
@@ -0,0 +1,52 @@
+import { test, expect } from "bun:test";
+
+import { which } from "bun";
+
+test("which", () => {
+ writeFixture("/tmp/myscript.sh");
+
+ // Our cwd is not /tmp
+ expect(which("myscript.sh")).toBe(null);
+
+ // "bun" is in our PATH
+ expect(which("bun")?.length > 0).toBe(true);
+
+ expect(
+ // You can override PATH
+ which("myscript.sh", {
+ PATH: "/tmp",
+ })
+ ).toBe("/tmp/myscript.sh");
+
+ expect(
+ which("myscript.sh", {
+ PATH: "/not-tmp",
+ })
+ ).toBe(null);
+
+ expect(
+ // PATH works like the $PATH environment variable, respecting colons
+ which("myscript.sh", {
+ PATH: "/not-tmp:/tmp",
+ })
+ ).toBe("/tmp/myscript.sh");
+
+ expect(
+ // cwd is checked first
+ which("myscript.sh", {
+ cwd: "/tmp",
+ })
+ ).toBe("/tmp/myscript.sh");
+});
+
+function writeFixture(path) {
+ var fs = require("fs");
+ try {
+ fs.unlinkSync(path);
+ } catch (e) {}
+
+ var script_name = path;
+ var script_content = "echo Hello world!";
+ fs.writeFileSync(script_name, script_content);
+ fs.chmodSync(script_name, "755");
+}