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
45 changes: 24 additions & 21 deletions src/install/PackageManager.zig
Original file line number Diff line number Diff line change
Expand Up @@ -793,30 +793,33 @@ pub fn init(

initializeStore();

if (bun.env_var.XDG_CONFIG_HOME.get() orelse bun.env_var.HOME.get()) |data_dir| {
{
var buf: bun.PathBuffer = undefined;
var parts = [_]string{
"./.npmrc",
const parts = [_]string{".npmrc"};

const install_ = ctx.install orelse brk: {
const new_install = bun.handleOom(ctx.allocator.create(Api.BunInstall));
new_install.* = std.mem.zeroes(Api.BunInstall);
ctx.install = new_install;
break :brk new_install;
};

bun.ini.loadNpmrcConfig(ctx.allocator, ctx.install orelse brk: {
const install_ = bun.handleOom(ctx.allocator.create(Api.BunInstall));
install_.* = std.mem.zeroes(Api.BunInstall);
ctx.install = install_;
break :brk install_;
}, env, true, &[_][:0]const u8{ Path.joinAbsStringBufZ(
data_dir,
&buf,
&parts,
.auto,
), ".npmrc" });
} else {
bun.ini.loadNpmrcConfig(ctx.allocator, ctx.install orelse brk: {
const install_ = bun.handleOom(ctx.allocator.create(Api.BunInstall));
install_.* = std.mem.zeroes(Api.BunInstall);
ctx.install = install_;
break :brk install_;
}, env, true, &[_][:0]const u8{".npmrc"});
const home_config_path: ?[:0]const u8 = path: {
if (bun.env_var.XDG_CONFIG_HOME.get()) |xdg_dir| {
const p = Path.joinAbsStringBufZ(xdg_dir, &buf, &parts, .auto);
if (bun.sys.existsZ(p)) break :path p;
}
if (bun.env_var.HOME.get()) |home_dir| {
break :path Path.joinAbsStringBufZ(home_dir, &buf, &parts, .auto);
}
break :path null;
};

if (home_config_path) |p| {
bun.ini.loadNpmrcConfig(ctx.allocator, install_, env, true, &[_][:0]const u8{ p, ".npmrc" });
} else {
bun.ini.loadNpmrcConfig(ctx.allocator, install_, env, true, &[_][:0]const u8{".npmrc"});
}
}
const cpu_count = bun.getThreadCount();

Expand Down
79 changes: 77 additions & 2 deletions test/cli/install/npmrc.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { write } from "bun";
import { afterAll, beforeAll, describe, expect, it, test } from "bun:test";
import { write } from "bun";
import { rm } from "fs/promises";
import { VerdaccioRegistry, bunExe, bunEnv as env, stderrForInstall } from "harness";
import { bunExe, bunEnv as env, stderrForInstall, VerdaccioRegistry } from "harness";
import { join } from "path";

const { iniInternals } = require("bun:internal-for-testing");
const { loadNpmrc } = iniInternals;

Expand Down Expand Up @@ -168,6 +169,80 @@ registry = http://localhost:${registry.port}/
.throws(true);
});

it("falls back to HOME/.npmrc when XDG_CONFIG_HOME is set but has no .npmrc", async () => {
const { packageDir, packageJson } = await registry.createTestDir();

await Bun.$`rm -rf ${packageDir}/bunfig.toml`;

// XDG_CONFIG_HOME exists but has no .npmrc
const xdgDir = `${packageDir}/xdg_config`;
await Bun.$`mkdir -p ${xdgDir}`;

// HOME has the .npmrc
const homeDir = `${packageDir}/home_dir`;
await Bun.$`mkdir -p ${homeDir}`;

const ini = /* ini */ `
registry=http://localhost:${registry.port}/
`;

await Bun.$`echo ${ini} > ${homeDir}/.npmrc`;
await Bun.$`echo ${JSON.stringify({
name: "foo",
dependencies: {
"no-deps": "1.0.0",
},
})} > package.json`.cwd(packageDir);
await Bun.$`${bunExe()} install`
.env({
...process.env,
XDG_CONFIG_HOME: `${xdgDir}`,
HOME: `${homeDir}`,
})
.cwd(packageDir)
.throws(true);
});

it("prefers XDG_CONFIG_HOME/.npmrc over HOME/.npmrc when both exist", async () => {
const { packageDir, packageJson } = await registry.createTestDir();

await Bun.$`rm -rf ${packageDir}/bunfig.toml`;

const xdgDir = `${packageDir}/xdg_config`;
await Bun.$`mkdir -p ${xdgDir}`;

const homeDir = `${packageDir}/home_dir`;
await Bun.$`mkdir -p ${homeDir}`;

// XDG has the correct registry
const xdgIni = /* ini */ `
registry=http://localhost:${registry.port}/
`;
await Bun.$`echo ${xdgIni} > ${xdgDir}/.npmrc`;

// HOME has an incorrect registry that would fail
const homeIni = /* ini */ `
registry=http://localhost:1/
`;
await Bun.$`echo ${homeIni} > ${homeDir}/.npmrc`;

await Bun.$`echo ${JSON.stringify({
name: "foo",
dependencies: {
"no-deps": "1.0.0",
},
})} > package.json`.cwd(packageDir);
// Should succeed because XDG_CONFIG_HOME/.npmrc is preferred
await Bun.$`${bunExe()} install`
.env({
...process.env,
XDG_CONFIG_HOME: `${xdgDir}`,
HOME: `${homeDir}`,
})
.cwd(packageDir)
.throws(true);
});

it("package config overrides home config", async () => {
const { packageDir, packageJson } = await registry.createTestDir();

Expand Down