diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index a15b917629..c85feff505 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -66,7 +66,7 @@ #include #include -extern "C" bool Bun__isBunMain(JSC::JSGlobalObject* global, const char* input_ptr, uint64_t input_len); +extern "C" bool Bun__isBunMain(JSC::JSGlobalObject* global, const BunString*); namespace Bun { using namespace JSC; @@ -321,8 +321,9 @@ JSC_DEFINE_CUSTOM_GETTER(getterParent, (JSC::JSGlobalObject * globalObject, JSC: // dont need `module.parent` and creating commonjs module records is done a ton. auto idValue = thisObject->m_id.get(); if (idValue) { - auto id = idValue->value(globalObject).utf8(); - if (Bun__isBunMain(globalObject, id.data(), id.length())) { + auto id = idValue->value(globalObject); + auto idStr = Bun::toString(id); + if (Bun__isBunMain(globalObject, &idStr)) { thisObject->m_parent.set(globalObject->vm(), thisObject, jsNull()); return JSValue::encode(jsNull()); } diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index f4fb0273aa..2818fa852e 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "JSBufferEncodingType.h" #include @@ -286,6 +288,8 @@ extern "C" JSC::EncodedJSValue functionImportMeta__resolveSync(JSC::JSGlobalObje return result; } +extern "C" bool Bun__isBunMain(JSC::JSGlobalObject* global, const BunString*); + extern "C" JSC::EncodedJSValue functionImportMeta__resolveSyncPrivate(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) { JSC::VM& vm = lexicalGlobalObject->vm(); @@ -320,10 +324,23 @@ extern "C" JSC::EncodedJSValue functionImportMeta__resolveSyncPrivate(JSC::JSGlo auto overrideHandler = globalObject->m_nodeModuleOverriddenResolveFilename.get(); if (UNLIKELY(overrideHandler)) { ASSERT(overrideHandler->isCallable()); + JSValue parentModuleObject = globalObject->requireMap()->get(globalObject, from); + + JSValue parentID = jsUndefined(); + if (auto* parent = jsDynamicCast(parentModuleObject)) { + parentID = parent->id(); + } else { + parentID = from; + } + MarkedArgumentBuffer args; args.append(moduleName); - args.append(from); - return JSValue::encode(JSC::call(lexicalGlobalObject, overrideHandler, JSC::getCallData(overrideHandler), JSC::jsUndefined(), args)); + args.append(parentModuleObject); + auto parentIdStr = parentID.toWTFString(globalObject); + auto bunStr = Bun::toString(parentIdStr); + args.append(jsBoolean(Bun__isBunMain(lexicalGlobalObject, &bunStr))); + + return JSValue::encode(JSC::call(lexicalGlobalObject, overrideHandler, JSC::getCallData(overrideHandler), parentModuleObject, args)); } } } diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index fc97594daf..77672ed92f 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -304,8 +304,8 @@ pub export fn Bun__Process__send( } } -pub export fn Bun__isBunMain(globalObject: *JSGlobalObject, input_ptr: [*]const u8, input_len: usize) bool { - return strings.eql(globalObject.bunVM().main, input_ptr[0..input_len]); +pub export fn Bun__isBunMain(globalObject: *JSGlobalObject, str: *const bun.String) bool { + return str.eqlUTF8(globalObject.bunVM().main); } pub export fn Bun__Process__disconnect( diff --git a/src/bun.js/modules/NodeModuleModule.h b/src/bun.js/modules/NodeModuleModule.h index cf8ad2be4e..3fb5e72f94 100644 --- a/src/bun.js/modules/NodeModuleModule.h +++ b/src/bun.js/modules/NodeModuleModule.h @@ -188,18 +188,24 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionNodeModuleCreateRequire, if (val.startsWith("file://"_s)) { WTF::URL url(val); if (!url.isValid()) { - throwTypeError(globalObject, scope, makeString("createRequire() was given an invalid URL '"_s, url.string(), "'"_s));; + throwTypeError(globalObject, scope, + makeString("createRequire() was given an invalid URL '"_s, + url.string(), "'"_s)); + ; RELEASE_AND_RETURN(scope, JSValue::encode({})); } if (!url.protocolIsFile()) { - throwTypeError(globalObject, scope, "createRequire() does not support non-file URLs"_s); + throwTypeError(globalObject, scope, + "createRequire() does not support non-file URLs"_s); RELEASE_AND_RETURN(scope, JSValue::encode({})); } val = url.fileSystemPath(); } RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined())); - RELEASE_AND_RETURN(scope, JSValue::encode(Bun::JSCommonJSModule::createBoundRequireFunction(vm, globalObject, val))); + RELEASE_AND_RETURN( + scope, JSValue::encode(Bun::JSCommonJSModule::createBoundRequireFunction( + vm, globalObject, val))); } extern "C" JSC::EncodedJSValue Resolver__nodeModulePathsForJS(JSGlobalObject *, CallFrame *); @@ -246,6 +252,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveFileName, } default: { JSC::JSValue moduleName = callFrame->argument(0); + JSC::JSValue fromValue = callFrame->argument(1); if (moduleName.isUndefinedOrNull()) { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); @@ -255,9 +262,26 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionResolveFileName, return JSC::JSValue::encode(JSC::JSValue{}); } + if ( + // fast path: it's a real CommonJS module object. + auto *cjs = jsDynamicCast(fromValue)) { + fromValue = cjs->id(); + } else if + // slow path: userland code did something weird. lets let them do that + // weird thing. + (fromValue.isObject()) { + + if (auto idValue = fromValue.getObject()->getIfPropertyExists( + globalObject, Identifier::fromString(vm, "filename"_s))) { + if (idValue.isString()) { + fromValue = idValue; + } + } + } + auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), - JSValue::encode(callFrame->argument(1)), false); + JSValue::encode(fromValue), false); auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); if (!JSC::JSValue::decode(result).isString()) { diff --git a/src/string.zig b/src/string.zig index 3ff9e2497a..47af6ddb19 100644 --- a/src/string.zig +++ b/src/string.zig @@ -1087,7 +1087,7 @@ pub const String = extern struct { } pub fn eqlUTF8(this: String, other: []const u8) bool { - return this.toZigString().eql(ZigString.initUTF8(other)); + return this.toZigString().eql(ZigString.fromUTF8(other)); } pub fn eql(this: String, other: String) bool { diff --git a/test/js/node/module/resolveFilenameOverwrite.cjs b/test/js/node/module/resolveFilenameOverwrite.cjs index e2d1327a73..e6a3670c39 100644 --- a/test/js/node/module/resolveFilenameOverwrite.cjs +++ b/test/js/node/module/resolveFilenameOverwrite.cjs @@ -4,8 +4,9 @@ const path = require("path"); const Module = require("module"); const original = Module._resolveFilename; -Module._resolveFilename = str => { - eql(str.endsWith("💔"), true); +Module._resolveFilename = (specifier, parent, isMain) => { + eql(specifier.endsWith("💔"), true); + eql(parent.filename, path.join(__dirname, "./resolveFilenameOverwrite.cjs")); return path.join(__dirname, "./resolveFilenameOverwrite-fixture.cjs"); }; eql(require("overwriting _resolveFilename broke 💔"), "winner");