bun/prefer-url-for-sibling-files

Prefer new URL("./file", import.meta.url) to a path joined onto the module's directory

Bundlers such as Vite and webpack recognize new URL("./file", import.meta.url). They copy the file to the output and point the URL at the copy. A path that's joined onto import.meta.dirname or __dirname is only a string to them, so after bundling it points to a file that isn't there.

The rule reports join() and resolve() from node:path, + and template literals, if the first part is the module's directory and the rest is string literals. The directory can be __dirname, import.meta.dirname, import.meta.dir, dirname(fileURLToPath(import.meta.url)), or a const that holds one of them.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"bun/prefer-url-for-sibling-files" = "error" # or "warn"

Examples#

example.js1 problem
import { join } from "node:path";

const schema = await Bun.file(join(import.meta.dirname, "schema.sql")).text();
example.js2 problems
import { readFileSync } from "node:fs";

const logo = readFileSync(`${import.meta.dirname}/assets/logo.svg`);
const icon = readFileSync(import.meta.dirname + "/assets/icon.svg");
example.js1 problem
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const folder = dirname(fileURLToPath(import.meta.url));
export const template = resolve(folder, "templates", "email.html");
example.jsNo problems
const schema = await Bun.file(new URL("./schema.sql", import.meta.url)).text();

A path with a part that's only known at run time, or one that ends in ., .. or /, isn't reported:

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

export const translations = language => join(import.meta.dirname, "locales", `${language}.json`);
export const root = join(import.meta.dirname, "..");

CommonJS files aren't checked, because they have no import.meta.url:

load.cjsNo problems
const { join } = require("node:path");

module.exports = join(__dirname, "schema.sql");

When not to use it#

Leave it off if your code is never bundled, or if your bundler doesn't follow new URL(.., import.meta.url).