Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
74 changes: 37 additions & 37 deletions src/runtime/cli/init_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,9 @@ impl InitCommand {

#[cfg(windows)]
let prev_file_pos = pkg.get_pos()?;
if pkg
.pread_all(package_json_contents.list.as_mut_slice(), 0)
.is_err()
{
package_json_file = None;
break 'read_package_json;
if let Err(err) = pkg.pread_all(package_json_contents.list.as_mut_slice(), 0) {
Output::err(err, "failed to read package.json", ());
Global::exit(1);
}
#[cfg(windows)]
pkg.seek_to(prev_file_pos)?;
Expand All @@ -466,45 +463,48 @@ impl InitCommand {
};
let mut did_load_package_json = false;
if !package_json_contents.list.is_empty() {
'process_package_json: {
let source = bun_ast::Source::init_path_string(
b"package.json",
package_json_contents.list.as_slice(),
);
let mut log = bun_ast::Log::init();
let package_json_expr: bun_ast::Expr =
match json::parse_package_json_utf8(&source, &mut log, &bump) {
Ok(e) => e,
Err(_) => {
package_json_file = None;
break 'process_package_json;
// A package.json that exists but does not parse must never be
// treated as absent: that would overwrite it with the scaffold.
Comment thread
robobun marked this conversation as resolved.
Outdated
let source = bun_ast::Source::init_path_string(
b"package.json",
package_json_contents.list.as_slice(),
);
let mut log = bun_ast::Log::init();
let package_json_expr: bun_ast::Expr =
match json::parse_package_json_utf8(&source, &mut log, &bump) {
Ok(e) => e,
Err(err) => {
if log.errors > 0 {
let _ = log.print(std::ptr::from_mut(Output::error_writer()));
}
};
Output::err(err, "failed to parse package.json", ());
Global::exit(1);
}
};

if !package_json_expr.data.is_e_object() {
package_json_file = None;
break 'process_package_json;
}
if !package_json_expr.data.is_e_object() {
Output::err_generic("package.json root must be an object", ());
Global::exit(1);
}

fields.object = package_json_expr.data.e_object();
fields.object = package_json_expr.data.e_object();

if let Some(name) = package_json_expr.get(b"name") {
if let Some(str) = name.as_utf8_string_literal() {
fields.name = str.to_vec();
}
if let Some(name) = package_json_expr.get(b"name") {
if let Some(str) = name.as_utf8_string_literal() {
fields.name = str.to_vec();
}
}
Comment thread
robobun marked this conversation as resolved.

if let Some(name) = package_json_expr
.get(b"module")
.or_else(|| package_json_expr.get(b"main"))
{
if let Some(str_) = name.as_utf8_string_literal() {
fields.entry_point = str_.to_vec();
}
if let Some(name) = package_json_expr
.get(b"module")
.or_else(|| package_json_expr.get(b"main"))
{
if let Some(str_) = name.as_utf8_string_literal() {
fields.entry_point = str_.to_vec();
}

did_load_package_json = true;
}

did_load_package_json = true;
}

if fields.entry_point.is_empty() && !minimal {
Expand Down
34 changes: 34 additions & 0 deletions test/cli/init/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,40 @@ const initEnv = { ...bunEnv, BUN_AGENT_RULE_DISABLED: "1" };
});
}, 30_000);

// A package.json that exists but cannot be loaded is an error, like it is
// for `bun add` / `bun pm pkg`. It used to be treated as "no package.json"
// and silently overwritten with the blank scaffold.
test.each([
{
name: "syntax error",
contents: '{\n "name": "myapp",\n "version": "2.0.0",\n "dependencies": {\n "foo": "^1.0.0",\n }\n',
error: 'error: Expected "}" but found end of file',
},
{
name: "non-object root",
contents: '["name", "myapp"]\n',
error: "error: package.json root must be an object",
},
])("bun init -y errors on an invalid package.json instead of overwriting it ($name)", async ({ contents, error }) => {
await using temp = tempDir("bun-init-invalid-package-json", {
"package.json": contents,
});

await using proc = Bun.spawn({
cmd: [bunExe(), "init", "-y"],
cwd: temp,
stdio: ["ignore", "pipe", "pipe"],
env: initEnv,
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

expect(stdout).toBe("");
expect(stderr).toContain(error);
Comment thread
claude[bot] marked this conversation as resolved.
Outdated
expect(await Bun.file(path.join(temp, "package.json")).text()).toBe(contents);
expect(readdirSync(temp).sort()).toEqual(["package.json"]);
expect(exitCode).toBe(1);
});

test("bun init --react works", async () => {
await using temp = tempDir("bun-init--react-works", {});

Expand Down
Loading