aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/bindings/CommonJSModuleRecord.cpp31
-rw-r--r--src/bun.js/bindings/CommonJSModuleRecord.h1
-rw-r--r--src/bun.js/modules/NodeModuleModule.cpp15
-rw-r--r--src/resolver/resolver.zig95
-rw-r--r--test/js/node/module/node-module-module.test.js19
5 files changed, 150 insertions, 11 deletions
diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp
index 8d4fe0a1e..3615db774 100644
--- a/src/bun.js/bindings/CommonJSModuleRecord.cpp
+++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp
@@ -295,6 +295,35 @@ JSC_DEFINE_CUSTOM_SETTER(setterPath,
return true;
}
+extern "C" EncodedJSValue Resolver__propForRequireMainPaths(JSGlobalObject*);
+
+JSC_DEFINE_CUSTOM_GETTER(getterPaths, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
+{
+ JSCommonJSModule* thisObject = jsDynamicCast<JSCommonJSModule*>(JSValue::decode(thisValue));
+ if (UNLIKELY(!thisObject)) {
+ return JSValue::encode(jsUndefined());
+ }
+
+ if (!thisObject->m_paths) {
+ JSValue paths = JSValue::decode(Resolver__propForRequireMainPaths(globalObject));
+ thisObject->m_paths.set(globalObject->vm(), thisObject, paths);
+ }
+
+ return JSValue::encode(thisObject->m_paths.get());
+}
+
+JSC_DEFINE_CUSTOM_SETTER(setterPaths,
+ (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue,
+ JSC::EncodedJSValue value, JSC::PropertyName propertyName))
+{
+ JSCommonJSModule* thisObject = jsDynamicCast<JSCommonJSModule*>(JSValue::decode(thisValue));
+ if (!thisObject)
+ return false;
+
+ thisObject->m_paths.set(globalObject->vm(), thisObject, JSValue::decode(value));
+ return true;
+}
+
JSC_DEFINE_CUSTOM_SETTER(setterFilename,
(JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue,
JSC::EncodedJSValue value, JSC::PropertyName propertyName))
@@ -340,6 +369,7 @@ static const struct HashTableValue JSCommonJSModulePrototypeTableValues[] = {
{ "loaded"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback | PropertyAttribute::DontEnum | 0), NoIntrinsic, { HashTableValue::LazyPropertyType, createLoaded } },
{ "parent"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback | PropertyAttribute::DontEnum | 0), NoIntrinsic, { HashTableValue::LazyPropertyType, createParent } },
{ "path"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterPath, setterPath } },
+ { "paths"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, getterPaths, setterPaths } },
};
class JSCommonJSModulePrototype final : public JSC::JSNonFinalObject {
@@ -675,6 +705,7 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor)
visitor.append(thisObject->sourceCode);
visitor.append(thisObject->m_filename);
visitor.append(thisObject->m_dirname);
+ visitor.append(thisObject->m_paths);
}
DEFINE_VISIT_CHILDREN(JSCommonJSModule);
diff --git a/src/bun.js/bindings/CommonJSModuleRecord.h b/src/bun.js/bindings/CommonJSModuleRecord.h
index 48f14b39c..a96ab5f75 100644
--- a/src/bun.js/bindings/CommonJSModuleRecord.h
+++ b/src/bun.js/bindings/CommonJSModuleRecord.h
@@ -24,6 +24,7 @@ public:
mutable JSC::WriteBarrier<JSC::JSString> m_id;
mutable JSC::WriteBarrier<JSC::JSString> m_filename;
mutable JSC::WriteBarrier<JSC::JSString> m_dirname;
+ mutable JSC::WriteBarrier<Unknown> m_paths;
mutable JSC::WriteBarrier<JSC::JSSourceCode> sourceCode;
static void destroy(JSC::JSCell*);
diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp
index 8b278ddd8..34d45698f 100644
--- a/src/bun.js/modules/NodeModuleModule.cpp
+++ b/src/bun.js/modules/NodeModuleModule.cpp
@@ -26,15 +26,8 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire,
scope, JSValue::encode(Zig::ImportMetaObject::createRequireFunction(
vm, globalObject, val)));
}
-JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModulePaths,
- (JSC::JSGlobalObject * globalObject,
- JSC::CallFrame *callFrame)) {
- return JSC::JSValue::encode(JSC::JSArray::create(
- globalObject->vm(),
- globalObject->arrayStructureForIndexingTypeDuringAllocation(
- ArrayWithContiguous),
- 0));
-}
+extern "C" EncodedJSValue Resolver__nodeModulePathsForJS(JSGlobalObject *,
+ CallFrame *);
JSC_DEFINE_HOST_FUNCTION(jsFunctionFindSourceMap,
(JSGlobalObject * globalObject,
@@ -114,7 +107,7 @@ void generateNodeModuleModule(JSC::JSGlobalObject *globalObject,
vm, globalObject, 1, String("createRequire"_s),
jsFunctionNodeModuleCreateRequire, ImplementationVisibility::Public));
exportValues.append(JSFunction::create(vm, globalObject, 1, String("paths"_s),
- jsFunctionNodeModulePaths,
+ Resolver__nodeModulePathsForJS,
ImplementationVisibility::Public));
exportValues.append(JSFunction::create(
vm, globalObject, 1, String("findSourceMap"_s), jsFunctionFindSourceMap,
@@ -143,7 +136,7 @@ void generateNodeModuleModule(JSC::JSGlobalObject *globalObject,
exportNames.append(JSC::Identifier::fromString(vm, "_nodeModulePaths"_s));
exportValues.append(JSFunction::create(
vm, globalObject, 0, String("_nodeModulePaths"_s),
- jsFunctionNodeModulePaths, ImplementationVisibility::Public));
+ Resolver__nodeModulePathsForJS, ImplementationVisibility::Public));
exportNames.append(JSC::Identifier::fromString(vm, "_cache"_s));
exportValues.append(
diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig
index 14bc358d0..40b106f3a 100644
--- a/src/resolver/resolver.zig
+++ b/src/resolver/resolver.zig
@@ -94,6 +94,7 @@ const bufs = struct {
threadlocal var remap_path_trailing_slash: [bun.MAX_PATH_BYTES]u8 = undefined;
threadlocal var path_in_global_disk_cache: [bun.MAX_PATH_BYTES]u8 = undefined;
threadlocal var abs_to_rel: [bun.MAX_PATH_BYTES]u8 = undefined;
+ threadlocal var node_modules_paths_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
pub inline fn bufs(comptime field: std.meta.DeclEnum(@This())) *@TypeOf(@field(@This(), @tagName(field))) {
return &@field(@This(), @tagName(field));
@@ -3107,6 +3108,93 @@ pub const Resolver = struct {
};
}
+ pub export fn Resolver__nodeModulePathsForJS(globalThis: *bun.JSC.JSGlobalObject, callframe: *bun.JSC.CallFrame) callconv(.C) bun.JSC.JSValue {
+ bun.JSC.markBinding(@src());
+ const argument: bun.JSC.JSValue = callframe.argument(0);
+
+ if (argument.isEmpty() or !argument.isString()) {
+ globalThis.throwInvalidArgumentType("nodeModulePaths", "path", "string");
+ return .zero;
+ }
+
+ const in_str = argument.toBunString(globalThis);
+ var r = &globalThis.bunVM().bundler.resolver;
+ return nodeModulePathsJSValue(r, in_str, globalThis);
+ }
+
+ pub export fn Resolver__propForRequireMainPaths(globalThis: *bun.JSC.JSGlobalObject) callconv(.C) bun.JSC.JSValue {
+ bun.JSC.markBinding(@src());
+
+ const in_str = bun.String.create(".");
+ var r = &globalThis.bunVM().bundler.resolver;
+ return nodeModulePathsJSValue(r, in_str, globalThis);
+ }
+
+ pub fn nodeModulePathsJSValue(
+ r: *ThisResolver,
+ in_str: bun.String,
+ globalObject: *bun.JSC.JSGlobalObject,
+ ) bun.JSC.JSValue {
+ var list = std.ArrayList(bun.String).init(bun.default_allocator);
+ defer list.deinit();
+
+ const sliced = in_str.toUTF8(bun.default_allocator);
+ defer sliced.deinit();
+
+ const str = brk: {
+ if (std.fs.path.isAbsolute(sliced.slice())) break :brk sliced.slice();
+ var dir_path_buf = bufs(.node_modules_paths_buf);
+ break :brk r.fs.joinBuf(&[_]string{ r.fs.top_level_dir, sliced.slice() }, dir_path_buf);
+ };
+ var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
+ defer arena.deinit();
+ var stack_fallback_allocator = std.heap.stackFallback(1024, arena.allocator());
+
+ if (r.readDirInfo(strings.withoutTrailingSlash(str)) catch null) |result| {
+ var dir_info = result;
+
+ while (true) {
+ const path_without_trailing_slash = strings.withoutTrailingSlash(dir_info.abs_path);
+ const path_parts = brk: {
+ if (path_without_trailing_slash.len == 1 and path_without_trailing_slash[0] == '/') {
+ break :brk [2]string{ "", "/node_modules" };
+ }
+
+ break :brk [2]string{ path_without_trailing_slash, "/node_modules" };
+ };
+ list.append(
+ bun.String.create(
+ bun.strings.concat(stack_fallback_allocator.get(), &path_parts) catch unreachable,
+ ),
+ ) catch unreachable;
+ dir_info = (r.readDirInfo(std.fs.path.dirname(path_without_trailing_slash) orelse break) catch null) orelse break;
+ }
+ } else {
+ // does not exist
+ const full_path = std.fs.path.resolve(r.allocator, &[1][]const u8{str}) catch unreachable;
+ var path = full_path;
+ while (true) {
+ const path_without_trailing_slash = strings.withoutTrailingSlash(path);
+
+ list.append(
+ bun.String.create(
+ bun.strings.concat(
+ stack_fallback_allocator.get(),
+ &[_]string{
+ path_without_trailing_slash,
+ "/node_modules",
+ },
+ ) catch unreachable,
+ ),
+ ) catch unreachable;
+
+ path = path[0 .. strings.lastIndexOfChar(path, '/') orelse break];
+ }
+ }
+
+ return bun.String.toJSArray(globalObject, list.items);
+ }
+
pub fn loadAsIndex(r: *ThisResolver, dir_info: *DirInfo, extension_order: []const string) ?MatchResult {
var rfs = &r.fs.fs;
// Try the "index" file with extensions
@@ -3892,3 +3980,10 @@ pub const GlobalCache = enum {
};
}
};
+
+comptime {
+ if (!bun.JSC.is_bindgen) {
+ _ = Resolver.Resolver__nodeModulePathsForJS;
+ _ = Resolver.Resolver__propForRequireMainPaths;
+ }
+}
diff --git a/test/js/node/module/node-module-module.test.js b/test/js/node/module/node-module-module.test.js
index 549b5e085..3ced63da1 100644
--- a/test/js/node/module/node-module-module.test.js
+++ b/test/js/node/module/node-module-module.test.js
@@ -1,5 +1,24 @@
import { expect, test } from "bun:test";
+import { _nodeModulePaths } from "module";
test("module.globalPaths exists", () => {
expect(Array.isArray(require("module").globalPaths)).toBe(true);
});
+
+test("_nodeModulePaths() works", () => {
+ expect(() => {
+ _nodeModulePaths();
+ }).toThrow();
+ expect(_nodeModulePaths(".").length).toBeGreaterThan(0);
+ expect(_nodeModulePaths(".").pop()).toBe("/node_modules");
+ expect(_nodeModulePaths("")).toEqual(_nodeModulePaths("."));
+ expect(_nodeModulePaths("/")).toEqual(["/node_modules"]);
+ expect(_nodeModulePaths("/a/b/c/d")).toEqual([
+ "/a/b/c/d/node_modules",
+ "/a/b/c/node_modules",
+ "/a/b/node_modules",
+ "/a/node_modules",
+ "/node_modules",
+ ]);
+ expect(_nodeModulePaths("/a/b/../d")).toEqual(["/a/d/node_modules", "/a/node_modules", "/node_modules"]);
+});