aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-07-30 07:54:17 -0700
committerGravatar GitHub <noreply@github.com> 2023-07-30 07:54:17 -0700
commit54a2d89bd277ef6ddbe95b322e6b8e1a308e059e (patch)
treeee784bf7bcd497480795fbb6e8e0dcad7e42095a
parent0ea59b4c93e2fa285955e9129b3f219b07e30779 (diff)
downloadbun-54a2d89bd277ef6ddbe95b322e6b8e1a308e059e.tar.gz
bun-54a2d89bd277ef6ddbe95b322e6b8e1a308e059e.tar.zst
bun-54a2d89bd277ef6ddbe95b322e6b8e1a308e059e.zip
Support `bun .` to run the entry point (#3891)
* Support `bun .` * Fix tests --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r--src/bun.js/javascript.zig3
-rw-r--r--src/bun.js/module_loader.zig123
-rw-r--r--src/bun_js.zig8
-rw-r--r--src/bundler.zig40
-rw-r--r--src/bundler/entry_points.zig4
-rw-r--r--src/cli.zig1
-rw-r--r--src/cli/run_command.zig23
-rw-r--r--test/cli/install/bun-run.test.ts72
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"),