bun/no-cwd-dependent-path

Disallow relative paths that depend on the directory the process was started in

readFileSync("config.json") doesn't look next to the file that calls it. It looks in the directory the process was started in. The code works when you run it from the project root and fails with ENOENT when it runs from anywhere else. Start from import.meta.dirname to name a file next to your source.

The rule checks a string or a template literal that starts with a relative path, in these places:

  • The node:fs and node:fs/promises functions, except glob and globSync. For copyFile, cp, link, and rename, both paths are checked. For symlink, only the second one is, because the target is relative to the link.
  • path.resolve(), if every argument is a literal like that.
  • Bun.file() and Bun.write().

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"bun/no-cwd-dependent-path" = "error" # or "warn"

Every option, set to its default:

bunfig.toml
[lint.rules]
"bun/no-cwd-dependent-path" = ["error", {
  functions = [], # list of strings
}]

Examples#

example.js1 problem
import { readFileSync } from "node:fs";

const config = JSON.parse(readFileSync("config.json", "utf8"));
example.js2 problems
import path from "node:path";

const schema = Bun.file(`schemas/${name}.json`);
const output = path.resolve("dist", "index.js");
example.jsNo problems
import { readFileSync } from "node:fs";
import path from "node:path";

const config = JSON.parse(readFileSync(path.join(import.meta.dirname, "config.json"), "utf8"));
const schema = Bun.file(new URL(`./schemas/${name}.json`, import.meta.url));
const output = path.resolve(import.meta.dirname, "dist", "index.js");

Absolute paths and paths that come from a variable are fine:

example.jsNo problems
import { readFileSync } from "node:fs";

readFileSync("/etc/hosts", "utf8");
readFileSync(process.argv[2], "utf8");
readFileSync(`${root}/config.json`, "utf8");

Options#

OptionDefaultWhat it does
functions[]Names of more functions that take a path as their first argument. A name matches both loadFixture() and helpers.loadFixture().
example.jswith { "functions": ["loadFixture"] }2 problems
const user = loadFixture("fixtures/user.json");
const team = helpers.loadFixture("fixtures/team.json");

When not to use it#

A command-line tool often means the working directory, like when it reads the package.json of the project it was started in. Say so with process.cwd(), or turn the rule off for those files:

example.jsNo problems
import { readFileSync } from "node:fs";
import path from "node:path";

const manifest = readFileSync(path.resolve(process.cwd(), "package.json"), "utf8");