diff options
-rw-r--r-- | src/bun.js/javascript.zig | 3 | ||||
-rw-r--r-- | src/bun.js/module_loader.zig | 123 | ||||
-rw-r--r-- | src/bun_js.zig | 8 | ||||
-rw-r--r-- | src/bundler.zig | 40 | ||||
-rw-r--r-- | src/bundler/entry_points.zig | 4 | ||||
-rw-r--r-- | src/cli.zig | 1 | ||||
-rw-r--r-- | src/cli/run_command.zig | 23 | ||||
-rw-r--r-- | test/cli/install/bun-run.test.ts | 72 |
8 files changed, 110 insertions, 164 deletions
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index d8043ca44..c8b23d7c3 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -1210,7 +1210,7 @@ pub const VirtualMachine = struct { ) anyerror!ResolvedSource { std.debug.assert(VirtualMachine.isLoaded()); - if (try ModuleLoader.fetchBuiltinModule(jsc_vm, _specifier, log, comptime flags.disableTranspiling())) |builtin| { + if (try ModuleLoader.fetchBuiltinModule(jsc_vm, _specifier)) |builtin| { return builtin; } var display_specifier = _specifier.toUTF8(bun.default_allocator); @@ -1782,6 +1782,7 @@ pub const VirtualMachine = struct { } pub fn reloadEntryPoint(this: *VirtualMachine, entry_path: []const u8) !*JSInternalPromise { + this.has_loaded = false; this.main = entry_path; try this.entry_point.generate( diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index ca6071d0d..c66649d3c 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -533,9 +533,9 @@ pub const RuntimeTranspilerStore = struct { var printer = source_code_printer.?.*; printer.ctx.reset(); - const written = brk: { + { defer source_code_printer.?.* = printer; - break :brk bundler.printWithSourceMap( + _ = bundler.printWithSourceMap( parse_result, @TypeOf(&printer), &printer, @@ -545,11 +545,6 @@ pub const RuntimeTranspilerStore = struct { this.parse_error = err; return; }; - }; - - if (written == 0) { - this.parse_error = error.PrintingErrorWriteFailed; - return; } if (comptime Environment.dump_source) { @@ -1235,10 +1230,7 @@ pub const ModuleLoader = struct { SavedSourceMap.SourceMapHandler.init(&jsc_vm.source_mappings), ); }; - - if (written == 0) { - return error.PrintingErrorWriteFailed; - } + _ = written; if (comptime Environment.dump_source) { try dumpSource(specifier, &printer); @@ -1653,7 +1645,6 @@ pub const ModuleLoader = struct { .hash = 0, }; } - return error.PrintingErrorWriteFailed; } if (comptime Environment.dump_source) { @@ -2025,7 +2016,11 @@ pub const ModuleLoader = struct { JSC.markBinding(@src()); var log = logger.Log.init(jsc_vm.bundler.allocator); defer log.deinit(); - if (ModuleLoader.fetchBuiltinModule(jsc_vm, specifier.*, &log, false) catch |err| { + + if (ModuleLoader.fetchBuiltinModule( + jsc_vm, + specifier.*, + ) catch |err| { if (err == error.AsyncModule) { unreachable; } @@ -2156,7 +2151,7 @@ pub const ModuleLoader = struct { return globalObject.runOnLoadPlugins(bun.String.init(namespace), bun.String.init(after_namespace), .bun) orelse return JSValue.zero; } - pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: bun.String, log: *logger.Log, comptime disable_transpilying: bool) !?ResolvedSource { + pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: bun.String) !?ResolvedSource { if (jsc_vm.node_modules != null and specifier.eqlComptime(JSC.bun_file_import_path)) { // We kind of need an abstraction around this. // Basically we should subclass JSC::SourceCode with: @@ -2183,9 +2178,6 @@ pub const ModuleLoader = struct { }; } else if (HardcodedModule.Map.getWithEql(specifier, bun.String.eqlComptime)) |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" => { defer { if (jsc_vm.worker) |worker| { @@ -2193,104 +2185,9 @@ pub const ModuleLoader = struct { } } - if (comptime disable_transpilying) { - return ResolvedSource{ - .allocator = null, - .source_code = bun.String.init(jsc_vm.entry_point.source.contents), - .specifier = bun.String.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), - .source_url = ZigString.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), - .hash = 0, - }; - } - defer jsc_vm.transpiled_count += 1; - var arena: bun.ArenaAllocator = undefined; - - // Attempt to reuse the Arena from the parser when we can - // This code is potentially re-entrant, so only one Arena can be reused at a time - // That's why we have to check if the Arena is null - // - // Using an Arena here is a significant memory optimization when loading many files - if (jsc_vm.parser_arena) |shared| { - arena = shared; - jsc_vm.parser_arena = null; - _ = arena.reset(.retain_capacity); - } else { - arena = bun.ArenaAllocator.init(jsc_vm.allocator); - } - - defer { - if (jsc_vm.parser_arena == null) { - jsc_vm.parser_arena = arena; - } else { - arena.deinit(); - } - } - - 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_legacy_bundling = false; - opts.legacy_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.top_level_await = true; - opts.features.react_fast_refresh = false; - opts.features.minify_identifiers = bundler.options.minify_identifiers; - opts.features.minify_syntax = bundler.options.minify_syntax; - 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(arena.allocator(), opts, bundler.options.define, bundler.log, &jsc_vm.entry_point.source) catch null) orelse { - return error.ParseError; - }).ast; - 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 = bun.String.createLatin1(printer.ctx.written), + .source_code = bun.String.create(jsc_vm.entry_point.source.contents), .specifier = specifier, .source_url = ZigString.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), .hash = 0, diff --git a/src/bun_js.zig b/src/bun_js.zig index 79f1c2596..6bbf093ae 100644 --- a/src/bun_js.zig +++ b/src/bun_js.zig @@ -129,8 +129,7 @@ pub const Run = struct { vm.global.vm().holdAPILock(&run, callback); } - pub fn boot(ctx_: Command.Context, file: std.fs.File, entry_path: string) !void { - _ = file; + pub fn boot(ctx_: Command.Context, entry_path: string) !void { var ctx = ctx_; JSC.markBinding(@src()); bun.JSC.initialize(); @@ -241,6 +240,11 @@ pub const Run = struct { if (this.ctx.debug.hot_reload != .none) { JSC.HotReloader.enableHotModuleReloading(vm); } + + if (strings.eqlComptime(this.entry_path, ".") and vm.bundler.fs.top_level_dir.len > 0) { + this.entry_path = vm.bundler.fs.top_level_dir; + } + if (vm.loadEntryPoint(this.entry_path)) |promise| { if (promise.status(vm.global.vm()) == .Rejected) { vm.runErrorHandler(promise.result(vm.global.vm()), null); diff --git a/src/bundler.zig b/src/bundler.zig index 87e2aecc1..3f744b8fc 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -668,45 +668,7 @@ pub const Bundler = struct { } } - // if you pass just a directory, activate the router configured for the pages directory - // for now: - // - "." is not supported - // - multiple pages directories is not supported - if (!this.options.routes.routes_enabled and this.options.entry_points.len == 1 and !this.options.serve) { - - // When inferring: - // - pages directory with a file extension is not supported. e.g. "pages.app/" won't work. - // This is a premature optimization to avoid this magical auto-detection we do here from meaningfully increasing startup time if you're just passing a file - // readDirInfo is a recursive lookup, top-down instead of bottom-up. It opens each folder handle and potentially reads the package.jsons - // So it is not fast! Unless it's already cached. - var paths = [_]string{std.mem.trimLeft(u8, this.options.entry_points[0], "./")}; - if (std.mem.indexOfScalar(u8, paths[0], '.') == null) { - var pages_dir_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - var entry = this.fs.absBuf(&paths, &pages_dir_buf); - - if (std.fs.path.extension(entry).len == 0) { - bun.constStrToU8(entry).ptr[entry.len] = '/'; - - // Only throw if they actually passed in a route config and the directory failed to load - var dir_info_ = this.resolver.readDirInfo(entry) catch return; - var dir_info = dir_info_ orelse return; - - this.options.routes.dir = dir_info.abs_path; - this.options.routes.extensions = options.RouteConfig.DefaultExtensions[0..]; - this.options.routes.routes_enabled = true; - this.router = try Router.init(this.fs, this.allocator, this.options.routes); - try this.router.?.loadRoutes( - this.log, - dir_info, - Resolver, - &this.resolver, - this.fs.top_level_dir, - ); - this.router.?.routes.client_framework_enabled = this.options.isFrontendFrameworkEnabled(); - return; - } - } - } else if (this.options.routes.routes_enabled) { + if (this.options.routes.routes_enabled) { var dir_info_ = try this.resolver.readDirInfo(this.options.routes.dir); var dir_info = dir_info_ orelse return error.MissingRoutesDir; diff --git a/src/bundler/entry_points.zig b/src/bundler/entry_points.zig index cf961817a..0d7e1a701 100644 --- a/src/bundler/entry_points.zig +++ b/src/bundler/entry_points.zig @@ -183,7 +183,7 @@ pub const ServerEntryPoint = struct { if (is_hot_reload_enabled) { break :brk try std.fmt.allocPrint( allocator, - \\//Auto-generated file + \\// @bun \\var hmrSymbol = Symbol.for("BunServerHMR"); \\import * as start from '{s}{s}'; \\var entryNamespace = start; @@ -218,7 +218,7 @@ pub const ServerEntryPoint = struct { } break :brk try std.fmt.allocPrint( allocator, - \\//Auto-generated file + \\// @bun \\import * as start from '{s}{s}'; \\var entryNamespace = start; \\if (typeof entryNamespace?.then === 'function') {{ diff --git a/src/cli.zig b/src/cli.zig index 1debd9254..d19ede800 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -1564,7 +1564,6 @@ pub const Command = struct { BunJS.Run.boot( ctx.*, - file, absolute_script_path, ) catch |err| { if (Output.enable_ansi_colors) { diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index 44b3935a3..6a559b342 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -872,6 +872,26 @@ pub const RunCommand = struct { const passthrough = ctx.passthrough; const force_using_bun = ctx.debug.run_in_bun; + // This doesn't cover every case + if ((script_name_to_search.len == 1 and script_name_to_search[0] == '.') or + (script_name_to_search.len == 2 and @as(u16, @bitCast(script_name_to_search[0..2].*)) == @as(u16, @bitCast([_]u8{ '.', '/' })))) + { + Run.boot(ctx, ".") catch |err| { + if (Output.enable_ansi_colors) { + ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; + } else { + ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; + } + + Output.prettyErrorln("<r><red>error<r>: Failed to run <b>{s}<r> due to error <b>{s}<r>", .{ + script_name_to_search, + @errorName(err), + }); + Global.exit(1); + }; + return true; + } + if (log_errors or force_using_bun) { if (script_name_to_search.len > 0) { possibly_open_with_bun_js: { @@ -952,8 +972,7 @@ pub const RunCommand = struct { } Global.configureAllocator(.{ .long_running = true }); - - Run.boot(ctx, file, ctx.allocator.dupe(u8, file_path) catch unreachable) catch |err| { + Run.boot(ctx, ctx.allocator.dupe(u8, file_path) catch unreachable) catch |err| { if (Output.enable_ansi_colors) { ctx.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; } else { diff --git a/test/cli/install/bun-run.test.ts b/test/cli/install/bun-run.test.ts index 95f33ebb8..4180c9716 100644 --- a/test/cli/install/bun-run.test.ts +++ b/test/cli/install/bun-run.test.ts @@ -1,6 +1,6 @@ -import { file, spawn } from "bun"; -import { afterEach, beforeEach, expect, it } from "bun:test"; -import { bunExe, bunEnv as env } from "harness"; +import { file, spawn, spawnSync } from "bun"; +import { afterEach, beforeEach, expect, it, describe } from "bun:test"; +import { bunEnv, bunExe, bunEnv as env } from "harness"; import { mkdtemp, realpath, rm, writeFile } from "fs/promises"; import { tmpdir } from "os"; import { join } from "path"; @@ -9,12 +9,76 @@ import { readdirSorted } from "./dummy.registry"; let run_dir: string; beforeEach(async () => { - run_dir = await realpath(await mkdtemp(join(tmpdir(), "bun-run.test"))); + run_dir = await realpath( + await mkdtemp(join(tmpdir(), "bun-run.test." + Math.trunc(Math.random() * 9999999).toString(32))), + ); }); afterEach(async () => { await rm(run_dir, { force: true, recursive: true }); }); +for (let withRun of [false, true]) { + describe(withRun ? "bun run" : "bun", () => { + describe("should work with .", () => { + it("respecting 'main' field", async () => { + await writeFile(join(run_dir, "test.js"), "console.log('Hello, world!');"); + await writeFile( + join(run_dir, "package.json"), + JSON.stringify({ + name: "test", + version: "0.0.0", + main: "test.js", + }), + ); + const { stdout, stderr, exitCode } = spawnSync({ + cmd: [bunExe(), withRun ? "run" : "", "."].filter(Boolean), + cwd: run_dir, + env: bunEnv, + }); + + expect(stderr.toString()).toBe(""); + expect(stdout.toString()).toBe("Hello, world!\n"); + expect(exitCode).toBe(0); + }); + + it("falling back to index", async () => { + await writeFile(join(run_dir, "index.ts"), "console.log('Hello, world!');"); + await writeFile( + join(run_dir, "package.json"), + JSON.stringify({ + name: "test", + version: "0.0.0", + }), + ); + + const { stdout, stderr, exitCode } = spawnSync({ + cmd: [bunExe(), withRun ? "run" : "", "."].filter(Boolean), + cwd: run_dir, + env: bunEnv, + }); + + expect(stderr.toString()).toBe(""); + expect(stdout.toString()).toBe("Hello, world!\n"); + expect(exitCode).toBe(0); + }); + + it("falling back to index with no package.json", async () => { + await writeFile(join(run_dir, "index.ts"), "console.log('Hello, world!');"); + + const { stdout, stderr, exitCode } = spawnSync({ + cmd: [bunExe(), withRun ? "run" : "", "."].filter(Boolean), + cwd: run_dir, + env: bunEnv, + }); + + expect(stderr.toString()).toBe(""); + expect(stdout.toString()).toBe("Hello, world!\n"); + expect(exitCode).toBe(0); + }); + }); + }); +} + it("should download dependency to run local file", async () => { await writeFile( join(run_dir, "test.js"), |