mirror of
https://github.com/oven-sh/bun
synced 2026-02-11 03:18:53 +00:00
## Summary Optimizes the `--lockfile-only` flag to skip downloading **npm package tarballs** since they're not needed for lockfile generation. This saves bandwidth and improves performance for lockfile-only operations while preserving accuracy for non-npm dependencies. ## Changes - **Add `prefetch_resolved_tarballs` flag** to `PackageManagerOptions.Do` struct (defaults to `true`) - **Set flag to `false`** when `--lockfile-only` is used - **Skip tarball downloads for npm packages only** when flag is disabled: - `getOrPutResolvedPackageWithFindResult` - Main npm package resolution (uses `Task.Id.forNPMPackage`) - `enqueuePackageForDownload` - NPM package downloads (uses `bun.Semver.Version`) - **Preserve tarball downloads for non-npm dependencies** to maintain lockfile accuracy: - Remote tarball URLs (needed for lockfile generation) - GitHub dependencies (needed for lockfile generation) - Generic tarball downloads (may be remote) - Patch-related downloads (needed for patch application) - **Add comprehensive test** that verifies only package manifests are fetched for npm packages with `--lockfile-only` ## Rationale Only npm registry packages can safely skip tarball downloads during lockfile generation because: ✅ **NPM packages**: Metadata is available from registry manifests, tarball not needed for lockfile ❌ **Remote URLs**: Need tarball content to determine package metadata and generate accurate lockfile ❌ **GitHub deps**: Need tarball content to extract package.json and determine dependencies ❌ **Tarball URIs**: Need content to determine package structure and dependencies This selective approach maximizes bandwidth savings while ensuring lockfile accuracy. ## Test Plan - ✅ New test in `test/cli/install/lockfile-only.test.ts` verifies only npm manifest URLs are requested - ✅ Uses absolute package versions to ensure the npm resolution code path is hit - ✅ Test output normalized to work with both debug and non-debug builds - ✅ All existing install/update tests still pass (including remote dependency tests) ## Performance Impact For `--lockfile-only` operations with npm packages, this eliminates unnecessary tarball downloads, reducing: - **Network bandwidth usage** (manifests only, not tarballs) - **Installation time** (no tarball extraction/processing) - **Cache storage requirements** (tarballs not cached) The optimization only affects npm packages in `--lockfile-only` mode and has zero impact on: - Regular installs (npm packages still download tarballs) - Remote dependencies (always download tarballs for accuracy) - GitHub dependencies (always download tarballs for accuracy) ## Files Changed - `src/install/PackageManager/PackageManagerOptions.zig` - Add flag and configure for lockfile-only - `src/install/PackageManager/PackageManagerEnqueue.zig` - Skip npm tarball generation selectively - `test/cli/install/lockfile-only.test.ts` - Test with dummy registry 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Jarred Sumner <jarred@jarredsumner.com> Co-authored-by: Alistair Smith <hi@alistair.sh>
82 lines
2.0 KiB
TypeScript
82 lines
2.0 KiB
TypeScript
import { spawn } from "bun";
|
|
import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test";
|
|
import { access, writeFile } from "fs/promises";
|
|
import { bunExe, bunEnv as env } from "harness";
|
|
import { join } from "path";
|
|
import {
|
|
dummyAfterAll,
|
|
dummyAfterEach,
|
|
dummyBeforeAll,
|
|
dummyBeforeEach,
|
|
dummyRegistry,
|
|
package_dir,
|
|
requested,
|
|
root_url,
|
|
setHandler,
|
|
} from "./dummy.registry.js";
|
|
|
|
beforeAll(dummyBeforeAll);
|
|
afterAll(dummyAfterAll);
|
|
beforeEach(dummyBeforeEach);
|
|
afterEach(dummyAfterEach);
|
|
|
|
it.each(["bun.lockb", "bun.lock"])("should not download tarballs with --lockfile-only using %s", async lockfile => {
|
|
const isLockb = lockfile === "bun.lockb";
|
|
|
|
const urls: string[] = [];
|
|
const registry = { "0.0.1": { as: "0.0.1" }, latest: "0.0.1" };
|
|
|
|
setHandler(dummyRegistry(urls, registry));
|
|
|
|
await writeFile(
|
|
join(package_dir, "package.json"),
|
|
JSON.stringify({
|
|
name: "foo",
|
|
dependencies: {
|
|
baz: "0.0.1",
|
|
},
|
|
}),
|
|
);
|
|
|
|
const cmd = [bunExe(), "install", "--lockfile-only"];
|
|
|
|
if (!isLockb) {
|
|
// the default beforeEach disables --save-text-lockfile in the dummy registry, so we should restore
|
|
// default behaviour
|
|
await writeFile(
|
|
join(package_dir, "bunfig.toml"),
|
|
`
|
|
[install]
|
|
cache = false
|
|
registry = "${root_url}/"
|
|
`,
|
|
);
|
|
}
|
|
|
|
const { stdout, stderr, exited } = spawn({
|
|
cmd,
|
|
cwd: package_dir,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
env,
|
|
});
|
|
|
|
expect(await exited).toBe(0);
|
|
const err = await stderr.text();
|
|
|
|
expect(err).not.toContain("error:");
|
|
expect(err).toContain("Saved lockfile");
|
|
|
|
const out = await stdout.text();
|
|
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
|
|
expect.stringContaining("bun install v1."),
|
|
"",
|
|
expect.stringContaining(`Saved ${lockfile}`),
|
|
]);
|
|
|
|
expect(urls.sort()).toEqual([`${root_url}/baz`]);
|
|
expect(requested).toBe(1);
|
|
|
|
await access(join(package_dir, lockfile));
|
|
});
|