static JSValue fetchESMSourceCode()

in src/bun.js/bindings/ModuleLoader.cpp [658:835]


static JSValue fetchESMSourceCode(
    Zig::GlobalObject* globalObject,
    JSC::JSValue specifierJS,
    ErrorableResolvedSource* res,
    BunString* specifier,
    BunString* referrer,
    BunString* typeAttribute)
{
    void* bunVM = globalObject->bunVM();
    auto& vm = globalObject->vm();
    auto scope = DECLARE_THROW_SCOPE(vm);
    ResolvedSourceCodeHolder sourceCodeHolder(res);

    const auto reject = [&](JSC::JSValue exception) -> JSValue {
        if constexpr (allowPromise) {
            return rejectedInternalPromise(globalObject, exception);
        } else {
            throwException(globalObject, scope, exception);
            return {};
        }
    };

    const auto rejectOrResolve = [&](JSValue code) -> JSValue {
        if (auto* exception = scope.exception()) {
            if constexpr (!allowPromise) {
                scope.release();
                return {};
            }

            scope.clearException();
            return rejectedInternalPromise(globalObject, exception);
        }

        if constexpr (allowPromise) {
            auto* ret = resolvedInternalPromise(globalObject, code);
            scope.release();
            return ret;
        } else {
            return code;
        }
    };

    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)) {
            return handleVirtualModuleResult<allowPromise>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock);
        }
    }

    if (Bun__fetchBuiltinModule(bunVM, globalObject, specifier, referrer, res)) {
        if (!res->success) {
            throwException(scope, res->result.err, globalObject);
            auto* exception = scope.exception();
            scope.clearException();
            return reject(exception);
        }

        auto moduleKey = specifier->toWTFString(BunString::ZeroCopy);

        auto tag = res->result.value.tag;
        switch (tag) {
        case SyntheticModuleType::ESM: {
            auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value, JSC::SourceProviderSourceType::Module, true);
            return rejectOrResolve(JSSourceCode::create(vm, JSC::SourceCode(provider)));
        }

#define CASE(str, name)                                                                                                                            \
    case (SyntheticModuleType::name): {                                                                                                            \
        auto source = JSC::SourceCode(JSC::SyntheticSourceProvider::create(generateNativeModule_##name, JSC::SourceOrigin(), WTFMove(moduleKey))); \
        return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source)));                                                                         \
    }
            BUN_FOREACH_NATIVE_MODULE(CASE)
#undef CASE

        // CommonJS modules from src/js/*
        default: {
            if (tag & SyntheticModuleType::InternalModuleRegistryFlag) {
                constexpr auto mask = (SyntheticModuleType::InternalModuleRegistryFlag - 1);
                auto source = JSC::SourceCode(JSC::SyntheticSourceProvider::create(generateInternalModuleSourceCode(globalObject, static_cast<InternalModuleRegistry::Field>(tag & mask)), JSC::SourceOrigin(URL(makeString("builtins://"_s, moduleKey))), moduleKey));
                return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source)));
            } else {
                auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value, JSC::SourceProviderSourceType::Module, true);
                return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)));
            }
        }
        }
    }

    // When "bun test" is NOT enabled, disable users from overriding builtin modules
    if (!isBunTest) {
        if (JSC::JSValue virtualModuleResult = Bun::runVirtualModule(globalObject, specifier, wasModuleMock)) {
            return handleVirtualModuleResult<allowPromise>(globalObject, virtualModuleResult, res, specifier, referrer, wasModuleMock);
        }
    }

    if constexpr (allowPromise) {
        auto* pendingCtx = Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, true);
        if (pendingCtx) {
            return pendingCtx;
        }
    } else {
        Bun__transpileFile(bunVM, globalObject, specifier, referrer, typeAttribute, res, false);
    }

    if (res->success && res->result.value.isCommonJSModule) {
        auto created = Bun::createCommonJSModule(globalObject, specifierJS, res->result.value);

        if (created.has_value()) {
            return rejectOrResolve(JSSourceCode::create(vm, WTFMove(created.value())));
        }

        if constexpr (allowPromise) {
            auto* exception = scope.exception();
            scope.clearException();
            return rejectedInternalPromise(globalObject, exception);
        } else {
            return {};
        }
    }

    if (!res->success) {
        throwException(scope, res->result.err, globalObject);
        auto* exception = scope.exception();
        scope.clearException();
        return reject(exception);
    }

    // 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) {
            return reject(JSC::JSValue(JSC::createSyntaxError(globalObject, "Failed to parse JSON"_s)));
        }

        // JSON can become strings, null, numbers, booleans so we must handle "export default 123"
        auto function = generateJSValueModuleSourceCode(
            globalObject,
            value);
        auto source = JSC::SourceCode(
            JSC::SyntheticSourceProvider::create(WTFMove(function),
                JSC::SourceOrigin(), specifier->toWTFString(BunString::ZeroCopy)));
        JSC::ensureStillAliveHere(value);
        return rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source)));
    }
    // 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) {
            return reject(JSC::JSValue(JSC::createSyntaxError(globalObject, "Failed to parse Object"_s)));
        }

        // JSON can become strings, null, numbers, booleans so we must handle "export default 123"
        auto function = generateJSValueModuleSourceCode(
            globalObject,
            value);
        auto source = JSC::SourceCode(
            JSC::SyntheticSourceProvider::create(WTFMove(function),
                JSC::SourceOrigin(), specifier->toWTFString(BunString::ZeroCopy)));
        JSC::ensureStillAliveHere(value);
        return rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source)));
    }

    return rejectOrResolve(JSC::JSSourceCode::create(vm,
        JSC::SourceCode(Zig::SourceProvider::create(globalObject, res->result.value))));
}