# 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.

{/* Generated by src/codegen/lint-rule-docs.ts. Do not edit this section. */}

## Configure

Off by default. To turn it on:

```toml 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.

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

{/* End of the generated section. */}

## 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:

{/* bad {"count": 3} */}
```js
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:

{/* good */}
```js
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:

{/* file "package.json" */}
```json
{
  "name": "app",
  "engines": { "node": ">=20.0.0" }
}
```

{/* good */}
```js
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`.

```toml 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:

{/* bad {"options": [{"version": ">=22.5.0"}]} */}
```js
import { DatabaseSync } from "node:sqlite";
```

{/* good {"options": [{"version": ">=22.5.0", "allowExperimental": true}]} */}
```js
import { DatabaseSync } from "node:sqlite";
```

{/* bad {"options": [{"version": ">=22.0.0", "allowExperimental": true}]} */}
```js
import { DatabaseSync } from "node:sqlite";
```

{/* good {"options": [{"ignores": ["fs.cpSync"]}]} */}
```js
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.
