eslint-plugin-n 15.1.0 → 15.2.2
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
CHANGED
|
@@ -93,7 +93,7 @@ $ npm install --save-dev eslint eslint-plugin-n
|
|
|
93
93
|
| Rule ID | Description | |
|
|
94
94
|
|:--------|:------------|:--:|
|
|
95
95
|
| [n/callback-return](./docs/rules/callback-return.md) | require `return` statements after callbacks | |
|
|
96
|
-
| [n/exports-style](./docs/rules/exports-style.md) | enforce either `module.exports` or `exports` |
|
|
96
|
+
| [n/exports-style](./docs/rules/exports-style.md) | enforce either `module.exports` or `exports` | ✒️ |
|
|
97
97
|
| [n/file-extension-in-import](./docs/rules/file-extension-in-import.md) | enforce the style of file extensions in `import` declarations | ✒️ |
|
|
98
98
|
| [n/global-require](./docs/rules/global-require.md) | require `require()` calls to be placed at top-level module scope | |
|
|
99
99
|
| [n/no-mixed-requires](./docs/rules/no-mixed-requires.md) | disallow `require` calls to be mixed with regular variable declarations | |
|
|
@@ -167,7 +167,7 @@ Those preset config:
|
|
|
167
167
|
|
|
168
168
|
## 📰 Changelog
|
|
169
169
|
|
|
170
|
-
- [GitHub Releases](https://github.com/weiran-zsd/eslint-plugin-
|
|
170
|
+
- [GitHub Releases](https://github.com/weiran-zsd/eslint-plugin-node/releases)
|
|
171
171
|
|
|
172
172
|
## ❤️ Contributing
|
|
173
173
|
|
|
@@ -139,6 +139,90 @@ function getExportsNodes(scope) {
|
|
|
139
139
|
return variable.references.map(reference => reference.identifier)
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
function getReplacementForProperty(property, sourceCode) {
|
|
143
|
+
if (property.type !== "Property" || property.kind !== "init") {
|
|
144
|
+
// We don't have a nice syntax for adding these directly on the exports object. Give up on fixing the whole thing:
|
|
145
|
+
// property.kind === 'get':
|
|
146
|
+
// module.exports = { get foo() { ... } }
|
|
147
|
+
// property.kind === 'set':
|
|
148
|
+
// module.exports = { set foo() { ... } }
|
|
149
|
+
// property.type === 'SpreadElement':
|
|
150
|
+
// module.exports = { ...foo }
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let fixedValue = sourceCode.getText(property.value)
|
|
155
|
+
if (property.method) {
|
|
156
|
+
fixedValue = `function${
|
|
157
|
+
property.value.generator ? "*" : ""
|
|
158
|
+
} ${fixedValue}`
|
|
159
|
+
if (property.value.async) {
|
|
160
|
+
fixedValue = `async ${fixedValue}`
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const lines = sourceCode
|
|
164
|
+
.getCommentsBefore(property)
|
|
165
|
+
.map(comment => sourceCode.getText(comment))
|
|
166
|
+
if (property.key.type === "Literal" || property.computed) {
|
|
167
|
+
// String or dynamic key:
|
|
168
|
+
// module.exports = { [ ... ]: ... } or { "foo": ... }
|
|
169
|
+
lines.push(
|
|
170
|
+
`exports[${sourceCode.getText(property.key)}] = ${fixedValue};`
|
|
171
|
+
)
|
|
172
|
+
} else if (property.key.type === "Identifier") {
|
|
173
|
+
// Regular identifier:
|
|
174
|
+
// module.exports = { foo: ... }
|
|
175
|
+
lines.push(`exports.${property.key.name} = ${fixedValue};`)
|
|
176
|
+
} else {
|
|
177
|
+
// Some other unknown property type. Conservatively give up on fixing the whole thing.
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
lines.push(
|
|
181
|
+
...sourceCode
|
|
182
|
+
.getCommentsAfter(property)
|
|
183
|
+
.map(comment => sourceCode.getText(comment))
|
|
184
|
+
)
|
|
185
|
+
return lines.join("\n")
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check for a top level module.exports = { ... }
|
|
189
|
+
function isModuleExportsObjectAssignment(node) {
|
|
190
|
+
return (
|
|
191
|
+
node.parent.type === "AssignmentExpression" &&
|
|
192
|
+
node.parent.parent.type === "ExpressionStatement" &&
|
|
193
|
+
node.parent.parent.parent.type === "Program" &&
|
|
194
|
+
node.parent.right.type === "ObjectExpression"
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Check for module.exports.foo or module.exports.bar reference or assignment
|
|
199
|
+
function isModuleExportsReference(node) {
|
|
200
|
+
return (
|
|
201
|
+
node.parent.type === "MemberExpression" && node.parent.object === node
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function fixModuleExports(node, sourceCode, fixer) {
|
|
206
|
+
if (isModuleExportsReference(node)) {
|
|
207
|
+
return fixer.replaceText(node, "exports")
|
|
208
|
+
}
|
|
209
|
+
if (!isModuleExportsObjectAssignment(node)) {
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
212
|
+
const statements = []
|
|
213
|
+
const properties = node.parent.right.properties
|
|
214
|
+
for (const property of properties) {
|
|
215
|
+
const statement = getReplacementForProperty(property, sourceCode)
|
|
216
|
+
if (statement) {
|
|
217
|
+
statements.push(statement)
|
|
218
|
+
} else {
|
|
219
|
+
// No replacement available, give up on the whole thing
|
|
220
|
+
return null
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return fixer.replaceText(node.parent, statements.join("\n\n"))
|
|
224
|
+
}
|
|
225
|
+
|
|
142
226
|
module.exports = {
|
|
143
227
|
meta: {
|
|
144
228
|
docs: {
|
|
@@ -148,7 +232,7 @@ module.exports = {
|
|
|
148
232
|
url: "https://github.com/weiran-zsd/eslint-plugin-node/blob/HEAD/docs/rules/exports-style.md",
|
|
149
233
|
},
|
|
150
234
|
type: "suggestion",
|
|
151
|
-
fixable:
|
|
235
|
+
fixable: "code",
|
|
152
236
|
schema: [
|
|
153
237
|
{
|
|
154
238
|
//
|
|
@@ -253,6 +337,9 @@ module.exports = {
|
|
|
253
337
|
loc: getLocation(node),
|
|
254
338
|
message:
|
|
255
339
|
"Unexpected access to 'module.exports'. Use 'exports' instead.",
|
|
340
|
+
fix(fixer) {
|
|
341
|
+
return fixModuleExports(node, sourceCode, fixer)
|
|
342
|
+
},
|
|
256
343
|
})
|
|
257
344
|
}
|
|
258
345
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const path = require("path")
|
|
8
8
|
const fs = require("fs")
|
|
9
|
+
const mapTypescriptExtension = require("../util/map-typescript-extension")
|
|
9
10
|
const visitImport = require("../util/visit-import")
|
|
10
11
|
const packageNamePattern = /^(?:@[^/\\]+[/\\])?[^/\\]+$/u
|
|
11
12
|
const corePackageOverridePattern =
|
|
@@ -78,23 +79,30 @@ module.exports = {
|
|
|
78
79
|
|
|
79
80
|
// Get extension.
|
|
80
81
|
const originalExt = path.extname(name)
|
|
81
|
-
const resolvedExt = path.extname(filePath)
|
|
82
82
|
const existingExts = getExistingExtensions(filePath)
|
|
83
|
-
const ext =
|
|
83
|
+
const ext = path.extname(filePath) || existingExts.join(" or ")
|
|
84
84
|
const style = overrideStyle[ext] || defaultStyle
|
|
85
85
|
|
|
86
86
|
// Verify.
|
|
87
87
|
if (style === "always" && ext !== originalExt) {
|
|
88
|
+
const fileExtensionToAdd = mapTypescriptExtension(
|
|
89
|
+
context,
|
|
90
|
+
filePath,
|
|
91
|
+
ext
|
|
92
|
+
)
|
|
88
93
|
context.report({
|
|
89
94
|
node,
|
|
90
95
|
messageId: "requireExt",
|
|
91
|
-
data: { ext },
|
|
96
|
+
data: { ext: fileExtensionToAdd },
|
|
92
97
|
fix(fixer) {
|
|
93
98
|
if (existingExts.length !== 1) {
|
|
94
99
|
return null
|
|
95
100
|
}
|
|
96
101
|
const index = node.range[1] - 1
|
|
97
|
-
return fixer.insertTextBeforeRange(
|
|
102
|
+
return fixer.insertTextBeforeRange(
|
|
103
|
+
[index, index],
|
|
104
|
+
fileExtensionToAdd
|
|
105
|
+
)
|
|
98
106
|
},
|
|
99
107
|
})
|
|
100
108
|
} else if (style === "never" && ext === originalExt) {
|
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
"use strict"
|
|
6
6
|
|
|
7
|
+
const path = require("path")
|
|
7
8
|
const exists = require("./exists")
|
|
8
9
|
const getAllowModules = require("./get-allow-modules")
|
|
10
|
+
const isTypescript = require("./is-typescript")
|
|
11
|
+
const mapTypescriptExtension = require("../util/map-typescript-extension")
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Checks whether or not each requirement target exists.
|
|
@@ -17,7 +20,7 @@ const getAllowModules = require("./get-allow-modules")
|
|
|
17
20
|
* @param {ImportTarget[]} targets - A list of target information to check.
|
|
18
21
|
* @returns {void}
|
|
19
22
|
*/
|
|
20
|
-
module.exports = function
|
|
23
|
+
module.exports = function checkExistence(context, targets) {
|
|
21
24
|
const allowed = new Set(getAllowModules(context))
|
|
22
25
|
|
|
23
26
|
for (const target of targets) {
|
|
@@ -25,8 +28,20 @@ module.exports = function checkForExistence(context, targets) {
|
|
|
25
28
|
target.moduleName != null &&
|
|
26
29
|
!allowed.has(target.moduleName) &&
|
|
27
30
|
target.filePath == null
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
|
|
32
|
+
let missingFile = target.moduleName == null && !exists(target.filePath)
|
|
33
|
+
if (missingFile && isTypescript(context)) {
|
|
34
|
+
const parsed = path.parse(target.filePath)
|
|
35
|
+
const reversedExt = mapTypescriptExtension(
|
|
36
|
+
context,
|
|
37
|
+
target.filePath,
|
|
38
|
+
parsed.ext,
|
|
39
|
+
true
|
|
40
|
+
)
|
|
41
|
+
const reversedPath =
|
|
42
|
+
path.resolve(parsed.dir, parsed.name) + reversedExt
|
|
43
|
+
missingFile = target.moduleName == null && !exists(reversedPath)
|
|
44
|
+
}
|
|
30
45
|
|
|
31
46
|
if (missingModule || missingFile) {
|
|
32
47
|
context.report({
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
const path = require("path")
|
|
4
|
+
|
|
5
|
+
const typescriptExtensions = [".ts", ".cts", ".mts"]
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Determine if the context source file is typescript.
|
|
9
|
+
*
|
|
10
|
+
* @param {RuleContext} context - A context
|
|
11
|
+
* @returns {boolean}
|
|
12
|
+
*/
|
|
13
|
+
module.exports = function isTypescript(context) {
|
|
14
|
+
const sourceFileExt = path.extname(context.getPhysicalFilename())
|
|
15
|
+
return typescriptExtensions.includes(sourceFileExt)
|
|
16
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
const path = require("path")
|
|
4
|
+
const isTypescript = require("../util/is-typescript")
|
|
5
|
+
|
|
6
|
+
const mapping = {
|
|
7
|
+
".ts": ".js",
|
|
8
|
+
".cts": ".cjs",
|
|
9
|
+
".mts": ".mjs",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const reverseMapping = {
|
|
13
|
+
".js": ".ts",
|
|
14
|
+
".cjs": ".cts",
|
|
15
|
+
".mjs": ".mts",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Maps the typescript file extension that should be added in an import statement,
|
|
20
|
+
* based on the given file extension of the referenced file OR fallsback to the original given extension.
|
|
21
|
+
*
|
|
22
|
+
* For example, in typescript, when referencing another typescript from a typescript file,
|
|
23
|
+
* a .js extension should be used instead of the original .ts extension of the referenced file.
|
|
24
|
+
*
|
|
25
|
+
* @param {RuleContext} context
|
|
26
|
+
* @param {string} filePath The filePath of the import
|
|
27
|
+
* @param {string} fallbackExtension The non-typescript fallback
|
|
28
|
+
* @param {boolean} reverse Execute a reverse path mapping
|
|
29
|
+
* @returns {string} The file extension to append to the import statement.
|
|
30
|
+
*/
|
|
31
|
+
module.exports = function mapTypescriptExtension(
|
|
32
|
+
context,
|
|
33
|
+
filePath,
|
|
34
|
+
fallbackExtension,
|
|
35
|
+
reverse = false
|
|
36
|
+
) {
|
|
37
|
+
const ext = path.extname(filePath)
|
|
38
|
+
if (reverse) {
|
|
39
|
+
if (isTypescript(context) && ext in reverseMapping) {
|
|
40
|
+
return reverseMapping[ext]
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
if (isTypescript(context) && ext in mapping) {
|
|
44
|
+
return mapping[ext]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return fallbackExtension
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-n",
|
|
3
|
-
"version": "15.
|
|
3
|
+
"version": "15.2.2",
|
|
4
4
|
"description": "Additional ESLint's rules for Node.js",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=12.22.0"
|
|
@@ -13,35 +13,34 @@
|
|
|
13
13
|
"eslint": ">=7.0.0"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"builtins": "^
|
|
16
|
+
"builtins": "^5.0.1",
|
|
17
17
|
"eslint-plugin-es": "^4.1.0",
|
|
18
18
|
"eslint-utils": "^3.0.0",
|
|
19
19
|
"ignore": "^5.1.1",
|
|
20
|
-
"is-core-module": "^2.
|
|
21
|
-
"minimatch": "^3.
|
|
20
|
+
"is-core-module": "^2.9.0",
|
|
21
|
+
"minimatch": "^3.1.2",
|
|
22
22
|
"resolve": "^1.10.1",
|
|
23
|
-
"semver": "^
|
|
23
|
+
"semver": "^7.3.7"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@mysticatea/eslint-plugin": "^13.0.0",
|
|
27
26
|
"codecov": "^3.3.0",
|
|
28
|
-
"esbuild": "^0.14.
|
|
29
|
-
"eslint": "^8.
|
|
30
|
-
"eslint-config-prettier": "^8.
|
|
27
|
+
"esbuild": "^0.14.39",
|
|
28
|
+
"eslint": "^8.15.0",
|
|
29
|
+
"eslint-config-prettier": "^8.5.0",
|
|
31
30
|
"eslint-plugin-eslint-plugin": "^4.0.1",
|
|
32
31
|
"eslint-plugin-n": "file:.",
|
|
33
|
-
"fast-glob": "^
|
|
34
|
-
"globals": "^
|
|
35
|
-
"husky": "^
|
|
32
|
+
"fast-glob": "^3.2.11",
|
|
33
|
+
"globals": "^13.14.0",
|
|
34
|
+
"husky": "^8.0.1",
|
|
36
35
|
"import-meta-resolve": "^1.1.1",
|
|
37
|
-
"lint-staged": "^12.
|
|
38
|
-
"mocha": "^
|
|
39
|
-
"nyc": "^
|
|
36
|
+
"lint-staged": "^12.4.1",
|
|
37
|
+
"mocha": "^10.0.0",
|
|
38
|
+
"nyc": "^15.1.0",
|
|
40
39
|
"opener": "^1.5.1",
|
|
41
|
-
"prettier": "^2.
|
|
40
|
+
"prettier": "^2.6.2",
|
|
42
41
|
"punycode": "^2.1.1",
|
|
43
|
-
"release-it": "^
|
|
44
|
-
"rimraf": "^
|
|
42
|
+
"release-it": "^15.0.0",
|
|
43
|
+
"rimraf": "^3.0.2"
|
|
45
44
|
},
|
|
46
45
|
"scripts": {
|
|
47
46
|
"build": "node scripts/update",
|