implement require.extensions (#18478)

This commit is contained in:
chloe caruso
2025-03-27 14:58:24 -07:00
committed by GitHub
parent 934e41ae59
commit 70ddfb55e6
33 changed files with 891 additions and 181 deletions

View File

@@ -37,6 +37,8 @@
#include "JSCommonJSModule.h"
#include "../modules/_NativeModule.h"
#include "JSCommonJSExtensions.h"
namespace Bun {
using namespace JSC;
using namespace Zig;
@@ -567,11 +569,39 @@ JSValue resolveAndFetchBuiltinModule(
return {};
}
void evaluateCommonJSCustomExtension(
Zig::GlobalObject* globalObject,
JSCommonJSModule* target,
String filename,
JSValue filenameValue,
uint32_t extensionIndex)
{
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
Bun::JSCommonJSExtensions* extensions = globalObject->lazyRequireExtensionsObject();
JSValue extension = extensions->m_registeredFunctions[extensionIndex].get();
if (!extension || !extension.isCallable()) {
throwTypeError(globalObject, scope, makeString("require.extension is not a function"_s));
return;
}
JSC::CallData callData = JSC::getCallData(extension.asCell());
if (callData.type == JSC::CallData::Type::None) {
throwTypeError(globalObject, scope, makeString("require.extension is not a function"_s));
return;
}
MarkedArgumentBuffer arguments;
arguments.append(target);
arguments.append(filenameValue);
JSC::profiledCall(globalObject, ProfilingReason::API, extension, callData, target, arguments);
RETURN_IF_EXCEPTION(scope, );
}
JSValue fetchCommonJSModule(
Zig::GlobalObject* globalObject,
JSCommonJSModule* target,
JSValue specifierValue,
BunString* specifier,
String specifierWtfString,
BunString* referrer,
BunString* typeAttribute)
{
@@ -584,13 +614,15 @@ JSValue fetchCommonJSModule(
ErrorableResolvedSource* res = &resValue;
ResolvedSourceCodeHolder sourceCodeHolder(res);
BunString specifier = Bun::toString(specifierWtfString);
bool wasModuleMock = false;
// When "bun test" is enabled, allow users to override builtin modules
// This is important for being able to trivially mock things like the filesystem.
if (isBunTest) {
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier, wasModuleMock)) {
JSValue promiseOrCommonJSModule = handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock, target);
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, &specifier, wasModuleMock)) {
JSValue promiseOrCommonJSModule = handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, &specifier, referrer, wasModuleMock, target);
RETURN_IF_EXCEPTION(scope, {});
// If we assigned module.exports to the virtual module, we're done here.
@@ -606,7 +638,7 @@ JSValue fetchCommonJSModule(
RELEASE_AND_RETURN(scope, JSValue {});
}
case JSPromise::Status::Pending: {
JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, specifier->toWTFString(BunString::ZeroCopy), "\" is unsupported. use \"await import()\" instead."_s));
JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, specifierWtfString, "\" is unsupported. use \"await import()\" instead."_s));
RELEASE_AND_RETURN(scope, JSValue {});
}
case JSPromise::Status::Fulfilled: {
@@ -625,7 +657,7 @@ JSValue fetchCommonJSModule(
}
}
if (auto builtin = fetchBuiltinModuleWithoutResolution(globalObject, specifier, res)) {
if (auto builtin = fetchBuiltinModuleWithoutResolution(globalObject, &specifier, res)) {
if (!res->success) {
RELEASE_AND_RETURN(scope, builtin);
}
@@ -636,8 +668,8 @@ JSValue fetchCommonJSModule(
// When "bun test" is NOT enabled, disable users from overriding builtin modules
if (!isBunTest) {
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier, wasModuleMock)) {
JSValue promiseOrCommonJSModule = handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock, target);
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, &specifier, wasModuleMock)) {
JSValue promiseOrCommonJSModule = handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, &specifier, referrer, wasModuleMock, target);
RETURN_IF_EXCEPTION(scope, {});
// If we assigned module.exports to the virtual module, we're done here.
@@ -653,7 +685,7 @@ JSValue fetchCommonJSModule(
RELEASE_AND_RETURN(scope, JSValue {});
}
case JSPromise::Status::Pending: {
JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, specifier->toWTFString(BunString::ZeroCopy), "\" is unsupported. use \"await import()\" instead."_s));
JSC::throwTypeError(globalObject, scope, makeString("require() async module \""_s, specifierWtfString, "\" is unsupported. use \"await import()\" instead."_s));
RELEASE_AND_RETURN(scope, JSValue {});
}
case JSPromise::Status::Fulfilled: {
@@ -689,9 +721,9 @@ JSValue fetchCommonJSModule(
RELEASE_AND_RETURN(scope, jsNumber(-1));
}
Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false);
Bun__transpileFile(bunVM, globalObject, &specifier, referrer, typeAttribute, res, false, true);
if (res->success && res->result.value.isCommonJSModule) {
target->evaluate(globalObject, specifier->toWTFString(BunString::ZeroCopy), res->result.value);
target->evaluate(globalObject, specifierWtfString, res->result.value);
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, target);
}
@@ -733,6 +765,10 @@ JSValue fetchCommonJSModule(
target->putDirect(vm, WebCore::clientData(vm)->builtinNames().exportsPublicName(), value, 0);
target->hasEvaluated = true;
RELEASE_AND_RETURN(scope, target);
} else if (res->result.value.tag == SyntheticModuleType::CommonJSCustomExtension) {
evaluateCommonJSCustomExtension(globalObject, target, specifierWtfString, specifierValue, res->result.value.cjsCustomExtensionIndex);
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, target);
}
auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value);
@@ -860,12 +896,12 @@ static JSValue fetchESMSourceCode(
}
if constexpr (allowPromise) {
auto* pendingCtx = Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, true);
auto* pendingCtx = Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, true, false);
if (pendingCtx) {
return pendingCtx;
}
} else {
Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false);
Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false, false);
}
if (res->success && res->result.value.isCommonJSModule) {