n/no-unsupported-features/node-builtins

Disallow Node.js APIs that the Node.js versions you support do not have

Also called node/no-unsupported-features/node-builtins

A Node.js API that's newer than the oldest version you support is missing there. An import of it fails when the file loads, and a call throws a TypeError. You only find out when somebody runs the code on that version.

The rule checks anything you take from built-in modules with import, require() or process.getBuiltinModule(), globals such as fetch, and the properties of import.meta. It takes the versions you support from engines.node in your package.json. If nothing sets a range, it assumes >=16.0.0. Every version in the range must have the API, and it must be stable there. An API that's still experimental is reported too.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"n/no-unsupported-features/node-builtins" = "error" # or "warn"

Every option, set to its default. The ones that are commented out are unset by default.

bunfig.toml
[lint.rules]
"n/no-unsupported-features/node-builtins" = ["error", {
  # version = <string>,
  allowExperimental = false,
  ignores = [], # list of strings
}]

Examples#

Reported, because util.parseArgs has been stable since 20.0.0, fetch since 21.0.0 and import.meta.dirname since 24.0.0:

example.js3 problems
import { parseArgs } from "node:util";

const { values } = parseArgs({ options: { url: { type: "string" } } });
const response = await fetch(values.url);
console.log(import.meta.dirname);
example.jsNo problems
import https from "node:https";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";

https.get(process.argv[2], (response) => response.pipe(process.stdout));
console.log(dirname(fileURLToPath(import.meta.url)));

With a newer engines.node, parseArgs is fine:

package.json
{
  "name": "app",
  "engines": { "node": ">=20.0.0" }
}
example.jsNo problems
import { parseArgs } from "node:util";

const { values } = parseArgs({ options: { url: { type: "string" } } });

Options#

OptionDefaultWhat it does
versionengines.node, or ">=16.0.0"The range of Node.js versions you support, written as in package.json.
allowExperimentalfalseAllow an API if every version in the range has it, even as experimental.
ignores[]APIs that are never reported, by the name in the message, such as "fs.cpSync" or "test".

Without version, the range comes from settings.n.version ([lint.settings.n] in bunfig.toml, or settings in an ESLint config), then from engines.node, then from devEngines.runtime in package.json.

bunfig.toml
[lint.rules]
"n/no-unsupported-features/node-builtins" = ["error", {
  version = ">=22.5.0",
  allowExperimental = true,
}]

node:sqlite has been experimental since 22.5.0:

example.jswith { "version": ">=22.5.0" }1 problem
import { DatabaseSync } from "node:sqlite";
example.jswith { "version": ">=22.5.0", "allowExperimental": true }No problems
import { DatabaseSync } from "node:sqlite";
example.jswith { "version": ">=22.0.0", "allowExperimental": true }2 problems
import { DatabaseSync } from "node:sqlite";
example.jswith { "ignores": ["fs.cpSync"] }No problems
import fs from "node:fs";

fs.cpSync("public", "dist", { recursive: true });

When not to use it#

If the code never runs in Node.js, for example because it only runs in Bun, the reports don't apply.