eslint-plugin-n 16.6.2 → 17.0.0-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 +5 -2
- package/lib/rules/file-extension-in-import.js +41 -32
- package/lib/rules/no-hide-core-modules.js +7 -24
- package/lib/util/check-existence.js +34 -12
- package/lib/util/check-publish.js +2 -1
- package/lib/util/exists.js +4 -0
- package/lib/util/get-try-extensions.js +39 -10
- package/lib/util/get-tsconfig.js +19 -1
- package/lib/util/get-typescript-extension-map.js +12 -11
- package/lib/util/import-target.js +232 -106
- package/lib/util/visit-import.js +7 -1
- package/lib/util/visit-require.js +7 -1
- package/package.json +8 -8
- package/lib/converted-esm/import-meta-resolve.js +0 -1274
|
@@ -4,98 +4,57 @@
|
|
|
4
4
|
*/
|
|
5
5
|
"use strict"
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
require("util").inspect.defaultOptions.depth = null
|
|
8
|
+
|
|
9
|
+
const { resolve } = require("path")
|
|
9
10
|
const isBuiltin = require("is-builtin-module")
|
|
10
|
-
const
|
|
11
|
-
const {
|
|
12
|
-
defaultResolve: importResolve,
|
|
13
|
-
} = require("../converted-esm/import-meta-resolve")
|
|
11
|
+
const resolver = require("enhanced-resolve")
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* @param {string} id The id to resolve.
|
|
19
|
-
* @param {object} options The options of node-resolve module.
|
|
20
|
-
* It requires `options.basedir`.
|
|
21
|
-
* @param {'import' | 'require'} moduleType - whether the target was require-ed or imported
|
|
22
|
-
* @returns {string|null} The resolved path.
|
|
23
|
-
*/
|
|
24
|
-
function getFilePath(isModule, id, options, moduleType) {
|
|
25
|
-
if (moduleType === "import") {
|
|
26
|
-
const paths =
|
|
27
|
-
options.paths && options.paths.length > 0
|
|
28
|
-
? options.paths.map(p => path.resolve(process.cwd(), p))
|
|
29
|
-
: [options.basedir]
|
|
30
|
-
for (const aPath of paths) {
|
|
31
|
-
try {
|
|
32
|
-
const { url } = importResolve(id, {
|
|
33
|
-
parentURL: pathToFileURL(path.join(aPath, "dummy-file.mjs"))
|
|
34
|
-
.href,
|
|
35
|
-
conditions: ["node", "import", "require"],
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
if (url) {
|
|
39
|
-
return fileURLToPath(url)
|
|
40
|
-
}
|
|
41
|
-
} catch (e) {
|
|
42
|
-
continue
|
|
43
|
-
}
|
|
44
|
-
}
|
|
13
|
+
const isTypescript = require("./is-typescript")
|
|
14
|
+
const { getTSConfigForContext } = require("./get-tsconfig.js")
|
|
15
|
+
const getTypescriptExtensionMap = require("./get-typescript-extension-map")
|
|
45
16
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return path.resolve(
|
|
50
|
-
(options.paths && options.paths[0]) || options.basedir,
|
|
51
|
-
id
|
|
52
|
-
)
|
|
53
|
-
} else {
|
|
54
|
-
try {
|
|
55
|
-
return resolve.sync(id, options)
|
|
56
|
-
} catch (_err) {
|
|
57
|
-
try {
|
|
58
|
-
const { url } = importResolve(id, {
|
|
59
|
-
parentURL: pathToFileURL(
|
|
60
|
-
path.join(options.basedir, "dummy-file.js")
|
|
61
|
-
).href,
|
|
62
|
-
conditions: ["node", "require"],
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
return fileURLToPath(url)
|
|
66
|
-
} catch (err) {
|
|
67
|
-
if (isModule) {
|
|
68
|
-
return null
|
|
69
|
-
}
|
|
70
|
-
return path.resolve(options.basedir, id)
|
|
71
|
-
}
|
|
72
|
-
}
|
|
17
|
+
function removeTrailWildcard(input) {
|
|
18
|
+
if (Array.isArray(input)) {
|
|
19
|
+
return [...input].map(removeTrailWildcard)
|
|
73
20
|
}
|
|
21
|
+
|
|
22
|
+
return input.replace(/[/\\*]+$/, "")
|
|
74
23
|
}
|
|
75
24
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Initialize this instance.
|
|
27
|
+
* @param {import('eslint').Rule.RuleContext} context - The context for the import origin.
|
|
28
|
+
* @returns {import('enhanced-resolve').ResolveOptions['alias'] | undefined}
|
|
29
|
+
*/
|
|
30
|
+
function getTSConfigAliases(context) {
|
|
31
|
+
const tsConfig = getTSConfigForContext(context)
|
|
32
|
+
|
|
33
|
+
const paths = tsConfig?.config?.compilerOptions?.paths
|
|
34
|
+
|
|
35
|
+
if (paths == null) {
|
|
36
|
+
return
|
|
81
37
|
}
|
|
38
|
+
|
|
39
|
+
return Object.entries(paths).map(([name, alias]) => ({
|
|
40
|
+
name: removeTrailWildcard(name),
|
|
41
|
+
alias: removeTrailWildcard(alias),
|
|
42
|
+
}))
|
|
82
43
|
}
|
|
83
44
|
|
|
84
45
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
|
|
90
|
-
|
|
46
|
+
* @typedef {Object} Options
|
|
47
|
+
* @property {string[]} [extensions]
|
|
48
|
+
* @property {string[]} [paths]
|
|
49
|
+
* @property {string} basedir
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* @typedef { 'unknown' | 'relative' | 'absolute' | 'node' | 'npm' | 'http' } ModuleType
|
|
53
|
+
* @typedef { 'import' | 'require' | 'type' } ModuleStyle
|
|
91
54
|
*/
|
|
92
|
-
function getModuleName(nameOrPath) {
|
|
93
|
-
let end = nameOrPath.indexOf("/")
|
|
94
|
-
if (end !== -1 && nameOrPath[0] === "@") {
|
|
95
|
-
end = nameOrPath.indexOf("/", 1 + end)
|
|
96
|
-
}
|
|
97
55
|
|
|
98
|
-
|
|
56
|
+
function trimAfter(string, matcher, count = 1) {
|
|
57
|
+
return string.split(matcher).slice(0, count).join(matcher)
|
|
99
58
|
}
|
|
100
59
|
|
|
101
60
|
/**
|
|
@@ -104,17 +63,22 @@ function getModuleName(nameOrPath) {
|
|
|
104
63
|
module.exports = class ImportTarget {
|
|
105
64
|
/**
|
|
106
65
|
* Initialize this instance.
|
|
107
|
-
* @param {
|
|
66
|
+
* @param {import('eslint').Rule.RuleContext} context - The context for the import origin.
|
|
67
|
+
* @param {import('eslint').Rule.Node} node - The node of a `require()` or a module declaraiton.
|
|
108
68
|
* @param {string} name - The name of an import target.
|
|
109
|
-
* @param {
|
|
69
|
+
* @param {Options} options - The options of `enhanced-resolve` module.
|
|
110
70
|
* @param {'import' | 'require'} moduleType - whether the target was require-ed or imported
|
|
111
71
|
*/
|
|
112
|
-
constructor(node, name, options, moduleType) {
|
|
113
|
-
|
|
72
|
+
constructor(context, node, name, options, moduleType) {
|
|
73
|
+
/**
|
|
74
|
+
* The context for the import origin
|
|
75
|
+
* @type {import('eslint').Rule.Node}
|
|
76
|
+
*/
|
|
77
|
+
this.context = context
|
|
114
78
|
|
|
115
79
|
/**
|
|
116
80
|
* The node of a `require()` or a module declaraiton.
|
|
117
|
-
* @type {
|
|
81
|
+
* @type {import('eslint').Rule.Node}
|
|
118
82
|
*/
|
|
119
83
|
this.node = node
|
|
120
84
|
|
|
@@ -125,35 +89,197 @@ module.exports = class ImportTarget {
|
|
|
125
89
|
this.name = name
|
|
126
90
|
|
|
127
91
|
/**
|
|
128
|
-
*
|
|
129
|
-
* @type {
|
|
92
|
+
* The import target options.
|
|
93
|
+
* @type {Options}
|
|
130
94
|
*/
|
|
131
|
-
this.
|
|
132
|
-
|
|
133
|
-
if (name.startsWith("./") || name.startsWith(".\\")) {
|
|
134
|
-
this.moduleType = "relative"
|
|
135
|
-
} else if (name.startsWith("/") || name.startsWith("\\")) {
|
|
136
|
-
this.moduleType = "absolute"
|
|
137
|
-
} else if (isBuiltin(name)) {
|
|
138
|
-
this.moduleType = "node"
|
|
139
|
-
} else if (isNodeModule(name, options)) {
|
|
140
|
-
this.moduleType = "npm"
|
|
141
|
-
} else if (name.startsWith("http://") || name.startsWith("https://")) {
|
|
142
|
-
this.moduleType = "http"
|
|
143
|
-
}
|
|
95
|
+
this.options = options
|
|
144
96
|
|
|
145
97
|
/**
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* @type {string|null}
|
|
98
|
+
* What type of module are we looking for?
|
|
99
|
+
* @type {ModuleType}
|
|
149
100
|
*/
|
|
150
|
-
this.
|
|
101
|
+
this.moduleType = this.getModuleType()
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* What import style are we using
|
|
105
|
+
* @type {ModuleStyle}
|
|
106
|
+
*/
|
|
107
|
+
this.moduleStyle = this.getModuleStyle(moduleType)
|
|
151
108
|
|
|
152
109
|
/**
|
|
153
110
|
* The module name of this import target.
|
|
154
111
|
* If the target is a relative path then this is `null`.
|
|
155
|
-
* @type {string|null}
|
|
112
|
+
* @type {string | null}
|
|
113
|
+
*/
|
|
114
|
+
this.moduleName = this.getModuleName()
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The full path of this import target.
|
|
118
|
+
* If the target is a module and it does not exist then this is `null`.
|
|
119
|
+
* @type {string | null}
|
|
156
120
|
*/
|
|
157
|
-
this.
|
|
121
|
+
this.filePath = this.getFilePath()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* What type of module is this
|
|
126
|
+
* @returns {ModuleType}
|
|
127
|
+
*/
|
|
128
|
+
getModuleType() {
|
|
129
|
+
if (/^\.{1,2}([\\/]|$)/.test(this.name)) {
|
|
130
|
+
return "relative"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (/^[\\/]/.test(this.name)) {
|
|
134
|
+
return "absolute"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (isBuiltin(this.name)) {
|
|
138
|
+
return "node"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (/^(@[\w~-][\w.~-]*\/)?[\w~-][\w.~-]*/.test(this.name)) {
|
|
142
|
+
return "npm"
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (/^https?:\/\//.test(this.name)) {
|
|
146
|
+
return "http"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return "unknown"
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* What module import style is used
|
|
154
|
+
* @param {'import' | 'require'} fallback
|
|
155
|
+
* @returns {ModuleStyle}
|
|
156
|
+
*/
|
|
157
|
+
getModuleStyle(fallback) {
|
|
158
|
+
/** @type {import('eslint').Rule.Node} */
|
|
159
|
+
let node = { parent: this.node }
|
|
160
|
+
|
|
161
|
+
do {
|
|
162
|
+
node = node.parent
|
|
163
|
+
|
|
164
|
+
// `const {} = require('')`
|
|
165
|
+
if (
|
|
166
|
+
node.type === "CallExpression" &&
|
|
167
|
+
node.callee.name === "require"
|
|
168
|
+
) {
|
|
169
|
+
return "require"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// `import type {} from '';`
|
|
173
|
+
if (
|
|
174
|
+
node.type === "ImportDeclaration" &&
|
|
175
|
+
node.importKind === "type"
|
|
176
|
+
) {
|
|
177
|
+
return "type"
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// `import {} from '';`
|
|
181
|
+
if (
|
|
182
|
+
node.type === "ImportDeclaration" &&
|
|
183
|
+
node.importKind === "value"
|
|
184
|
+
) {
|
|
185
|
+
return "import"
|
|
186
|
+
}
|
|
187
|
+
} while (node.parent)
|
|
188
|
+
|
|
189
|
+
return fallback
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get the node or npm module name
|
|
194
|
+
* @returns {string}
|
|
195
|
+
*/
|
|
196
|
+
getModuleName() {
|
|
197
|
+
if (this.moduleType === "relative") return
|
|
198
|
+
|
|
199
|
+
if (this.moduleType === "npm") {
|
|
200
|
+
if (this.name.startsWith("@")) {
|
|
201
|
+
return trimAfter(this.name, "/", 2)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return trimAfter(this.name, "/")
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (this.moduleType === "node") {
|
|
208
|
+
if (this.name.startsWith("node:")) {
|
|
209
|
+
return trimAfter(this.name.slice(5), "/")
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return trimAfter(this.name, "/")
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
getPaths() {
|
|
217
|
+
if (Array.isArray(this.options.paths)) {
|
|
218
|
+
return [...this.options.paths, this.options.basedir]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return [this.options.basedir]
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Resolve the given id to file paths.
|
|
226
|
+
* @returns {string | null} The resolved path.
|
|
227
|
+
*/
|
|
228
|
+
getFilePath() {
|
|
229
|
+
const conditionNames = ["node", "require"]
|
|
230
|
+
const { extensions } = this.options
|
|
231
|
+
const mainFields = []
|
|
232
|
+
const mainFiles = []
|
|
233
|
+
|
|
234
|
+
if (this.moduleStyle === "import") {
|
|
235
|
+
conditionNames.push("import")
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (this.moduleStyle === "type") {
|
|
239
|
+
conditionNames.push("import", "types")
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (
|
|
243
|
+
this.moduleStyle === "require" ||
|
|
244
|
+
this.moduleType === "npm" ||
|
|
245
|
+
this.moduleType === "node"
|
|
246
|
+
) {
|
|
247
|
+
mainFields.push("main")
|
|
248
|
+
mainFiles.push("index")
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let alias = undefined
|
|
252
|
+
let extensionAlias = undefined
|
|
253
|
+
|
|
254
|
+
if (isTypescript(this.context)) {
|
|
255
|
+
alias = getTSConfigAliases(this.context)
|
|
256
|
+
extensionAlias = getTypescriptExtensionMap(this.context).backward
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const requireResolve = resolver.create.sync({
|
|
260
|
+
conditionNames,
|
|
261
|
+
extensions,
|
|
262
|
+
mainFields,
|
|
263
|
+
mainFiles,
|
|
264
|
+
|
|
265
|
+
extensionAlias,
|
|
266
|
+
alias,
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
const cwd = this.context.settings?.cwd ?? process.cwd()
|
|
270
|
+
for (const directory of this.getPaths()) {
|
|
271
|
+
try {
|
|
272
|
+
const baseDir = resolve(cwd, directory)
|
|
273
|
+
return requireResolve(baseDir, this.name)
|
|
274
|
+
} catch {
|
|
275
|
+
continue
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (this.moduleType === "absolute" || this.moduleType === "relative") {
|
|
280
|
+
return resolve(this.options.basedir, this.name)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return null
|
|
158
284
|
}
|
|
159
285
|
}
|
package/lib/util/visit-import.js
CHANGED
|
@@ -68,7 +68,13 @@ module.exports = function visitImport(
|
|
|
68
68
|
// Note: "999" arbitrary to check current/future Node.js version
|
|
69
69
|
if (name && (includeCore || !isCoreModule(name, "999"))) {
|
|
70
70
|
targets.push(
|
|
71
|
-
new ImportTarget(
|
|
71
|
+
new ImportTarget(
|
|
72
|
+
context,
|
|
73
|
+
sourceNode,
|
|
74
|
+
name,
|
|
75
|
+
options,
|
|
76
|
+
"import"
|
|
77
|
+
)
|
|
72
78
|
)
|
|
73
79
|
}
|
|
74
80
|
},
|
|
@@ -60,7 +60,13 @@ module.exports = function visitRequire(
|
|
|
60
60
|
// Note: "999" arbitrary to check current/future Node.js version
|
|
61
61
|
if (name && (includeCore || !isCoreModule(name, "999"))) {
|
|
62
62
|
targets.push(
|
|
63
|
-
new ImportTarget(
|
|
63
|
+
new ImportTarget(
|
|
64
|
+
context,
|
|
65
|
+
targetNode,
|
|
66
|
+
name,
|
|
67
|
+
options,
|
|
68
|
+
"require"
|
|
69
|
+
)
|
|
64
70
|
)
|
|
65
71
|
}
|
|
66
72
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-n",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0-0",
|
|
4
4
|
"description": "Additional ESLint's rules for Node.js",
|
|
5
5
|
"engines": {
|
|
6
|
-
"node": "
|
|
6
|
+
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
7
7
|
},
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
"configs/"
|
|
12
12
|
],
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"eslint": ">=
|
|
14
|
+
"eslint": ">=8.23.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@eslint-community/eslint-utils": "^4.4.0",
|
|
18
|
-
"
|
|
18
|
+
"enhanced-resolve": "^5.15.0",
|
|
19
19
|
"eslint-plugin-es-x": "^7.5.0",
|
|
20
20
|
"get-tsconfig": "^4.7.0",
|
|
21
21
|
"globals": "^13.24.0",
|
|
@@ -23,22 +23,19 @@
|
|
|
23
23
|
"is-builtin-module": "^3.2.1",
|
|
24
24
|
"is-core-module": "^2.12.1",
|
|
25
25
|
"minimatch": "^3.1.2",
|
|
26
|
-
"resolve": "^1.22.2",
|
|
27
26
|
"semver": "^7.5.3"
|
|
28
27
|
},
|
|
29
28
|
"devDependencies": {
|
|
30
29
|
"@eslint/js": "^8.43.0",
|
|
31
30
|
"@types/eslint": "^8.44.6",
|
|
32
31
|
"@typescript-eslint/parser": "^5.60.0",
|
|
33
|
-
"
|
|
34
|
-
"eslint": "^8.56.0",
|
|
32
|
+
"eslint": "^8",
|
|
35
33
|
"eslint-config-prettier": "^8.8.0",
|
|
36
34
|
"eslint-doc-generator": "^1.6.1",
|
|
37
35
|
"eslint-plugin-eslint-plugin": "^5.2.1",
|
|
38
36
|
"eslint-plugin-n": "file:.",
|
|
39
37
|
"fast-glob": "^3.2.12",
|
|
40
38
|
"husky": "^8.0.3",
|
|
41
|
-
"import-meta-resolve": "^3.0.0",
|
|
42
39
|
"lint-staged": "^13.2.2",
|
|
43
40
|
"markdownlint-cli": "^0.35.0",
|
|
44
41
|
"mocha": "^10.2.0",
|
|
@@ -111,5 +108,8 @@
|
|
|
111
108
|
"lint-staged": {
|
|
112
109
|
"*.js": "eslint --cache --fix",
|
|
113
110
|
"*.{json,js}": "prettier --write --ignore-path .eslintignore"
|
|
111
|
+
},
|
|
112
|
+
"imports": {
|
|
113
|
+
"#eslint-rule-tester": "./tests/eslint-rule-tester.js"
|
|
114
114
|
}
|
|
115
115
|
}
|