eslint-plugin-n 17.17.0 → 17.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/index.js +1 -0
- package/lib/rules/no-deprecated-api.js +7 -0
- package/lib/rules/no-sync.js +125 -5
- package/lib/rules/no-top-level-await.js +132 -0
- package/lib/rules/no-unpublished-import.js +3 -0
- package/lib/rules/prefer-node-protocol.js +55 -11
- package/lib/rules/prefer-promises/dns.js +7 -0
- package/lib/rules/prefer-promises/fs.js +7 -0
- package/lib/unsupported-features/node-builtins-modules/https.js +5 -5
- package/lib/unsupported-features/node-builtins.js +1 -0
- package/lib/util/check-prefer-global.js +7 -0
- package/lib/util/check-unsupported-builtins.js +7 -0
- package/lib/util/get-full-type-name.js +47 -0
- package/lib/util/get-parser-services.js +24 -0
- package/lib/util/get-type-of-node.js +21 -0
- package/lib/util/iterate-process-get-builtin-module-references.js +56 -0
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -156,6 +156,7 @@ For [Shareable Configs](https://eslint.org/docs/latest/developer-guide/shareable
|
|
|
156
156
|
| [no-restricted-import](docs/rules/no-restricted-import.md) | disallow specified modules when loaded by `import` declarations | | | |
|
|
157
157
|
| [no-restricted-require](docs/rules/no-restricted-require.md) | disallow specified modules when loaded by `require` | | | |
|
|
158
158
|
| [no-sync](docs/rules/no-sync.md) | disallow synchronous methods | | | |
|
|
159
|
+
| [no-top-level-await](docs/rules/no-top-level-await.md) | disallow top-level `await` in published modules | | | |
|
|
159
160
|
| [no-unpublished-bin](docs/rules/no-unpublished-bin.md) | disallow `bin` files that npm ignores | 🟢 ✅ | | |
|
|
160
161
|
| [no-unpublished-import](docs/rules/no-unpublished-import.md) | disallow `import` declarations which import private modules | 🟢 ✅ | | |
|
|
161
162
|
| [no-unpublished-require](docs/rules/no-unpublished-require.md) | disallow `require()` expressions which import private modules | 🟢 ✅ | | |
|
package/lib/index.js
CHANGED
|
@@ -34,6 +34,7 @@ const base = {
|
|
|
34
34
|
"no-restricted-import": require("./rules/no-restricted-import"),
|
|
35
35
|
"no-restricted-require": require("./rules/no-restricted-require"),
|
|
36
36
|
"no-sync": require("./rules/no-sync"),
|
|
37
|
+
"no-top-level-await": require("./rules/no-top-level-await"),
|
|
37
38
|
"no-unpublished-bin": require("./rules/no-unpublished-bin"),
|
|
38
39
|
"no-unpublished-import": require("./rules/no-unpublished-import"),
|
|
39
40
|
"no-unpublished-require": require("./rules/no-unpublished-require"),
|
|
@@ -16,6 +16,9 @@ const getSemverRange = require("../util/get-semver-range")
|
|
|
16
16
|
const extendTrackmapWithNodePrefix = require("../util/extend-trackmap-with-node-prefix")
|
|
17
17
|
const unprefixNodeColon = require("../util/unprefix-node-colon")
|
|
18
18
|
const { getScope } = require("../util/eslint-compat")
|
|
19
|
+
const {
|
|
20
|
+
iterateProcessGetBuiltinModuleReferences,
|
|
21
|
+
} = require("../util/iterate-process-get-builtin-module-references")
|
|
19
22
|
|
|
20
23
|
/** @typedef {import('../unsupported-features/types.js').DeprecatedInfo} DeprecatedInfo */
|
|
21
24
|
/**
|
|
@@ -842,6 +845,10 @@ module.exports = {
|
|
|
842
845
|
}
|
|
843
846
|
for (const report of [
|
|
844
847
|
...tracker.iterateCjsReferences(modules),
|
|
848
|
+
...iterateProcessGetBuiltinModuleReferences(
|
|
849
|
+
tracker,
|
|
850
|
+
modules
|
|
851
|
+
),
|
|
845
852
|
...tracker.iterateEsmReferences(modules),
|
|
846
853
|
]) {
|
|
847
854
|
const { node, path, type, info } = report
|
package/lib/rules/no-sync.js
CHANGED
|
@@ -4,6 +4,22 @@
|
|
|
4
4
|
*/
|
|
5
5
|
"use strict"
|
|
6
6
|
|
|
7
|
+
let typeMatchesSpecifier =
|
|
8
|
+
/** @type {import('ts-declaration-location').default | undefined} */
|
|
9
|
+
(undefined)
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
typeMatchesSpecifier =
|
|
13
|
+
/** @type {import('ts-declaration-location').default} */ (
|
|
14
|
+
/** @type {unknown} */ (require("ts-declaration-location"))
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line no-empty -- Deliberately left empty.
|
|
18
|
+
} catch {}
|
|
19
|
+
const getTypeOfNode = require("../util/get-type-of-node")
|
|
20
|
+
const getParserServices = require("../util/get-parser-services")
|
|
21
|
+
const getFullTypeName = require("../util/get-full-type-name")
|
|
22
|
+
|
|
7
23
|
const selectors = [
|
|
8
24
|
// fs.readFileSync()
|
|
9
25
|
// readFileSync.call(null, 'path')
|
|
@@ -16,7 +32,7 @@ const selectors = [
|
|
|
16
32
|
* @typedef {[
|
|
17
33
|
* {
|
|
18
34
|
* allowAtRootLevel?: boolean
|
|
19
|
-
* ignores?: string[]
|
|
35
|
+
* ignores?: (string | { from: "file"; path?: string; name?: string[]; } | { from: "package"; package?: string; name?: string[]; } | { from: "lib"; name?: string[]; })[]
|
|
20
36
|
* }?
|
|
21
37
|
* ]} RuleOptions
|
|
22
38
|
*/
|
|
@@ -40,7 +56,56 @@ module.exports = {
|
|
|
40
56
|
},
|
|
41
57
|
ignores: {
|
|
42
58
|
type: "array",
|
|
43
|
-
items: {
|
|
59
|
+
items: {
|
|
60
|
+
oneOf: [
|
|
61
|
+
{ type: "string" },
|
|
62
|
+
{
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
from: { const: "file" },
|
|
66
|
+
path: {
|
|
67
|
+
type: "string",
|
|
68
|
+
},
|
|
69
|
+
name: {
|
|
70
|
+
type: "array",
|
|
71
|
+
items: {
|
|
72
|
+
type: "string",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
additionalProperties: false,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {
|
|
81
|
+
from: { const: "lib" },
|
|
82
|
+
name: {
|
|
83
|
+
type: "array",
|
|
84
|
+
items: {
|
|
85
|
+
type: "string",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
additionalProperties: false,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
from: { const: "package" },
|
|
95
|
+
package: {
|
|
96
|
+
type: "string",
|
|
97
|
+
},
|
|
98
|
+
name: {
|
|
99
|
+
type: "array",
|
|
100
|
+
items: {
|
|
101
|
+
type: "string",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
additionalProperties: false,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
44
109
|
default: [],
|
|
45
110
|
},
|
|
46
111
|
},
|
|
@@ -65,15 +130,70 @@ module.exports = {
|
|
|
65
130
|
* @returns {void}
|
|
66
131
|
*/
|
|
67
132
|
[selector.join(",")](node) {
|
|
68
|
-
|
|
69
|
-
|
|
133
|
+
const parserServices = getParserServices(context)
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @type {import('typescript').Type | undefined | null}
|
|
137
|
+
*/
|
|
138
|
+
let type = undefined
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @type {string | undefined | null}
|
|
142
|
+
*/
|
|
143
|
+
let fullName = undefined
|
|
144
|
+
|
|
145
|
+
for (const ignore of ignores) {
|
|
146
|
+
if (typeof ignore === "string") {
|
|
147
|
+
if (ignore === node.name) {
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (
|
|
155
|
+
parserServices === null ||
|
|
156
|
+
parserServices.program === null
|
|
157
|
+
) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
'TypeScript parser services not available. Rule "n/no-sync" is configured to use "ignores" option with a non-string value. This requires TypeScript parser services to be available.'
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (typeMatchesSpecifier === undefined) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
'ts-declaration-location not available. Rule "n/no-sync" is configured to use "ignores" option with a non-string value. This requires ts-declaration-location to be available.'
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
type =
|
|
170
|
+
type === undefined
|
|
171
|
+
? getTypeOfNode(node, parserServices)
|
|
172
|
+
: type
|
|
173
|
+
|
|
174
|
+
fullName =
|
|
175
|
+
fullName === undefined
|
|
176
|
+
? getFullTypeName(type)
|
|
177
|
+
: fullName
|
|
178
|
+
|
|
179
|
+
if (
|
|
180
|
+
typeMatchesSpecifier(
|
|
181
|
+
parserServices.program,
|
|
182
|
+
ignore,
|
|
183
|
+
type
|
|
184
|
+
) &&
|
|
185
|
+
(ignore.name === undefined ||
|
|
186
|
+
ignore.name.includes(fullName ?? node.name))
|
|
187
|
+
) {
|
|
188
|
+
return
|
|
189
|
+
}
|
|
70
190
|
}
|
|
71
191
|
|
|
72
192
|
context.report({
|
|
73
193
|
node: node.parent,
|
|
74
194
|
messageId: "noSync",
|
|
75
195
|
data: {
|
|
76
|
-
propertyName: node.name,
|
|
196
|
+
propertyName: fullName ?? node.name,
|
|
77
197
|
},
|
|
78
198
|
})
|
|
79
199
|
},
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Yosuke Ota <https://github.com/ota-meshi>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const path = require("path")
|
|
8
|
+
const { getSourceCode } = require("../util/eslint-compat")
|
|
9
|
+
const getConvertPath = require("../util/get-convert-path")
|
|
10
|
+
const { getPackageJson } = require("../util/get-package-json")
|
|
11
|
+
const { isBinFile } = require("../util/is-bin-file")
|
|
12
|
+
const getNpmignore = require("../util/get-npmignore")
|
|
13
|
+
|
|
14
|
+
const HASHBANG_ENV = "#!/usr/bin/env"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {[
|
|
18
|
+
* {
|
|
19
|
+
* ignoreBin?:boolean
|
|
20
|
+
* convertPath?: import('../util/get-convert-path').ConvertPath;
|
|
21
|
+
* }?
|
|
22
|
+
* ]} RuleOptions
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks whether the code has a hashbang comment or not.
|
|
27
|
+
* @param {import('./rule-module').PluginRuleContext<{RuleOptions: RuleOptions}>} context
|
|
28
|
+
*/
|
|
29
|
+
function hasHashbang(context) {
|
|
30
|
+
const sourceCode = getSourceCode(context)
|
|
31
|
+
return Boolean(sourceCode.text.startsWith(HASHBANG_ENV))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** @param {import('./rule-module').PluginRuleContext<{RuleOptions: RuleOptions}>} context */
|
|
35
|
+
function ignore(context) {
|
|
36
|
+
const options = context.options[0] || {}
|
|
37
|
+
const ignoreBin = options.ignoreBin ?? false
|
|
38
|
+
if (ignoreBin && hasHashbang(context)) {
|
|
39
|
+
// If the code has a hashbang comment, it is considered an executable file.
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const filePath = context.filename ?? context.getFilename()
|
|
44
|
+
if (filePath === "<input>") {
|
|
45
|
+
// The file path is "<input>" (not specified), so it will be ignored.
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
const originalAbsolutePath = path.resolve(filePath)
|
|
49
|
+
|
|
50
|
+
// Find package.json
|
|
51
|
+
const packageJson = getPackageJson(originalAbsolutePath)
|
|
52
|
+
if (typeof packageJson?.filePath !== "string") {
|
|
53
|
+
// The file is not in a package, so it will be ignored.
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Convert by convertPath option
|
|
58
|
+
const packageDirectory = path.dirname(packageJson.filePath)
|
|
59
|
+
const convertedRelativePath = getConvertPath(context)(
|
|
60
|
+
path
|
|
61
|
+
.relative(packageDirectory, originalAbsolutePath)
|
|
62
|
+
.replace(/\\/gu, "/")
|
|
63
|
+
)
|
|
64
|
+
const convertedAbsolutePath = path.resolve(
|
|
65
|
+
packageDirectory,
|
|
66
|
+
convertedRelativePath
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if (
|
|
70
|
+
ignoreBin &&
|
|
71
|
+
isBinFile(convertedAbsolutePath, packageJson.bin, packageDirectory)
|
|
72
|
+
) {
|
|
73
|
+
// The file is defined in the `bin` field of `package.json`
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check ignored or not
|
|
78
|
+
const npmignore = getNpmignore(convertedAbsolutePath)
|
|
79
|
+
if (npmignore.match(convertedRelativePath)) {
|
|
80
|
+
// The file is unpublished file, so it will be ignored.
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** @type {import('./rule-module').RuleModule<{RuleOptions: RuleOptions}>} */
|
|
88
|
+
module.exports = {
|
|
89
|
+
meta: {
|
|
90
|
+
docs: {
|
|
91
|
+
description: "disallow top-level `await` in published modules",
|
|
92
|
+
recommended: false,
|
|
93
|
+
url: "https://github.com/eslint-community/eslint-plugin-n/blob/HEAD/docs/rules/no-top-level-await.md",
|
|
94
|
+
},
|
|
95
|
+
fixable: null,
|
|
96
|
+
messages: {
|
|
97
|
+
forbidden: "Top-level `await` is forbidden in published modules.",
|
|
98
|
+
},
|
|
99
|
+
schema: [
|
|
100
|
+
{
|
|
101
|
+
type: "object",
|
|
102
|
+
properties: {
|
|
103
|
+
ignoreBin: { type: "boolean" },
|
|
104
|
+
convertPath: getConvertPath.schema,
|
|
105
|
+
},
|
|
106
|
+
additionalProperties: false,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
type: "problem",
|
|
110
|
+
},
|
|
111
|
+
create(context) {
|
|
112
|
+
if (ignore(context)) {
|
|
113
|
+
return {}
|
|
114
|
+
}
|
|
115
|
+
let functionDepth = 0
|
|
116
|
+
return {
|
|
117
|
+
":function"() {
|
|
118
|
+
functionDepth++
|
|
119
|
+
},
|
|
120
|
+
":function:exit"() {
|
|
121
|
+
functionDepth--
|
|
122
|
+
},
|
|
123
|
+
"AwaitExpression, ForOfStatement[await=true]"(node) {
|
|
124
|
+
if (functionDepth > 0) {
|
|
125
|
+
// not top-level
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
context.report({ node, messageId: "forbidden" })
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
}
|
|
@@ -9,6 +9,7 @@ const getAllowModules = require("../util/get-allow-modules")
|
|
|
9
9
|
const getConvertPath = require("../util/get-convert-path")
|
|
10
10
|
const getResolvePaths = require("../util/get-resolve-paths")
|
|
11
11
|
const getResolverConfig = require("../util/get-resolver-config")
|
|
12
|
+
const getTryExtensions = require("../util/get-try-extensions")
|
|
12
13
|
const visitImport = require("../util/visit-import")
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -18,6 +19,7 @@ const visitImport = require("../util/visit-import")
|
|
|
18
19
|
* convertPath?: import('../util/get-convert-path').ConvertPath;
|
|
19
20
|
* resolvePaths?: import('../util/get-resolve-paths').ResolvePaths;
|
|
20
21
|
* resolverConfig?: import('../util/get-resolver-config').ResolverConfig;
|
|
22
|
+
* tryExtensions?: import('../util/get-try-extensions').TryExtensions;
|
|
21
23
|
* ignoreTypeImport?: boolean;
|
|
22
24
|
* ignorePrivate?: boolean;
|
|
23
25
|
* }?
|
|
@@ -42,6 +44,7 @@ module.exports = {
|
|
|
42
44
|
convertPath: getConvertPath.schema,
|
|
43
45
|
resolvePaths: getResolvePaths.schema,
|
|
44
46
|
resolverConfig: getResolverConfig.schema,
|
|
47
|
+
tryExtensions: getTryExtensions.schema,
|
|
45
48
|
ignoreTypeImport: { type: "boolean", default: false },
|
|
46
49
|
ignorePrivate: { type: "boolean", default: true },
|
|
47
50
|
},
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
"use strict"
|
|
6
6
|
|
|
7
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
getStringIfConstant,
|
|
9
|
+
getPropertyName,
|
|
10
|
+
} = require("@eslint-community/eslint-utils")
|
|
8
11
|
|
|
9
12
|
const { Range } = require("semver")
|
|
10
13
|
|
|
@@ -15,6 +18,10 @@ const {
|
|
|
15
18
|
NodeBuiltinModules,
|
|
16
19
|
} = require("../unsupported-features/node-builtins.js")
|
|
17
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @typedef { 'import' | 'require' | 'getBuiltinModule' } ModuleStyle
|
|
23
|
+
*/
|
|
24
|
+
|
|
18
25
|
/**
|
|
19
26
|
* @param {string} name The name of the node module
|
|
20
27
|
* @returns {boolean}
|
|
@@ -38,10 +45,15 @@ function isStringLiteral(node) {
|
|
|
38
45
|
|
|
39
46
|
/**
|
|
40
47
|
* @param {import('eslint').Rule.RuleContext} context
|
|
41
|
-
* @param {
|
|
48
|
+
* @param {ModuleStyle} moduleStyle
|
|
42
49
|
* @returns {boolean}
|
|
43
50
|
*/
|
|
44
51
|
function isEnablingThisRule(context, moduleStyle) {
|
|
52
|
+
// The availability of `process.getBuiltinModule()` means that `node:` protocol is supported.
|
|
53
|
+
if (moduleStyle === "getBuiltinModule") {
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
const version = getConfiguredNodeVersion(context)
|
|
46
58
|
|
|
47
59
|
// Only check Node.js version because this rule is meaningless if configured Node.js version doesn't match semver range.
|
|
@@ -81,7 +93,7 @@ function isValidRequireArgument(node) {
|
|
|
81
93
|
/**
|
|
82
94
|
* @param {import('estree').Node | null | undefined} node
|
|
83
95
|
* @param {import('eslint').Rule.RuleContext} context
|
|
84
|
-
* @param {
|
|
96
|
+
* @param {ModuleStyle} moduleStyle
|
|
85
97
|
*/
|
|
86
98
|
function validate(node, context, moduleStyle) {
|
|
87
99
|
if (node == null) {
|
|
@@ -96,7 +108,10 @@ function validate(node, context, moduleStyle) {
|
|
|
96
108
|
return
|
|
97
109
|
}
|
|
98
110
|
|
|
99
|
-
if (
|
|
111
|
+
if (
|
|
112
|
+
(moduleStyle === "require" || moduleStyle === "getBuiltinModule") &&
|
|
113
|
+
!isValidRequireArgument(node)
|
|
114
|
+
) {
|
|
100
115
|
return
|
|
101
116
|
}
|
|
102
117
|
|
|
@@ -126,6 +141,25 @@ function validate(node, context, moduleStyle) {
|
|
|
126
141
|
})
|
|
127
142
|
}
|
|
128
143
|
|
|
144
|
+
/**
|
|
145
|
+
* @param {import('estree').Expression | import('estree').Super} node
|
|
146
|
+
*/
|
|
147
|
+
function isProcess(node) {
|
|
148
|
+
if (node.type === "Identifier" && node.name === "process") {
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
if (node.type === "MemberExpression") {
|
|
152
|
+
if (getPropertyName(node) !== "process") {
|
|
153
|
+
return false
|
|
154
|
+
}
|
|
155
|
+
return (
|
|
156
|
+
node.object.type === "Identifier" &&
|
|
157
|
+
node.object.name === "globalThis"
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
162
|
+
|
|
129
163
|
/** @type {import('./rule-module').RuleModule} */
|
|
130
164
|
module.exports = {
|
|
131
165
|
meta: {
|
|
@@ -158,15 +192,25 @@ module.exports = {
|
|
|
158
192
|
}
|
|
159
193
|
|
|
160
194
|
if (
|
|
161
|
-
node.optional
|
|
162
|
-
node.arguments.length
|
|
163
|
-
node.callee.type
|
|
164
|
-
node.callee.name
|
|
195
|
+
!node.optional &&
|
|
196
|
+
node.arguments.length === 1 &&
|
|
197
|
+
node.callee.type === "Identifier" &&
|
|
198
|
+
node.callee.name === "require"
|
|
165
199
|
) {
|
|
166
|
-
return
|
|
200
|
+
return validate(node.arguments[0], context, "require")
|
|
201
|
+
}
|
|
202
|
+
if (
|
|
203
|
+
node.arguments.length >= 1 &&
|
|
204
|
+
node.callee.type === "MemberExpression" &&
|
|
205
|
+
isProcess(node.callee.object) &&
|
|
206
|
+
getPropertyName(node.callee) === "getBuiltinModule"
|
|
207
|
+
) {
|
|
208
|
+
return validate(
|
|
209
|
+
node.arguments[0],
|
|
210
|
+
context,
|
|
211
|
+
"getBuiltinModule"
|
|
212
|
+
)
|
|
167
213
|
}
|
|
168
|
-
|
|
169
|
-
return validate(node.arguments[0], context, "require")
|
|
170
214
|
},
|
|
171
215
|
|
|
172
216
|
ExportAllDeclaration(node) {
|
|
@@ -10,6 +10,9 @@ const {
|
|
|
10
10
|
ReferenceTracker,
|
|
11
11
|
} = require("@eslint-community/eslint-utils")
|
|
12
12
|
const { getScope } = require("../../util/eslint-compat")
|
|
13
|
+
const {
|
|
14
|
+
iterateProcessGetBuiltinModuleReferences,
|
|
15
|
+
} = require("../../util/iterate-process-get-builtin-module-references")
|
|
13
16
|
|
|
14
17
|
/** @type {import('@eslint-community/eslint-utils').TraceMap<boolean>} */
|
|
15
18
|
const dns = {
|
|
@@ -63,6 +66,10 @@ module.exports = {
|
|
|
63
66
|
const tracker = new ReferenceTracker(scope, { mode: "legacy" })
|
|
64
67
|
const references = [
|
|
65
68
|
...tracker.iterateCjsReferences(traceMap),
|
|
69
|
+
...iterateProcessGetBuiltinModuleReferences(
|
|
70
|
+
tracker,
|
|
71
|
+
traceMap
|
|
72
|
+
),
|
|
66
73
|
...tracker.iterateEsmReferences(traceMap),
|
|
67
74
|
]
|
|
68
75
|
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils")
|
|
8
8
|
const { getScope } = require("../../util/eslint-compat")
|
|
9
|
+
const {
|
|
10
|
+
iterateProcessGetBuiltinModuleReferences,
|
|
11
|
+
} = require("../../util/iterate-process-get-builtin-module-references")
|
|
9
12
|
|
|
10
13
|
/** @type {import('@eslint-community/eslint-utils').TraceMap<boolean>} */
|
|
11
14
|
const traceMap = {
|
|
@@ -61,6 +64,10 @@ module.exports = {
|
|
|
61
64
|
const tracker = new ReferenceTracker(scope, { mode: "legacy" })
|
|
62
65
|
const references = [
|
|
63
66
|
...tracker.iterateCjsReferences(traceMap),
|
|
67
|
+
...iterateProcessGetBuiltinModuleReferences(
|
|
68
|
+
tracker,
|
|
69
|
+
traceMap
|
|
70
|
+
),
|
|
64
71
|
...tracker.iterateEsmReferences(traceMap),
|
|
65
72
|
]
|
|
66
73
|
|
|
@@ -5,7 +5,7 @@ const { READ } = require("@eslint-community/eslint-utils")
|
|
|
5
5
|
/**
|
|
6
6
|
* @satisfies {import('../types.js').SupportVersionTraceMap}
|
|
7
7
|
*/
|
|
8
|
-
const
|
|
8
|
+
const https = {
|
|
9
9
|
globalAgent: { [READ]: { supported: ["0.5.9"] } },
|
|
10
10
|
createServer: { [READ]: { supported: ["0.3.4"] } },
|
|
11
11
|
get: { [READ]: { supported: ["0.3.6"] } },
|
|
@@ -18,12 +18,12 @@ const http = {
|
|
|
18
18
|
* @satisfies {import('../types.js').SupportVersionTraceMap}
|
|
19
19
|
*/
|
|
20
20
|
module.exports = {
|
|
21
|
-
|
|
21
|
+
https: {
|
|
22
22
|
[READ]: { supported: ["0.3.4"] },
|
|
23
|
-
...
|
|
23
|
+
...https,
|
|
24
24
|
},
|
|
25
|
-
"node:
|
|
25
|
+
"node:https": {
|
|
26
26
|
[READ]: { supported: ["14.13.1", "12.20.0"] },
|
|
27
|
-
...
|
|
27
|
+
...https,
|
|
28
28
|
},
|
|
29
29
|
}
|
|
@@ -37,6 +37,7 @@ const NodeBuiltinModules = {
|
|
|
37
37
|
...require("./node-builtins-modules/sea.js"),
|
|
38
38
|
...require("./node-builtins-modules/stream.js"),
|
|
39
39
|
...require("./node-builtins-modules/string_decoder.js"),
|
|
40
|
+
...require("./node-builtins-modules/sqlite.js"),
|
|
40
41
|
...require("./node-builtins-modules/test.js"),
|
|
41
42
|
...require("./node-builtins-modules/timers.js"),
|
|
42
43
|
...require("./node-builtins-modules/tls.js"),
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
const { ReferenceTracker } = require("@eslint-community/eslint-utils")
|
|
8
8
|
const { getScope } = require("../util/eslint-compat")
|
|
9
|
+
const {
|
|
10
|
+
iterateProcessGetBuiltinModuleReferences,
|
|
11
|
+
} = require("../util/iterate-process-get-builtin-module-references")
|
|
9
12
|
/**
|
|
10
13
|
* @typedef TraceMap
|
|
11
14
|
* @property {import('@eslint-community/eslint-utils').TraceMap<boolean>} globals
|
|
@@ -43,6 +46,10 @@ class Verifier {
|
|
|
43
46
|
|
|
44
47
|
for (const { node } of [
|
|
45
48
|
...tracker.iterateCjsReferences(traceMap.modules),
|
|
49
|
+
...iterateProcessGetBuiltinModuleReferences(
|
|
50
|
+
tracker,
|
|
51
|
+
traceMap.modules
|
|
52
|
+
),
|
|
46
53
|
...tracker.iterateEsmReferences(traceMap.modules),
|
|
47
54
|
]) {
|
|
48
55
|
context.report({ node, messageId: "preferGlobal" })
|
|
@@ -11,6 +11,9 @@ const getSemverRange = require("./get-semver-range")
|
|
|
11
11
|
const unprefixNodeColon = require("./unprefix-node-colon")
|
|
12
12
|
const semverRangeSubset = require("semver/ranges/subset")
|
|
13
13
|
const { getScope } = require("../util/eslint-compat")
|
|
14
|
+
const {
|
|
15
|
+
iterateProcessGetBuiltinModuleReferences,
|
|
16
|
+
} = require("./iterate-process-get-builtin-module-references")
|
|
14
17
|
|
|
15
18
|
/**
|
|
16
19
|
* Parses the options.
|
|
@@ -158,6 +161,10 @@ module.exports.checkUnsupportedBuiltins = function checkUnsupportedBuiltins(
|
|
|
158
161
|
const tracker = new ReferenceTracker(scope, { mode: "legacy" })
|
|
159
162
|
const references = [
|
|
160
163
|
...tracker.iterateCjsReferences(traceMap.modules ?? {}),
|
|
164
|
+
...iterateProcessGetBuiltinModuleReferences(
|
|
165
|
+
tracker,
|
|
166
|
+
traceMap.modules ?? {}
|
|
167
|
+
),
|
|
161
168
|
...tracker.iterateEsmReferences(traceMap.modules ?? {}),
|
|
162
169
|
...tracker.iterateGlobalReferences(traceMap.globals ?? {}),
|
|
163
170
|
]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
const ts = (() => {
|
|
4
|
+
try {
|
|
5
|
+
// eslint-disable-next-line n/no-unpublished-require
|
|
6
|
+
return require("typescript")
|
|
7
|
+
} catch {
|
|
8
|
+
return null
|
|
9
|
+
}
|
|
10
|
+
})()
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {import('typescript').Type | null} type
|
|
14
|
+
* @returns {string | null}
|
|
15
|
+
*/
|
|
16
|
+
module.exports = function getFullTypeName(type) {
|
|
17
|
+
if (ts === null || type === null) {
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @type {string[]}
|
|
23
|
+
*/
|
|
24
|
+
let nameParts = []
|
|
25
|
+
let currentSymbol = type.getSymbol()
|
|
26
|
+
while (currentSymbol !== undefined) {
|
|
27
|
+
if (
|
|
28
|
+
currentSymbol.valueDeclaration?.kind === ts.SyntaxKind.SourceFile ||
|
|
29
|
+
currentSymbol.valueDeclaration?.kind ===
|
|
30
|
+
ts.SyntaxKind.ModuleDeclaration
|
|
31
|
+
) {
|
|
32
|
+
break
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
nameParts.unshift(currentSymbol.getName())
|
|
36
|
+
currentSymbol =
|
|
37
|
+
/** @type {import('typescript').Symbol & {parent: import('typescript').Symbol | undefined}} */ (
|
|
38
|
+
currentSymbol
|
|
39
|
+
).parent
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (nameParts.length === 0) {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return nameParts.join(".")
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getParserServices: getParserServicesFromTsEslint,
|
|
5
|
+
} = require("@typescript-eslint/utils/eslint-utils")
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get the TypeScript parser services.
|
|
9
|
+
* If TypeScript isn't present, returns `null`.
|
|
10
|
+
*
|
|
11
|
+
* @param {import('eslint').Rule.RuleContext} context - rule context
|
|
12
|
+
* @returns {import('@typescript-eslint/parser').ParserServices | null}
|
|
13
|
+
*/
|
|
14
|
+
module.exports = function getParserServices(context) {
|
|
15
|
+
// Not using tseslint parser?
|
|
16
|
+
if (
|
|
17
|
+
context.sourceCode.parserServices?.esTreeNodeToTSNodeMap == null ||
|
|
18
|
+
context.sourceCode.parserServices.tsNodeToESTreeNodeMap == null
|
|
19
|
+
) {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return getParserServicesFromTsEslint(/** @type {any} */ (context), true)
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the type of a node.
|
|
5
|
+
* If TypeScript isn't present, returns `null`.
|
|
6
|
+
*
|
|
7
|
+
* @param {import('estree').Node} node - A node
|
|
8
|
+
* @param {import('@typescript-eslint/parser').ParserServices} parserServices - A parserServices
|
|
9
|
+
* @returns {import('typescript').Type | null}
|
|
10
|
+
*/
|
|
11
|
+
module.exports = function getTypeOfNode(node, parserServices) {
|
|
12
|
+
const { esTreeNodeToTSNodeMap, program } = parserServices
|
|
13
|
+
if (program === null) {
|
|
14
|
+
return null
|
|
15
|
+
}
|
|
16
|
+
const tsNode = esTreeNodeToTSNodeMap.get(/** @type {any} */ (node))
|
|
17
|
+
const checker = program.getTypeChecker()
|
|
18
|
+
const nodeType = checker.getTypeAtLocation(tsNode)
|
|
19
|
+
const constrained = checker.getBaseConstraintOfType(nodeType)
|
|
20
|
+
return constrained ?? nodeType
|
|
21
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
const {
|
|
3
|
+
CALL,
|
|
4
|
+
getStringIfConstant,
|
|
5
|
+
READ,
|
|
6
|
+
} = require("@eslint-community/eslint-utils")
|
|
7
|
+
const processGetBuiltinModuleCall = {
|
|
8
|
+
process: {
|
|
9
|
+
getBuiltinModule: {
|
|
10
|
+
[CALL]: true,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Iterate the references of process.getBuiltinModule() modules.
|
|
16
|
+
* @template Info
|
|
17
|
+
* @param {import("@eslint-community/eslint-utils").ReferenceTracker} tracker The reference tracker.
|
|
18
|
+
* @param {import("@eslint-community/eslint-utils").TraceMap<Info>} traceMap The trace map.
|
|
19
|
+
* @returns {IterableIterator<import("@eslint-community/eslint-utils").Reference<Info>>} The iterator.
|
|
20
|
+
*/
|
|
21
|
+
function* iterateProcessGetBuiltinModuleReferences(tracker, traceMap) {
|
|
22
|
+
for (const { node } of tracker.iterateGlobalReferences(
|
|
23
|
+
processGetBuiltinModuleCall
|
|
24
|
+
)) {
|
|
25
|
+
if (node.type !== "CallExpression") continue
|
|
26
|
+
const key = node.arguments[0] && getStringIfConstant(node.arguments[0])
|
|
27
|
+
if (key == null) {
|
|
28
|
+
continue
|
|
29
|
+
}
|
|
30
|
+
const nextTraceMap = Object.hasOwn(traceMap, key) && traceMap[key]
|
|
31
|
+
if (!nextTraceMap) {
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (nextTraceMap[READ]) {
|
|
36
|
+
yield {
|
|
37
|
+
node,
|
|
38
|
+
path: [key],
|
|
39
|
+
type: READ,
|
|
40
|
+
info: nextTraceMap[READ],
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const ref of tracker.iteratePropertyReferences(
|
|
45
|
+
node,
|
|
46
|
+
nextTraceMap
|
|
47
|
+
)) {
|
|
48
|
+
yield {
|
|
49
|
+
...ref,
|
|
50
|
+
path: [key, ...ref.path],
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { iterateProcessGetBuiltinModuleReferences }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-n",
|
|
3
|
-
"version": "17.
|
|
3
|
+
"version": "17.19.0",
|
|
4
4
|
"description": "Additional ESLint's rules for Node.js",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
@@ -18,21 +18,23 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@eslint-community/eslint-utils": "^4.5.0",
|
|
21
|
+
"@typescript-eslint/utils": "^8.26.1",
|
|
21
22
|
"enhanced-resolve": "^5.17.1",
|
|
22
23
|
"eslint-plugin-es-x": "^7.8.0",
|
|
23
24
|
"get-tsconfig": "^4.8.1",
|
|
24
25
|
"globals": "^15.11.0",
|
|
25
26
|
"ignore": "^5.3.2",
|
|
26
27
|
"minimatch": "^9.0.5",
|
|
27
|
-
"semver": "^7.6.3"
|
|
28
|
+
"semver": "^7.6.3",
|
|
29
|
+
"ts-declaration-location": "^1.0.6"
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
30
32
|
"@eslint/js": "^9.14.0",
|
|
31
33
|
"@types/eslint": "^9.6.1",
|
|
32
34
|
"@types/estree": "^1.0.6",
|
|
33
35
|
"@types/node": "^20.17.5",
|
|
34
|
-
"@typescript-eslint/parser": "^8.
|
|
35
|
-
"@typescript-eslint/typescript-estree": "^8.
|
|
36
|
+
"@typescript-eslint/parser": "^8.26.1",
|
|
37
|
+
"@typescript-eslint/typescript-estree": "^8.26.1",
|
|
36
38
|
"eslint": "^9.14.0",
|
|
37
39
|
"eslint-config-prettier": "^9.1.0",
|
|
38
40
|
"eslint-doc-generator": "^1.7.1",
|
|
@@ -120,4 +122,4 @@
|
|
|
120
122
|
"imports": {
|
|
121
123
|
"#test-helpers": "./tests/test-helpers.js"
|
|
122
124
|
}
|
|
123
|
-
}
|
|
125
|
+
}
|