diff --git a/src/bun.js/ModuleLoader.zig b/src/bun.js/ModuleLoader.zig index d1ce74545a..1264ae0431 100644 --- a/src/bun.js/ModuleLoader.zig +++ b/src/bun.js/ModuleLoader.zig @@ -817,7 +817,110 @@ pub export fn Bun__getDefaultLoader(global: *JSGlobalObject, str: *const bun.Str return loader; } -pub fn transpileSourceCode( +/// Helper: Convert ResolvedSource to ModuleResult +fn resolvedSourceToModuleResult(resolved: ResolvedSource, input_specifier: bun.String, source_url: []const u8) ModuleResult { + // Handle special module types (JSON/TOML/YAML that export JSValue) + if (resolved.tag == .exports_object or resolved.tag == .export_default_object) { + return .{ + .tag = .special, + .value = .{ + .special = .{ + .tag = if (resolved.tag == .exports_object) .exports_object else .export_default_object, + .jsvalue = resolved.jsvalue_for_export, + }, + }, + }; + } + + // Handle custom extensions + if (resolved.tag == .common_js_custom_extension) { + return .{ + .tag = .special, + .value = .{ + .special = .{ + .tag = .custom_extension, + .jsvalue = resolved.cjs_custom_extension_index, + }, + }, + }; + } + + // Handle builtin modules (ResolvedSourceTag values >= 512 are builtin IDs) + if (@intFromEnum(resolved.tag) >= 512) { + return .{ + .tag = .builtin, + .value = .{ .builtin_id = @intFromEnum(resolved.tag) }, + }; + } + + // Handle transpiled source (normal case) + return .{ + .tag = .transpiled, + .value = .{ + .transpiled = .{ + .source_code = resolved.source_code, + .source_url = input_specifier.createIfDifferent(source_url), + .bytecode_cache = resolved.bytecode_cache, + .bytecode_cache_len = resolved.bytecode_cache_size, + .flags = .{ + .is_commonjs = resolved.is_commonjs_module, + .is_already_bundled = resolved.already_bundled, + }, + }, + }, + }; +} + +/// Helper: Convert ModuleResult to ResolvedSource for backward compatibility +pub fn moduleResultToResolvedSource(result: ModuleResult, specifier: bun.String) ResolvedSource { + switch (result.tag) { + .transpiled => { + const trans = result.value.transpiled; + return .{ + .allocator = null, + .source_code = trans.source_code, + .specifier = specifier, + .source_url = trans.source_url, + .is_commonjs_module = trans.flags.is_commonjs, + .already_bundled = trans.flags.is_already_bundled, + .bytecode_cache = trans.bytecode_cache, + .bytecode_cache_size = trans.bytecode_cache_len, + .source_code_needs_deref = true, + }; + }, + .special => { + const spec = result.value.special; + return .{ + .allocator = null, + .source_code = bun.String.empty, + .specifier = specifier, + .source_url = specifier, + .jsvalue_for_export = spec.jsvalue, + .tag = switch (spec.tag) { + .exports_object => .exports_object, + .export_default_object => .export_default_object, + .custom_extension => .common_js_custom_extension, + }, + .source_code_needs_deref = false, + }; + }, + .builtin => { + // Builtin modules use the ID as the tag (values >= 512) + const tag: ResolvedSource.Tag = @enumFromInt(result.value.builtin_id); + return .{ + .allocator = null, + .source_code = bun.String.empty, + .specifier = specifier, + .source_url = bun.String.static(@tagName(tag)), + .tag = tag, + .source_code_needs_deref = false, + }; + }, + } +} + +/// Internal implementation that still returns ResolvedSource for backward compat +fn transpileSourceCodeInternal( jsc_vm: *VirtualMachine, specifier: string, referrer: string, @@ -1032,7 +1135,7 @@ pub fn transpileSourceCode( const source = &parse_result.source; if (parse_result.loader == .wasm) { - return transpileSourceCode( + return transpileSourceCodeInternal( jsc_vm, specifier, referrer, @@ -1370,7 +1473,7 @@ pub fn transpileSourceCode( }; } - return transpileSourceCode( + return transpileSourceCodeInternal( jsc_vm, specifier, referrer, @@ -1581,8 +1684,9 @@ pub export fn Bun__fetchBuiltinModule( VirtualMachine.processFetchLog(globalObject, specifier.*, referrer.*, &log, ret, err); return true; - }) |builtin| { - ret.* = jsc.ErrorableResolvedSource.ok(builtin); + }) |module_result| { + // Convert ModuleResult to ResolvedSource for C++ compatibility + ret.* = jsc.ErrorableResolvedSource.ok(moduleResultToResolvedSource(module_result, specifier.*)); return true; } else { return false; @@ -1796,39 +1900,40 @@ pub export fn Bun__transpileFile( defer jsc_vm.module_loader.resetArena(jsc_vm); var promise: ?*jsc.JSInternalPromise = null; - ret.* = jsc.ErrorableResolvedSource.ok( - ModuleLoader.transpileSourceCode( - jsc_vm, - lr.specifier, - referrer_slice.slice(), - specifier_ptr.*, - lr.path, - synchronous_loader, - module_type, - &log, - lr.virtual_source, - if (allow_promise) &promise else null, - VirtualMachine.source_code_printer.?, - globalObject, - FetchFlags.transpile, - ) catch |err| { - switch (err) { - error.AsyncModule => { - bun.assert(promise != null); - return promise; - }, - error.PluginError => return null, - error.JSError => { - ret.* = jsc.ErrorableResolvedSource.err(error.JSError, globalObject.takeError(error.JSError)); - return null; - }, - else => { - VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer.*, &log, ret, err); - return null; - }, - } - }, - ); + const module_result = ModuleLoader.transpileSourceCode( + jsc_vm, + lr.specifier, + referrer_slice.slice(), + specifier_ptr.*, + lr.path, + synchronous_loader, + module_type, + &log, + lr.virtual_source, + if (allow_promise) &promise else null, + VirtualMachine.source_code_printer.?, + globalObject, + FetchFlags.transpile, + ) catch |err| { + switch (err) { + error.AsyncModule => { + bun.assert(promise != null); + return promise; + }, + error.PluginError => return null, + error.JSError => { + ret.* = jsc.ErrorableResolvedSource.err(error.JSError, globalObject.takeError(error.JSError)); + return null; + }, + else => { + VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer.*, &log, ret, err); + return null; + }, + } + }; + + // Convert ModuleResult to ResolvedSource for C++ compatibility + ret.* = jsc.ErrorableResolvedSource.ok(moduleResultToResolvedSource(module_result, specifier_ptr.*)); return promise; } @@ -1883,7 +1988,43 @@ fn getHardcodedModule(jsc_vm: *VirtualMachine, specifier: bun.String, hardcoded: }; } -pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: bun.String) !?ResolvedSource { +/// New public API that returns ModuleResult +pub fn transpileSourceCode( + jsc_vm: *VirtualMachine, + specifier: string, + referrer: string, + input_specifier: String, + path: Fs.Path, + loader: options.Loader, + module_type: options.ModuleType, + log: *logger.Log, + virtual_source: ?*const logger.Source, + promise_ptr: ?*?*jsc.JSInternalPromise, + source_code_printer: *js_printer.BufferPrinter, + globalObject: ?*JSGlobalObject, + comptime flags: FetchFlags, +) !ModuleResult { + const resolved = try transpileSourceCodeInternal( + jsc_vm, + specifier, + referrer, + input_specifier, + path, + loader, + module_type, + log, + virtual_source, + promise_ptr, + source_code_printer, + globalObject, + flags, + ); + + return resolvedSourceToModuleResult(resolved, input_specifier, path.text); +} + +/// Internal implementation that returns ResolvedSource for backward compat +fn fetchBuiltinModuleInternal(jsc_vm: *VirtualMachine, specifier: bun.String) !?ResolvedSource { if (HardcodedModule.map.getWithEql(specifier, bun.String.eqlComptime)) |hardcoded| { return getHardcodedModule(jsc_vm, specifier, hardcoded); } @@ -1938,6 +2079,14 @@ pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: bun.String) !?Reso return null; } +/// New public API that returns ModuleResult for builtin modules +pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: bun.String) !?ModuleResult { + const resolved = try fetchBuiltinModuleInternal(jsc_vm, specifier) orelse return null; + // Builtin modules might be special imports (macros, standalone) or actual builtins + // For now, convert using the same logic as transpileSourceCode + return resolvedSourceToModuleResult(resolved, specifier, specifier.byteSlice()); +} + export fn Bun__transpileVirtualModule( globalObject: *JSGlobalObject, specifier_ptr: *const bun.String, @@ -1977,35 +2126,36 @@ export fn Bun__transpileVirtualModule( defer log.deinit(); defer jsc_vm.module_loader.resetArena(jsc_vm); - ret.* = jsc.ErrorableResolvedSource.ok( - ModuleLoader.transpileSourceCode( - jsc_vm, - specifier_slice.slice(), - referrer_slice.slice(), - specifier_ptr.*, - path, - loader, - .unknown, - &log, - &virtual_source, - null, - VirtualMachine.source_code_printer.?, - globalObject, - FetchFlags.transpile, - ) catch |err| { - switch (err) { - error.PluginError => return true, - error.JSError => { - ret.* = jsc.ErrorableResolvedSource.err(error.JSError, globalObject.takeError(error.JSError)); - return true; - }, - else => { - VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer_ptr.*, &log, ret, err); - return true; - }, - } - }, - ); + const module_result = ModuleLoader.transpileSourceCode( + jsc_vm, + specifier_slice.slice(), + referrer_slice.slice(), + specifier_ptr.*, + path, + loader, + .unknown, + &log, + &virtual_source, + null, + VirtualMachine.source_code_printer.?, + globalObject, + FetchFlags.transpile, + ) catch |err| { + switch (err) { + error.PluginError => return true, + error.JSError => { + ret.* = jsc.ErrorableResolvedSource.err(error.JSError, globalObject.takeError(error.JSError)); + return true; + }, + else => { + VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer_ptr.*, &log, ret, err); + return true; + }, + } + }; + + // Convert ModuleResult to ResolvedSource for C++ compatibility + ret.* = jsc.ErrorableResolvedSource.ok(moduleResultToResolvedSource(module_result, specifier_ptr.*)); analytics.Features.virtual_modules += 1; return true; } @@ -3098,6 +3248,9 @@ const jsc = bun.jsc; const JSGlobalObject = bun.jsc.JSGlobalObject; const JSValue = bun.jsc.JSValue; const ResolvedSource = bun.jsc.ResolvedSource; +const ModuleResult = bun.jsc.ModuleResult; +const TranspiledSource = bun.jsc.TranspiledSource; +const SpecialModule = bun.jsc.SpecialModule; const VirtualMachine = bun.jsc.VirtualMachine; const ZigString = bun.jsc.ZigString; const Bun = jsc.API.Bun; diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index 61f3fa5ae6..5988513908 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -1522,8 +1522,9 @@ pub fn fetchWithoutOnLoadPlugins( ) anyerror!ResolvedSource { bun.assert(VirtualMachine.isLoaded()); - if (try ModuleLoader.fetchBuiltinModule(jsc_vm, _specifier)) |builtin| { - return builtin; + if (try ModuleLoader.fetchBuiltinModule(jsc_vm, _specifier)) |module_result| { + // Convert ModuleResult to ResolvedSource + return ModuleLoader.moduleResultToResolvedSource(module_result, _specifier); } const specifier_clone = _specifier.toUTF8(bun.default_allocator); @@ -1545,7 +1546,7 @@ pub fn fetchWithoutOnLoadPlugins( defer if (flags != .print_source) jsc_vm.module_loader.resetArena(jsc_vm); errdefer if (flags == .print_source) jsc_vm.module_loader.resetArena(jsc_vm); - return try ModuleLoader.transpileSourceCode( + const module_result = try ModuleLoader.transpileSourceCode( jsc_vm, lr.specifier, referrer_clone.slice(), @@ -1560,6 +1561,9 @@ pub fn fetchWithoutOnLoadPlugins( globalObject, flags, ); + + // Convert ModuleResult to ResolvedSource + return ModuleLoader.moduleResultToResolvedSource(module_result, _specifier); } pub const ResolveFunctionResult = struct { diff --git a/src/bun.js/bindings/BunSourceProvider.cpp b/src/bun.js/bindings/BunSourceProvider.cpp new file mode 100644 index 0000000000..3c3785a216 --- /dev/null +++ b/src/bun.js/bindings/BunSourceProvider.cpp @@ -0,0 +1,167 @@ +#include "root.h" + +#include "BunSourceProvider.h" +#include "ZigGlobalObject.h" +#include "helpers.h" +#include "BunString.h" + +#include +#include +#include +#include + +// Forward declare the C structs from Zig +extern "C" { + // Must match TranspiledSource.zig + struct TranspiledSource { + BunString source_code; + BunString source_url; + uint8_t* bytecode_cache; + size_t bytecode_cache_len; + uint32_t flags; // packed struct: is_commonjs:1, is_already_bundled:1, padding:30 + }; +} + +namespace Zig { + +using String = WTF::String; + +// Helper functions for source providers +JSC::SourceOrigin toSourceOrigin(const String& sourceURL, bool isBuiltin) +{ + ASSERT_WITH_MESSAGE(!sourceURL.startsWith("file://"_s), "specifier should not already be a file URL"); + + if (isBuiltin) { + if (sourceURL.startsWith("node:"_s)) { + return JSC::SourceOrigin(WTF::URL(makeString("builtin://node/"_s, sourceURL.substring(5)))); + } else if (sourceURL.startsWith("bun:"_s)) { + return JSC::SourceOrigin(WTF::URL(makeString("builtin://bun/"_s, sourceURL.substring(4)))); + } else { + return JSC::SourceOrigin(WTF::URL(makeString("builtin://"_s, sourceURL))); + } + } + return JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(sourceURL)); +} + +extern "C" int ByteRangeMapping__getSourceID(void* mappings, BunString sourceURL); +extern "C" void* ByteRangeMapping__find(BunString sourceURL); + +void* sourceMappingForSourceURL(const WTF::String& sourceURL) +{ + return ByteRangeMapping__find(Bun::toString(sourceURL)); +} + +JSC::SourceID sourceIDForSourceURL(const WTF::String& sourceURL) +{ + void* mappings = ByteRangeMapping__find(Bun::toString(sourceURL)); + if (!mappings) { + return 0; + } + + return ByteRangeMapping__getSourceID(mappings, Bun::toString(sourceURL)); +} + +Ref BunSourceProvider::create( + Zig::GlobalObject* globalObject, + Ref&& source, + const WTF::String& sourceURL, + JSC::SourceOrigin&& origin, + JSC::SourceProviderSourceType sourceType, + RefPtr&& cachedBytecode) +{ + return adoptRef(*new BunSourceProvider( + globalObject, + WTFMove(source), + sourceURL, + WTFMove(origin), + sourceType, + WTFMove(cachedBytecode))); +} + +BunSourceProvider::BunSourceProvider( + Zig::GlobalObject* globalObject, + Ref&& source, + const WTF::String& sourceURL, + JSC::SourceOrigin&& origin, + JSC::SourceProviderSourceType sourceType, + RefPtr&& cachedBytecode) + : Base(origin, String(sourceURL), String(), JSC::SourceTaintedOrigin::Untainted, TextPosition(), sourceType) + , m_globalObject(globalObject) + , m_source(WTFMove(source)) + , m_cachedBytecode(WTFMove(cachedBytecode)) + , m_hash(0) +{ + // m_globalObject is stored for potential future use (e.g., destructor cleanup) + UNUSED_PARAM(m_globalObject); +} + +StringView BunSourceProvider::source() const +{ + return StringView(m_source.get()); +} + +unsigned BunSourceProvider::hash() const +{ + if (m_hash) { + return m_hash; + } + return m_source->hash(); +} + +// Forward declare the C bridge function for registering sourcemaps +extern "C" void Bun__addSourceProviderSourceMap(void* bun_vm, JSC::SourceProvider* opaque_source_provider, BunString* specifier); + +// C bridge function for creating a BunSourceProvider from TranspiledSource +extern "C" JSC::SourceProvider* Bun__createSourceProvider( + Zig::GlobalObject* globalObject, + const TranspiledSource* transpiled) +{ + // Convert BunStrings to WTF strings + auto sourceCode = transpiled->source_code.toWTFString(BunString::ZeroCopy); + auto sourceURL = transpiled->source_url.toWTFString(BunString::ZeroCopy); + + // Extract flags + bool isCommonJS = (transpiled->flags & 0x1) != 0; + bool alreadyBundled = (transpiled->flags & 0x2) != 0; + + // Determine source type + JSC::SourceProviderSourceType sourceType = isCommonJS + ? JSC::SourceProviderSourceType::Program + : JSC::SourceProviderSourceType::Module; + + // Create source origin + JSC::SourceOrigin origin = toSourceOrigin(sourceURL, false); + + // Handle bytecode cache if present + RefPtr cachedBytecode = nullptr; + if (transpiled->bytecode_cache != nullptr && transpiled->bytecode_cache_len > 0) { + JSC::CachePayload::Destructor destructor = [](const void* ptr) { + mi_free(const_cast(ptr)); + }; + cachedBytecode = JSC::CachedBytecode::create( + std::span(transpiled->bytecode_cache, transpiled->bytecode_cache_len), + WTFMove(destructor), + {}); + } + + // Create the source provider + auto provider = BunSourceProvider::create( + globalObject, + sourceCode.isNull() ? Ref(*WTF::StringImpl::empty()) : Ref(*sourceCode.impl()), + sourceURL, + WTFMove(origin), + sourceType, + WTFMove(cachedBytecode)); + + // Register sourcemap if already bundled + if (alreadyBundled) { + auto sourceURLBun = transpiled->source_url; + Bun__addSourceProviderSourceMap(globalObject->bunVM(), provider.ptr(), &sourceURLBun); + } + + // Return leaked ref (caller takes ownership) + provider->ref(); + return provider.ptr(); +} + +} // namespace Zig diff --git a/src/bun.js/bindings/BunSourceProvider.h b/src/bun.js/bindings/BunSourceProvider.h new file mode 100644 index 0000000000..2fa5a6568f --- /dev/null +++ b/src/bun.js/bindings/BunSourceProvider.h @@ -0,0 +1,63 @@ +#pragma once + +#include "root.h" +#include "headers.h" + +#include +#include +#include +#include +#include + +namespace Zig { + +class GlobalObject; + +// Helper functions for source providers +void forEachSourceProvider(WTF::Function); +JSC::SourceID sourceIDForSourceURL(const WTF::String& sourceURL); +void* sourceMappingForSourceURL(const WTF::String& sourceURL); +JSC::SourceOrigin toSourceOrigin(const WTF::String& sourceURL, bool isBuiltin); + +/// New cleaner SourceProvider implementation for Bun +/// Uses TranspiledSource instead of ResolvedSource for better separation of concerns +class BunSourceProvider final : public JSC::SourceProvider { + WTF_DEPRECATED_MAKE_FAST_ALLOCATED(BunSourceProvider); + using Base = JSC::SourceProvider; + +public: + static Ref create( + Zig::GlobalObject* globalObject, + Ref&& source, + const WTF::String& sourceURL, + JSC::SourceOrigin&& origin, + JSC::SourceProviderSourceType sourceType, + RefPtr&& cachedBytecode = nullptr); + + virtual ~BunSourceProvider() = default; + + // Required JSC::SourceProvider overrides + StringView source() const override; + unsigned hash() const override; + + RefPtr cachedBytecode() const final + { + return m_cachedBytecode; + } + +private: + BunSourceProvider( + Zig::GlobalObject* globalObject, + Ref&& source, + const WTF::String& sourceURL, + JSC::SourceOrigin&& origin, + JSC::SourceProviderSourceType sourceType, + RefPtr&& cachedBytecode); + + Zig::GlobalObject* m_globalObject; + Ref m_source; + RefPtr m_cachedBytecode; + unsigned m_hash; +}; + +} // namespace Zig diff --git a/src/bun.js/bindings/CodeCoverage.cpp b/src/bun.js/bindings/CodeCoverage.cpp index 51ea142f9e..fdcd031e81 100644 --- a/src/bun.js/bindings/CodeCoverage.cpp +++ b/src/bun.js/bindings/CodeCoverage.cpp @@ -1,5 +1,4 @@ #include "root.h" -#include "ZigSourceProvider.h" #include using namespace JSC; diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index 7727c7411b..6561c8f69e 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -11,7 +11,7 @@ #include #include -#include "ZigSourceProvider.h" +#include "BunSourceProvider.h" #include #include @@ -41,6 +41,39 @@ #include "BunProcess.h" +// Forward declare the new Zig C structs +extern "C" { + // Must match TranspiledSource.zig + struct TranspiledSource { + BunString source_code; + BunString source_url; + uint8_t* bytecode_cache; + size_t bytecode_cache_len; + uint32_t flags; // packed struct: is_commonjs:1, is_already_bundled:1, padding:30 + }; + + // Must match SpecialModule.zig + struct SpecialModule { + uint32_t tag; // exports_object=0, export_default_object=1, custom_extension=2 + JSC::EncodedJSValue jsvalue; + }; + + // Must match ModuleResult.zig + struct ModuleResult { + uint32_t tag; // transpiled=0, special=1, builtin=2 + union { + TranspiledSource transpiled; + SpecialModule special; + uint32_t builtin_id; + } value; + }; + + // Bridge function to create BunSourceProvider from TranspiledSource + JSC::SourceProvider* Bun__createSourceProvider( + Zig::GlobalObject* globalObject, + const TranspiledSource* transpiled); +} + namespace Bun { using namespace JSC; using namespace Zig; @@ -64,6 +97,24 @@ public: ErrorableResolvedSource* res; }; +// Helper: Convert ResolvedSource to TranspiledSource +static TranspiledSource resolvedSourceToTranspiledSource(const ResolvedSource& resolved) +{ + TranspiledSource transpiled; + transpiled.source_code = resolved.source_code; + transpiled.source_url = resolved.source_url; + transpiled.bytecode_cache = resolved.bytecode_cache; + transpiled.bytecode_cache_len = resolved.bytecode_cache_size; + + // Pack flags + uint32_t flags = 0; + if (resolved.isCommonJSModule) flags |= 0x1; + if (resolved.already_bundled) flags |= 0x2; + transpiled.flags = flags; + + return transpiled; +} + extern "C" BunLoaderType Bun__getDefaultLoader(JSC::JSGlobalObject*, BunString* specifier); static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value) @@ -389,8 +440,11 @@ static JSValue handleVirtualModuleResult( RELEASE_AND_RETURN(scope, reject(JSValue::decode(res->result.err.value))); } - auto provider = Zig::SourceProvider::create(globalObject, res->result.value); - return resolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); + // Use new BunSourceProvider for virtual module transpiled code + auto transpiled = resolvedSourceToTranspiledSource(res->result.value); + auto* providerPtr = Bun__createSourceProvider(globalObject, &transpiled); + Ref provider = adoptRef(*providerPtr); + return resolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(WTFMove(provider)))); } case OnLoadResultTypeError: { RELEASE_AND_RETURN(scope, reject(onLoadResult.value.error)); @@ -508,8 +562,11 @@ extern "C" void Bun__onFulfillAsyncModule( } } } else { - auto&& provider = Zig::SourceProvider::create(jsDynamicCast(globalObject), res->result.value); - promise->resolve(globalObject, JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); + // Use new BunSourceProvider for transpiled ESM code + auto transpiled = resolvedSourceToTranspiledSource(res->result.value); + auto* providerPtr = Bun__createSourceProvider(jsDynamicCast(globalObject), &transpiled); + Ref provider = adoptRef(*providerPtr); + promise->resolve(globalObject, JSC::JSSourceCode::create(vm, JSC::SourceCode(WTFMove(provider)))); scope.assertNoExceptionExceptTermination(); } } else { @@ -852,8 +909,11 @@ JSValue fetchCommonJSModuleNonBuiltin( RELEASE_AND_RETURN(scope, target); } - auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value); - globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, JSC::SourceCode(provider)); + // Use new BunSourceProvider for transpiled code + auto transpiled = resolvedSourceToTranspiledSource(res->result.value); + auto* providerPtr = Bun__createSourceProvider(globalObject, &transpiled); + Ref provider = adoptRef(*providerPtr); + globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, JSC::SourceCode(WTFMove(provider))); RETURN_IF_EXCEPTION(scope, {}); RELEASE_AND_RETURN(scope, jsNumber(-1)); } @@ -974,8 +1034,11 @@ static JSValue fetchESMSourceCode( auto tag = res->result.value.tag; switch (tag) { case SyntheticModuleType::ESM: { - auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value, JSC::SourceProviderSourceType::Module, true); - RELEASE_AND_RETURN(scope, rejectOrResolve(JSSourceCode::create(vm, JSC::SourceCode(provider)))); + // Use new BunSourceProvider for builtin ESM modules + auto transpiled = resolvedSourceToTranspiledSource(res->result.value); + auto* providerPtr = Bun__createSourceProvider(globalObject, &transpiled); + Ref provider = adoptRef(*providerPtr); + RELEASE_AND_RETURN(scope, rejectOrResolve(JSSourceCode::create(vm, JSC::SourceCode(WTFMove(provider))))); } #define CASE(str, name) \ @@ -993,8 +1056,11 @@ static JSValue fetchESMSourceCode( auto source = JSC::SourceCode(JSC::SyntheticSourceProvider::create(generateInternalModuleSourceCode(globalObject, static_cast(tag & mask)), JSC::SourceOrigin(URL(makeString("builtins://"_s, moduleKey))), moduleKey)); RELEASE_AND_RETURN(scope, rejectOrResolve(JSSourceCode::create(vm, WTFMove(source)))); } else { - auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value, JSC::SourceProviderSourceType::Module, true); - RELEASE_AND_RETURN(scope, rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider)))); + // Use new BunSourceProvider for builtin transpiled modules + auto transpiled = resolvedSourceToTranspiledSource(res->result.value); + auto* providerPtr = Bun__createSourceProvider(globalObject, &transpiled); + Ref provider = adoptRef(*providerPtr); + RELEASE_AND_RETURN(scope, rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(WTFMove(provider))))); } } } @@ -1105,7 +1171,11 @@ static JSValue fetchESMSourceCode( RELEASE_AND_RETURN(scope, rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source)))); } - RELEASE_AND_RETURN(scope, rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(Zig::SourceProvider::create(globalObject, res->result.value))))); + // Use new BunSourceProvider for transpiled code + auto transpiled = resolvedSourceToTranspiledSource(res->result.value); + auto* providerPtr = Bun__createSourceProvider(globalObject, &transpiled); + Ref provider = adoptRef(*providerPtr); + RELEASE_AND_RETURN(scope, rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(WTFMove(provider))))); } JSValue fetchESMSourceCodeSync( diff --git a/src/bun.js/bindings/ModuleResult.zig b/src/bun.js/bindings/ModuleResult.zig new file mode 100644 index 0000000000..a77fa29908 --- /dev/null +++ b/src/bun.js/bindings/ModuleResult.zig @@ -0,0 +1,21 @@ +const bun = @import("bun"); +const TranspiledSource = @import("./TranspiledSource.zig").TranspiledSource; +const SpecialModule = @import("./SpecialModule.zig").SpecialModule; + +/// Tagged union return type for module resolution +pub const ModuleResult = extern struct { + tag: Tag, + value: Value, + + pub const Tag = enum(u32) { + transpiled, + special, + builtin, + }; + + pub const Value = extern union { + transpiled: TranspiledSource, + special: SpecialModule, + builtin_id: u32, + }; +}; diff --git a/src/bun.js/bindings/SpecialModule.zig b/src/bun.js/bindings/SpecialModule.zig new file mode 100644 index 0000000000..f4dfa7c35c --- /dev/null +++ b/src/bun.js/bindings/SpecialModule.zig @@ -0,0 +1,15 @@ +/// For special cases that need JSValue handling +pub const SpecialModule = extern struct { + tag: Tag, + jsvalue: JSValue, + + pub const Tag = enum(u32) { + exports_object, + export_default_object, + custom_extension, + }; +}; + +const bun = @import("bun"); +const jsc = bun.jsc; +const JSValue = jsc.JSValue; diff --git a/src/bun.js/bindings/TranspiledSource.zig b/src/bun.js/bindings/TranspiledSource.zig new file mode 100644 index 0000000000..b942bd9620 --- /dev/null +++ b/src/bun.js/bindings/TranspiledSource.zig @@ -0,0 +1,17 @@ +/// Minimal POD struct for transpiled source code +/// Thread-safe - contains no JSValue fields +pub const TranspiledSource = extern struct { + source_code: bun.String, + source_url: bun.String, + bytecode_cache: ?[*]u8, + bytecode_cache_len: usize, + flags: Flags, + + pub const Flags = packed struct(u32) { + is_commonjs: bool, + is_already_bundled: bool, + _padding: u30 = 0, + }; +}; + +const bun = @import("bun"); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index a2f8b35b4c..d52eda33cc 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -153,7 +153,7 @@ #include "webcrypto/JSCryptoKey.h" #include "webcrypto/JSSubtleCrypto.h" #include "ZigGeneratedClasses.h" -#include "ZigSourceProvider.h" +#include "BunSourceProvider.h" #include "UtilInspect.h" #include "Base64Helpers.h" #include "wtf/text/OrdinalNumber.h" diff --git a/src/bun.js/bindings/ZigSourceProvider.cpp b/src/bun.js/bindings/ZigSourceProvider.cpp index 021b567551..11fa5910f5 100644 --- a/src/bun.js/bindings/ZigSourceProvider.cpp +++ b/src/bun.js/bindings/ZigSourceProvider.cpp @@ -3,6 +3,7 @@ #include "helpers.h" #include "ZigSourceProvider.h" +#include "BunSourceProvider.h" #include #include "ZigGlobalObject.h" @@ -29,42 +30,10 @@ using SourceOrigin = JSC::SourceOrigin; using String = WTF::String; using SourceProviderSourceType = JSC::SourceProviderSourceType; -SourceOrigin toSourceOrigin(const String& sourceURL, bool isBuiltin) -{ - - ASSERT_WITH_MESSAGE(!sourceURL.startsWith("file://"_s), "specifier should not already be a file URL"); - - if (isBuiltin) { - if (sourceURL.startsWith("node:"_s)) { - return SourceOrigin(WTF::URL(makeString("builtin://node/"_s, sourceURL.substring(5)))); - } else if (sourceURL.startsWith("bun:"_s)) { - return SourceOrigin(WTF::URL(makeString("builtin://bun/"_s, sourceURL.substring(4)))); - } else { - return SourceOrigin(WTF::URL(makeString("builtin://"_s, sourceURL))); - } - } - return SourceOrigin(WTF::URL::fileURLWithFileSystemPath(sourceURL)); -} - -extern "C" int ByteRangeMapping__getSourceID(void* mappings, BunString sourceURL); -extern "C" void* ByteRangeMapping__find(BunString sourceURL); -void* sourceMappingForSourceURL(const WTF::String& sourceURL) -{ - return ByteRangeMapping__find(Bun::toString(sourceURL)); -} - +// Helper functions are now defined in BunSourceProvider.cpp +// They are declared in both headers for compatibility extern "C" void ByteRangeMapping__generate(BunString sourceURL, BunString code, int sourceID); -JSC::SourceID sourceIDForSourceURL(const WTF::String& sourceURL) -{ - void* mappings = ByteRangeMapping__find(Bun::toString(sourceURL)); - if (!mappings) { - return 0; - } - - return ByteRangeMapping__getSourceID(mappings, Bun::toString(sourceURL)); -} - extern "C" bool BunTest__shouldGenerateCodeCoverage(BunString sourceURL); extern "C" void Bun__addSourceProviderSourceMap(void* bun_vm, SourceProvider* opaque_source_provider, BunString* specifier); extern "C" void Bun__removeSourceProviderSourceMap(void* bun_vm, SourceProvider* opaque_source_provider, BunString* specifier); diff --git a/src/bun.js/jsc.zig b/src/bun.js/jsc.zig index 53cac93cfc..9fe7002eeb 100644 --- a/src/bun.js/jsc.zig +++ b/src/bun.js/jsc.zig @@ -90,6 +90,9 @@ pub const RegularExpression = @import("./bindings/RegularExpression.zig").Regula // JavaScript-related pub const Errorable = @import("./bindings/Errorable.zig").Errorable; pub const ResolvedSource = @import("./bindings/ResolvedSource.zig").ResolvedSource; +pub const ModuleResult = @import("./bindings/ModuleResult.zig").ModuleResult; +pub const TranspiledSource = @import("./bindings/TranspiledSource.zig").TranspiledSource; +pub const SpecialModule = @import("./bindings/SpecialModule.zig").SpecialModule; pub const ErrorCode = @import("./bindings/ErrorCode.zig").ErrorCode; pub const JSErrorCode = @import("./bindings/JSErrorCode.zig").JSErrorCode; pub const ZigErrorType = @import("./bindings/ZigErrorType.zig").ZigErrorType; diff --git a/src/bun.js/modules/BunJSCModule.h b/src/bun.js/modules/BunJSCModule.h index f9804d70bc..9c3dbeaa19 100644 --- a/src/bun.js/modules/BunJSCModule.h +++ b/src/bun.js/modules/BunJSCModule.h @@ -44,7 +44,7 @@ #endif #include "JSDOMConvertBase.h" -#include "ZigSourceProvider.h" +#include "BunSourceProvider.h" #include "mimalloc.h" #include