diff options
author | 2022-11-06 21:42:05 -0800 | |
---|---|---|
committer | 2022-11-06 21:42:05 -0800 | |
commit | e45f72e8e422191adeb4fd1bad896dc6a47c76b3 (patch) | |
tree | 3a76da8b343c081dba84e0ac95f3c2cc2423106a /src/bun.js | |
parent | 645cf903350a7fe5f5076100b7c4a6bc8cd1b431 (diff) | |
download | bun-e45f72e8e422191adeb4fd1bad896dc6a47c76b3.tar.gz bun-e45f72e8e422191adeb4fd1bad896dc6a47c76b3.tar.zst bun-e45f72e8e422191adeb4fd1bad896dc6a47c76b3.zip |
Automatically install npm packages when running a script in Bun's runtime (#1459)
* Update bundler.zig
* WIP
* Update README.md
* Update README.md
* wip
* Support running scripts without package.json
* Add `--no-auto-install` and `--prefer-offline` flags
* WIP
* wip
* Update headers-handwritten.h
* WIP
* Build fixes
* Fix UAF
* Update install.zig
* Must call .allocate()
* Micro-optimization: only call .timestamp() once per tick when installing packages
* Support progress bar
* Extend the timestamp for package staleness checks to 1 day
* Add `--prefer-latest`, `-i` CLI Flags
* Fix crash
* Support line text manually being set on an Error instance
* Add a few more fields for error messages
* Fix bug when counting 8 character strings in string builder
* Implement error handling for automatic package installs!
* Fix crash
* Make it say module when there's a slash
* Update module_loader.zig
* Ban dependency versions in import specifiers when a package.json is present
* Remove unused field
* Update README.md
* Update README.md
* Update README.md
* Update README.md
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/bindings/ModuleLoader.cpp | 33 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 16 | ||||
-rw-r--r-- | src/bun.js/bindings/exports.zig | 4 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-handwritten.h | 4 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 7 | ||||
-rw-r--r-- | src/bun.js/javascript.zig | 1387 | ||||
-rw-r--r-- | src/bun.js/module_loader.zig | 2052 |
7 files changed, 2241 insertions, 1262 deletions
diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index cb5ff0864..45f3cc46b 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -325,6 +325,29 @@ static JSValue handleVirtualModuleResult( } } +extern "C" void Bun__onFulfillAsyncModule( + EncodedJSValue promiseValue, + ErrorableResolvedSource* res, + ZigString* specifier, + ZigString* referrer) +{ + JSC::JSValue value = JSValue::decode(promiseValue); + JSC::JSInternalPromise* promise = jsCast<JSC::JSInternalPromise*>(value); + auto* globalObject = promise->globalObject(); + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (!res->success) { + throwException(scope, res->result.err, globalObject); + auto* exception = scope.exception(); + scope.clearException(); + return promise->reject(promise->globalObject(), exception); + } + + auto provider = Zig::SourceProvider::create(res->result.value); + promise->resolve(promise->globalObject(), JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); +} + template<bool allowPromise> static JSValue fetchSourceCode( Zig::GlobalObject* globalObject, @@ -435,7 +458,15 @@ static JSValue fetchSourceCode( return handleVirtualModuleResult<allowPromise>(globalObject, virtualModuleResult, res, specifier, referrer); } - Bun__transpileFile(bunVM, globalObject, specifier, referrer, res); + if constexpr (allowPromise) { + void* pendingCtx = Bun__transpileFile(bunVM, globalObject, specifier, referrer, res, true); + if (pendingCtx) { + return reinterpret_cast<JSC::JSInternalPromise*>(pendingCtx); + } + } else { + Bun__transpileFile(bunVM, globalObject, specifier, referrer, res, false); + } + if (!res->success) { throwException(scope, res->result.err, globalObject); auto* exception = scope.exception(); diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 65c2c458f..cc6c237ed 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -2419,12 +2419,22 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global, if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, global->vm().propertyNames->sourceURL)) { except->stack.frames_ptr[0].source_url = Zig::toZigString(sourceURL, global); + if (JSC::JSValue column = obj->getIfPropertyExists(global, global->vm().propertyNames->column)) { + except->stack.frames_ptr[0].position.column_start = column.toInt32(global); + } + if (JSC::JSValue line = obj->getIfPropertyExists(global, global->vm().propertyNames->line)) { except->stack.frames_ptr[0].position.line = line.toInt32(global); - } - if (JSC::JSValue column = obj->getIfPropertyExists(global, global->vm().propertyNames->column)) { - except->stack.frames_ptr[0].position.column_start = column.toInt32(global); + if (JSC::JSValue lineText = obj->getIfPropertyExists(global, JSC::Identifier::fromString(global->vm(), "lineText"_s))) { + if (JSC::JSString* jsStr = lineText.toStringOrNull(global)) { + auto str = jsStr->value(global); + except->stack.source_lines_ptr[0] = Zig::toZigString(str); + except->stack.source_lines_numbers[0] = except->stack.frames_ptr[0].position.line; + except->stack.source_lines_len = 1; + except->remapped = true; + } + } } except->stack.frames_len = 1; } diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index eb4f33001..f534a3677 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -2555,7 +2555,9 @@ pub const ZigConsoleClient = struct { _: usize, // args _: *ScriptArguments, - ) callconv(.C) void {} + ) callconv(.C) void { + + } pub fn profile( // console _: ZigConsoleClient.Type, diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index 5b8f6ad8d..553259ec0 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -229,12 +229,12 @@ extern "C" JSC::EncodedJSValue Bun__runVirtualModule( JSC::JSGlobalObject* global, ZigString* specifier); -extern "C" bool Bun__transpileFile( +extern "C" void* Bun__transpileFile( void* bunVM, JSC::JSGlobalObject* global, ZigString* specifier, ZigString* referrer, - ErrorableResolvedSource* result); + ErrorableResolvedSource* result, bool allowPromise); extern "C" JSC::EncodedJSValue CallbackJob__onResolve(JSC::JSGlobalObject*, JSC::CallFrame*); extern "C" JSC::EncodedJSValue CallbackJob__onReject(JSC::JSGlobalObject*, JSC::CallFrame*); diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index d87a7edcb..0c99a949a 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -183,6 +183,7 @@ pub const CppTask = opaque { const ThreadSafeFunction = JSC.napi.ThreadSafeFunction; const MicrotaskForDefaultGlobalObject = JSC.MicrotaskForDefaultGlobalObject; const HotReloadTask = JSC.HotReloader.HotReloadTask; +const PollPendingModulesTask = JSC.ModuleLoader.AsyncModule.Queue; // const PromiseTask = JSInternalPromise.Completion.PromiseTask; pub const Task = TaggedPointerUnion(.{ FetchTasklet, @@ -197,6 +198,7 @@ pub const Task = TaggedPointerUnion(.{ ThreadSafeFunction, CppTask, HotReloadTask, + PollPendingModulesTask, // PromiseTask, // TimeoutTasklet, }); @@ -223,7 +225,7 @@ pub const EventLoop = struct { tasks: Queue = undefined, concurrent_tasks: ConcurrentTask.Queue = ConcurrentTask.Queue{}, global: *JSGlobalObject = undefined, - virtual_machine: *VirtualMachine = undefined, + virtual_machine: *JSC.VirtualMachine = undefined, waker: ?AsyncIO.Waker = null, start_server_on_next_tick: bool = false, defer_count: std.atomic.Atomic(usize) = std.atomic.Atomic(usize).init(0), @@ -289,6 +291,9 @@ pub const EventLoop = struct { var any: *CppTask = task.get(CppTask).?; any.run(global); }, + @field(Task.Tag, typeBaseName(@typeName(PollPendingModulesTask))) => { + this.virtual_machine.modules.onPoll(); + }, else => if (Environment.allow_assert) { bun.Output.prettyln("\nUnexpected tag: {s}\n", .{@tagName(task.tag())}); } else unreachable, diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 7a78db237..4068e9cfe 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -16,9 +16,6 @@ const Arena = @import("../mimalloc_arena.zig").Arena; const C = bun.C; const NetworkThread = @import("http").NetworkThread; const IO = @import("io"); -pub fn zigCast(comptime Destination: type, value: anytype) *Destination { - return @ptrCast(*Destination, @alignCast(@alignOf(*Destination), value)); -} const Allocator = std.mem.Allocator; const IdentityContext = @import("../identity_context.zig").IdentityContext; const Fs = @import("../fs.zig"); @@ -26,6 +23,7 @@ const Resolver = @import("../resolver/resolver.zig"); const ast = @import("../import_record.zig"); const NodeModuleBundle = @import("../node_module_bundle.zig").NodeModuleBundle; const MacroEntryPoint = @import("../bundler.zig").MacroEntryPoint; +const ParseResult = @import("../bundler.zig").ParseResult; const logger = @import("../logger.zig"); const Api = @import("../api/schema.zig").Api; const options = @import("../options.zig"); @@ -45,7 +43,6 @@ const Runtime = @import("../runtime.zig"); const Router = @import("./api/router.zig"); const ImportRecord = ast.ImportRecord; const DotEnv = @import("../env_loader.zig"); -const ParseResult = @import("../bundler.zig").ParseResult; const PackageJSON = @import("../resolver/package_json.zig").PackageJSON; const MacroRemap = @import("../resolver/package_json.zig").MacroMap; const WebCore = @import("../jsc.zig").WebCore; @@ -86,7 +83,13 @@ const URL = @import("../url.zig").URL; const Transpiler = @import("./api/transpiler.zig"); const Bun = JSC.API.Bun; const EventLoop = JSC.EventLoop; +const PendingResolution = @import("../resolver/resolver.zig").PendingResolution; const ThreadSafeFunction = JSC.napi.ThreadSafeFunction; +const PackageManager = @import("../install/install.zig").PackageManager; + +const ModuleLoader = JSC.ModuleLoader; +const FetchFlags = JSC.FetchFlags; + pub const GlobalConstructors = [_]type{ JSC.Cloudflare.HTMLRewriter.Constructor, }; @@ -126,7 +129,7 @@ pub fn OpaqueWrap(comptime Context: type, comptime Function: fn (this: *Context) }.callback; } -const bun_file_import_path = "/node_modules.server.bun"; +pub const bun_file_import_path = "/node_modules.server.bun"; const SourceMap = @import("../sourcemap/sourcemap.zig"); const MappingList = SourceMap.Mapping.List; @@ -346,6 +349,7 @@ pub const VirtualMachine = struct { is_printing_plugin: bool = false, plugin_runner: ?PluginRunner = null, + is_main_thread: bool = false, /// Do not access this field directly /// It exists in the VirtualMachine struct so that @@ -404,9 +408,17 @@ pub const VirtualMachine = struct { us_loop_reference_count: usize = 0, is_us_loop_entered: bool = false, pending_internal_promise: *JSC.JSInternalPromise = undefined, - + auto_install_dependencies: bool = false, load_builtins_from_path: []const u8 = "", + modules: ModuleLoader.AsyncModule.Queue = .{}, + + pub threadlocal var is_main_thread_vm: bool = false; + + pub inline fn packageManager(this: *VirtualMachine) *PackageManager { + return this.bundler.getPackageManager(); + } + pub fn reload(this: *VirtualMachine) void { Output.debug("Reloading...", .{}); this.global.reload(); @@ -462,7 +474,7 @@ pub const VirtualMachine = struct { this.eventLoop().enqueueTask(task); } - pub inline fn enqueueTaskConcurrent(this: *VirtualMachine, task: JSC.ConcurrentTask) void { + pub inline fn enqueueTaskConcurrent(this: *VirtualMachine, task: *JSC.ConcurrentTask) void { this.eventLoop().enqueueTaskConcurrent(task); } @@ -627,6 +639,12 @@ pub const VirtualMachine = struct { vm.bundler.macro_context = null; + VirtualMachine.vm.bundler.resolver.onWakePackageManager = .{ + .context = &VirtualMachine.vm.modules, + .handler = ModuleLoader.AsyncModule.Queue.onWakeHandler, + .onDependencyError = JSC.ModuleLoader.AsyncModule.Queue.onDependencyError, + }; + VirtualMachine.vm.bundler.configureLinker(); try VirtualMachine.vm.bundler.configureFramework(false); @@ -742,422 +760,18 @@ pub const VirtualMachine = struct { this.resolved_count = 0; } - const shared_library_suffix = if (Environment.isMac) "dylib" else if (Environment.isLinux) "so" else ""; - - pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: string, log: *logger.Log, comptime disable_transpilying: bool) !?ResolvedSource { - if (jsc_vm.node_modules != null and strings.eqlComptime(specifier, bun_file_import_path)) { - // We kind of need an abstraction around this. - // Basically we should subclass JSC::SourceCode with: - // - hash - // - file descriptor for source input - // - file path + file descriptor for bytecode caching - // - separate bundles for server build vs browser build OR at least separate sections - const code = try jsc_vm.node_modules.?.readCodeAsStringSlow(jsc_vm.allocator); - - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(code), - .specifier = ZigString.init(bun_file_import_path), - .source_url = ZigString.init(bun_file_import_path[1..]), - .hash = 0, // TODO - }; - } else if (jsc_vm.node_modules == null and strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(Runtime.Runtime.sourceContentBun()), - .specifier = ZigString.init(Runtime.Runtime.Imports.Name), - .source_url = ZigString.init(Runtime.Runtime.Imports.Name), - .hash = Runtime.Runtime.versionHash(), - }; - } else if (HardcodedModule.Map.get(specifier)) |hardcoded| { - switch (hardcoded) { - // This is all complicated because the imports have to be linked and we want to run the printer on it - // so it consistently handles bundled imports - // we can't take the shortcut of just directly importing the file, sadly. - .@"bun:main" => { - if (comptime disable_transpilying) { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsc_vm.entry_point.source.contents), - .specifier = ZigString.init(std.mem.span(main_file_name)), - .source_url = ZigString.init(std.mem.span(main_file_name)), - .hash = 0, - }; - } - defer jsc_vm.transpiled_count += 1; - - var bundler = &jsc_vm.bundler; - var old = jsc_vm.bundler.log; - jsc_vm.bundler.log = log; - jsc_vm.bundler.linker.log = log; - jsc_vm.bundler.resolver.log = log; - defer { - jsc_vm.bundler.log = old; - jsc_vm.bundler.linker.log = old; - jsc_vm.bundler.resolver.log = old; - } - - var jsx = bundler.options.jsx; - jsx.parse = false; - var opts = js_parser.Parser.Options.init(jsx, .js); - opts.enable_bundling = false; - opts.transform_require_to_import = false; - opts.features.dynamic_require = true; - opts.can_import_from_bundle = bundler.options.node_modules_bundle != null; - opts.features.hot_module_reloading = false; - opts.features.react_fast_refresh = false; - opts.filepath_hash_for_hmr = 0; - opts.warn_about_unbundled_modules = false; - opts.macro_context = &jsc_vm.bundler.macro_context.?; - const main_ast = (bundler.resolver.caches.js.parse(jsc_vm.allocator, opts, bundler.options.define, bundler.log, &jsc_vm.entry_point.source) catch null) orelse { - return error.ParseError; - }; - var parse_result = ParseResult{ .source = jsc_vm.entry_point.source, .ast = main_ast, .loader = .js, .input_fd = null }; - var file_path = Fs.Path.init(bundler.fs.top_level_dir); - file_path.name.dir = bundler.fs.top_level_dir; - file_path.name.base = "bun:main"; - try bundler.linker.link( - file_path, - &parse_result, - jsc_vm.origin, - .absolute_path, - false, - true, - ); - var printer = source_code_printer.?.*; - var written: usize = undefined; - printer.ctx.reset(); - { - defer source_code_printer.?.* = printer; - written = try jsc_vm.bundler.printWithSourceMap( - parse_result, - @TypeOf(&printer), - &printer, - .esm_ascii, - SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings), - ); - } - - if (comptime Environment.dump_source) - try dumpSource(main_file_name, &printer); - - if (written == 0) { - return error.PrintingErrorWriteFailed; - } - - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, printer.ctx.written) catch unreachable), - .specifier = ZigString.init(std.mem.span(main_file_name)), - .source_url = ZigString.init(std.mem.span(main_file_name)), - .hash = 0, - }; - }, - .@"bun:jsc" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "bun-jsc.exports.js")), - .specifier = ZigString.init("bun:jsc"), - .source_url = ZigString.init("bun:jsc"), - .hash = 0, - }; - }, - .@"node:child_process" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "child_process.exports.js")), - .specifier = ZigString.init("node:child_process"), - .source_url = ZigString.init("node:child_process"), - .hash = 0, - }; - }, - .@"node:net" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "net.exports.js")), - .specifier = ZigString.init("node:net"), - .source_url = ZigString.init("node:net"), - .hash = 0, - }; - }, - .@"node:fs" => { - if (comptime Environment.isDebug) { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(strings.append(bun.default_allocator, jsModuleFromFile(jsc_vm.load_builtins_from_path, "fs.exports.js"), JSC.Node.fs.constants_string) catch unreachable), - .specifier = ZigString.init("node:fs"), - .source_url = ZigString.init("node:fs"), - .hash = 0, - }; - } - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(@embedFile("fs.exports.js") ++ JSC.Node.fs.constants_string), - .specifier = ZigString.init("node:fs"), - .source_url = ZigString.init("node:fs"), - .hash = 0, - }; - }, - .@"node:buffer" => return jsSyntheticModule(.@"node:buffer"), - .@"node:string_decoder" => return jsSyntheticModule(.@"node:string_decoder"), - .@"node:module" => return jsSyntheticModule(.@"node:module"), - .@"node:events" => return jsSyntheticModule(.@"node:events"), - .@"node:process" => return jsSyntheticModule(.@"node:process"), - .@"node:tty" => return jsSyntheticModule(.@"node:tty"), - .@"node:stream" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "streams.exports.js")), - .specifier = ZigString.init("node:stream"), - .source_url = ZigString.init("node:stream"), - .hash = 0, - }; - }, - - .@"node:fs/promises" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(@embedFile("fs_promises.exports.js") ++ JSC.Node.fs.constants_string), - .specifier = ZigString.init("node:fs/promises"), - .source_url = ZigString.init("node:fs/promises"), - .hash = 0, - }; - }, - .@"node:path" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "path.exports.js")), - .specifier = ZigString.init("node:path"), - .source_url = ZigString.init("node:path"), - .hash = 0, - }; - }, - .@"node:path/win32" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "path-win32.exports.js")), - .specifier = ZigString.init("node:path/win32"), - .source_url = ZigString.init("node:path/win32"), - .hash = 0, - }; - }, - .@"node:path/posix" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "path-posix.exports.js")), - .specifier = ZigString.init("node:path/posix"), - .source_url = ZigString.init("node:path/posix"), - .hash = 0, - }; - }, - - .@"node:os" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "os.exports.js")), - .specifier = ZigString.init("node:os"), - .source_url = ZigString.init("node:os"), - .hash = 0, - }; - }, - .@"bun:ffi" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - "export const FFIType = " ++ - JSC.FFI.ABIType.map_to_js_object ++ - ";\n\n" ++ - "export const suffix = '" ++ shared_library_suffix ++ "';\n\n" ++ - @embedFile("ffi.exports.js") ++ - "\n", - ), - .specifier = ZigString.init("bun:ffi"), - .source_url = ZigString.init("bun:ffi"), - .hash = 0, - }; - }, - .@"detect-libc" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, @embedFile(if (Environment.isLinux) "detect-libc.linux.js" else "detect-libc.js")), - ), - .specifier = ZigString.init("detect-libc"), - .source_url = ZigString.init("detect-libc"), - .hash = 0, - }; - }, - .@"node:url" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "url.exports.js")), - ), - .specifier = ZigString.init("node:url"), - .source_url = ZigString.init("node:url"), - .hash = 0, - }; - }, - .@"node:assert" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "assert.exports.js")), - ), - .specifier = ZigString.init("node:assert"), - .source_url = ZigString.init("node:assert"), - .hash = 0, - }; - }, - .@"bun:sqlite" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./bindings/sqlite/sqlite.exports.js")), - ), - .specifier = ZigString.init("bun:sqlite"), - .source_url = ZigString.init("bun:sqlite"), - .hash = 0, - }; - }, - .@"node:perf_hooks" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./perf_hooks.exports.js")), - ), - .specifier = ZigString.init("node:perf_hooks"), - .source_url = ZigString.init("node:perf_hooks"), - .hash = 0, - }; - }, - .@"ws" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./ws.exports.js")), - ), - .specifier = ZigString.init("ws"), - .source_url = ZigString.init("ws"), - .hash = 0, - }; - }, - .@"node:timers" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./node_timers.exports.js")), - ), - .specifier = ZigString.init("node:timers"), - .source_url = ZigString.init("node:timers"), - .hash = 0, - }; - }, - .@"node:timers/promises" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./node_timers_promises.exports.js")), - ), - .specifier = ZigString.init("node:timers/promises"), - .source_url = ZigString.init("node:timers/promises"), - .hash = 0, - }; - }, - .@"node:stream/web" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./node_streams_web.exports.js")), - ), - .specifier = ZigString.init("node:stream/web"), - .source_url = ZigString.init("node:stream/web"), - .hash = 0, - }; - }, - .@"node:stream/consumer" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./node_streams_consumer.exports.js")), - ), - .specifier = ZigString.init("node:stream/consumer"), - .source_url = ZigString.init("node:stream/consumer"), - .hash = 0, - }; - }, - .@"undici" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./undici.exports.js")), - ), - .specifier = ZigString.init("undici"), - .source_url = ZigString.init("undici"), - .hash = 0, - }; - }, - .@"node:http" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./http.exports.js")), - ), - .specifier = ZigString.init("node:http"), - .source_url = ZigString.init("node:http"), - .hash = 0, - }; - }, - .@"node:https" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./https.exports.js")), - ), - .specifier = ZigString.init("node:https"), - .source_url = ZigString.init("node:https"), - .hash = 0, - }; - }, - .@"depd" => { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init( - @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./depd.exports.js")), - ), - .specifier = ZigString.init("depd"), - .source_url = ZigString.init("depd"), - .hash = 0, - }; - }, - } - } else if (specifier.len > js_ast.Macro.namespaceWithColon.len and - strings.eqlComptimeIgnoreLen(specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon)) - { - if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(specifier))) |entry| { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(entry.source.contents), - .specifier = ZigString.init(specifier), - .source_url = ZigString.init(specifier), - .hash = 0, - }; - } - } - - return null; - } - pub fn fetchWithoutOnLoadPlugins( jsc_vm: *VirtualMachine, + globalObject: *JSC.JSGlobalObject, _specifier: string, + referrer: string, log: *logger.Log, ret: *ErrorableResolvedSource, comptime flags: FetchFlags, ) !ResolvedSource { std.debug.assert(VirtualMachine.vm_loaded); - if (try fetchBuiltinModule(jsc_vm, _specifier, log, comptime flags.disableTranspiling())) |builtin| { + if (try ModuleLoader.fetchBuiltinModule(jsc_vm, _specifier, log, comptime flags.disableTranspiling())) |builtin| { return builtin; } @@ -1174,12 +788,15 @@ pub const VirtualMachine = struct { return try ModuleLoader.transpileSourceCode( jsc_vm, specifier, + referrer, path, loader, log, null, ret, + null, VirtualMachine.source_code_printer.?, + globalObject, flags, ); } @@ -1221,7 +838,7 @@ pub const VirtualMachine = struct { ret.result = null; ret.path = specifier; return; - } else if (HardcodedModule.Map.get(specifier)) |result| { + } else if (JSC.HardcodedModule.Map.get(specifier)) |result| { ret.result = null; ret.path = @as(string, @tagName(result)); return; @@ -1229,7 +846,7 @@ pub const VirtualMachine = struct { const is_special_source = strings.eqlComptime(source, main_file_name) or js_ast.Macro.isMacroPath(source); - const result = try jsc_vm.bundler.resolver.resolve( + const result = try switch (jsc_vm.bundler.resolver.resolveAndAutoInstall( if (!is_special_source) if (is_a_file_path) Fs.PathName.init(source).dirWithTrailingSlash() @@ -1240,7 +857,13 @@ pub const VirtualMachine = struct { // TODO: do we need to handle things like query string params? if (strings.hasPrefixComptime(specifier, "file://")) specifier["file://".len..] else specifier, .stmt, - ); + .read_only, + )) { + .success => |r| r, + .failure => |e| e, + .not_found => error.ModuleNotFound, + .pending => unreachable, + }; if (!jsc_vm.macro_mode) { jsc_vm.has_any_macro_remappings = jsc_vm.has_any_macro_remappings or jsc_vm.bundler.options.macro_remap.count() > 0; @@ -1339,30 +962,50 @@ pub const VirtualMachine = struct { } } - if (HardcodedModule.Aliases.getWithEql(specifier, ZigString.eqlComptime)) |hardcoded| { + if (JSC.HardcodedModule.Aliases.getWithEql(specifier, ZigString.eqlComptime)) |hardcoded| { res.* = ErrorableZigString.ok(ZigString.init(hardcoded)); return; } + var old_log = jsc_vm.log; + var log = logger.Log.init(jsc_vm.allocator); + defer log.deinit(); + jsc_vm.log = &log; + jsc_vm.bundler.resolver.log = &log; + jsc_vm.bundler.linker.log = &log; + defer { + jsc_vm.log = old_log; + jsc_vm.bundler.linker.log = old_log; + jsc_vm.bundler.resolver.log = old_log; + } + _resolve(&result, global, specifier.slice(), source.slice(), is_a_file_path, realpath) catch |err_| { + var err = err_; + const msg: logger.Msg = brk: { + var msgs: []logger.Msg = log.msgs.items; + + for (msgs) |m| { + if (m.metadata == .resolve) { + err = m.metadata.resolve.err; + break :brk m; + } + } - _resolve(&result, global, specifier.slice(), source.slice(), is_a_file_path, realpath) catch |err| { - // This should almost always just apply to dynamic imports - - const printed = ResolveError.fmt( - jsc_vm.allocator, - specifier.slice(), - source.slice(), - err, - ) catch unreachable; - const msg = logger.Msg{ - .data = logger.rangeData( - null, - logger.Range.None, - printed, - ), - .metadata = .{ - // import_kind is wrong probably - .resolve = .{ .specifier = logger.BabyString.in(printed, specifier.slice()), .import_kind = .stmt }, - }, + const printed = ResolveError.fmt( + jsc_vm.allocator, + specifier.slice(), + source.slice(), + err, + ) catch unreachable; + break :brk logger.Msg{ + .data = logger.rangeData( + null, + logger.Range.None, + printed, + ), + .metadata = .{ + // import_kind is wrong probably + .resolve = .{ .specifier = logger.BabyString.in(printed, specifier.slice()), .import_kind = .stmt }, + }, + }; }; { @@ -1385,24 +1028,27 @@ pub const VirtualMachine = struct { // return JSValue.jsUndefined(); // } - const main_file_name: string = "bun:main"; + pub const main_file_name: string = "bun:main"; pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) void { - var log = logger.Log.init(vm.bundler.allocator); - const spec = specifier.slice(); - // threadlocal is cheaper in linux var jsc_vm: *VirtualMachine = if (comptime Environment.isLinux) vm else global.bunVM(); + var log = logger.Log.init(vm.bundler.allocator); + var spec = specifier.toSlice(jsc_vm.allocator); + defer spec.deinit(); + var refer = source.toSlice(jsc_vm.allocator); + defer refer.deinit(); + const result = if (!jsc_vm.bundler.options.disable_transpilation) - @call(.{ .modifier = .always_inline }, fetchWithoutOnLoadPlugins, .{ jsc_vm, spec, &log, ret, .transpile }) catch |err| { + @call(.{ .modifier = .always_inline }, fetchWithoutOnLoadPlugins, .{ jsc_vm, global, spec.slice(), refer.slice(), &log, ret, .transpile }) catch |err| { processFetchLog(global, specifier, source, &log, ret, err); return; } else - fetchWithoutOnLoadPlugins(jsc_vm, spec, &log, ret, .print_source_and_clone) catch |err| { + fetchWithoutOnLoadPlugins(jsc_vm, global, spec.slice(), refer.slice(), &log, ret, .print_source_and_clone) catch |err| { processFetchLog(global, specifier, source, &log, ret, err); return; }; @@ -1433,10 +1079,10 @@ pub const VirtualMachine = struct { if (vm.blobs) |blobs| { const specifier_blob = brk: { - if (strings.hasPrefix(spec, VirtualMachine.vm.bundler.fs.top_level_dir)) { - break :brk spec[VirtualMachine.vm.bundler.fs.top_level_dir.len..]; + if (strings.hasPrefix(spec.slice(), VirtualMachine.vm.bundler.fs.top_level_dir)) { + break :brk spec.slice()[VirtualMachine.vm.bundler.fs.top_level_dir.len..]; } - break :brk spec; + break :brk spec.slice(); }; if (vm.has_loaded) { @@ -1893,7 +1539,7 @@ pub const VirtualMachine = struct { )) |mapping| { var log = logger.Log.init(default_allocator); var errorable: ErrorableResolvedSource = undefined; - var original_source = fetchWithoutOnLoadPlugins(this, top.source_url.slice(), &log, &errorable, .print_source) catch return; + var original_source = fetchWithoutOnLoadPlugins(this, this.global, top.source_url.slice(), "", &log, &errorable, .print_source) catch return; const code = original_source.source_code.slice(); top.position.line = mapping.original.lines; top.position.line_start = mapping.original.lines; @@ -2052,6 +1698,35 @@ pub const VirtualMachine = struct { .fd = exception.fd != -1, }; + const extra_fields = .{ + "url", + "info", + "pkg", + }; + + if (error_instance.isCell()) { + inline for (extra_fields) |field| { + if (error_instance.get(this.global, field)) |value| { + if (!value.isEmptyOrUndefinedOrNull()) { + const kind = value.jsType(); + if (kind.isStringLike()) { + if (value.toStringOrNull(this.global)) |str| { + var zig_str = str.toSlice(this.global, bun.default_allocator); + defer zig_str.deinit(); + try writer.print(comptime Output.prettyFmt(" {s}<d>: <r>\"{s}\"<r>\n", allow_ansi_color), .{ field, zig_str.slice() }); + add_extra_line = true; + } + } else if (kind.isObject() or kind.isArray()) { + var zig_str = ZigString.init(""); + value.jsonStringify(this.global, 2, &zig_str); + try writer.print(comptime Output.prettyFmt(" {s}<d>: <r>{s}<r>\n", allow_ansi_color), .{ field, zig_str }); + add_extra_line = true; + } + } + } + } + } + if (show.path) { if (show.syscall) { try writer.writeAll(" "); @@ -2061,6 +1736,16 @@ pub const VirtualMachine = struct { try writer.print(comptime Output.prettyFmt(" path<d>: <r><cyan>\"{s}\"<r>\n", allow_ansi_color), .{exception.path}); } + if (show.fd) { + if (show.syscall) { + try writer.writeAll(" "); + } else if (show.errno) { + try writer.writeAll(" "); + } + + try writer.print(comptime Output.prettyFmt(" fd<d>: <r><cyan>\"{d}\"<r>\n", allow_ansi_color), .{exception.fd}); + } + if (show.system_code) { if (show.syscall) { try writer.writeAll(" "); @@ -2284,7 +1969,7 @@ pub const ResolveError = struct { pub fn fmt(allocator: std.mem.Allocator, specifier: string, referrer: string, err: anyerror) !string { switch (err) { error.ModuleNotFound => { - if (Resolver.isPackagePath(specifier)) { + if (Resolver.isPackagePath(specifier) and !strings.containsChar(specifier, '/')) { return try std.fmt.allocPrint(allocator, "Cannot find package \"{s}\" from \"{s}\"", .{ specifier, referrer }); } else { return try std.fmt.allocPrint(allocator, "Cannot find module \"{s}\" from \"{s}\"", .{ specifier, referrer }); @@ -2655,812 +2340,6 @@ pub const BuildError = struct { pub const JSPrivateDataTag = JSPrivateDataPtr.Tag; -pub const HardcodedModule = enum { - @"bun:ffi", - @"bun:jsc", - @"bun:main", - @"bun:sqlite", - @"depd", - @"detect-libc", - @"node:assert", - @"node:buffer", - @"node:child_process", - @"node:events", - @"node:fs", - @"node:fs/promises", - @"node:http", - @"node:https", - @"node:module", - @"node:net", - @"node:os", - @"node:path", - @"node:path/posix", - @"node:path/win32", - @"node:perf_hooks", - @"node:process", - @"node:stream", - @"node:stream/consumer", - @"node:stream/web", - @"node:string_decoder", - @"node:timers", - @"node:timers/promises", - @"node:tty", - @"node:url", - @"undici", - @"ws", - /// Already resolved modules go in here. - /// This does not remap the module name, it is just a hash table. - /// Do not put modules that have aliases in here - /// Put those in Aliases - pub const Map = bun.ComptimeStringMap( - HardcodedModule, - .{ - .{ "buffer", HardcodedModule.@"node:buffer" }, - .{ "bun:ffi", HardcodedModule.@"bun:ffi" }, - .{ "bun:jsc", HardcodedModule.@"bun:jsc" }, - .{ "bun:main", HardcodedModule.@"bun:main" }, - .{ "bun:sqlite", HardcodedModule.@"bun:sqlite" }, - .{ "depd", HardcodedModule.@"depd" }, - .{ "detect-libc", HardcodedModule.@"detect-libc" }, - .{ "node:assert", HardcodedModule.@"node:assert" }, - .{ "node:buffer", HardcodedModule.@"node:buffer" }, - .{ "node:child_process", HardcodedModule.@"node:child_process" }, - .{ "node:events", HardcodedModule.@"node:events" }, - .{ "node:fs", HardcodedModule.@"node:fs" }, - .{ "node:fs/promises", HardcodedModule.@"node:fs/promises" }, - .{ "node:http", HardcodedModule.@"node:http" }, - .{ "node:https", HardcodedModule.@"node:https" }, - .{ "node:module", HardcodedModule.@"node:module" }, - .{ "node:net", HardcodedModule.@"node:net" }, - .{ "node:os", HardcodedModule.@"node:os" }, - .{ "node:path", HardcodedModule.@"node:path" }, - .{ "node:path/posix", HardcodedModule.@"node:path/posix" }, - .{ "node:path/win32", HardcodedModule.@"node:path/win32" }, - .{ "node:perf_hooks", HardcodedModule.@"node:perf_hooks" }, - .{ "node:process", HardcodedModule.@"node:process" }, - .{ "node:stream", HardcodedModule.@"node:stream" }, - .{ "node:stream/consumer", HardcodedModule.@"node:stream/consumer" }, - .{ "node:stream/web", HardcodedModule.@"node:stream/web" }, - .{ "node:string_decoder", HardcodedModule.@"node:string_decoder" }, - .{ "node:timers", HardcodedModule.@"node:timers" }, - .{ "node:timers/promises", HardcodedModule.@"node:timers/promises" }, - .{ "node:tty", HardcodedModule.@"node:tty" }, - .{ "node:url", HardcodedModule.@"node:url" }, - .{ "undici", HardcodedModule.@"undici" }, - .{ "ws", HardcodedModule.@"ws" }, - }, - ); - pub const Aliases = bun.ComptimeStringMap( - string, - .{ - .{ "assert", "node:assert" }, - .{ "buffer", "node:buffer" }, - .{ "bun", "bun" }, - .{ "bun:ffi", "bun:ffi" }, - .{ "bun:jsc", "bun:jsc" }, - .{ "bun:sqlite", "bun:sqlite" }, - .{ "bun:wrap", "bun:wrap" }, - .{ "child_process", "node:child_process" }, - .{ "depd", "depd" }, - .{ "detect-libc", "detect-libc" }, - .{ "detect-libc/lib/detect-libc.js", "detect-libc" }, - .{ "events", "node:events" }, - .{ "ffi", "bun:ffi" }, - .{ "fs", "node:fs" }, - .{ "fs/promises", "node:fs/promises" }, - .{ "http", "node:http" }, - .{ "https", "node:https" }, - .{ "module", "node:module" }, - .{ "net", "node:net" }, - .{ "node:assert", "node:assert" }, - .{ "node:buffer", "node:buffer" }, - .{ "node:child_process", "node:child_process" }, - .{ "node:events", "node:events" }, - .{ "node:fs", "node:fs" }, - .{ "node:fs/promises", "node:fs/promises" }, - .{ "node:http", "node:http" }, - .{ "node:https", "node:https" }, - .{ "node:module", "node:module" }, - .{ "node:net", "node:net" }, - .{ "node:os", "node:os" }, - .{ "node:path", "node:path" }, - .{ "node:path/posix", "node:path/posix" }, - .{ "node:path/win32", "node:path/win32" }, - .{ "node:perf_hooks", "node:perf_hooks" }, - .{ "node:process", "node:process" }, - .{ "node:stream", "node:stream" }, - .{ "node:stream/consumer", "node:stream/consumer" }, - .{ "node:stream/web", "node:stream/web" }, - .{ "node:string_decoder", "node:string_decoder" }, - .{ "node:timers", "node:timers" }, - .{ "node:timers/promises", "node:timers/promises" }, - .{ "node:tty", "node:tty" }, - .{ "node:url", "node:url" }, - .{ "os", "node:os" }, - .{ "path", "node:path" }, - .{ "path/posix", "node:path/posix" }, - .{ "path/win32", "node:path/win32" }, - .{ "perf_hooks", "node:perf_hooks" }, - .{ "process", "node:process" }, - .{ "stream", "node:stream" }, - .{ "stream/consumer", "node:stream/consumer" }, - .{ "stream/web", "node:stream/web" }, - .{ "string_decoder", "node:string_decoder" }, - .{ "timers", "node:timers" }, - .{ "timers/promises", "node:timers/promises" }, - .{ "tty", "node:tty" }, - .{ "undici", "undici" }, - .{ "url", "node:url" }, - .{ "ws", "ws" }, - .{ "ws/lib/websocket", "ws" }, - }, - ); -}; - -pub const DisabledModule = bun.ComptimeStringMap( - void, - .{ - .{"node:tls"}, - .{"node:worker_threads"}, - .{"tls"}, - .{"worker_threads"}, - }, -); - -// This exists to make it so we can reload these quicker in development -fn jsModuleFromFile(from_path: string, comptime input: string) string { - const absolute_path = comptime std.fs.path.dirname(@src().file).? ++ "/" ++ input; - const Holder = struct { - pub const file = @embedFile(absolute_path); - }; - - if (comptime !Environment.allow_assert) { - if (from_path.len == 0) { - return Holder.file; - } - } - - var file: std.fs.File = undefined; - - if (comptime Environment.allow_assert) { - file = std.fs.openFileAbsoluteZ(absolute_path, .{ .mode = .read_only }) catch { - const WarnOnce = struct { - pub var warned = false; - }; - if (!WarnOnce.warned) { - WarnOnce.warned = true; - Output.prettyErrorln("Could not find file: " ++ absolute_path ++ " - using embedded version", .{}); - } - return Holder.file; - }; - } else { - var parts = [_]string{ from_path, input }; - var buf: [bun.MAX_PATH_BYTES]u8 = undefined; - var absolute_path_to_use = Fs.FileSystem.instance.absBuf(&parts, &buf); - buf[absolute_path_to_use.len] = 0; - file = std.fs.openFileAbsoluteZ(std.meta.assumeSentinel(absolute_path_to_use.ptr, 0), .{ .mode = .read_only }) catch { - const WarnOnce = struct { - pub var warned = false; - }; - if (!WarnOnce.warned) { - WarnOnce.warned = true; - Output.prettyErrorln("Could not find file: {s}, so using embedded version", .{absolute_path_to_use}); - } - return Holder.file; - }; - } - - var contents = file.readToEndAlloc(bun.default_allocator, std.math.maxInt(usize)) catch @panic("Cannot read file: " ++ absolute_path); - if (comptime !Environment.allow_assert) { - file.close(); - } - return contents; -} - -inline fn jsSyntheticModule(comptime name: ResolvedSource.Tag) ResolvedSource { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(""), - .specifier = ZigString.init(@tagName(name)), - .source_url = ZigString.init(@tagName(name)), - .hash = 0, - .tag = name, - }; -} - -fn dumpSource(specifier: string, printer: anytype) !void { - const BunDebugHolder = struct { - pub var dir: ?std.fs.Dir = null; - }; - if (BunDebugHolder.dir == null) { - BunDebugHolder.dir = try std.fs.cwd().makeOpenPath("/tmp/bun-debug-src/", .{ .iterate = true }); - } - - if (std.fs.path.dirname(specifier)) |dir_path| { - var parent = try BunDebugHolder.dir.?.makeOpenPath(dir_path[1..], .{ .iterate = true }); - defer parent.close(); - try parent.writeFile(std.fs.path.basename(specifier), printer.ctx.getWritten()); - } else { - try BunDebugHolder.dir.?.writeFile(std.fs.path.basename(specifier), printer.ctx.getWritten()); - } -} - -pub const ModuleLoader = struct { - pub export fn Bun__getDefaultLoader(global: *JSC.JSGlobalObject, str: *ZigString) Api.Loader { - var jsc_vm = global.bunVM(); - const filename = str.toSlice(jsc_vm.allocator); - defer filename.deinit(); - const loader = jsc_vm.bundler.options.loader(Fs.PathName.init(filename.slice()).ext).toAPI(); - if (loader == .file) { - return Api.Loader.js; - } - - return loader; - } - pub fn transpileSourceCode( - jsc_vm: *VirtualMachine, - specifier: string, - path: Fs.Path, - loader: options.Loader, - log: *logger.Log, - virtual_source: ?*const logger.Source, - ret: *ErrorableResolvedSource, - source_code_printer: *js_printer.BufferPrinter, - comptime flags: FetchFlags, - ) !ResolvedSource { - const disable_transpilying = comptime flags.disableTranspiling(); - - switch (loader) { - .js, .jsx, .ts, .tsx, .json, .toml => { - jsc_vm.transpiled_count += 1; - jsc_vm.bundler.resetStore(); - const hash = http.Watcher.getHash(path.text); - - var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator; - - var fd: ?StoredFileDescriptorType = null; - var package_json: ?*PackageJSON = null; - - if (jsc_vm.bun_dev_watcher) |watcher| { - if (watcher.indexOf(hash)) |index| { - const _fd = watcher.watchlist.items(.fd)[index]; - fd = if (_fd > 0) _fd else null; - package_json = watcher.watchlist.items(.package_json)[index]; - } - } else if (jsc_vm.bun_watcher) |watcher| { - if (watcher.indexOf(hash)) |index| { - const _fd = watcher.watchlist.items(.fd)[index]; - fd = if (_fd > 0) _fd else null; - package_json = watcher.watchlist.items(.package_json)[index]; - } - } - - var old = jsc_vm.bundler.log; - jsc_vm.bundler.log = log; - jsc_vm.bundler.linker.log = log; - jsc_vm.bundler.resolver.log = log; - - defer { - jsc_vm.bundler.log = old; - jsc_vm.bundler.linker.log = old; - jsc_vm.bundler.resolver.log = old; - } - - // this should be a cheap lookup because 24 bytes == 8 * 3 so it's read 3 machine words - const is_node_override = specifier.len > "/bun-vfs/node_modules/".len and strings.eqlComptimeIgnoreLen(specifier[0.."/bun-vfs/node_modules/".len], "/bun-vfs/node_modules/"); - - const macro_remappings = if (jsc_vm.macro_mode or !jsc_vm.has_any_macro_remappings or is_node_override) - MacroRemap{} - else - jsc_vm.bundler.options.macro_remap; - - var fallback_source: logger.Source = undefined; - - var parse_options = Bundler.ParseOptions{ - .allocator = allocator, - .path = path, - .loader = loader, - .dirname_fd = 0, - .file_descriptor = fd, - .file_hash = hash, - .macro_remappings = macro_remappings, - .jsx = jsc_vm.bundler.options.jsx, - .virtual_source = virtual_source, - .hoist_bun_plugin = true, - }; - - if (is_node_override) { - if (NodeFallbackModules.contentsFromPath(specifier)) |code| { - const fallback_path = Fs.Path.initWithNamespace(specifier, "node"); - fallback_source = logger.Source{ .path = fallback_path, .contents = code, .key_path = fallback_path }; - parse_options.virtual_source = &fallback_source; - } - } - - var parse_result = jsc_vm.bundler.parseMaybeReturnFileOnly( - parse_options, - null, - disable_transpilying, - ) orelse { - return error.ParseError; - }; - - if (jsc_vm.bundler.log.errors > 0) { - return error.ParseError; - } - - if (comptime disable_transpilying) { - return ResolvedSource{ - .allocator = null, - .source_code = switch (comptime flags) { - .print_source_and_clone => ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), - .print_source => ZigString.init(parse_result.source.contents), - else => unreachable, - }, - .specifier = ZigString.init(specifier), - .source_url = ZigString.init(path.text), - .hash = 0, - }; - } - - const has_bun_plugin = parse_result.ast.bun_plugin.hoisted_stmts.items.len > 0; - - if (has_bun_plugin) { - try ModuleLoader.runBunPlugin(jsc_vm, source_code_printer, &parse_result, ret); - } - - var printer = source_code_printer.*; - printer.ctx.reset(); - - const start_count = jsc_vm.bundler.linker.import_counter; - // We _must_ link because: - // - node_modules bundle won't be properly - try jsc_vm.bundler.linker.link( - path, - &parse_result, - jsc_vm.origin, - .absolute_path, - false, - true, - ); - - if (!jsc_vm.macro_mode) - jsc_vm.resolved_count += jsc_vm.bundler.linker.import_counter - start_count; - jsc_vm.bundler.linker.import_counter = 0; - - const written = brk: { - defer source_code_printer.* = printer; - break :brk try jsc_vm.bundler.printWithSourceMap( - parse_result, - @TypeOf(&printer), - &printer, - .esm_ascii, - SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings), - ); - }; - - if (written == 0) { - // if it's an empty file but there were plugins - // we don't want it to break if you try to import from it - if (has_bun_plugin) { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init("// auto-generated plugin stub\nexport default undefined\n"), - .specifier = ZigString.init(specifier), - .source_url = ZigString.init(path.text), - // // TODO: change hash to a bitfield - // .hash = 1, - - // having JSC own the memory causes crashes - .hash = 0, - }; - } - return error.PrintingErrorWriteFailed; - } - - if (comptime Environment.dump_source) { - try dumpSource(specifier, &printer); - } - - if (jsc_vm.isWatcherEnabled()) { - const resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, specifier, path.text, null); - - if (parse_result.input_fd) |fd_| { - if (jsc_vm.bun_watcher != null and !is_node_override and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { - jsc_vm.bun_watcher.?.addFile( - fd_, - path.text, - hash, - loader, - 0, - package_json, - true, - ) catch {}; - } - } - - return resolved_source; - } - - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())), - .specifier = ZigString.init(specifier), - .source_url = ZigString.init(path.text), - // // TODO: change hash to a bitfield - // .hash = 1, - - // having JSC own the memory causes crashes - .hash = 0, - }; - }, - // provideFetch() should be called - .napi => unreachable, - // .wasm => { - // jsc_vm.transpiled_count += 1; - // var fd: ?StoredFileDescriptorType = null; - - // var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator; - - // const hash = http.Watcher.getHash(path.text); - // if (jsc_vm.watcher) |watcher| { - // if (watcher.indexOf(hash)) |index| { - // const _fd = watcher.watchlist.items(.fd)[index]; - // fd = if (_fd > 0) _fd else null; - // } - // } - - // var parse_options = Bundler.ParseOptions{ - // .allocator = allocator, - // .path = path, - // .loader = loader, - // .dirname_fd = 0, - // .file_descriptor = fd, - // .file_hash = hash, - // .macro_remappings = MacroRemap{}, - // .jsx = jsc_vm.bundler.options.jsx, - // }; - - // var parse_result = jsc_vm.bundler.parse( - // parse_options, - // null, - // ) orelse { - // return error.ParseError; - // }; - - // return ResolvedSource{ - // .allocator = if (jsc_vm.has_loaded) &jsc_vm.allocator else null, - // .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), - // .specifier = ZigString.init(specifier), - // .source_url = ZigString.init(path.text), - // .hash = 0, - // .tag = ResolvedSource.Tag.wasm, - // }; - // }, - else => { - return ResolvedSource{ - .allocator = &jsc_vm.allocator, - .source_code = ZigString.init(try strings.quotedAlloc(jsc_vm.allocator, path.pretty)), - .specifier = ZigString.init(path.text), - .source_url = ZigString.init(path.text), - .hash = 0, - }; - }, - } - } - - pub fn runBunPlugin( - jsc_vm: *VirtualMachine, - source_code_printer: *js_printer.BufferPrinter, - parse_result: *ParseResult, - ret: *ErrorableResolvedSource, - ) !void { - var printer = source_code_printer.*; - printer.ctx.reset(); - - defer printer.ctx.reset(); - // If we start transpiling in the middle of an existing transpilation session - // we will hit undefined memory bugs - // unless we disable resetting the store until we are done transpiling - const prev_disable_reset = js_ast.Stmt.Data.Store.disable_reset; - js_ast.Stmt.Data.Store.disable_reset = true; - js_ast.Expr.Data.Store.disable_reset = true; - - // flip the source code we use - // unless we're already transpiling a plugin - // that case could happen when - const was_printing_plugin = jsc_vm.is_printing_plugin; - const prev = jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache; - jsc_vm.is_printing_plugin = true; - defer { - js_ast.Stmt.Data.Store.disable_reset = prev_disable_reset; - js_ast.Expr.Data.Store.disable_reset = prev_disable_reset; - if (!was_printing_plugin) jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache = prev; - jsc_vm.is_printing_plugin = was_printing_plugin; - } - // we flip use_alternate_source_cache - if (!was_printing_plugin) jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache = !prev; - - // this is a bad idea, but it should work for now. - const original_name = parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name; - parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name = "globalThis.Bun.plugin"; - defer { - parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name = original_name; - } - const hoisted_stmts = parse_result.ast.bun_plugin.hoisted_stmts.items; - - var parts = [1]js_ast.Part{ - js_ast.Part{ - .stmts = hoisted_stmts, - }, - }; - var ast_copy = parse_result.ast; - ast_copy.import_records = try jsc_vm.allocator.dupe(ImportRecord, ast_copy.import_records); - defer jsc_vm.allocator.free(ast_copy.import_records); - ast_copy.parts = &parts; - ast_copy.prepend_part = null; - var temporary_source = parse_result.source; - var source_name = try std.fmt.allocPrint(jsc_vm.allocator, "{s}.plugin.{s}", .{ temporary_source.path.text, temporary_source.path.name.ext[1..] }); - temporary_source.path = Fs.Path.init(source_name); - - var temp_parse_result = parse_result.*; - temp_parse_result.ast = ast_copy; - - try jsc_vm.bundler.linker.link( - temporary_source.path, - &temp_parse_result, - jsc_vm.origin, - .absolute_path, - false, - true, - ); - - _ = brk: { - defer source_code_printer.* = printer; - break :brk try jsc_vm.bundler.printWithSourceMapMaybe( - temp_parse_result.ast, - &temporary_source, - @TypeOf(&printer), - &printer, - .esm_ascii, - true, - SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings), - ); - }; - const wrote = printer.ctx.getWritten(); - - if (wrote.len > 0) { - if (comptime Environment.dump_source) - try dumpSource(temporary_source.path.text, &printer); - - var exception = [1]JSC.JSValue{JSC.JSValue.zero}; - const promise = JSC.JSModuleLoader.evaluate( - jsc_vm.global, - wrote.ptr, - wrote.len, - temporary_source.path.text.ptr, - temporary_source.path.text.len, - parse_result.source.path.text.ptr, - parse_result.source.path.text.len, - JSC.JSValue.jsUndefined(), - &exception, - ); - if (!exception[0].isEmpty()) { - ret.* = JSC.ErrorableResolvedSource.err( - error.JSErrorObject, - exception[0].asVoid(), - ); - return error.PluginError; - } - - if (!promise.isEmptyOrUndefinedOrNull()) { - if (promise.asInternalPromise()) |promise_value| { - jsc_vm.waitForPromise(promise_value); - - if (promise_value.status(jsc_vm.global.vm()) == .Rejected) { - ret.* = JSC.ErrorableResolvedSource.err( - error.JSErrorObject, - promise_value.result(jsc_vm.global.vm()).asVoid(), - ); - return error.PluginError; - } - } - } - } - } - pub fn normalizeSpecifier(jsc_vm: *VirtualMachine, slice_: string) string { - var slice = slice_; - if (slice.len == 0) return slice; - var was_http = false; - if (strings.hasPrefixComptime(slice, "https://")) { - slice = slice["https://".len..]; - was_http = true; - } else if (strings.hasPrefixComptime(slice, "http://")) { - slice = slice["http://".len..]; - was_http = true; - } - - if (strings.hasPrefix(slice, jsc_vm.origin.host)) { - slice = slice[jsc_vm.origin.host.len..]; - } else if (was_http) { - if (strings.indexOfChar(slice, '/')) |i| { - slice = slice[i..]; - } - } - - if (jsc_vm.origin.path.len > 1) { - if (strings.hasPrefix(slice, jsc_vm.origin.path)) { - slice = slice[jsc_vm.origin.path.len..]; - } - } - - if (jsc_vm.bundler.options.routes.asset_prefix_path.len > 0) { - if (strings.hasPrefix(slice, jsc_vm.bundler.options.routes.asset_prefix_path)) { - slice = slice[jsc_vm.bundler.options.routes.asset_prefix_path.len..]; - } - } - - return slice; - } - - pub export fn Bun__fetchBuiltinModule( - jsc_vm: *VirtualMachine, - globalObject: *JSC.JSGlobalObject, - specifier: *ZigString, - referrer: *ZigString, - ret: *ErrorableResolvedSource, - ) bool { - JSC.markBinding(@src()); - var log = logger.Log.init(jsc_vm.bundler.allocator); - defer log.deinit(); - if (jsc_vm.fetchBuiltinModule(specifier.slice(), &log, false) catch |err| { - VirtualMachine.processFetchLog(globalObject, specifier.*, referrer.*, &log, ret, err); - return true; - }) |builtin| { - ret.* = ErrorableResolvedSource.ok(builtin); - return true; - } else { - return false; - } - } - - pub export fn Bun__transpileFile( - jsc_vm: *VirtualMachine, - globalObject: *JSC.JSGlobalObject, - specifier_ptr: *ZigString, - referrer: *ZigString, - ret: *ErrorableResolvedSource, - ) bool { - JSC.markBinding(@src()); - var log = logger.Log.init(jsc_vm.bundler.allocator); - defer log.deinit(); - var _specifier = specifier_ptr.toSlice(jsc_vm.allocator); - defer _specifier.deinit(); - var specifier = normalizeSpecifier(jsc_vm, _specifier.slice()); - const path = Fs.Path.init(specifier); - const loader = jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: { - if (strings.eqlLong(specifier, jsc_vm.main, true)) { - break :brk options.Loader.js; - } - - break :brk options.Loader.file; - }; - ret.* = ErrorableResolvedSource.ok( - ModuleLoader.transpileSourceCode( - jsc_vm, - specifier, - path, - loader, - &log, - null, - ret, - VirtualMachine.source_code_printer.?, - FetchFlags.transpile, - ) catch |err| { - if (err == error.PluginError) { - return true; - } - VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer.*, &log, ret, err); - return true; - }, - ); - return true; - } - - export fn Bun__runVirtualModule(globalObject: *JSC.JSGlobalObject, specifier_ptr: *ZigString) JSValue { - JSC.markBinding(@src()); - if (globalObject.bunVM().plugin_runner == null) return JSValue.zero; - - const specifier = specifier_ptr.slice(); - - if (!PluginRunner.couldBePlugin(specifier)) { - return JSValue.zero; - } - - const namespace = PluginRunner.extractNamespace(specifier); - const after_namespace = if (namespace.len == 0) - specifier - else - specifier[@minimum(namespace.len + 1, specifier.len)..]; - - return globalObject.runOnLoadPlugins(ZigString.init(namespace), ZigString.init(after_namespace), .bun) orelse return JSValue.zero; - } - - export fn Bun__transpileVirtualModule( - globalObject: *JSC.JSGlobalObject, - specifier_ptr: *ZigString, - referrer_ptr: *ZigString, - source_code: *ZigString, - loader_: Api.Loader, - ret: *ErrorableResolvedSource, - ) bool { - JSC.markBinding(@src()); - const jsc_vm = globalObject.bunVM(); - std.debug.assert(jsc_vm.plugin_runner != null); - - var specifier_slice = specifier_ptr.toSlice(jsc_vm.allocator); - const specifier = specifier_slice.slice(); - defer specifier_slice.deinit(); - var source_code_slice = source_code.toSlice(jsc_vm.allocator); - defer source_code_slice.deinit(); - - var virtual_source = logger.Source.initPathString(specifier, source_code_slice.slice()); - var log = logger.Log.init(jsc_vm.allocator); - const path = Fs.Path.init(specifier); - - const loader = if (loader_ != ._none) - options.Loader.fromString(@tagName(loader_)).? - else - jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: { - if (strings.eqlLong(specifier, jsc_vm.main, true)) { - break :brk options.Loader.js; - } - - break :brk options.Loader.file; - }; - - defer log.deinit(); - ret.* = ErrorableResolvedSource.ok( - ModuleLoader.transpileSourceCode( - jsc_vm, - specifier, - path, - options.Loader.fromString(@tagName(loader)).?, - &log, - &virtual_source, - ret, - VirtualMachine.source_code_printer.?, - FetchFlags.transpile, - ) catch |err| { - if (err == error.PluginError) { - return true; - } - VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer_ptr.*, &log, ret, err); - return true; - }, - ); - return true; - } - - comptime { - _ = Bun__transpileVirtualModule; - _ = Bun__runVirtualModule; - _ = Bun__transpileFile; - _ = Bun__fetchBuiltinModule; - _ = Bun__getDefaultLoader; - } -}; - -const FetchFlags = enum { - transpile, - print_source, - print_source_and_clone, - - pub fn disableTranspiling(this: FetchFlags) bool { - return this != .transpile; - } -}; - pub const Watcher = @import("../watcher.zig").NewWatcher(*HotReloader); pub const HotReloader = struct { diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig new file mode 100644 index 000000000..bee5fa1a1 --- /dev/null +++ b/src/bun.js/module_loader.zig @@ -0,0 +1,2052 @@ +const std = @import("std"); +const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false; +const StaticExport = @import("./bindings/static_export.zig"); +const c_char = StaticExport.c_char; +const bun = @import("../global.zig"); +const string = bun.string; +const Output = bun.Output; +const Global = bun.Global; +const Environment = bun.Environment; +const strings = bun.strings; +const MutableString = bun.MutableString; +const stringZ = bun.stringZ; +const default_allocator = bun.default_allocator; +const StoredFileDescriptorType = bun.StoredFileDescriptorType; +const Arena = @import("../mimalloc_arena.zig").Arena; +const C = bun.C; +const NetworkThread = @import("http").NetworkThread; +const IO = @import("io"); +const Allocator = std.mem.Allocator; +const IdentityContext = @import("../identity_context.zig").IdentityContext; +const Fs = @import("../fs.zig"); +const Resolver = @import("../resolver/resolver.zig"); +const ast = @import("../import_record.zig"); +const NodeModuleBundle = @import("../node_module_bundle.zig").NodeModuleBundle; +const MacroEntryPoint = @import("../bundler.zig").MacroEntryPoint; +const ParseResult = @import("../bundler.zig").ParseResult; +const logger = @import("../logger.zig"); +const Api = @import("../api/schema.zig").Api; +const options = @import("../options.zig"); +const Bundler = @import("../bundler.zig").Bundler; +const PluginRunner = @import("../bundler.zig").PluginRunner; +const ServerEntryPoint = @import("../bundler.zig").ServerEntryPoint; +const js_printer = @import("../js_printer.zig"); +const js_parser = @import("../js_parser.zig"); +const js_ast = @import("../js_ast.zig"); +const hash_map = @import("../hash_map.zig"); +const http = @import("../http.zig"); +const NodeFallbackModules = @import("../node_fallbacks.zig"); +const ImportKind = ast.ImportKind; +const Analytics = @import("../analytics/analytics_thread.zig"); +const ZigString = @import("../jsc.zig").ZigString; +const Runtime = @import("../runtime.zig"); +const Router = @import("./api/router.zig"); +const ImportRecord = ast.ImportRecord; +const DotEnv = @import("../env_loader.zig"); +const PackageJSON = @import("../resolver/package_json.zig").PackageJSON; +const MacroRemap = @import("../resolver/package_json.zig").MacroMap; +const WebCore = @import("../jsc.zig").WebCore; +const Request = WebCore.Request; +const Response = WebCore.Response; +const Headers = WebCore.Headers; +const Fetch = WebCore.Fetch; +const FetchEvent = WebCore.FetchEvent; +const js = @import("../jsc.zig").C; +const JSC = @import("../jsc.zig"); +const JSError = @import("./base.zig").JSError; +const d = @import("./base.zig").d; +const MarkedArrayBuffer = @import("./base.zig").MarkedArrayBuffer; +const getAllocator = @import("./base.zig").getAllocator; +const JSValue = @import("../jsc.zig").JSValue; +const NewClass = @import("./base.zig").NewClass; +const Microtask = @import("../jsc.zig").Microtask; +const JSGlobalObject = @import("../jsc.zig").JSGlobalObject; +const ExceptionValueRef = @import("../jsc.zig").ExceptionValueRef; +const JSPrivateDataPtr = @import("../jsc.zig").JSPrivateDataPtr; +const ZigConsoleClient = @import("../jsc.zig").ZigConsoleClient; +const Node = @import("../jsc.zig").Node; +const ZigException = @import("../jsc.zig").ZigException; +const ZigStackTrace = @import("../jsc.zig").ZigStackTrace; +const ErrorableResolvedSource = @import("../jsc.zig").ErrorableResolvedSource; +const ResolvedSource = @import("../jsc.zig").ResolvedSource; +const JSPromise = @import("../jsc.zig").JSPromise; +const JSInternalPromise = @import("../jsc.zig").JSInternalPromise; +const JSModuleLoader = @import("../jsc.zig").JSModuleLoader; +const JSPromiseRejectionOperation = @import("../jsc.zig").JSPromiseRejectionOperation; +const Exception = @import("../jsc.zig").Exception; +const ErrorableZigString = @import("../jsc.zig").ErrorableZigString; +const ZigGlobalObject = @import("../jsc.zig").ZigGlobalObject; +const VM = @import("../jsc.zig").VM; +const JSFunction = @import("../jsc.zig").JSFunction; +const Config = @import("./config.zig"); +const URL = @import("../url.zig").URL; +const Transpiler = @import("./api/transpiler.zig"); +const Bun = JSC.API.Bun; +const EventLoop = JSC.EventLoop; +const PendingResolution = @import("../resolver/resolver.zig").PendingResolution; +const ThreadSafeFunction = JSC.napi.ThreadSafeFunction; +const PackageManager = @import("../install/install.zig").PackageManager; +const Install = @import("../install/install.zig"); +const VirtualMachine = JSC.VirtualMachine; +const Dependency = @import("../install/dependency.zig"); + +// This exists to make it so we can reload these quicker in development +fn jsModuleFromFile(from_path: string, comptime input: string) string { + const absolute_path = comptime std.fs.path.dirname(@src().file).? ++ "/" ++ input; + const Holder = struct { + pub const file = @embedFile(absolute_path); + }; + + if (comptime !Environment.allow_assert) { + if (from_path.len == 0) { + return Holder.file; + } + } + + var file: std.fs.File = undefined; + + if (comptime Environment.allow_assert) { + file = std.fs.openFileAbsoluteZ(absolute_path, .{ .mode = .read_only }) catch { + const WarnOnce = struct { + pub var warned = false; + }; + if (!WarnOnce.warned) { + WarnOnce.warned = true; + Output.prettyErrorln("Could not find file: " ++ absolute_path ++ " - using embedded version", .{}); + } + return Holder.file; + }; + } else { + var parts = [_]string{ from_path, input }; + var buf: [bun.MAX_PATH_BYTES]u8 = undefined; + var absolute_path_to_use = Fs.FileSystem.instance.absBuf(&parts, &buf); + buf[absolute_path_to_use.len] = 0; + file = std.fs.openFileAbsoluteZ(std.meta.assumeSentinel(absolute_path_to_use.ptr, 0), .{ .mode = .read_only }) catch { + const WarnOnce = struct { + pub var warned = false; + }; + if (!WarnOnce.warned) { + WarnOnce.warned = true; + Output.prettyErrorln("Could not find file: {s}, so using embedded version", .{absolute_path_to_use}); + } + return Holder.file; + }; + } + + var contents = file.readToEndAlloc(bun.default_allocator, std.math.maxInt(usize)) catch @panic("Cannot read file: " ++ absolute_path); + if (comptime !Environment.allow_assert) { + file.close(); + } + return contents; +} + +inline fn jsSyntheticModule(comptime name: ResolvedSource.Tag) ResolvedSource { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(""), + .specifier = ZigString.init(@tagName(name)), + .source_url = ZigString.init(@tagName(name)), + .hash = 0, + .tag = name, + }; +} + +fn dumpSource(specifier: string, printer: anytype) !void { + const BunDebugHolder = struct { + pub var dir: ?std.fs.Dir = null; + }; + if (BunDebugHolder.dir == null) { + BunDebugHolder.dir = try std.fs.cwd().makeOpenPath("/tmp/bun-debug-src/", .{ .iterate = true }); + } + + if (std.fs.path.dirname(specifier)) |dir_path| { + var parent = try BunDebugHolder.dir.?.makeOpenPath(dir_path[1..], .{ .iterate = true }); + defer parent.close(); + try parent.writeFile(std.fs.path.basename(specifier), printer.ctx.getWritten()); + } else { + try BunDebugHolder.dir.?.writeFile(std.fs.path.basename(specifier), printer.ctx.getWritten()); + } +} + +pub const ModuleLoader = struct { + pub const AsyncModule = struct { + + // This is all the state used by the printer to print the module + parse_result: ParseResult, + stmt_blocks: []*js_ast.Stmt.Data.Store.All.Block = &[_]*js_ast.Stmt.Data.Store.All.Block{}, + expr_blocks: []*js_ast.Expr.Data.Store.All.Block = &[_]*js_ast.Expr.Data.Store.All.Block{}, + promise: JSC.Strong = .{}, + path: Fs.Path, + specifier: string = "", + referrer: string = "", + string_buf: []u8 = &[_]u8{}, + fd: ?StoredFileDescriptorType = null, + package_json: ?*PackageJSON = null, + loader: Api.Loader, + hash: u32 = std.math.maxInt(u32), + globalThis: *JSC.JSGlobalObject = undefined, + + // This is the specific state for making it async + poll_ref: JSC.PollRef = .{}, + + pub const Id = u32; + const debug = Output.scoped(.ModuleLoader, false); + + const PackageDownloadError = struct { + name: []const u8, + resolution: Install.Resolution, + err: anyerror, + url: []const u8, + }; + + const PackageResolveError = struct { + name: []const u8, + err: anyerror, + url: []const u8, + version: Dependency.Version, + }; + + pub const Queue = struct { + map: Map = .{}, + concurrent_task_count: std.atomic.Atomic(u32) = std.atomic.Atomic(u32).init(0), + + const DeferredDependencyError = struct { + dependency: Dependency, + root_dependency_id: Install.PackageID, + err: anyerror, + }; + + pub const Map = std.ArrayListUnmanaged(AsyncModule); + + pub fn enqueue(this: *Queue, globalObject: *JSC.JSGlobalObject, opts: anytype) void { + debug("enqueue: {s}", .{opts.specifier}); + var module = AsyncModule.init(opts, globalObject) catch unreachable; + module.poll_ref.ref(this.vm()); + + this.map.append(this.vm().allocator, module) catch unreachable; + this.vm().packageManager().flushDependencyQueue(); + _ = this.vm().packageManager().scheduleNetworkTasks(); + } + + pub fn onDependencyError(ctx: *anyopaque, dependency: Dependency, root_dependency_id: Install.PackageID, err: anyerror) void { + var this = bun.cast(*Queue, ctx); + debug("onDependencyError: {s}", .{this.vm().packageManager().lockfile.str(dependency.name)}); + + var modules: []AsyncModule = this.map.items; + var i: usize = 0; + outer: for (modules) |module_| { + var module = module_; + var root_dependency_ids = module.parse_result.pending_imports.items(.root_dependency_id); + for (root_dependency_ids) |dep, dep_i| { + if (dep != root_dependency_id) continue; + module.resolveError( + this.vm(), + module.parse_result.pending_imports.items(.import_record_id)[dep_i], + .{ + .name = this.vm().packageManager().lockfile.str(dependency.name), + .err = err, + .url = "", + .version = dependency.version, + }, + ) catch unreachable; + continue :outer; + } + + modules[i] = module; + i += 1; + } + this.map.items.len = i; + } + pub fn onWakeHandler(ctx: *anyopaque, _: *PackageManager) void { + debug("onWake", .{}); + var this = bun.cast(*Queue, ctx); + var concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch @panic("OOM"); + concurrent_task.* = .{ + .task = JSC.Task.init(this), + .auto_delete = true, + }; + this.vm().enqueueTaskConcurrent(concurrent_task); + } + + pub fn onPoll(this: *Queue) void { + debug("onPoll", .{}); + var pm = this.vm().packageManager(); + + this.runTasks(); + this.pollModules(); + _ = pm.flushDependencyQueue(); + _ = pm.scheduleNetworkTasks(); + } + + pub fn runTasks(this: *Queue) void { + var pm = this.vm().packageManager(); + + if (Output.enable_ansi_colors_stderr) { + pm.startProgressBarIfNone(); + pm.runTasks( + *Queue, + this, + .{ + .onExtract = onExtract, + .onResolve = onResolve, + .onPackageManifestError = onPackageManifestError, + .onPackageDownloadError = onPackageDownloadError, + .progress_bar = true, + }, + PackageManager.Options.LogLevel.default, + ) catch unreachable; + } else { + pm.runTasks( + *Queue, + this, + .{ + .onExtract = onExtract, + .onResolve = onResolve, + .onPackageManifestError = onPackageManifestError, + .onPackageDownloadError = onPackageDownloadError, + }, + PackageManager.Options.LogLevel.default_no_progress, + ) catch unreachable; + } + } + + pub fn onResolve(_: *Queue) void { + debug("onResolve", .{}); + } + + pub fn onPackageManifestError( + this: *Queue, + name: []const u8, + err: anyerror, + url: []const u8, + ) void { + debug("onPackageManifestError: {s}", .{name}); + + var modules: []AsyncModule = this.map.items; + var i: usize = 0; + outer: for (modules) |module_| { + var module = module_; + var tags = module.parse_result.pending_imports.items(.tag); + for (tags) |tag, tag_i| { + if (tag == .resolve) { + var esms = module.parse_result.pending_imports.items(.esm); + const esm = esms[tag_i]; + var string_bufs = module.parse_result.pending_imports.items(.string_buf); + + if (!strings.eql(esm.name.slice(string_bufs[tag_i]), name)) continue; + + var versions = module.parse_result.pending_imports.items(.dependency); + + module.resolveError( + this.vm(), + module.parse_result.pending_imports.items(.import_record_id)[tag_i], + .{ + .name = name, + .err = err, + .url = url, + .version = versions[tag_i], + }, + ) catch unreachable; + continue :outer; + } + } + + modules[i] = module; + i += 1; + } + this.map.items.len = i; + } + + pub fn onPackageDownloadError( + this: *Queue, + package_id: Install.PackageID, + name: []const u8, + resolution: Install.Resolution, + err: anyerror, + url: []const u8, + ) void { + debug("onPackageDownloadError: {s}", .{name}); + + var modules: []AsyncModule = this.map.items; + var i: usize = 0; + outer: for (modules) |module_| { + var module = module_; + var root_dependency_ids = module.parse_result.pending_imports.items(.root_dependency_id); + for (root_dependency_ids) |dep, dep_i| { + if (this.vm().packageManager().dynamicRootDependencies().items[dep].resolution_id != package_id) continue; + module.downloadError( + this.vm(), + module.parse_result.pending_imports.items(.import_record_id)[dep_i], + .{ + .name = name, + .resolution = resolution, + .err = err, + .url = url, + }, + ) catch unreachable; + continue :outer; + } + + modules[i] = module; + i += 1; + } + this.map.items.len = i; + } + + pub fn onExtract(this: *Queue, package_id: u32, comptime _: PackageManager.Options.LogLevel) void { + if (comptime Environment.allow_assert) + debug("onExtract: {s} ({d})", .{ + this.vm().packageManager().lockfile.str(this.vm().packageManager().lockfile.packages.get(package_id).name), + package_id, + }); + this.onPackageID(package_id); + } + + pub fn onPackageID(this: *Queue, package_id: u32) void { + var values = this.map.items; + for (values) |value| { + var package_ids = value.parse_result.pending_imports.items(.resolution_id); + + _ = package_id; + _ = package_ids; + } + } + + pub fn pollModules(this: *Queue) void { + var pm = this.vm().packageManager(); + var modules = this.map.items; + var i: usize = 0; + for (modules) |mod| { + var module = mod; + var tags = module.parse_result.pending_imports.items(.tag); + var root_dependency_ids = module.parse_result.pending_imports.items(.root_dependency_id); + // var esms = module.parse_result.pending_imports.items(.esm); + // var versions = module.parse_result.pending_imports.items(.dependency); + var done_count: usize = 0; + for (tags) |tag, tag_i| { + const root_id = root_dependency_ids[tag_i]; + if (root_id == Install.invalid_package_id) continue; + const root_items = pm.dynamicRootDependencies().items; + if (root_items.len <= root_id) continue; + const package_id = root_items[root_id].resolution_id; + + switch (tag) { + .resolve => { + if (package_id == Install.invalid_package_id) { + continue; + } + + // if we get here, the package has already been resolved. + tags[tag_i] = .download; + }, + .download => { + if (package_id == Install.invalid_package_id) { + unreachable; + } + }, + .done => { + done_count += 1; + continue; + }, + } + + if (package_id == Install.invalid_package_id) { + continue; + } + + const package = pm.lockfile.packages.get(package_id); + std.debug.assert(package.resolution.tag != .root); + + switch (pm.determinePreinstallState(package, pm.lockfile)) { + .done => { + done_count += 1; + tags[tag_i] = .done; + }, + .extracting => { + // we are extracting the package + // we need to wait for the next poll + continue; + }, + .extract => {}, + else => {}, + } + } + + if (done_count == tags.len) { + if (i + 1 >= modules.len) { + this.vm().packageManager().endProgressBar(); + } + module.done(this.vm()); + } else { + modules[i] = module; + i += 1; + } + } + this.map.items.len = i; + if (i == 0) { + // ensure we always end the progress bar + this.vm().packageManager().endProgressBar(); + } + } + + pub fn vm(this: *Queue) *VirtualMachine { + return @fieldParentPtr(VirtualMachine, "modules", this); + } + }; + + pub fn init(opts: anytype, globalObject: *JSC.JSGlobalObject) !AsyncModule { + var promise = JSC.Strong{}; + var stmt_blocks = js_ast.Stmt.Data.Store.toOwnedSlice(); + var expr_blocks = js_ast.Expr.Data.Store.toOwnedSlice(); + const this_promise = JSValue.createInternalPromise(globalObject); + promise.set(globalObject, this_promise); + + var buf = bun.StringBuilder{}; + buf.count(opts.referrer); + buf.count(opts.specifier); + buf.count(opts.path.text); + + try buf.allocate(bun.default_allocator); + opts.promise_ptr.?.* = this_promise.asInternalPromise().?; + const referrer = buf.append(opts.referrer); + const specifier = buf.append(opts.specifier); + const path = Fs.Path.init(buf.append(opts.path.text)); + + return AsyncModule{ + .parse_result = opts.parse_result, + .promise = promise, + .path = path, + .specifier = specifier, + .referrer = referrer, + .fd = opts.fd, + .package_json = opts.package_json, + .loader = opts.loader.toAPI(), + .string_buf = buf.allocatedSlice(), + .stmt_blocks = stmt_blocks, + .globalThis = globalObject, + .expr_blocks = expr_blocks, + }; + } + + pub fn done(this: *AsyncModule, jsc_vm: *JSC.VirtualMachine) void { + var log = logger.Log.init(jsc_vm.allocator); + defer log.deinit(); + var errorable: ErrorableResolvedSource = undefined; + this.poll_ref.unref(jsc_vm); + outer: { + errorable = ErrorableResolvedSource.ok(this.resumeLoadingModule(&log) catch |err| { + JSC.VirtualMachine.processFetchLog( + this.globalThis, + ZigString.init(this.specifier), + ZigString.init(this.referrer), + &log, + &errorable, + err, + ); + break :outer; + }); + } + + var spec = ZigString.init(this.specifier).withEncoding(); + var ref = ZigString.init(this.referrer).withEncoding(); + Bun__onFulfillAsyncModule( + this.promise.get().?, + &errorable, + &spec, + &ref, + ); + this.deinit(); + } + + pub fn resolveError(this: *AsyncModule, vm: *JSC.VirtualMachine, import_record_id: u32, result: PackageResolveError) !void { + var globalThis = this.globalThis; + + var msg: []u8 = try switch (result.err) { + error.PackageManifestHTTP400 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 400 while resolving package '{s}' at '{s}'", + .{ result.name, result.url }, + ), + error.PackageManifestHTTP401 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 401 while resolving package '{s}' at '{s}'", + .{ result.name, result.url }, + ), + error.PackageManifestHTTP402 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 402 while resolving package '{s}' at '{s}'", + .{ result.name, result.url }, + ), + error.PackageManifestHTTP403 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 403 while resolving package '{s}' at '{s}'", + .{ result.name, result.url }, + ), + error.PackageManifestHTTP404 => std.fmt.allocPrint( + bun.default_allocator, + "Package '{s}' was not found", + .{result.name}, + ), + error.PackageManifestHTTP4xx => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 4xx while resolving package '{s}' at '{s}'", + .{ result.name, result.url }, + ), + error.PackageManifestHTTP5xx => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 5xx while resolving package '{s}' at '{s}'", + .{ result.name, result.url }, + ), + error.DistTagNotFound, error.NoMatchingVersion => brk: { + const prefix: []const u8 = if (result.err == error.NoMatchingVersion and result.version.tag == .npm and result.version.value.npm.isExact()) + "Version not found" + else if (result.version.tag == .npm and !result.version.value.npm.isExact()) + "No matching version found" + else + "No match found"; + + break :brk std.fmt.allocPrint( + bun.default_allocator, + "{s} '{s}' for package '{s}' (but package exists)", + .{ prefix, vm.packageManager().lockfile.str(result.version.literal), result.name }, + ); + }, + else => |err| std.fmt.allocPrint( + bun.default_allocator, + "{s} resolving package '{s}' at '{s}'", + .{ std.mem.span(@errorName(err)), result.name, result.url }, + ), + }; + + const name: []const u8 = switch (result.err) { + error.NoMatchingVersion => "PackageVersionNotFound", + error.DistTagNotFound => "PackageTagNotFound", + error.PackageManifestHTTP403 => "PackageForbidden", + error.PackageManifestHTTP404 => "PackageNotFound", + else => "PackageResolveError", + }; + + var error_instance = ZigString.init(msg).withEncoding().toErrorInstance(globalThis); + if (result.url.len > 0) + error_instance.put(globalThis, ZigString.static("url"), ZigString.init(result.url).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("name"), ZigString.init(name).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("pkg"), ZigString.init(result.name).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("specifier"), ZigString.init(this.specifier).withEncoding().toValueGC(globalThis)); + const location = logger.rangeData(&this.parse_result.source, this.parse_result.ast.import_records[import_record_id].range, "").location.?; + error_instance.put(globalThis, ZigString.static("sourceURL"), ZigString.init(this.parse_result.source.path.text).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("line"), JSValue.jsNumber(location.line)); + if (location.line_text) |line_text| { + error_instance.put(globalThis, ZigString.static("lineText"), ZigString.init(line_text).withEncoding().toValueGC(globalThis)); + } + error_instance.put(globalThis, ZigString.static("column"), JSValue.jsNumber(location.column)); + if (this.referrer.len > 0 and !strings.eqlComptime(this.referrer, "undefined")) { + error_instance.put(globalThis, ZigString.static("referrer"), ZigString.init(this.referrer).withEncoding().toValueGC(globalThis)); + } + + const promise_value = this.promise.swap(); + var promise = promise_value.asInternalPromise().?; + promise_value.ensureStillAlive(); + this.poll_ref.unref(vm); + this.deinit(); + promise.rejectAsHandled(globalThis, error_instance); + } + pub fn downloadError(this: *AsyncModule, vm: *JSC.VirtualMachine, import_record_id: u32, result: PackageDownloadError) !void { + var globalThis = this.globalThis; + + const msg_args = .{ + result.name, + result.resolution.fmt(vm.packageManager().lockfile.buffers.string_bytes.items), + }; + + var msg: []u8 = try switch (result.err) { + error.TarballHTTP400 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 400 downloading package '{s}@{any}'", + msg_args, + ), + error.TarballHTTP401 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 401 downloading package '{s}@{any}'", + msg_args, + ), + error.TarballHTTP402 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 402 downloading package '{s}@{any}'", + msg_args, + ), + error.TarballHTTP403 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 403 downloading package '{s}@{any}'", + msg_args, + ), + error.TarballHTTP404 => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 404 downloading package '{s}@{any}'", + msg_args, + ), + error.TarballHTTP4xx => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 4xx downloading package '{s}@{any}'", + msg_args, + ), + error.TarballHTTP5xx => std.fmt.allocPrint( + bun.default_allocator, + "HTTP 5xx downloading package '{s}@{any}'", + msg_args, + ), + error.TarballFailedToExtract => std.fmt.allocPrint( + bun.default_allocator, + "Failed to extract tarball for package '{s}@{any}'", + msg_args, + ), + else => |err| std.fmt.allocPrint( + bun.default_allocator, + "{s} downloading package '{s}@{any}'", + .{ + std.mem.span(@errorName(err)), result.name, + result.resolution.fmt(vm.packageManager().lockfile.buffers.string_bytes.items), + }, + ), + }; + + const name: []const u8 = switch (result.err) { + error.TarballFailedToExtract => "PackageExtractionError", + error.TarballHTTP403 => "TarballForbiddenError", + error.TarballHTTP404 => "TarballNotFoundError", + else => "TarballDownloadError", + }; + + var error_instance = ZigString.init(msg).withEncoding().toErrorInstance(globalThis); + if (result.url.len > 0) + error_instance.put(globalThis, ZigString.static("url"), ZigString.init(result.url).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("name"), ZigString.init(name).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("pkg"), ZigString.init(result.name).withEncoding().toValueGC(globalThis)); + if (this.specifier.len > 0 and !strings.eqlComptime(this.specifier, "undefined")) { + error_instance.put(globalThis, ZigString.static("referrer"), ZigString.init(this.specifier).withEncoding().toValueGC(globalThis)); + } + + const location = logger.rangeData(&this.parse_result.source, this.parse_result.ast.import_records[import_record_id].range, "").location.?; + error_instance.put(globalThis, ZigString.static("specifier"), ZigString.init( + this.parse_result.ast.import_records[import_record_id].path.text, + ).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("sourceURL"), ZigString.init(this.parse_result.source.path.text).withEncoding().toValueGC(globalThis)); + error_instance.put(globalThis, ZigString.static("line"), JSValue.jsNumber(location.line)); + if (location.line_text) |line_text| { + error_instance.put(globalThis, ZigString.static("lineText"), ZigString.init(line_text).withEncoding().toValueGC(globalThis)); + } + error_instance.put(globalThis, ZigString.static("column"), JSValue.jsNumber(location.column)); + + const promise_value = this.promise.swap(); + var promise = promise_value.asInternalPromise().?; + promise_value.ensureStillAlive(); + this.poll_ref.unref(vm); + this.deinit(); + promise.rejectAsHandled(globalThis, error_instance); + } + + pub fn resumeLoadingModule(this: *AsyncModule, log: *logger.Log) !ResolvedSource { + debug("resumeLoadingModule: {s}", .{this.specifier}); + var parse_result = this.parse_result; + var path = this.path; + var jsc_vm = JSC.VirtualMachine.vm; + var specifier = this.specifier; + var old_log = jsc_vm.log; + + jsc_vm.bundler.linker.log = log; + jsc_vm.bundler.log = log; + jsc_vm.bundler.resolver.log = log; + jsc_vm.packageManager().log = log; + defer { + jsc_vm.bundler.linker.log = old_log; + jsc_vm.bundler.log = old_log; + jsc_vm.bundler.resolver.log = old_log; + jsc_vm.packageManager().log = old_log; + } + + // We _must_ link because: + // - node_modules bundle won't be properly + try jsc_vm.bundler.linker.link( + path, + &parse_result, + jsc_vm.origin, + .absolute_path, + false, + true, + ); + this.parse_result = parse_result; + + var printer = VirtualMachine.source_code_printer.?.*; + printer.ctx.reset(); + + const written = brk: { + defer VirtualMachine.source_code_printer.?.* = printer; + break :brk try jsc_vm.bundler.printWithSourceMap( + parse_result, + @TypeOf(&printer), + &printer, + .esm_ascii, + SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings), + ); + }; + + if (written == 0) { + return error.PrintingErrorWriteFailed; + } + + if (comptime Environment.dump_source) { + try dumpSource(specifier, &printer); + } + + if (jsc_vm.isWatcherEnabled()) { + const resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, specifier, path.text, null); + + if (parse_result.input_fd) |fd_| { + if (jsc_vm.bun_watcher != null and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { + jsc_vm.bun_watcher.?.addFile( + fd_, + path.text, + this.hash, + options.Loader.fromAPI(this.loader), + 0, + this.package_json, + true, + ) catch {}; + } + } + + return resolved_source; + } + + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())), + .specifier = ZigString.init(specifier), + .source_url = ZigString.init(path.text), + // // TODO: change hash to a bitfield + // .hash = 1, + + // having JSC own the memory causes crashes + .hash = 0, + }; + } + + pub fn deinit(this: *AsyncModule) void { + this.parse_result.deinit(); + bun.default_allocator.free(this.stmt_blocks); + bun.default_allocator.free(this.expr_blocks); + this.promise.deinit(); + bun.default_allocator.free(this.string_buf); + } + + extern "C" fn Bun__onFulfillAsyncModule( + promiseValue: JSC.JSValue, + res: *JSC.ErrorableResolvedSource, + specifier: *ZigString, + referrer: *ZigString, + ) void; + }; + + pub export fn Bun__getDefaultLoader(global: *JSC.JSGlobalObject, str: *ZigString) Api.Loader { + var jsc_vm = global.bunVM(); + const filename = str.toSlice(jsc_vm.allocator); + defer filename.deinit(); + const loader = jsc_vm.bundler.options.loader(Fs.PathName.init(filename.slice()).ext).toAPI(); + if (loader == .file) { + return Api.Loader.js; + } + + return loader; + } + + pub fn transpileSourceCode( + jsc_vm: *VirtualMachine, + specifier: string, + referrer: string, + path: Fs.Path, + loader: options.Loader, + log: *logger.Log, + virtual_source: ?*const logger.Source, + ret: *ErrorableResolvedSource, + promise_ptr: ?*?*JSC.JSInternalPromise, + source_code_printer: *js_printer.BufferPrinter, + globalObject: ?*JSC.JSGlobalObject, + comptime flags: FetchFlags, + ) !ResolvedSource { + const disable_transpilying = comptime flags.disableTranspiling(); + + switch (loader) { + .js, .jsx, .ts, .tsx, .json, .toml => { + jsc_vm.transpiled_count += 1; + jsc_vm.bundler.resetStore(); + const hash = http.Watcher.getHash(path.text); + + var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator; + + var fd: ?StoredFileDescriptorType = null; + var package_json: ?*PackageJSON = null; + + if (jsc_vm.bun_dev_watcher) |watcher| { + if (watcher.indexOf(hash)) |index| { + const _fd = watcher.watchlist.items(.fd)[index]; + fd = if (_fd > 0) _fd else null; + package_json = watcher.watchlist.items(.package_json)[index]; + } + } else if (jsc_vm.bun_watcher) |watcher| { + if (watcher.indexOf(hash)) |index| { + const _fd = watcher.watchlist.items(.fd)[index]; + fd = if (_fd > 0) _fd else null; + package_json = watcher.watchlist.items(.package_json)[index]; + } + } + + var old = jsc_vm.bundler.log; + jsc_vm.bundler.log = log; + jsc_vm.bundler.linker.log = log; + jsc_vm.bundler.resolver.log = log; + if (jsc_vm.bundler.resolver.package_manager) |pm| { + pm.log = log; + } + + defer { + jsc_vm.bundler.log = old; + jsc_vm.bundler.linker.log = old; + jsc_vm.bundler.resolver.log = old; + if (jsc_vm.bundler.resolver.package_manager) |pm| { + pm.log = old; + } + } + + // this should be a cheap lookup because 24 bytes == 8 * 3 so it's read 3 machine words + const is_node_override = specifier.len > "/bun-vfs/node_modules/".len and strings.eqlComptimeIgnoreLen(specifier[0.."/bun-vfs/node_modules/".len], "/bun-vfs/node_modules/"); + + const macro_remappings = if (jsc_vm.macro_mode or !jsc_vm.has_any_macro_remappings or is_node_override) + MacroRemap{} + else + jsc_vm.bundler.options.macro_remap; + + var fallback_source: logger.Source = undefined; + + var parse_options = Bundler.ParseOptions{ + .allocator = allocator, + .path = path, + .loader = loader, + .dirname_fd = 0, + .file_descriptor = fd, + .file_hash = hash, + .macro_remappings = macro_remappings, + .jsx = jsc_vm.bundler.options.jsx, + .virtual_source = virtual_source, + .hoist_bun_plugin = true, + }; + + if (is_node_override) { + if (NodeFallbackModules.contentsFromPath(specifier)) |code| { + const fallback_path = Fs.Path.initWithNamespace(specifier, "node"); + fallback_source = logger.Source{ .path = fallback_path, .contents = code, .key_path = fallback_path }; + parse_options.virtual_source = &fallback_source; + } + } + + var parse_result = jsc_vm.bundler.parseMaybeReturnFileOnly( + parse_options, + null, + disable_transpilying, + ) orelse { + return error.ParseError; + }; + + if (jsc_vm.bundler.log.errors > 0) { + return error.ParseError; + } + + if (comptime disable_transpilying) { + return ResolvedSource{ + .allocator = null, + .source_code = switch (comptime flags) { + .print_source_and_clone => ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), + .print_source => ZigString.init(parse_result.source.contents), + else => unreachable, + }, + .specifier = ZigString.init(specifier), + .source_url = ZigString.init(path.text), + .hash = 0, + }; + } + + const has_bun_plugin = parse_result.ast.bun_plugin.hoisted_stmts.items.len > 0; + + if (has_bun_plugin) { + try ModuleLoader.runBunPlugin(jsc_vm, JSC.VirtualMachine.source_code_printer.?, &parse_result, ret); + } + + const start_count = jsc_vm.bundler.linker.import_counter; + + // We _must_ link because: + // - node_modules bundle won't be properly + try jsc_vm.bundler.linker.link( + path, + &parse_result, + jsc_vm.origin, + .absolute_path, + false, + true, + ); + + if (parse_result.pending_imports.len > 0) { + if (promise_ptr == null) { + return error.UnexpectedPendingResolution; + } + + if (jsc_vm.isWatcherEnabled()) { + if (parse_result.input_fd) |fd_| { + if (jsc_vm.bun_watcher != null and !is_node_override and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { + jsc_vm.bun_watcher.?.addFile( + fd_, + path.text, + hash, + loader, + 0, + package_json, + true, + ) catch {}; + } + } + } + + if (parse_result.source.contents_is_recycled) { + // this shared buffer is about to become owned by the AsyncModule struct + jsc_vm.bundler.resolver.caches.fs.resetSharedBuffer( + jsc_vm.bundler.resolver.caches.fs.sharedBuffer(), + ); + } + + jsc_vm.modules.enqueue( + globalObject.?, + .{ + .parse_result = parse_result, + .path = path, + .loader = loader, + .fd = fd, + .package_json = package_json, + .hash = hash, + .promise_ptr = promise_ptr, + .specifier = specifier, + .referrer = referrer, + }, + ); + return error.AsyncModule; + } + + if (!jsc_vm.macro_mode) + jsc_vm.resolved_count += jsc_vm.bundler.linker.import_counter - start_count; + jsc_vm.bundler.linker.import_counter = 0; + + var printer = source_code_printer.*; + printer.ctx.reset(); + + const written = brk: { + defer source_code_printer.* = printer; + break :brk try jsc_vm.bundler.printWithSourceMap( + parse_result, + @TypeOf(&printer), + &printer, + .esm_ascii, + SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings), + ); + }; + + if (written == 0) { + // if it's an empty file but there were plugins + // we don't want it to break if you try to import from it + if (has_bun_plugin) { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init("// auto-generated plugin stub\nexport default undefined\n"), + .specifier = ZigString.init(specifier), + .source_url = ZigString.init(path.text), + // // TODO: change hash to a bitfield + // .hash = 1, + + // having JSC own the memory causes crashes + .hash = 0, + }; + } + return error.PrintingErrorWriteFailed; + } + + if (comptime Environment.dump_source) { + try dumpSource(specifier, &printer); + } + + if (jsc_vm.isWatcherEnabled()) { + const resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, specifier, path.text, null); + + if (parse_result.input_fd) |fd_| { + if (jsc_vm.bun_watcher != null and !is_node_override and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { + jsc_vm.bun_watcher.?.addFile( + fd_, + path.text, + hash, + loader, + 0, + package_json, + true, + ) catch {}; + } + } + + return resolved_source; + } + + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())), + .specifier = ZigString.init(specifier), + .source_url = ZigString.init(path.text), + // // TODO: change hash to a bitfield + // .hash = 1, + + // having JSC own the memory causes crashes + .hash = 0, + }; + }, + // provideFetch() should be called + .napi => unreachable, + // .wasm => { + // jsc_vm.transpiled_count += 1; + // var fd: ?StoredFileDescriptorType = null; + + // var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator; + + // const hash = http.Watcher.getHash(path.text); + // if (jsc_vm.watcher) |watcher| { + // if (watcher.indexOf(hash)) |index| { + // const _fd = watcher.watchlist.items(.fd)[index]; + // fd = if (_fd > 0) _fd else null; + // } + // } + + // var parse_options = Bundler.ParseOptions{ + // .allocator = allocator, + // .path = path, + // .loader = loader, + // .dirname_fd = 0, + // .file_descriptor = fd, + // .file_hash = hash, + // .macro_remappings = MacroRemap{}, + // .jsx = jsc_vm.bundler.options.jsx, + // }; + + // var parse_result = jsc_vm.bundler.parse( + // parse_options, + // null, + // ) orelse { + // return error.ParseError; + // }; + + // return ResolvedSource{ + // .allocator = if (jsc_vm.has_loaded) &jsc_vm.allocator else null, + // .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), + // .specifier = ZigString.init(specifier), + // .source_url = ZigString.init(path.text), + // .hash = 0, + // .tag = ResolvedSource.Tag.wasm, + // }; + // }, + else => { + return ResolvedSource{ + .allocator = &jsc_vm.allocator, + .source_code = ZigString.init(try strings.quotedAlloc(jsc_vm.allocator, path.pretty)), + .specifier = ZigString.init(path.text), + .source_url = ZigString.init(path.text), + .hash = 0, + }; + }, + } + } + + pub fn runBunPlugin( + jsc_vm: *VirtualMachine, + source_code_printer: *js_printer.BufferPrinter, + parse_result: *ParseResult, + ret: *ErrorableResolvedSource, + ) !void { + var printer = source_code_printer.*; + printer.ctx.reset(); + + defer printer.ctx.reset(); + // If we start transpiling in the middle of an existing transpilation session + // we will hit undefined memory bugs + // unless we disable resetting the store until we are done transpiling + const prev_disable_reset = js_ast.Stmt.Data.Store.disable_reset; + js_ast.Stmt.Data.Store.disable_reset = true; + js_ast.Expr.Data.Store.disable_reset = true; + + // flip the source code we use + // unless we're already transpiling a plugin + // that case could happen when + const was_printing_plugin = jsc_vm.is_printing_plugin; + const prev = jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache; + jsc_vm.is_printing_plugin = true; + defer { + js_ast.Stmt.Data.Store.disable_reset = prev_disable_reset; + js_ast.Expr.Data.Store.disable_reset = prev_disable_reset; + if (!was_printing_plugin) jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache = prev; + jsc_vm.is_printing_plugin = was_printing_plugin; + } + // we flip use_alternate_source_cache + if (!was_printing_plugin) jsc_vm.bundler.resolver.caches.fs.use_alternate_source_cache = !prev; + + // this is a bad idea, but it should work for now. + const original_name = parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name; + parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name = "globalThis.Bun.plugin"; + defer { + parse_result.ast.symbols[parse_result.ast.bun_plugin.ref.innerIndex()].original_name = original_name; + } + const hoisted_stmts = parse_result.ast.bun_plugin.hoisted_stmts.items; + + var parts = [1]js_ast.Part{ + js_ast.Part{ + .stmts = hoisted_stmts, + }, + }; + var ast_copy = parse_result.ast; + ast_copy.import_records = try jsc_vm.allocator.dupe(ImportRecord, ast_copy.import_records); + defer jsc_vm.allocator.free(ast_copy.import_records); + ast_copy.parts = &parts; + ast_copy.prepend_part = null; + var temporary_source = parse_result.source; + var source_name = try std.fmt.allocPrint(jsc_vm.allocator, "{s}.plugin.{s}", .{ temporary_source.path.text, temporary_source.path.name.ext[1..] }); + temporary_source.path = Fs.Path.init(source_name); + + var temp_parse_result = parse_result.*; + temp_parse_result.ast = ast_copy; + + try jsc_vm.bundler.linker.link( + temporary_source.path, + &temp_parse_result, + jsc_vm.origin, + .absolute_path, + false, + true, + ); + + _ = brk: { + defer source_code_printer.* = printer; + break :brk try jsc_vm.bundler.printWithSourceMapMaybe( + temp_parse_result.ast, + &temporary_source, + @TypeOf(&printer), + &printer, + .esm_ascii, + true, + SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings), + ); + }; + const wrote = printer.ctx.getWritten(); + + if (wrote.len > 0) { + if (comptime Environment.dump_source) + try dumpSource(temporary_source.path.text, &printer); + + var exception = [1]JSC.JSValue{JSC.JSValue.zero}; + const promise = JSC.JSModuleLoader.evaluate( + jsc_vm.global, + wrote.ptr, + wrote.len, + temporary_source.path.text.ptr, + temporary_source.path.text.len, + parse_result.source.path.text.ptr, + parse_result.source.path.text.len, + JSC.JSValue.jsUndefined(), + &exception, + ); + if (!exception[0].isEmpty()) { + ret.* = JSC.ErrorableResolvedSource.err( + error.JSErrorObject, + exception[0].asVoid(), + ); + return error.PluginError; + } + + if (!promise.isEmptyOrUndefinedOrNull()) { + if (promise.asInternalPromise()) |promise_value| { + jsc_vm.waitForPromise(promise_value); + + if (promise_value.status(jsc_vm.global.vm()) == .Rejected) { + ret.* = JSC.ErrorableResolvedSource.err( + error.JSErrorObject, + promise_value.result(jsc_vm.global.vm()).asVoid(), + ); + return error.PluginError; + } + } + } + } + } + pub fn normalizeSpecifier(jsc_vm: *VirtualMachine, slice_: string) string { + var slice = slice_; + if (slice.len == 0) return slice; + var was_http = false; + if (strings.hasPrefixComptime(slice, "https://")) { + slice = slice["https://".len..]; + was_http = true; + } else if (strings.hasPrefixComptime(slice, "http://")) { + slice = slice["http://".len..]; + was_http = true; + } + + if (strings.hasPrefix(slice, jsc_vm.origin.host)) { + slice = slice[jsc_vm.origin.host.len..]; + } else if (was_http) { + if (strings.indexOfChar(slice, '/')) |i| { + slice = slice[i..]; + } + } + + if (jsc_vm.origin.path.len > 1) { + if (strings.hasPrefix(slice, jsc_vm.origin.path)) { + slice = slice[jsc_vm.origin.path.len..]; + } + } + + if (jsc_vm.bundler.options.routes.asset_prefix_path.len > 0) { + if (strings.hasPrefix(slice, jsc_vm.bundler.options.routes.asset_prefix_path)) { + slice = slice[jsc_vm.bundler.options.routes.asset_prefix_path.len..]; + } + } + + return slice; + } + + pub export fn Bun__fetchBuiltinModule( + jsc_vm: *VirtualMachine, + globalObject: *JSC.JSGlobalObject, + specifier: *ZigString, + referrer: *ZigString, + ret: *ErrorableResolvedSource, + ) bool { + JSC.markBinding(@src()); + var log = logger.Log.init(jsc_vm.bundler.allocator); + defer log.deinit(); + if (ModuleLoader.fetchBuiltinModule(jsc_vm, specifier.slice(), &log, false) catch |err| { + if (err == error.AsyncModule) { + unreachable; + } + VirtualMachine.processFetchLog(globalObject, specifier.*, referrer.*, &log, ret, err); + return true; + }) |builtin| { + ret.* = ErrorableResolvedSource.ok(builtin); + return true; + } else { + return false; + } + } + + pub export fn Bun__transpileFile( + jsc_vm: *VirtualMachine, + globalObject: *JSC.JSGlobalObject, + specifier_ptr: *ZigString, + referrer: *ZigString, + ret: *ErrorableResolvedSource, + allow_promise: bool, + ) ?*anyopaque { + JSC.markBinding(@src()); + var log = logger.Log.init(jsc_vm.bundler.allocator); + defer log.deinit(); + var _specifier = specifier_ptr.toSlice(jsc_vm.allocator); + var referrer_slice = referrer.toSlice(jsc_vm.allocator); + defer _specifier.deinit(); + defer referrer_slice.deinit(); + var specifier = normalizeSpecifier(jsc_vm, _specifier.slice()); + const path = Fs.Path.init(specifier); + const loader = jsc_vm.bundler.options.loaders.get(path.name.ext) orelse options.Loader.js; + var promise: ?*JSC.JSInternalPromise = null; + ret.* = ErrorableResolvedSource.ok( + ModuleLoader.transpileSourceCode( + jsc_vm, + specifier, + referrer_slice.slice(), + path, + loader, + &log, + null, + ret, + if (allow_promise) &promise else null, + VirtualMachine.source_code_printer.?, + globalObject, + FetchFlags.transpile, + ) catch |err| { + if (err == error.AsyncModule) { + std.debug.assert(promise != null); + return promise; + } + + if (err == error.PluginError) { + return null; + } + VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer.*, &log, ret, err); + return null; + }, + ); + return promise; + } + + export fn Bun__runVirtualModule(globalObject: *JSC.JSGlobalObject, specifier_ptr: *ZigString) JSValue { + JSC.markBinding(@src()); + if (globalObject.bunVM().plugin_runner == null) return JSValue.zero; + + const specifier = specifier_ptr.slice(); + + if (!PluginRunner.couldBePlugin(specifier)) { + return JSValue.zero; + } + + const namespace = PluginRunner.extractNamespace(specifier); + const after_namespace = if (namespace.len == 0) + specifier + else + specifier[@minimum(namespace.len + 1, specifier.len)..]; + + return globalObject.runOnLoadPlugins(ZigString.init(namespace), ZigString.init(after_namespace), .bun) orelse return JSValue.zero; + } + + const shared_library_suffix = if (Environment.isMac) "dylib" else if (Environment.isLinux) "so" else ""; + + pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: string, log: *logger.Log, comptime disable_transpilying: bool) !?ResolvedSource { + if (jsc_vm.node_modules != null and strings.eqlComptime(specifier, JSC.bun_file_import_path)) { + // We kind of need an abstraction around this. + // Basically we should subclass JSC::SourceCode with: + // - hash + // - file descriptor for source input + // - file path + file descriptor for bytecode caching + // - separate bundles for server build vs browser build OR at least separate sections + const code = try jsc_vm.node_modules.?.readCodeAsStringSlow(jsc_vm.allocator); + + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(code), + .specifier = ZigString.init(JSC.bun_file_import_path), + .source_url = ZigString.init(JSC.bun_file_import_path[1..]), + .hash = 0, // TODO + }; + } else if (jsc_vm.node_modules == null and strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(Runtime.Runtime.sourceContentBun()), + .specifier = ZigString.init(Runtime.Runtime.Imports.Name), + .source_url = ZigString.init(Runtime.Runtime.Imports.Name), + .hash = Runtime.Runtime.versionHash(), + }; + } else if (HardcodedModule.Map.get(specifier)) |hardcoded| { + switch (hardcoded) { + // This is all complicated because the imports have to be linked and we want to run the printer on it + // so it consistently handles bundled imports + // we can't take the shortcut of just directly importing the file, sadly. + .@"bun:main" => { + if (comptime disable_transpilying) { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsc_vm.entry_point.source.contents), + .specifier = ZigString.init(std.mem.span(JSC.VirtualMachine.main_file_name)), + .source_url = ZigString.init(std.mem.span(JSC.VirtualMachine.main_file_name)), + .hash = 0, + }; + } + defer jsc_vm.transpiled_count += 1; + + var bundler = &jsc_vm.bundler; + var old = jsc_vm.bundler.log; + jsc_vm.bundler.log = log; + jsc_vm.bundler.linker.log = log; + jsc_vm.bundler.resolver.log = log; + defer { + jsc_vm.bundler.log = old; + jsc_vm.bundler.linker.log = old; + jsc_vm.bundler.resolver.log = old; + } + + var jsx = bundler.options.jsx; + jsx.parse = false; + var opts = js_parser.Parser.Options.init(jsx, .js); + opts.enable_bundling = false; + opts.transform_require_to_import = false; + opts.features.dynamic_require = true; + opts.can_import_from_bundle = bundler.options.node_modules_bundle != null; + opts.features.hot_module_reloading = false; + opts.features.react_fast_refresh = false; + opts.filepath_hash_for_hmr = 0; + opts.warn_about_unbundled_modules = false; + opts.macro_context = &jsc_vm.bundler.macro_context.?; + const main_ast = (bundler.resolver.caches.js.parse(jsc_vm.allocator, opts, bundler.options.define, bundler.log, &jsc_vm.entry_point.source) catch null) orelse { + return error.ParseError; + }; + var parse_result = ParseResult{ .source = jsc_vm.entry_point.source, .ast = main_ast, .loader = .js, .input_fd = null }; + var file_path = Fs.Path.init(bundler.fs.top_level_dir); + file_path.name.dir = bundler.fs.top_level_dir; + file_path.name.base = "bun:main"; + try bundler.linker.link( + file_path, + &parse_result, + jsc_vm.origin, + .absolute_path, + false, + true, + ); + var printer = JSC.VirtualMachine.source_code_printer.?.*; + var written: usize = undefined; + printer.ctx.reset(); + { + defer JSC.VirtualMachine.source_code_printer.?.* = printer; + written = try jsc_vm.bundler.printWithSourceMap( + parse_result, + @TypeOf(&printer), + &printer, + .esm_ascii, + SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings), + ); + } + + if (comptime Environment.dump_source) + try dumpSource(JSC.VirtualMachine.main_file_name, &printer); + + if (written == 0) { + return error.PrintingErrorWriteFailed; + } + + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, printer.ctx.written) catch unreachable), + .specifier = ZigString.init(std.mem.span(JSC.VirtualMachine.main_file_name)), + .source_url = ZigString.init(std.mem.span(JSC.VirtualMachine.main_file_name)), + .hash = 0, + }; + }, + .@"bun:jsc" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "bun-jsc.exports.js")), + .specifier = ZigString.init("bun:jsc"), + .source_url = ZigString.init("bun:jsc"), + .hash = 0, + }; + }, + .@"node:child_process" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "child_process.exports.js")), + .specifier = ZigString.init("node:child_process"), + .source_url = ZigString.init("node:child_process"), + .hash = 0, + }; + }, + .@"node:net" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "net.exports.js")), + .specifier = ZigString.init("node:net"), + .source_url = ZigString.init("node:net"), + .hash = 0, + }; + }, + .@"node:fs" => { + if (comptime Environment.isDebug) { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(strings.append(bun.default_allocator, jsModuleFromFile(jsc_vm.load_builtins_from_path, "fs.exports.js"), JSC.Node.fs.constants_string) catch unreachable), + .specifier = ZigString.init("node:fs"), + .source_url = ZigString.init("node:fs"), + .hash = 0, + }; + } + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(@embedFile("fs.exports.js") ++ JSC.Node.fs.constants_string), + .specifier = ZigString.init("node:fs"), + .source_url = ZigString.init("node:fs"), + .hash = 0, + }; + }, + .@"node:buffer" => return jsSyntheticModule(.@"node:buffer"), + .@"node:string_decoder" => return jsSyntheticModule(.@"node:string_decoder"), + .@"node:module" => return jsSyntheticModule(.@"node:module"), + .@"node:events" => return jsSyntheticModule(.@"node:events"), + .@"node:process" => return jsSyntheticModule(.@"node:process"), + .@"node:tty" => return jsSyntheticModule(.@"node:tty"), + .@"node:stream" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "streams.exports.js")), + .specifier = ZigString.init("node:stream"), + .source_url = ZigString.init("node:stream"), + .hash = 0, + }; + }, + + .@"node:fs/promises" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(@embedFile("fs_promises.exports.js") ++ JSC.Node.fs.constants_string), + .specifier = ZigString.init("node:fs/promises"), + .source_url = ZigString.init("node:fs/promises"), + .hash = 0, + }; + }, + .@"node:path" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "path.exports.js")), + .specifier = ZigString.init("node:path"), + .source_url = ZigString.init("node:path"), + .hash = 0, + }; + }, + .@"node:path/win32" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "path-win32.exports.js")), + .specifier = ZigString.init("node:path/win32"), + .source_url = ZigString.init("node:path/win32"), + .hash = 0, + }; + }, + .@"node:path/posix" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "path-posix.exports.js")), + .specifier = ZigString.init("node:path/posix"), + .source_url = ZigString.init("node:path/posix"), + .hash = 0, + }; + }, + + .@"node:os" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(jsModuleFromFile(jsc_vm.load_builtins_from_path, "os.exports.js")), + .specifier = ZigString.init("node:os"), + .source_url = ZigString.init("node:os"), + .hash = 0, + }; + }, + .@"bun:ffi" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + "export const FFIType = " ++ + JSC.FFI.ABIType.map_to_js_object ++ + ";\n\n" ++ + "export const suffix = '" ++ shared_library_suffix ++ "';\n\n" ++ + @embedFile("ffi.exports.js") ++ + "\n", + ), + .specifier = ZigString.init("bun:ffi"), + .source_url = ZigString.init("bun:ffi"), + .hash = 0, + }; + }, + .@"detect-libc" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, @embedFile(if (Environment.isLinux) "detect-libc.linux.js" else "detect-libc.js")), + ), + .specifier = ZigString.init("detect-libc"), + .source_url = ZigString.init("detect-libc"), + .hash = 0, + }; + }, + .@"node:url" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "url.exports.js")), + ), + .specifier = ZigString.init("node:url"), + .source_url = ZigString.init("node:url"), + .hash = 0, + }; + }, + .@"node:assert" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "assert.exports.js")), + ), + .specifier = ZigString.init("node:assert"), + .source_url = ZigString.init("node:assert"), + .hash = 0, + }; + }, + .@"bun:sqlite" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./bindings/sqlite/sqlite.exports.js")), + ), + .specifier = ZigString.init("bun:sqlite"), + .source_url = ZigString.init("bun:sqlite"), + .hash = 0, + }; + }, + .@"node:perf_hooks" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./perf_hooks.exports.js")), + ), + .specifier = ZigString.init("node:perf_hooks"), + .source_url = ZigString.init("node:perf_hooks"), + .hash = 0, + }; + }, + .@"ws" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./ws.exports.js")), + ), + .specifier = ZigString.init("ws"), + .source_url = ZigString.init("ws"), + .hash = 0, + }; + }, + .@"node:timers" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./node_timers.exports.js")), + ), + .specifier = ZigString.init("node:timers"), + .source_url = ZigString.init("node:timers"), + .hash = 0, + }; + }, + .@"node:timers/promises" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./node_timers_promises.exports.js")), + ), + .specifier = ZigString.init("node:timers/promises"), + .source_url = ZigString.init("node:timers/promises"), + .hash = 0, + }; + }, + .@"node:stream/web" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./node_streams_web.exports.js")), + ), + .specifier = ZigString.init("node:stream/web"), + .source_url = ZigString.init("node:stream/web"), + .hash = 0, + }; + }, + .@"node:stream/consumer" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./node_streams_consumer.exports.js")), + ), + .specifier = ZigString.init("node:stream/consumer"), + .source_url = ZigString.init("node:stream/consumer"), + .hash = 0, + }; + }, + .@"undici" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./undici.exports.js")), + ), + .specifier = ZigString.init("undici"), + .source_url = ZigString.init("undici"), + .hash = 0, + }; + }, + .@"node:http" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./http.exports.js")), + ), + .specifier = ZigString.init("node:http"), + .source_url = ZigString.init("node:http"), + .hash = 0, + }; + }, + .@"node:https" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./https.exports.js")), + ), + .specifier = ZigString.init("node:https"), + .source_url = ZigString.init("node:https"), + .hash = 0, + }; + }, + .@"depd" => { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init( + @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "./depd.exports.js")), + ), + .specifier = ZigString.init("depd"), + .source_url = ZigString.init("depd"), + .hash = 0, + }; + }, + } + } else if (specifier.len > js_ast.Macro.namespaceWithColon.len and + strings.eqlComptimeIgnoreLen(specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon)) + { + if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(specifier))) |entry| { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(entry.source.contents), + .specifier = ZigString.init(specifier), + .source_url = ZigString.init(specifier), + .hash = 0, + }; + } + } + + return null; + } + + export fn Bun__transpileVirtualModule( + globalObject: *JSC.JSGlobalObject, + specifier_ptr: *ZigString, + referrer_ptr: *ZigString, + source_code: *ZigString, + loader_: Api.Loader, + ret: *ErrorableResolvedSource, + ) bool { + JSC.markBinding(@src()); + const jsc_vm = globalObject.bunVM(); + std.debug.assert(jsc_vm.plugin_runner != null); + + var specifier_slice = specifier_ptr.toSlice(jsc_vm.allocator); + const specifier = specifier_slice.slice(); + defer specifier_slice.deinit(); + var source_code_slice = source_code.toSlice(jsc_vm.allocator); + defer source_code_slice.deinit(); + var referrer_slice = referrer_ptr.toSlice(jsc_vm.allocator); + defer referrer_slice.deinit(); + + var virtual_source = logger.Source.initPathString(specifier, source_code_slice.slice()); + var log = logger.Log.init(jsc_vm.allocator); + const path = Fs.Path.init(specifier); + + const loader = if (loader_ != ._none) + options.Loader.fromString(@tagName(loader_)).? + else + jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: { + if (strings.eqlLong(specifier, jsc_vm.main, true)) { + break :brk options.Loader.js; + } + + break :brk options.Loader.file; + }; + + defer log.deinit(); + ret.* = ErrorableResolvedSource.ok( + ModuleLoader.transpileSourceCode( + jsc_vm, + specifier, + referrer_slice.slice(), + path, + options.Loader.fromString(@tagName(loader)).?, + &log, + &virtual_source, + ret, + null, + VirtualMachine.source_code_printer.?, + globalObject, + FetchFlags.transpile, + ) catch |err| { + if (err == error.PluginError) { + return true; + } + VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer_ptr.*, &log, ret, err); + return true; + }, + ); + return true; + } + + comptime { + _ = Bun__transpileVirtualModule; + _ = Bun__runVirtualModule; + _ = Bun__transpileFile; + _ = Bun__fetchBuiltinModule; + _ = Bun__getDefaultLoader; + } +}; + +pub const FetchFlags = enum { + transpile, + print_source, + print_source_and_clone, + + pub fn disableTranspiling(this: FetchFlags) bool { + return this != .transpile; + } +}; + +const SavedSourceMap = JSC.SavedSourceMap; + +pub const HardcodedModule = enum { + @"bun:ffi", + @"bun:jsc", + @"bun:main", + @"bun:sqlite", + @"depd", + @"detect-libc", + @"node:assert", + @"node:buffer", + @"node:child_process", + @"node:events", + @"node:fs", + @"node:fs/promises", + @"node:http", + @"node:https", + @"node:module", + @"node:net", + @"node:os", + @"node:path", + @"node:path/posix", + @"node:path/win32", + @"node:perf_hooks", + @"node:process", + @"node:stream", + @"node:stream/consumer", + @"node:stream/web", + @"node:string_decoder", + @"node:timers", + @"node:timers/promises", + @"node:tty", + @"node:url", + @"undici", + @"ws", + /// Already resolved modules go in here. + /// This does not remap the module name, it is just a hash table. + /// Do not put modules that have aliases in here + /// Put those in Aliases + pub const Map = bun.ComptimeStringMap( + HardcodedModule, + .{ + .{ "buffer", HardcodedModule.@"node:buffer" }, + .{ "bun:ffi", HardcodedModule.@"bun:ffi" }, + .{ "bun:jsc", HardcodedModule.@"bun:jsc" }, + .{ "bun:main", HardcodedModule.@"bun:main" }, + .{ "bun:sqlite", HardcodedModule.@"bun:sqlite" }, + .{ "depd", HardcodedModule.@"depd" }, + .{ "detect-libc", HardcodedModule.@"detect-libc" }, + .{ "node:assert", HardcodedModule.@"node:assert" }, + .{ "node:buffer", HardcodedModule.@"node:buffer" }, + .{ "node:child_process", HardcodedModule.@"node:child_process" }, + .{ "node:events", HardcodedModule.@"node:events" }, + .{ "node:fs", HardcodedModule.@"node:fs" }, + .{ "node:fs/promises", HardcodedModule.@"node:fs/promises" }, + .{ "node:http", HardcodedModule.@"node:http" }, + .{ "node:https", HardcodedModule.@"node:https" }, + .{ "node:module", HardcodedModule.@"node:module" }, + .{ "node:net", HardcodedModule.@"node:net" }, + .{ "node:os", HardcodedModule.@"node:os" }, + .{ "node:path", HardcodedModule.@"node:path" }, + .{ "node:path/posix", HardcodedModule.@"node:path/posix" }, + .{ "node:path/win32", HardcodedModule.@"node:path/win32" }, + .{ "node:perf_hooks", HardcodedModule.@"node:perf_hooks" }, + .{ "node:process", HardcodedModule.@"node:process" }, + .{ "node:stream", HardcodedModule.@"node:stream" }, + .{ "node:stream/consumer", HardcodedModule.@"node:stream/consumer" }, + .{ "node:stream/web", HardcodedModule.@"node:stream/web" }, + .{ "node:string_decoder", HardcodedModule.@"node:string_decoder" }, + .{ "node:timers", HardcodedModule.@"node:timers" }, + .{ "node:timers/promises", HardcodedModule.@"node:timers/promises" }, + .{ "node:tty", HardcodedModule.@"node:tty" }, + .{ "node:url", HardcodedModule.@"node:url" }, + .{ "undici", HardcodedModule.@"undici" }, + .{ "ws", HardcodedModule.@"ws" }, + }, + ); + pub const Aliases = bun.ComptimeStringMap( + string, + .{ + .{ "assert", "node:assert" }, + .{ "buffer", "node:buffer" }, + .{ "bun", "bun" }, + .{ "bun:ffi", "bun:ffi" }, + .{ "bun:jsc", "bun:jsc" }, + .{ "bun:sqlite", "bun:sqlite" }, + .{ "bun:wrap", "bun:wrap" }, + .{ "child_process", "node:child_process" }, + .{ "depd", "depd" }, + .{ "detect-libc", "detect-libc" }, + .{ "detect-libc/lib/detect-libc.js", "detect-libc" }, + .{ "events", "node:events" }, + .{ "ffi", "bun:ffi" }, + .{ "fs", "node:fs" }, + .{ "fs/promises", "node:fs/promises" }, + .{ "http", "node:http" }, + .{ "https", "node:https" }, + .{ "module", "node:module" }, + .{ "net", "node:net" }, + .{ "node:assert", "node:assert" }, + .{ "node:buffer", "node:buffer" }, + .{ "node:child_process", "node:child_process" }, + .{ "node:events", "node:events" }, + .{ "node:fs", "node:fs" }, + .{ "node:fs/promises", "node:fs/promises" }, + .{ "node:http", "node:http" }, + .{ "node:https", "node:https" }, + .{ "node:module", "node:module" }, + .{ "node:net", "node:net" }, + .{ "node:os", "node:os" }, + .{ "node:path", "node:path" }, + .{ "node:path/posix", "node:path/posix" }, + .{ "node:path/win32", "node:path/win32" }, + .{ "node:perf_hooks", "node:perf_hooks" }, + .{ "node:process", "node:process" }, + .{ "node:stream", "node:stream" }, + .{ "node:stream/consumer", "node:stream/consumer" }, + .{ "node:stream/web", "node:stream/web" }, + .{ "node:string_decoder", "node:string_decoder" }, + .{ "node:timers", "node:timers" }, + .{ "node:timers/promises", "node:timers/promises" }, + .{ "node:tty", "node:tty" }, + .{ "node:url", "node:url" }, + .{ "os", "node:os" }, + .{ "path", "node:path" }, + .{ "path/posix", "node:path/posix" }, + .{ "path/win32", "node:path/win32" }, + .{ "perf_hooks", "node:perf_hooks" }, + .{ "process", "node:process" }, + .{ "stream", "node:stream" }, + .{ "stream/consumer", "node:stream/consumer" }, + .{ "stream/web", "node:stream/web" }, + .{ "string_decoder", "node:string_decoder" }, + .{ "timers", "node:timers" }, + .{ "timers/promises", "node:timers/promises" }, + .{ "tty", "node:tty" }, + .{ "undici", "undici" }, + .{ "url", "node:url" }, + .{ "ws", "ws" }, + .{ "ws/lib/websocket", "ws" }, + }, + ); +}; + +pub const DisabledModule = bun.ComptimeStringMap( + void, + .{ + .{"node:tls"}, + .{"node:worker_threads"}, + .{"tls"}, + .{"worker_threads"}, + }, +); |