fix: prefer bun.lock over bun.lockb when migrating lockfiles

When migrating from npm/yarn/pnpm lockfiles, Bun was incorrectly
setting the lockfile format to binary (.lockb) in both success and
error cases. This caused Bun to create bun.lockb instead of the
preferred bun.lock format.

This change ensures that all migration paths (npm, yarn, pnpm) return
.format = .text instead of .format = .binary, causing Bun to create
bun.lock files as expected.

Changes:
- src/install/migration.zig: Changed error paths for npm, yarn, and
  pnpm migrations to return .format = .text
- src/install/migration.zig: Changed npm successful migration path
  to return .format = .text
- src/install/yarn.zig: Changed yarn successful migration path to
  return .format = .text
- test/regression/issue/migration-lockfile-format.test.ts: Added
  regression tests to verify lockfile format after migration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2025-10-12 02:59:08 +00:00
parent 85a2ebb717
commit ea65d7903f
3 changed files with 137 additions and 5 deletions

View File

@@ -30,7 +30,7 @@ pub fn detectAndLoadOtherLockfile(
.step = .migrating,
.value = err,
.lockfile_path = "package-lock.json",
.format = .binary,
.format = .text,
} };
};
@@ -54,7 +54,7 @@ pub fn detectAndLoadOtherLockfile(
.step = .migrating,
.value = err,
.lockfile_path = "yarn.lock",
.format = .binary,
.format = .text,
} };
};
@@ -126,7 +126,7 @@ pub fn detectAndLoadOtherLockfile(
.step = .migrating,
.value = err,
.lockfile_path = "pnpm-lock.yaml",
.format = .binary,
.format = .text,
} };
};
@@ -1089,7 +1089,7 @@ pub fn migrateNPMLockfile(
.migrated = .npm,
.loaded_from_binary_lockfile = false,
.serializer_result = .{},
.format = .binary,
.format = .text,
},
};
}

View File

@@ -1684,7 +1684,7 @@ pub fn migrateYarnLockfile(
.migrated = .yarn,
.loaded_from_binary_lockfile = false,
.serializer_result = .{},
.format = .binary,
.format = .text,
} };
return result;

View File

@@ -0,0 +1,132 @@
import { expect, test } from "bun:test";
import { existsSync } from "fs";
import { bunEnv, bunExe, tempDir } from "harness";
import { join } from "path";
test("failing to migrate pnpm-lock.yaml should create bun.lock not bun.lockb", async () => {
using dir = tempDir("pnpm-migration-format", {
"package.json": JSON.stringify({
name: "test",
version: "1.0.0",
dependencies: {
"is-even": "1.0.0",
},
}),
// Create an invalid pnpm-lock.yaml that will fail to migrate
"pnpm-lock.yaml": `lockfileVersion: '9.0'
importers:
.:
dependencies:
is-even:
specifier: 1.0.0
version: 1.0.0
packages:
/is-even@1.0.0:
resolution: {integrity: sha512-invalid-integrity}
engines: {node: '>=0.10.0'}
`,
});
const result = await Bun.spawn({
cmd: [bunExe(), "install"],
cwd: String(dir),
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
}).exited;
// After failed migration, bun.lock should exist (not bun.lockb)
const bunLockPath = join(String(dir), "bun.lock");
const bunLockbPath = join(String(dir), "bun.lockb");
expect(existsSync(bunLockPath)).toBe(true);
expect(existsSync(bunLockbPath)).toBe(false);
});
// Note: This test verifies the fix but may pass even if migration succeeds
// The key fix is that error paths return .format = .text instead of .format = .binary
test("after yarn.lock exists, bun install should create bun.lock not bun.lockb", async () => {
using dir = tempDir("yarn-migration-format", {
"package.json": JSON.stringify({
name: "test",
version: "1.0.0",
dependencies: {
"is-number": "7.0.0",
},
}),
// Create a valid yarn.lock to ensure migration happens
"yarn.lock": `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
is-number@7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
`,
});
await Bun.spawn({
cmd: [bunExe(), "install"],
cwd: String(dir),
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
}).exited;
// After migration, bun.lock should exist (not bun.lockb)
const bunLockPath = join(String(dir), "bun.lock");
const bunLockbPath = join(String(dir), "bun.lockb");
expect(existsSync(bunLockPath)).toBe(true);
expect(existsSync(bunLockbPath)).toBe(false);
});
test("successful migration from package-lock.json should create bun.lock not bun.lockb", async () => {
using dir = tempDir("npm-migration-format", {
"package.json": JSON.stringify({
name: "test",
version: "1.0.0",
dependencies: {
"is-number": "7.0.0",
},
}),
"package-lock.json": JSON.stringify({
name: "test",
version: "1.0.0",
lockfileVersion: 3,
requires: true,
packages: {
"": {
name: "test",
version: "1.0.0",
dependencies: {
"is-number": "7.0.0",
},
},
"node_modules/is-number": {
version: "7.0.0",
resolved: "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
integrity: "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
engines: {
node: ">=0.12.0",
},
},
},
}),
});
const result = await Bun.spawn({
cmd: [bunExe(), "install"],
cwd: String(dir),
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
}).exited;
// After successful migration, bun.lock should exist (not bun.lockb)
const bunLockPath = join(String(dir), "bun.lock");
const bunLockbPath = join(String(dir), "bun.lockb");
expect(existsSync(bunLockPath)).toBe(true);
expect(existsSync(bunLockbPath)).toBe(false);
});