in src/bun.js/bindings/ModuleLoader.cpp [460:653]
JSValue fetchCommonJSModule(
Zig::GlobalObject* globalObject,
JSCommonJSModule* target,
JSValue specifierValue,
BunString* specifier,
BunString* referrer,
BunString* typeAttribute)
{
void* bunVM = globalObject->bunVM();
auto& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
ErrorableResolvedSource resValue;
memset(&resValue, 0, sizeof(ErrorableResolvedSource));
ErrorableResolvedSource* res = &resValue;
ResolvedSourceCodeHolder sourceCodeHolder(res);
auto& builtinNames = WebCore::clientData(vm)->builtinNames();
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)) {
JSPromise* promise = jsCast<JSPromise*>(handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock));
switch (promise->status(vm)) {
case JSPromise::Status::Rejected: {
uint32_t promiseFlags = promise->internalField(JSPromise::Field::Flags).get().asUInt32AsAnyInt();
promise->internalField(JSPromise::Field::Flags).set(vm, promise, jsNumber(promiseFlags | JSPromise::isHandledFlag));
JSC::throwException(globalObject, scope, promise->result(vm));
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));
RELEASE_AND_RETURN(scope, JSValue {});
}
case JSPromise::Status::Fulfilled: {
if (!res->success) {
throwException(scope, res->result.err, globalObject);
RELEASE_AND_RETURN(scope, {});
}
if (!wasModuleMock) {
auto* jsSourceCode = jsCast<JSSourceCode*>(promise->result(vm));
globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, jsSourceCode->sourceCode());
RETURN_IF_EXCEPTION(scope, {});
}
RELEASE_AND_RETURN(scope, jsNumber(-1));
}
}
}
}
if (Bun__fetchBuiltinModule(bunVM, globalObject, specifier, referrer, res)) {
if (!res->success) {
throwException(scope, res->result.err, globalObject);
return JSValue();
}
auto tag = res->result.value.tag;
switch (tag) {
// Generated native module cases
#define CASE(str, name) \
case SyntheticModuleType::name: { \
target->evaluate(globalObject, specifier->toWTFString(BunString::ZeroCopy), generateNativeModule_##name); \
RETURN_IF_EXCEPTION(scope, {}); \
RELEASE_AND_RETURN(scope, target); \
}
BUN_FOREACH_NATIVE_MODULE(CASE)
#undef CASE
case SyntheticModuleType::ESM: {
RELEASE_AND_RETURN(scope, jsNumber(-1));
}
default: {
if (tag & SyntheticModuleType::InternalModuleRegistryFlag) {
constexpr auto mask = (SyntheticModuleType::InternalModuleRegistryFlag - 1);
auto result = globalObject->internalModuleRegistry()->requireId(globalObject, vm, static_cast<InternalModuleRegistry::Field>(tag & mask));
RETURN_IF_EXCEPTION(scope, {});
target->putDirect(
vm,
builtinNames.exportsPublicName(),
result,
JSC::PropertyAttribute::ReadOnly | 0);
RELEASE_AND_RETURN(scope, target);
} else {
RELEASE_AND_RETURN(scope, jsNumber(-1));
}
}
}
}
// When "bun test" is NOT enabled, disable users from overriding builtin modules
if (!isBunTest) {
if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier, wasModuleMock)) {
JSPromise* promise = jsCast<JSPromise*>(handleVirtualModuleResult<true>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock));
switch (promise->status(vm)) {
case JSPromise::Status::Rejected: {
uint32_t promiseFlags = promise->internalField(JSPromise::Field::Flags).get().asUInt32AsAnyInt();
promise->internalField(JSPromise::Field::Flags).set(vm, promise, jsNumber(promiseFlags | JSPromise::isHandledFlag));
JSC::throwException(globalObject, scope, promise->result(vm));
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));
RELEASE_AND_RETURN(scope, JSValue {});
}
case JSPromise::Status::Fulfilled: {
if (!res->success) {
throwException(scope, res->result.err, globalObject);
RELEASE_AND_RETURN(scope, {});
}
if (!wasModuleMock) {
auto* jsSourceCode = jsCast<JSSourceCode*>(promise->result(vm));
globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, jsSourceCode->sourceCode());
RETURN_IF_EXCEPTION(scope, {});
}
RELEASE_AND_RETURN(scope, jsNumber(-1));
}
}
}
}
JSMap* registry = globalObject->esmRegistryMap();
const auto hasAlreadyLoadedESMVersionSoWeShouldntTranspileItTwice = [&]() -> bool {
JSValue entry = registry->get(globalObject, specifierValue);
if (!entry || !entry.isObject()) {
return false;
}
int status = entry.getObject()->getDirect(vm, WebCore::clientData(vm)->builtinNames().statePublicName()).asInt32();
return status > JSModuleLoader::Status::Fetch;
};
if (hasAlreadyLoadedESMVersionSoWeShouldntTranspileItTwice()) {
RELEASE_AND_RETURN(scope, jsNumber(-1));
}
Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false);
if (res->success && res->result.value.isCommonJSModule) {
target->evaluate(globalObject, specifier->toWTFString(BunString::ZeroCopy), res->result.value);
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, target);
}
if (!res->success) {
throwException(scope, res->result.err, globalObject);
RELEASE_AND_RETURN(scope, {});
}
// The JSONForObjectLoader tag is source code returned from Bun that needs
// to go through the JSON parser in JSC.
//
// We don't use JSON.parse directly in JS because we want the top-level keys of the JSON
// object to be accessible as named imports.
//
// We don't use Bun's JSON parser because JSON.parse is faster and
// handles stack overflow better.
//
// When parsing tsconfig.*.json or jsconfig.*.json, we go through Bun's JSON
// parser instead to support comments and trailing commas.
if (res->result.value.tag == SyntheticModuleType::JSONForObjectLoader) {
JSC::JSValue value = JSC::JSONParse(globalObject, res->result.value.source_code.toWTFString(BunString::ZeroCopy));
if (!value) {
JSC::throwException(globalObject, scope, JSC::createSyntaxError(globalObject, "Failed to parse JSON"_s));
RELEASE_AND_RETURN(scope, {});
}
target->putDirect(vm, WebCore::clientData(vm)->builtinNames().exportsPublicName(), value, 0);
target->hasEvaluated = true;
RELEASE_AND_RETURN(scope, target);
}
// TOML and JSONC may go through here
else if (res->result.value.tag == SyntheticModuleType::ExportsObject) {
JSC::JSValue value = JSC::JSValue::decode(res->result.value.jsvalue_for_export);
if (!value) {
JSC::throwException(globalObject, scope, JSC::createSyntaxError(globalObject, "Failed to parse Object"_s));
RELEASE_AND_RETURN(scope, {});
}
target->putDirect(vm, WebCore::clientData(vm)->builtinNames().exportsPublicName(), value, 0);
target->hasEvaluated = true;
RELEASE_AND_RETURN(scope, target);
}
auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value);
globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, JSC::SourceCode(provider));
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, jsNumber(-1));
}