Skip to content

Commit 430c1dd

Browse files
authored
add install.saveTextLockfile to bunfig.toml (#15827)
1 parent ad1738d commit 430c1dd

File tree

11 files changed

+129
-4
lines changed

11 files changed

+129
-4
lines changed

docs/cli/bun-install.md

+4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ registry = "https://registry.yarnpkg.com/"
4747
# Install for production? This is the equivalent to the "--production" CLI argument
4848
production = false
4949

50+
# Save a text-based lockfile? This is equivalent to the "--save-text-lockfile" CLI argument
51+
saveTextLockfile = false
52+
5053
# Disallow changes to lockfile? This is the equivalent to the "--frozen-lockfile" CLI argument
5154
frozenLockfile = false
5255

@@ -108,6 +111,7 @@ export interface Install {
108111
scopes: Scopes;
109112
registry: Registry;
110113
production: boolean;
114+
saveTextLockfile: boolean;
111115
frozenLockfile: boolean;
112116
dryRun: boolean;
113117
optional: boolean;

docs/cli/install.md

+3
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ peer = true
174174
# equivalent to `--production` flag
175175
production = false
176176

177+
# equivalent to `--save-text-lockfile` flag
178+
saveTextLockfile = false
179+
177180
# equivalent to `--frozen-lockfile` flag
178181
frozenLockfile = false
179182

docs/install/index.md

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ peer = true
8686
# equivalent to `--production` flag
8787
production = false
8888

89+
# equivalent to `--save-text-lockfile` flag
90+
saveTextLockfile = false
91+
8992
# equivalent to `--frozen-lockfile` flag
9093
frozenLockfile = false
9194

docs/install/lockfile.md

+18
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,24 @@ $ bun install --yarn
7272
print = "yarn"
7373
```
7474

75+
### Text-based lockfile
76+
77+
Bun v1.1.39 introduced `bun.lock`, a JSONC formatted lockfile. `bun.lock` is human-readable and git-diffable without configuration, at [no cost to performance](https://bun.sh/blog/bun-lock-text-lockfile#cached-bun-install-gets-30-faster).
78+
79+
To generate the lockfile, use `--save-text-lockfile` with `bun install`. You can do this for new projects and existing projects already using `bun.lockb` (resolutions will be preserved).
80+
81+
```bash
82+
$ bun install --save-text-lockfile
83+
$ head -n3 bun.lock
84+
{
85+
"lockfileVersion": 0,
86+
"workspaces": {
87+
```
88+
89+
Once `bun.lock` is generated, Bun will use it for all subsequent installs and updates through commands that read and modify the lockfile. If both lockfiles exist, `bun.lock` will be choosen over `bun.lockb`.
90+
91+
Bun v1.2.0 will switch the default lockfile format to `bun.lock`.
92+
7593
{% /codetabs %}
7694
7795
{% details summary="Configuring lockfile" %}

docs/runtime/bunfig.md

+11
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,17 @@ By default Bun uses caret ranges; if the `latest` version of a package is `2.4.1
238238
exact = false
239239
```
240240

241+
### `install.saveTextLockfile`
242+
243+
Generate `bun.lock`, a human-readable text-based lockfile. Once generated, Bun will use this file instead of `bun.lockb`, choosing it over the binary lockfile if both are present.
244+
245+
Default `false`. In Bun v1.2.0 the default lockfile format will change to `bun.lock`.
246+
247+
```toml
248+
[install]
249+
saveTextLockfile = true
250+
```
251+
241252
<!--
242253
### `install.prefer`
243254

src/api/schema.zig

+2
Original file line numberDiff line numberDiff line change
@@ -2979,6 +2979,8 @@ pub const Api = struct {
29792979

29802980
cafile: ?[]const u8 = null,
29812981

2982+
save_text_lockfile: ?bool = null,
2983+
29822984
ca: ?union(enum) {
29832985
str: []const u8,
29842986
list: []const []const u8,

src/bunfig.zig

+6
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,12 @@ pub const Bunfig = struct {
469469
}
470470
}
471471

472+
if (install_obj.get("saveTextLockfile")) |save_text_lockfile| {
473+
if (save_text_lockfile.asBool()) |value| {
474+
install.save_text_lockfile = value;
475+
}
476+
}
477+
472478
if (install_obj.get("concurrentScripts")) |jobs| {
473479
if (jobs.data == .e_number) {
474480
install.concurrent_scripts = jobs.data.e_number.toU32();

src/install/install.zig

+8-2
Original file line numberDiff line numberDiff line change
@@ -7237,6 +7237,10 @@ pub const PackageManager = struct {
72377237
}
72387238
}
72397239

7240+
if (config.save_text_lockfile) |save_text_lockfile| {
7241+
this.save_text_lockfile = save_text_lockfile;
7242+
}
7243+
72407244
if (config.concurrent_scripts) |jobs| {
72417245
this.max_concurrent_lifecycle_scripts = jobs;
72427246
}
@@ -7395,7 +7399,9 @@ pub const PackageManager = struct {
73957399
this.do.trust_dependencies_from_args = true;
73967400
}
73977401

7398-
this.save_text_lockfile = cli.save_text_lockfile;
7402+
if (cli.save_text_lockfile) |save_text_lockfile| {
7403+
this.save_text_lockfile = save_text_lockfile;
7404+
}
73997405

74007406
this.local_package_features.optional_dependencies = !cli.omit.optional;
74017407

@@ -9498,7 +9504,7 @@ pub const PackageManager = struct {
94989504
ca: []const string = &.{},
94999505
ca_file_name: string = "",
95009506

9501-
save_text_lockfile: bool = false,
9507+
save_text_lockfile: ?bool = null,
95029508

95039509
const PatchOpts = union(enum) {
95049510
nothing: struct {},

test/cli/install/__snapshots__/bun-install.test.ts.snap

+17
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,20 @@ note: Package name is also declared here
5454
at [dir]/bar/package.json:1:9
5555
"
5656
`;
57+
58+
exports[`should read install.saveTextLockfile from bunfig.toml 1`] = `
59+
"{
60+
"lockfileVersion": 0,
61+
"workspaces": {
62+
"": {},
63+
"packages/pkg1": {
64+
"name": "pkg-one",
65+
"version": "1.0.0",
66+
},
67+
},
68+
"packages": {
69+
"pkg-one": ["pkg-one@workspace:packages/pkg1", {}],
70+
}
71+
}
72+
"
73+
`;

test/cli/install/bun-install.test.ts

+53-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { file, listen, Socket, spawn } from "bun";
1+
import { file, listen, Socket, spawn, write } from "bun";
22
import {
33
afterAll,
44
afterEach,
@@ -11,7 +11,7 @@ import {
1111
setDefaultTimeout,
1212
test,
1313
} from "bun:test";
14-
import { access, mkdir, readlink, rm, writeFile, cp, stat } from "fs/promises";
14+
import { access, mkdir, readlink, rm, writeFile, cp, stat, exists } from "fs/promises";
1515
import {
1616
bunEnv,
1717
bunExe,
@@ -35,6 +35,7 @@ import {
3535
requested,
3636
root_url,
3737
setHandler,
38+
getPort,
3839
} from "./dummy.registry.js";
3940

4041
expect.extend({
@@ -8361,3 +8362,53 @@ cache = false
83618362
expect(await new Response(stdout).text()).toContain("installed @~39/[email protected]");
83628363
expect(await exited).toBe(0);
83638364
});
8365+
8366+
it("should read install.saveTextLockfile from bunfig.toml", async () => {
8367+
await Promise.all([
8368+
write(
8369+
join(package_dir, "bunfig.toml"),
8370+
`
8371+
[install]
8372+
cache = false
8373+
registry = "http://localhost:${getPort()}/"
8374+
saveTextLockfile = true
8375+
`,
8376+
),
8377+
write(
8378+
join(package_dir, "package.json"),
8379+
JSON.stringify({
8380+
name: "foo",
8381+
workspaces: ["packages/*"],
8382+
}),
8383+
),
8384+
write(
8385+
join(package_dir, "packages", "pkg1", "package.json"),
8386+
JSON.stringify({
8387+
name: "pkg-one",
8388+
version: "1.0.0",
8389+
}),
8390+
),
8391+
]);
8392+
8393+
const { stdout, stderr, exited } = spawn({
8394+
cmd: [bunExe(), "install"],
8395+
cwd: package_dir,
8396+
stdout: "pipe",
8397+
stderr: "pipe",
8398+
env,
8399+
});
8400+
8401+
const err = await Bun.readableStreamToText(stderr);
8402+
expect(err).not.toContain("error:");
8403+
expect(err).toContain("Saved lockfile");
8404+
const out = await Bun.readableStreamToText(stdout);
8405+
expect(out).toContain("1 package installed");
8406+
8407+
expect(await exited).toBe(0);
8408+
expect(await Bun.file(join(package_dir, "node_modules", "pkg-one", "package.json")).json()).toEqual({
8409+
name: "pkg-one",
8410+
version: "1.0.0",
8411+
});
8412+
expect(await exists(join(package_dir, "bun.lockb"))).toBeFalse();
8413+
expect(await file(join(package_dir, "bun.lock")).text()).toMatchSnapshot();
8414+
});

test/cli/install/dummy.registry.ts

+4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ export function dummyAfterAll() {
116116
server.stop();
117117
}
118118

119+
export function getPort() {
120+
return server.port;
121+
}
122+
119123
let packageDirGetter: () => string = () => {
120124
return tmpdirSync();
121125
};

0 commit comments

Comments
 (0)