Linter · Rules
n/no-unsupported-features/node-builtins
Disallow Node.js APIs that the Node.js versions you support do not have
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:
[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.
[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:
import { parseArgs } from "node:util";
const { values } = parseArgs({ options: { url: { type: "string" } } });
const response = await fetch(values.url);
console.log(import.meta.dirname);
Not reported:
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:
{
"name": "app",
"engines": { "node": ">=20.0.0" }
}
import { parseArgs } from "node:util";
const { values } = parseArgs({ options: { url: { type: "string" } } });
Options
| Option | Default | What it does |
|---|---|---|
version | engines.node, or ">=16.0.0" | The range of Node.js versions you support, written as in package.json. |
allowExperimental | false | Allow 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.
[lint.rules]
"n/no-unsupported-features/node-builtins" = ["error", {
version = ">=22.5.0",
allowExperimental = true,
}]
node:sqlite has been experimental since 22.5.0:
import { DatabaseSync } from "node:sqlite";
import { DatabaseSync } from "node:sqlite";
import { DatabaseSync } from "node:sqlite";
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.