eslint-plugin-n 14.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/LICENSE +22 -0
- package/README.md +189 -0
- package/lib/configs/_commons.js +76 -0
- package/lib/configs/recommended-module.js +27 -0
- package/lib/configs/recommended-script.js +24 -0
- package/lib/configs/recommended.js +18 -0
- package/lib/index.js +55 -0
- package/lib/rules/callback-return.js +185 -0
- package/lib/rules/exports-style.js +297 -0
- package/lib/rules/file-extension-in-import.js +129 -0
- package/lib/rules/global-require.js +91 -0
- package/lib/rules/handle-callback-err.js +94 -0
- package/lib/rules/no-callback-literal.js +82 -0
- package/lib/rules/no-deprecated-api.js +782 -0
- package/lib/rules/no-exports-assign.js +75 -0
- package/lib/rules/no-extraneous-import.js +49 -0
- package/lib/rules/no-extraneous-require.js +49 -0
- package/lib/rules/no-hide-core-modules.js +157 -0
- package/lib/rules/no-missing-import.js +47 -0
- package/lib/rules/no-missing-require.js +47 -0
- package/lib/rules/no-mixed-requires.js +255 -0
- package/lib/rules/no-new-require.js +39 -0
- package/lib/rules/no-path-concat.js +212 -0
- package/lib/rules/no-process-env.js +45 -0
- package/lib/rules/no-process-exit.js +36 -0
- package/lib/rules/no-restricted-import.js +61 -0
- package/lib/rules/no-restricted-require.js +61 -0
- package/lib/rules/no-sync.js +53 -0
- package/lib/rules/no-unpublished-bin.js +97 -0
- package/lib/rules/no-unpublished-import.js +49 -0
- package/lib/rules/no-unpublished-require.js +49 -0
- package/lib/rules/no-unsupported-features/es-builtins.js +181 -0
- package/lib/rules/no-unsupported-features/es-syntax.js +659 -0
- package/lib/rules/no-unsupported-features/node-builtins.js +417 -0
- package/lib/rules/no-unsupported-features.js +1542 -0
- package/lib/rules/prefer-global/buffer.js +49 -0
- package/lib/rules/prefer-global/console.js +46 -0
- package/lib/rules/prefer-global/process.js +46 -0
- package/lib/rules/prefer-global/text-decoder.js +49 -0
- package/lib/rules/prefer-global/text-encoder.js +49 -0
- package/lib/rules/prefer-global/url-search-params.js +49 -0
- package/lib/rules/prefer-global/url.js +48 -0
- package/lib/rules/prefer-promises/dns.js +74 -0
- package/lib/rules/prefer-promises/fs.js +76 -0
- package/lib/rules/process-exit-as-throw.js +164 -0
- package/lib/rules/shebang.js +170 -0
- package/lib/util/cache.js +58 -0
- package/lib/util/check-existence.js +40 -0
- package/lib/util/check-extraneous.js +52 -0
- package/lib/util/check-prefer-global.js +63 -0
- package/lib/util/check-publish.js +71 -0
- package/lib/util/check-restricted.js +109 -0
- package/lib/util/check-unsupported-builtins.js +108 -0
- package/lib/util/enumerate-property-names.js +39 -0
- package/lib/util/exists.js +58 -0
- package/lib/util/get-allow-modules.js +47 -0
- package/lib/util/get-configured-node-version.js +39 -0
- package/lib/util/get-convert-path.js +189 -0
- package/lib/util/get-npmignore.js +184 -0
- package/lib/util/get-package-json.js +75 -0
- package/lib/util/get-resolve-paths.js +44 -0
- package/lib/util/get-semver-range.js +30 -0
- package/lib/util/get-try-extensions.js +47 -0
- package/lib/util/import-target.js +85 -0
- package/lib/util/merge-visitors-in-place.js +46 -0
- package/lib/util/strip-import-path-params.js +10 -0
- package/lib/util/visit-import.js +66 -0
- package/lib/util/visit-require.js +60 -0
- package/package.json +91 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const path = require("path")
|
|
8
|
+
const getConvertPath = require("../util/get-convert-path")
|
|
9
|
+
const getPackageJson = require("../util/get-package-json")
|
|
10
|
+
|
|
11
|
+
const NODE_SHEBANG = "#!/usr/bin/env node\n"
|
|
12
|
+
const SHEBANG_PATTERN = /^(#!.+?)?(\r)?\n/u
|
|
13
|
+
const NODE_SHEBANG_PATTERN = /#!\/usr\/bin\/env node(?: [^\r\n]+?)?\n/u
|
|
14
|
+
|
|
15
|
+
function simulateNodeResolutionAlgorithm(filePath, binField) {
|
|
16
|
+
const possibilities = [filePath]
|
|
17
|
+
let newFilePath = filePath.replace(/\.js$/u, "")
|
|
18
|
+
possibilities.push(newFilePath)
|
|
19
|
+
newFilePath = newFilePath.replace(/[/\\]index$/u, "")
|
|
20
|
+
possibilities.push(newFilePath)
|
|
21
|
+
return possibilities.includes(binField)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Checks whether or not a given path is a `bin` file.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} filePath - A file path to check.
|
|
28
|
+
* @param {string|object|undefined} binField - A value of the `bin` field of `package.json`.
|
|
29
|
+
* @param {string} basedir - A directory path that `package.json` exists.
|
|
30
|
+
* @returns {boolean} `true` if the file is a `bin` file.
|
|
31
|
+
*/
|
|
32
|
+
function isBinFile(filePath, binField, basedir) {
|
|
33
|
+
if (!binField) {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
if (typeof binField === "string") {
|
|
37
|
+
return simulateNodeResolutionAlgorithm(
|
|
38
|
+
filePath,
|
|
39
|
+
path.resolve(basedir, binField)
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
return Object.keys(binField).some(key =>
|
|
43
|
+
simulateNodeResolutionAlgorithm(
|
|
44
|
+
filePath,
|
|
45
|
+
path.resolve(basedir, binField[key])
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Gets the shebang line (includes a line ending) from a given code.
|
|
52
|
+
*
|
|
53
|
+
* @param {SourceCode} sourceCode - A source code object to check.
|
|
54
|
+
* @returns {{length: number, bom: boolean, shebang: string, cr: boolean}}
|
|
55
|
+
* shebang's information.
|
|
56
|
+
* `retv.shebang` is an empty string if shebang doesn't exist.
|
|
57
|
+
*/
|
|
58
|
+
function getShebangInfo(sourceCode) {
|
|
59
|
+
const m = SHEBANG_PATTERN.exec(sourceCode.text)
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
bom: sourceCode.hasBOM,
|
|
63
|
+
cr: Boolean(m && m[2]),
|
|
64
|
+
length: (m && m[0].length) || 0,
|
|
65
|
+
shebang: (m && m[1] && `${m[1]}\n`) || "",
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = {
|
|
70
|
+
meta: {
|
|
71
|
+
docs: {
|
|
72
|
+
description: "suggest correct usage of shebang",
|
|
73
|
+
category: "Possible Errors",
|
|
74
|
+
recommended: true,
|
|
75
|
+
url:
|
|
76
|
+
"https://github.com/mysticatea/eslint-plugin-node/blob/HEAD/docs/rules/shebang.md",
|
|
77
|
+
},
|
|
78
|
+
type: "problem",
|
|
79
|
+
fixable: "code",
|
|
80
|
+
schema: [
|
|
81
|
+
{
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
//
|
|
85
|
+
convertPath: getConvertPath.schema,
|
|
86
|
+
},
|
|
87
|
+
additionalProperties: false,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
create(context) {
|
|
92
|
+
const sourceCode = context.getSourceCode()
|
|
93
|
+
let filePath = context.getFilename()
|
|
94
|
+
if (filePath === "<input>") {
|
|
95
|
+
return {}
|
|
96
|
+
}
|
|
97
|
+
filePath = path.resolve(filePath)
|
|
98
|
+
|
|
99
|
+
const p = getPackageJson(filePath)
|
|
100
|
+
if (!p) {
|
|
101
|
+
return {}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const basedir = path.dirname(p.filePath)
|
|
105
|
+
filePath = path.join(
|
|
106
|
+
basedir,
|
|
107
|
+
getConvertPath(context)(
|
|
108
|
+
path.relative(basedir, filePath).replace(/\\/gu, "/")
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
const needsShebang = isBinFile(filePath, p.bin, basedir)
|
|
113
|
+
const info = getShebangInfo(sourceCode)
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
Program(node) {
|
|
117
|
+
if (
|
|
118
|
+
needsShebang
|
|
119
|
+
? NODE_SHEBANG_PATTERN.test(info.shebang)
|
|
120
|
+
: !info.shebang
|
|
121
|
+
) {
|
|
122
|
+
// Good the shebang target.
|
|
123
|
+
// Checks BOM and \r.
|
|
124
|
+
if (needsShebang && info.bom) {
|
|
125
|
+
context.report({
|
|
126
|
+
node,
|
|
127
|
+
message: "This file must not have Unicode BOM.",
|
|
128
|
+
fix(fixer) {
|
|
129
|
+
return fixer.removeRange([-1, 0])
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
if (needsShebang && info.cr) {
|
|
134
|
+
context.report({
|
|
135
|
+
node,
|
|
136
|
+
message:
|
|
137
|
+
"This file must have Unix linebreaks (LF).",
|
|
138
|
+
fix(fixer) {
|
|
139
|
+
const index = sourceCode.text.indexOf("\r")
|
|
140
|
+
return fixer.removeRange([index, index + 1])
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
} else if (needsShebang) {
|
|
145
|
+
// Shebang is lacking.
|
|
146
|
+
context.report({
|
|
147
|
+
node,
|
|
148
|
+
message:
|
|
149
|
+
'This file needs shebang "#!/usr/bin/env node".',
|
|
150
|
+
fix(fixer) {
|
|
151
|
+
return fixer.replaceTextRange(
|
|
152
|
+
[-1, info.length],
|
|
153
|
+
NODE_SHEBANG
|
|
154
|
+
)
|
|
155
|
+
},
|
|
156
|
+
})
|
|
157
|
+
} else {
|
|
158
|
+
// Shebang is extra.
|
|
159
|
+
context.report({
|
|
160
|
+
node,
|
|
161
|
+
message: "This file needs no shebang.",
|
|
162
|
+
fix(fixer) {
|
|
163
|
+
return fixer.removeRange([0, info.length])
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const SKIP_TIME = 5000
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The class of cache.
|
|
11
|
+
* The cache will dispose of each value if the value has not been accessed
|
|
12
|
+
* during 5 seconds.
|
|
13
|
+
*/
|
|
14
|
+
module.exports = class Cache {
|
|
15
|
+
/**
|
|
16
|
+
* Initialize this cache instance.
|
|
17
|
+
*/
|
|
18
|
+
constructor() {
|
|
19
|
+
this.map = new Map()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the cached value of the given key.
|
|
24
|
+
* @param {any} key The key to get.
|
|
25
|
+
* @returns {any} The cached value or null.
|
|
26
|
+
*/
|
|
27
|
+
get(key) {
|
|
28
|
+
const entry = this.map.get(key)
|
|
29
|
+
const now = Date.now()
|
|
30
|
+
|
|
31
|
+
if (entry) {
|
|
32
|
+
if (entry.expire > now) {
|
|
33
|
+
entry.expire = now + SKIP_TIME
|
|
34
|
+
return entry.value
|
|
35
|
+
}
|
|
36
|
+
this.map.delete(key)
|
|
37
|
+
}
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Set the value of the given key.
|
|
43
|
+
* @param {any} key The key to set.
|
|
44
|
+
* @param {any} value The value to set.
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
47
|
+
set(key, value) {
|
|
48
|
+
const entry = this.map.get(key)
|
|
49
|
+
const expire = Date.now() + SKIP_TIME
|
|
50
|
+
|
|
51
|
+
if (entry) {
|
|
52
|
+
entry.value = value
|
|
53
|
+
entry.expire = expire
|
|
54
|
+
} else {
|
|
55
|
+
this.map.set(key, { value, expire })
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const exists = require("./exists")
|
|
8
|
+
const getAllowModules = require("./get-allow-modules")
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks whether or not each requirement target exists.
|
|
12
|
+
*
|
|
13
|
+
* It looks up the target according to the logic of Node.js.
|
|
14
|
+
* See Also: https://nodejs.org/api/modules.html
|
|
15
|
+
*
|
|
16
|
+
* @param {RuleContext} context - A context to report.
|
|
17
|
+
* @param {ImportTarget[]} targets - A list of target information to check.
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
module.exports = function checkForExistence(context, targets) {
|
|
21
|
+
const allowed = new Set(getAllowModules(context))
|
|
22
|
+
|
|
23
|
+
for (const target of targets) {
|
|
24
|
+
const missingModule =
|
|
25
|
+
target.moduleName != null &&
|
|
26
|
+
!allowed.has(target.moduleName) &&
|
|
27
|
+
target.filePath == null
|
|
28
|
+
const missingFile =
|
|
29
|
+
target.moduleName == null && !exists(target.filePath)
|
|
30
|
+
|
|
31
|
+
if (missingModule || missingFile) {
|
|
32
|
+
context.report({
|
|
33
|
+
node: target.node,
|
|
34
|
+
loc: target.node.loc,
|
|
35
|
+
message: '"{{name}}" is not found.',
|
|
36
|
+
data: target,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const getAllowModules = require("./get-allow-modules")
|
|
8
|
+
const getPackageJson = require("./get-package-json")
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks whether or not each requirement target is published via package.json.
|
|
12
|
+
*
|
|
13
|
+
* It reads package.json and checks the target exists in `dependencies`.
|
|
14
|
+
*
|
|
15
|
+
* @param {RuleContext} context - A context to report.
|
|
16
|
+
* @param {string} filePath - The current file path.
|
|
17
|
+
* @param {ImportTarget[]} targets - A list of target information to check.
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
module.exports = function checkForExtraneous(context, filePath, targets) {
|
|
21
|
+
const packageInfo = getPackageJson(filePath)
|
|
22
|
+
if (!packageInfo) {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const allowed = new Set(getAllowModules(context))
|
|
27
|
+
const dependencies = new Set(
|
|
28
|
+
[].concat(
|
|
29
|
+
Object.keys(packageInfo.dependencies || {}),
|
|
30
|
+
Object.keys(packageInfo.devDependencies || {}),
|
|
31
|
+
Object.keys(packageInfo.peerDependencies || {}),
|
|
32
|
+
Object.keys(packageInfo.optionalDependencies || {})
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
for (const target of targets) {
|
|
37
|
+
const extraneous =
|
|
38
|
+
target.moduleName != null &&
|
|
39
|
+
target.filePath != null &&
|
|
40
|
+
!dependencies.has(target.moduleName) &&
|
|
41
|
+
!allowed.has(target.moduleName)
|
|
42
|
+
|
|
43
|
+
if (extraneous) {
|
|
44
|
+
context.report({
|
|
45
|
+
node: target.node,
|
|
46
|
+
loc: target.node.loc,
|
|
47
|
+
message: '"{{moduleName}}" is extraneous.',
|
|
48
|
+
data: target,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const { ReferenceTracker } = require("eslint-utils")
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Verifier for `prefer-global/*` rules.
|
|
11
|
+
*/
|
|
12
|
+
class Verifier {
|
|
13
|
+
/**
|
|
14
|
+
* Initialize this instance.
|
|
15
|
+
* @param {RuleContext} context The rule context to report.
|
|
16
|
+
* @param {{modules:object,globals:object}} trackMap The track map.
|
|
17
|
+
*/
|
|
18
|
+
constructor(context, trackMap) {
|
|
19
|
+
this.context = context
|
|
20
|
+
this.trackMap = trackMap
|
|
21
|
+
this.verify =
|
|
22
|
+
context.options[0] === "never"
|
|
23
|
+
? this.verifyToPreferModules
|
|
24
|
+
: this.verifyToPreferGlobals
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Verify the code to suggest the use of globals.
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
31
|
+
verifyToPreferGlobals() {
|
|
32
|
+
const { context, trackMap } = this
|
|
33
|
+
const tracker = new ReferenceTracker(context.getScope(), {
|
|
34
|
+
mode: "legacy",
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
for (const { node } of [
|
|
38
|
+
...tracker.iterateCjsReferences(trackMap.modules),
|
|
39
|
+
...tracker.iterateEsmReferences(trackMap.modules),
|
|
40
|
+
]) {
|
|
41
|
+
context.report({ node, messageId: "preferGlobal" })
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Verify the code to suggest the use of modules.
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
verifyToPreferModules() {
|
|
50
|
+
const { context, trackMap } = this
|
|
51
|
+
const tracker = new ReferenceTracker(context.getScope())
|
|
52
|
+
|
|
53
|
+
for (const { node } of tracker.iterateGlobalReferences(
|
|
54
|
+
trackMap.globals
|
|
55
|
+
)) {
|
|
56
|
+
context.report({ node, messageId: "preferModule" })
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = function checkForPreferGlobal(context, trackMap) {
|
|
62
|
+
new Verifier(context, trackMap).verify()
|
|
63
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const path = require("path")
|
|
8
|
+
const getAllowModules = require("./get-allow-modules")
|
|
9
|
+
const getConvertPath = require("./get-convert-path")
|
|
10
|
+
const getNpmignore = require("./get-npmignore")
|
|
11
|
+
const getPackageJson = require("./get-package-json")
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Checks whether or not each requirement target is published via package.json.
|
|
15
|
+
*
|
|
16
|
+
* It reads package.json and checks the target exists in `dependencies`.
|
|
17
|
+
*
|
|
18
|
+
* @param {RuleContext} context - A context to report.
|
|
19
|
+
* @param {string} filePath - The current file path.
|
|
20
|
+
* @param {ImportTarget[]} targets - A list of target information to check.
|
|
21
|
+
* @returns {void}
|
|
22
|
+
*/
|
|
23
|
+
module.exports = function checkForPublish(context, filePath, targets) {
|
|
24
|
+
const packageInfo = getPackageJson(filePath)
|
|
25
|
+
if (!packageInfo) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const allowed = new Set(getAllowModules(context))
|
|
30
|
+
const convertPath = getConvertPath(context)
|
|
31
|
+
const basedir = path.dirname(packageInfo.filePath)
|
|
32
|
+
|
|
33
|
+
const toRelative = fullPath => {
|
|
34
|
+
const retv = path.relative(basedir, fullPath).replace(/\\/gu, "/")
|
|
35
|
+
return convertPath(retv)
|
|
36
|
+
}
|
|
37
|
+
const npmignore = getNpmignore(filePath)
|
|
38
|
+
const devDependencies = new Set(
|
|
39
|
+
Object.keys(packageInfo.devDependencies || {})
|
|
40
|
+
)
|
|
41
|
+
const dependencies = new Set(
|
|
42
|
+
[].concat(
|
|
43
|
+
Object.keys(packageInfo.dependencies || {}),
|
|
44
|
+
Object.keys(packageInfo.peerDependencies || {}),
|
|
45
|
+
Object.keys(packageInfo.optionalDependencies || {})
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
if (!npmignore.match(toRelative(filePath))) {
|
|
50
|
+
// This file is published, so this cannot import private files.
|
|
51
|
+
for (const target of targets) {
|
|
52
|
+
const isPrivateFile =
|
|
53
|
+
target.moduleName == null &&
|
|
54
|
+
npmignore.match(toRelative(target.filePath))
|
|
55
|
+
const isDevPackage =
|
|
56
|
+
target.moduleName != null &&
|
|
57
|
+
devDependencies.has(target.moduleName) &&
|
|
58
|
+
!dependencies.has(target.moduleName) &&
|
|
59
|
+
!allowed.has(target.moduleName)
|
|
60
|
+
|
|
61
|
+
if (isPrivateFile || isDevPackage) {
|
|
62
|
+
context.report({
|
|
63
|
+
node: target.node,
|
|
64
|
+
loc: target.node.loc,
|
|
65
|
+
message: '"{{name}}" is not published.',
|
|
66
|
+
data: { name: target.moduleName || target.name },
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const path = require("path")
|
|
8
|
+
const { Minimatch } = require("minimatch")
|
|
9
|
+
|
|
10
|
+
/** @typedef {import("../util/import-target")} ImportTarget */
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Object} DefinitionData
|
|
13
|
+
* @property {string | string[]} name The name to disallow.
|
|
14
|
+
* @property {string} [message] The custom message to show.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if matched or not.
|
|
19
|
+
* @param {InstanceType<Minimatch>} matcher The matcher.
|
|
20
|
+
* @param {boolean} absolute The flag that the matcher is for absolute paths.
|
|
21
|
+
* @param {ImportTarget} importee The importee information.
|
|
22
|
+
*/
|
|
23
|
+
function match(matcher, absolute, { filePath, name }) {
|
|
24
|
+
if (absolute) {
|
|
25
|
+
return filePath != null && matcher.match(filePath)
|
|
26
|
+
}
|
|
27
|
+
return matcher.match(name)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Restriction. */
|
|
31
|
+
class Restriction {
|
|
32
|
+
/**
|
|
33
|
+
* Initialize this restriction.
|
|
34
|
+
* @param {DefinitionData} def The definition of a restriction.
|
|
35
|
+
*/
|
|
36
|
+
constructor({ name, message }) {
|
|
37
|
+
const names = Array.isArray(name) ? name : [name]
|
|
38
|
+
const matchers = names.map(raw => {
|
|
39
|
+
const negate = raw[0] === "!" && raw[1] !== "("
|
|
40
|
+
const pattern = negate ? raw.slice(1) : raw
|
|
41
|
+
const absolute = path.isAbsolute(pattern)
|
|
42
|
+
const matcher = new Minimatch(pattern, { dot: true })
|
|
43
|
+
return { absolute, matcher, negate }
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
this.matchers = matchers
|
|
47
|
+
this.message = message ? ` ${message}` : ""
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if a given importee is disallowed.
|
|
52
|
+
* @param {ImportTarget} importee The importee to check.
|
|
53
|
+
* @returns {boolean} `true` if the importee is disallowed.
|
|
54
|
+
*/
|
|
55
|
+
match(importee) {
|
|
56
|
+
return this.matchers.reduce(
|
|
57
|
+
(ret, { absolute, matcher, negate }) =>
|
|
58
|
+
negate
|
|
59
|
+
? ret && !match(matcher, absolute, importee)
|
|
60
|
+
: ret || match(matcher, absolute, importee),
|
|
61
|
+
false
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Create a restriction.
|
|
68
|
+
* @param {string | DefinitionData} def A definition.
|
|
69
|
+
* @returns {Restriction} Created restriction.
|
|
70
|
+
*/
|
|
71
|
+
function createRestriction(def) {
|
|
72
|
+
if (typeof def === "string") {
|
|
73
|
+
return new Restriction({ name: def })
|
|
74
|
+
}
|
|
75
|
+
return new Restriction(def)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create restrictions.
|
|
80
|
+
* @param {(string | DefinitionData | GlobDefinition)[]} defs Definitions.
|
|
81
|
+
* @returns {(Restriction | GlobRestriction)[]} Created restrictions.
|
|
82
|
+
*/
|
|
83
|
+
function createRestrictions(defs) {
|
|
84
|
+
return (defs || []).map(createRestriction)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Checks if given importees are disallowed or not.
|
|
89
|
+
* @param {RuleContext} context - A context to report.
|
|
90
|
+
* @param {ImportTarget[]} targets - A list of target information to check.
|
|
91
|
+
* @returns {void}
|
|
92
|
+
*/
|
|
93
|
+
module.exports = function checkForRestriction(context, targets) {
|
|
94
|
+
const restrictions = createRestrictions(context.options[0])
|
|
95
|
+
|
|
96
|
+
for (const target of targets) {
|
|
97
|
+
const restriction = restrictions.find(r => r.match(target))
|
|
98
|
+
if (restriction) {
|
|
99
|
+
context.report({
|
|
100
|
+
node: target.node,
|
|
101
|
+
messageId: "restricted",
|
|
102
|
+
data: {
|
|
103
|
+
name: target.name,
|
|
104
|
+
customMessage: restriction.message,
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const { Range, lt, major } = require("semver") //eslint-disable-line no-unused-vars
|
|
8
|
+
const { ReferenceTracker } = require("eslint-utils")
|
|
9
|
+
const getConfiguredNodeVersion = require("./get-configured-node-version")
|
|
10
|
+
const getSemverRange = require("./get-semver-range")
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} SupportInfo
|
|
14
|
+
* @property {string | null} supported The stably supported version. If `null` is present, it hasn't been supported yet.
|
|
15
|
+
* @property {string[]} [backported] The backported versions.
|
|
16
|
+
* @property {string} [experimental] The added version as experimental.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parses the options.
|
|
21
|
+
* @param {RuleContext} context The rule context.
|
|
22
|
+
* @returns {{version:Range,ignores:Set<string>}} Parsed value.
|
|
23
|
+
*/
|
|
24
|
+
function parseOptions(context) {
|
|
25
|
+
const raw = context.options[0] || {}
|
|
26
|
+
const filePath = context.getFilename()
|
|
27
|
+
const version = getConfiguredNodeVersion(raw.version, filePath)
|
|
28
|
+
const ignores = new Set(raw.ignores || [])
|
|
29
|
+
|
|
30
|
+
return Object.freeze({ version, ignores })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check if it has been supported.
|
|
35
|
+
* @param {SupportInfo} info The support info.
|
|
36
|
+
* @param {Range} configured The configured version range.
|
|
37
|
+
*/
|
|
38
|
+
function isSupported({ backported, supported }, configured) {
|
|
39
|
+
if (
|
|
40
|
+
backported &&
|
|
41
|
+
backported.length >= 2 &&
|
|
42
|
+
!backported.every((v, i) => i === 0 || lt(backported[i - 1], v))
|
|
43
|
+
) {
|
|
44
|
+
throw new Error("Invalid BackportConfiguration")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (supported == null) {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
if (backported == null || backported.length === 0) {
|
|
51
|
+
return !configured.intersects(getSemverRange(`<${supported}`))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return !configured.intersects(
|
|
55
|
+
getSemverRange(
|
|
56
|
+
[...backported, supported]
|
|
57
|
+
.map((v, i) => (i === 0 ? `<${v}` : `>=${major(v)}.0.0 <${v}`))
|
|
58
|
+
.join(" || ")
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get the formatted text of a given supported version.
|
|
65
|
+
* @param {SupportInfo} info The support info.
|
|
66
|
+
*/
|
|
67
|
+
function supportedVersionToString({ backported, supported }) {
|
|
68
|
+
if (supported == null) {
|
|
69
|
+
return "(none yet)"
|
|
70
|
+
}
|
|
71
|
+
if (backported == null || backported.length === 0) {
|
|
72
|
+
return supported
|
|
73
|
+
}
|
|
74
|
+
return `${supported} (backported: ^${backported.join(", ^")})`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Verify the code to report unsupported APIs.
|
|
79
|
+
* @param {RuleContext} context The rule context.
|
|
80
|
+
* @param {{modules:object,globals:object}} trackMap The map for APIs to report.
|
|
81
|
+
* @returns {void}
|
|
82
|
+
*/
|
|
83
|
+
module.exports = function checkUnsupportedBuiltins(context, trackMap) {
|
|
84
|
+
const options = parseOptions(context)
|
|
85
|
+
const tracker = new ReferenceTracker(context.getScope(), { mode: "legacy" })
|
|
86
|
+
const references = [
|
|
87
|
+
...tracker.iterateCjsReferences(trackMap.modules || {}),
|
|
88
|
+
...tracker.iterateEsmReferences(trackMap.modules || {}),
|
|
89
|
+
...tracker.iterateGlobalReferences(trackMap.globals || {}),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
for (const { node, path, info } of references) {
|
|
93
|
+
const name = path.join(".")
|
|
94
|
+
const supported = isSupported(info, options.version)
|
|
95
|
+
|
|
96
|
+
if (!supported && !options.ignores.has(name)) {
|
|
97
|
+
context.report({
|
|
98
|
+
node,
|
|
99
|
+
messageId: "unsupported",
|
|
100
|
+
data: {
|
|
101
|
+
name,
|
|
102
|
+
supported: supportedVersionToString(info),
|
|
103
|
+
version: options.version.raw,
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|