Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/bunfig/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ fn load_global_bunfig(cmd: CommandTag, ctx: Context<'_>) -> Result<(), crate::Er
Ok(())
}

pub fn load_config_path(
fn load_config_path(
cmd: CommandTag,
auto_loaded: bool,
config_path: &ZStr,
Expand Down Expand Up @@ -249,3 +249,18 @@ pub fn load_config_with_cmd_args(
) -> Result<(), crate::Error> {
load_config(cmd, args.option(b"--config"), ctx)
}

/// `bun run`, `bun repl`, the `node` shim and compiled executables load `./bunfig.toml` here.
pub fn load_cwd_config_or_exit(ctx: Context<'_>) {
if ctx.debug.loaded_bunfig {
return;
}
if let Err(err) = load_config_path(
CommandTag::RunCommand,
true,
bun_core::zstr!("bunfig.toml"),
ctx,
) {
report_bunfig_load_failure(ctx.log, err);
}
}
2 changes: 1 addition & 1 deletion src/bunfig/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ pub mod arguments;
pub mod bunfig;
pub mod error;

pub use arguments::{load_config, load_config_path, load_config_with_cmd_args};
pub use arguments::{load_config, load_config_with_cmd_args, load_cwd_config_or_exit};
pub use error::{Error, Result};
2 changes: 1 addition & 1 deletion src/runtime/cli/Arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ pub(crate) static Bun__Node__UseSystemCA: core::sync::atomic::AtomicBool =
// their private helpers moved to `bun_bunfig::arguments` so `bun_install` can
// call them without a tier-6 dependency. Re-export here so existing
// `crate::cli::arguments::load_config*` callers are unaffected.
pub use bun_bunfig::arguments::{load_config_path, load_config_with_cmd_args};
pub use bun_bunfig::arguments::{load_config_with_cmd_args, load_cwd_config_or_exit};

/// node aliases `-pe` to `--print --eval` as a whole token (node_options.cc):
/// it can't be a short in either runtime, being ambiguous with `-p` carrying
Expand Down
10 changes: 1 addition & 9 deletions src/runtime/cli/repl_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,7 @@ impl ReplCommand {
ctx: Command::Context<'_>,
repl: &mut Repl<'r>,
) -> Result<(), crate::Error> {
// Load bunfig if not already loaded
if !ctx.debug.loaded_bunfig {
Arguments::load_config_path(
Command::Tag::RunCommand,
true,
bun_core::zstr!("bunfig.toml"),
ctx,
)?;
}
Arguments::load_cwd_config_or_exit(ctx);

jsc::initialize(jsc::InitializeOptions {
eval_mode: true,
Expand Down
31 changes: 5 additions & 26 deletions src/runtime/cli/run_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use bun_which::which;

use crate::cli;
use crate::cli::arguments;
use crate::cli::command::{ContextData, Tag as CommandTag};
use crate::cli::command::ContextData;
use crate::cli::shell_completions::ShellCompletions;

bun_core::declare_scope!(RUN_LOG, visible);
Expand Down Expand Up @@ -936,14 +936,7 @@ Full documentation is available at <magenta>https://bun.com/docs/cli/run<r>
entry_path: Box<[u8]>,
loader: Option<Loader>,
) -> crate::Result<()> {
if !ctx.debug.loaded_bunfig {
arguments::load_config_path(
CommandTag::RunCommand,
true,
bun_core::zstr!("bunfig.toml"),
ctx,
)?;
}
arguments::load_cwd_config_or_exit(ctx);

// The shell does not need to initialize JSC (saves 1-3ms).
if strings::has_suffix_comptime(&entry_path, b".sh") {
Expand Down Expand Up @@ -1136,13 +1129,8 @@ Full documentation is available at <magenta>https://bun.com/docs/cli/run<r>

// Load bunfig.toml unless disabled by compile flags. Config loading
// with execArgv is handled earlier in `Command::start` via `init()`.
if !ctx.debug.loaded_bunfig && !graph.flags.contains(GraphFlags::DISABLE_AUTOLOAD_BUNFIG) {
arguments::load_config_path(
CommandTag::RunCommand,
true,
bun_core::zstr!("bunfig.toml"),
ctx,
)?;
if !graph.flags.contains(GraphFlags::DISABLE_AUTOLOAD_BUNFIG) {
arguments::load_cwd_config_or_exit(ctx);
}

// layering — `Options::graph` is the resolver's trait object
Expand Down Expand Up @@ -2326,16 +2314,7 @@ impl RunCommand {
}
}

if !ctx.debug.loaded_bunfig {
// `Arguments::load_config_path` — loads global bunfig (if the
// command opts in via `read_global_config`) then `bunfig.toml`.
let _ = arguments::load_config_path(
CommandTag::RunCommand,
true,
bun_core::zstr!("bunfig.toml"),
ctx,
);
}
arguments::load_cwd_config_or_exit(ctx);
Comment thread
robobun marked this conversation as resolved.

// ── try fast run (file exists & not a dir → boot VM) ────────────────
if try_fast_run && Self::maybe_open_with_bun_js(ctx, target_name) {
Expand Down
35 changes: 34 additions & 1 deletion test/bundler/bundler_compile_autoload.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe } from "bun:test";
import { describe, expect } from "bun:test";
import { itBundled } from "./expectBundled";

// Not describe.concurrent: the backend:"cli" cases each spawn a full
Expand Down Expand Up @@ -152,6 +152,39 @@ console.log("PRELOAD");
},
});

// A bunfig.toml that does not parse stops the executable with the same report as `bun run`.
itBundled("compile/AutoloadBunfigDoesNotParse", {
compile: true,
files: {
"/entry.ts": /* js */ `
console.log("ENTRY");
`,
},
runtimeFiles: {
"/bunfig.toml": `[run]\nbun = "yes"\n`,
},
run: {
exitCode: 1,
setCwd: true,
// With flags in BUN_OPTIONS the executable loads the config during argument parsing instead.
env: { BUN_OPTIONS: "" },
validate({ stdout, stderr }) {
expect({ stdout, stderr }).toEqual({
stdout: "",
stderr: [
'2 | bun = "yes"',
" ^",
Comment thread
robobun marked this conversation as resolved.
"error: Expected boolean",
" at bunfig.toml:2:7",
"",
"Invalid Bunfig: failed to load bunfig",
"",
].join("\n"),
});
},
},
});

// Test CLI backend with autoloadDotenv: false
itBundled("compile/AutoloadDotenvDisabledCLI", {
compile: {
Expand Down
81 changes: 80 additions & 1 deletion test/cli/install/bun-run-bunfig.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test";
import { realpathSync } from "fs";
import { realpathSync, symlinkSync } from "fs";
import { bunEnv, bunExe, isWindows, tempDir, toTOMLString } from "harness";
import { join as pathJoin } from "node:path";

Expand Down Expand Up @@ -248,3 +248,82 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
expect(result.success).toBeTrue();
});
});

// These entry points read ./bunfig.toml after argument parsing. A file that does not parse
// stops them with the same report as `bun <file>` and `bun run --filter`.
describe.concurrent("bunfig.toml that does not parse", () => {
const configs: [problem: string, bunfig: string, stderr: string[]][] = [
[
"a TOML syntax error",
"[install]\nregistry =\n",
[
"2 | registry =",
" ^",
"error: Missing value after '='; values must be on the same line",
" at bunfig.toml:2:11",
"",
"SyntaxError: failed to load bunfig",
"",
],
],
[
"a value of the wrong type",
'[run]\nbun = "yes"\n',
[
'2 | bun = "yes"',
" ^",
"error: Expected boolean",
" at bunfig.toml:2:7",
"",
"Invalid Bunfig: failed to load bunfig",
"",
],
],
];
const commands: [command: string, args: string[]][] = [
["bun run <script>", ["run", "hello"]],
["bun <script>", ["hello"]],
["bun run <file>", ["run", "index.js"]],
["bun run ./<file>", ["run", "./index.js"]],
["bun run -", ["run", "-"]], // the script comes from stdin
["bun run --watch <file>", ["run", "--watch", "index.js"]],
["bun run --hot <file>", ["run", "--hot", "index.js"]],
["bun repl", ["repl"]],
];

async function run(bunfig: string, cmd: (dir: string) => string[]) {
using dir = tempDir("bunfig-does-not-parse", {
"bunfig.toml": bunfig,
"package.json": JSON.stringify({ scripts: { hello: "echo ran" } }),
"index.js": `console.log("ran");`,
});
await using proc = Bun.spawn({
cmd: cmd(String(dir)),
env: bunEnv,
cwd: String(dir),
stdin: new Blob([`console.log("ran");`]),
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
return { stdout, stderr, exitCode, signalCode: proc.signalCode };
}

describe.each(configs)("%s", (_, bunfig, expectedStderr) => {
const stopped = { stdout: "", stderr: expectedStderr.join("\n"), exitCode: 1, signalCode: null };

test.each(commands)("stops %s", async (_, args) => {
expect(await run(bunfig, () => [bunExe(), ...args])).toEqual(stopped);
});

// Bun runs as node when its executable is named node. A symlink needs privileges on Windows.
test.skipIf(isWindows)("stops node <file> when node is bun", async () => {
expect(
await run(bunfig, dir => {
symlinkSync(bunExe(), pathJoin(dir, "node"));
return [pathJoin(dir, "node"), "index.js"];
}),
).toEqual(stopped);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
});
});
6 changes: 4 additions & 2 deletions test/js/bun/util/highlighter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ test("bunfig error on a line ending in `${}` does not crash", async () => {
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).toContain("expected string");
expect(stdout).toContain("hi");
expect(exitCode).toBe(0);
// Printed after the highlighted line, so the highlighter returned.
expect(stderr).toContain("failed to load bunfig");
expect(stdout).toBe("");
expect(exitCode).toBe(1);
});
Loading