Skip to content

Commit b1a5f53

Browse files
fiskersindresorhus
andauthored
Add prefer-node-protocol rule (#1203)
Co-authored-by: Sindre Sorhus <[email protected]>
1 parent a7e393c commit b1a5f53

16 files changed

+451
-15
lines changed

docs/rules/prefer-node-protocol.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Prefer using the `node:` protocol when importing Node.js builtin modules
2+
3+
When importing builtin modules, it's better to use the [`node:` protocol](https://nodejs.org/api/esm.html#esm_node_imports) as it makes it perfectly clear that the package is a Node.js builtin module.
4+
5+
And don't forget to [upvote this issue](https://github.com/nodejs/node/issues/38343) if you agree.
6+
7+
This rule is fixable.
8+
9+
## Fail
10+
11+
```js
12+
import dgram from 'dgram';
13+
```
14+
15+
```js
16+
export {strict as default} from 'assert';
17+
```
18+
19+
```js
20+
import fs from 'fs/promises';
21+
```
22+
23+
## Pass
24+
25+
```js
26+
import dgram from 'node:dgram';
27+
```
28+
29+
```js
30+
export {strict as default} from 'node:assert';
31+
```
32+
33+
```js
34+
import fs from 'node:fs/promises';
35+
```
36+
37+
```js
38+
const fs = require('fs');
39+
```
40+
41+
```js
42+
import _ from 'lodash';
43+
```
44+
45+
```js
46+
import fs from './fs.js';
47+
```

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ module.exports = {
101101
'unicorn/prefer-modern-dom-apis': 'error',
102102
'unicorn/prefer-module': 'error',
103103
'unicorn/prefer-negative-index': 'error',
104+
'unicorn/prefer-node-protocol': 'error',
104105
'unicorn/prefer-number-properties': 'error',
105106
'unicorn/prefer-optional-catch-binding': 'error',
106107
'unicorn/prefer-query-selector': 'error',

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"eslint-utils": "^2.1.0",
4343
"eslint-visitor-keys": "^2.0.0",
4444
"import-modules": "^2.1.0",
45+
"is-builtin-module": "^3.1.0",
4546
"lodash": "^4.17.21",
4647
"pluralize": "^8.0.0",
4748
"read-pkg-up": "^7.0.1",

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ Configure it in `package.json`.
9292
"unicorn/prefer-modern-dom-apis": "error",
9393
"unicorn/prefer-module": "error",
9494
"unicorn/prefer-negative-index": "error",
95+
"unicorn/prefer-node-protocol": "error",
9596
"unicorn/prefer-number-properties": "error",
9697
"unicorn/prefer-optional-catch-binding": "error",
9798
"unicorn/prefer-query-selector": "error",
@@ -184,6 +185,7 @@ Each rule has emojis denoting:
184185
| [prefer-modern-dom-apis](docs/rules/prefer-modern-dom-apis.md) | Prefer `.before()` over `.insertBefore()`, `.replaceWith()` over `.replaceChild()`, prefer one of `.before()`, `.after()`, `.append()` or `.prepend()` over `insertAdjacentText()` and `insertAdjacentElement()`. || 🔧 |
185186
| [prefer-module](docs/rules/prefer-module.md) | Prefer JavaScript modules (ESM) over CommonJS. || 🔧 |
186187
| [prefer-negative-index](docs/rules/prefer-negative-index.md) | Prefer negative index over `.length - index` for `{String,Array,TypedArray}#slice()` and `Array#splice()`. || 🔧 |
188+
| [prefer-node-protocol](docs/rules/prefer-node-protocol.md) | Prefer using the `node:` protocol when importing Node.js builtin modules. || 🔧 |
187189
| [prefer-number-properties](docs/rules/prefer-number-properties.md) | Prefer `Number` static properties over global ones. || 🔧 |
188190
| [prefer-optional-catch-binding](docs/rules/prefer-optional-catch-binding.md) | Prefer omitting the `catch` binding parameter. || 🔧 |
189191
| [prefer-query-selector](docs/rules/prefer-query-selector.md) | Prefer `.querySelector()` over `.getElementById()`, `.querySelectorAll()` over `.getElementsByClassName()` and `.getElementsByTagName()`. || 🔧 |

rules/prefer-node-protocol.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
const isBuiltinModule = require('is-builtin-module');
3+
const getDocumentationUrl = require('./utils/get-documentation-url');
4+
5+
const MESSAGE_ID = 'prefer-node-protocol';
6+
const messages = {
7+
[MESSAGE_ID]: 'Prefer `node:{{moduleName}}` over `{{moduleName}}`.'
8+
};
9+
10+
const selector = [
11+
':matches(ImportDeclaration, ExportNamedDeclaration, ImportExpression)',
12+
' > ',
13+
'Literal.source'
14+
].join('');
15+
16+
/** @param {import('eslint').Rule.RuleContext} context */
17+
const create = context => {
18+
return {
19+
[selector](node) {
20+
const {value} = node;
21+
if (
22+
typeof value !== 'string' ||
23+
value.startsWith('node:') ||
24+
!isBuiltinModule(value)
25+
) {
26+
return;
27+
}
28+
29+
const firstCharacterIndex = node.range[0] + 1;
30+
context.report({
31+
node,
32+
messageId: MESSAGE_ID,
33+
data: {moduleName: value},
34+
/** @param {import('eslint').Rule.RuleFixer} fixer */
35+
fix: fixer => fixer.insertTextBeforeRange([firstCharacterIndex, firstCharacterIndex], 'node:')
36+
});
37+
}
38+
};
39+
};
40+
41+
module.exports = {
42+
create,
43+
meta: {
44+
type: 'suggestion',
45+
docs: {
46+
description: 'Prefer using the `node:` protocol when importing Node.js builtin modules.',
47+
url: getDocumentationUrl(__filename)
48+
},
49+
fixable: 'code',
50+
schema: [],
51+
messages
52+
}
53+
};

scripts/generate-rules-table.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// Automatically regenerates the rules table in readme.md.
22

3-
import {readFileSync, writeFileSync} from 'fs';
4-
import path from 'path';
5-
import package_ from '../index.js';
6-
import {fileURLToPath} from 'url';
3+
import {readFileSync, writeFileSync} from 'node:fs';
4+
import path from 'node:path';
5+
import {fileURLToPath} from 'node:url';
76
import outdent from 'outdent';
7+
import package_ from '../index.js';
88

99
const __filename = fileURLToPath(import.meta.url);
1010
const __dirname = path.dirname(__filename);

test/better-regex.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {createRequire} from 'module';
1+
import {createRequire} from 'node:module';
22
import {getTester} from './utils/test.mjs';
33

44
const {test} = getTester(import.meta);

test/custom-error-definition.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {createRequire} from 'module';
1+
import {createRequire} from 'node:module';
22
import test from 'ava';
33
import avaRuleTester from 'eslint-ava-rule-tester';
44
import outdent from 'outdent';

test/no-hex-escape.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {createRequire} from 'module';
1+
import {createRequire} from 'node:module';
22
import test from 'ava';
33
import avaRuleTester from 'eslint-ava-rule-tester';
44
import {getTester} from './utils/test.mjs';

test/number-literal-case.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {createRequire} from 'module';
1+
import {createRequire} from 'node:module';
22
import test from 'ava';
33
import avaRuleTester from 'eslint-ava-rule-tester';
44
import outdent from 'outdent';

0 commit comments

Comments
 (0)