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.
Files changed (69) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +189 -0
  3. package/lib/configs/_commons.js +76 -0
  4. package/lib/configs/recommended-module.js +27 -0
  5. package/lib/configs/recommended-script.js +24 -0
  6. package/lib/configs/recommended.js +18 -0
  7. package/lib/index.js +55 -0
  8. package/lib/rules/callback-return.js +185 -0
  9. package/lib/rules/exports-style.js +297 -0
  10. package/lib/rules/file-extension-in-import.js +129 -0
  11. package/lib/rules/global-require.js +91 -0
  12. package/lib/rules/handle-callback-err.js +94 -0
  13. package/lib/rules/no-callback-literal.js +82 -0
  14. package/lib/rules/no-deprecated-api.js +782 -0
  15. package/lib/rules/no-exports-assign.js +75 -0
  16. package/lib/rules/no-extraneous-import.js +49 -0
  17. package/lib/rules/no-extraneous-require.js +49 -0
  18. package/lib/rules/no-hide-core-modules.js +157 -0
  19. package/lib/rules/no-missing-import.js +47 -0
  20. package/lib/rules/no-missing-require.js +47 -0
  21. package/lib/rules/no-mixed-requires.js +255 -0
  22. package/lib/rules/no-new-require.js +39 -0
  23. package/lib/rules/no-path-concat.js +212 -0
  24. package/lib/rules/no-process-env.js +45 -0
  25. package/lib/rules/no-process-exit.js +36 -0
  26. package/lib/rules/no-restricted-import.js +61 -0
  27. package/lib/rules/no-restricted-require.js +61 -0
  28. package/lib/rules/no-sync.js +53 -0
  29. package/lib/rules/no-unpublished-bin.js +97 -0
  30. package/lib/rules/no-unpublished-import.js +49 -0
  31. package/lib/rules/no-unpublished-require.js +49 -0
  32. package/lib/rules/no-unsupported-features/es-builtins.js +181 -0
  33. package/lib/rules/no-unsupported-features/es-syntax.js +659 -0
  34. package/lib/rules/no-unsupported-features/node-builtins.js +417 -0
  35. package/lib/rules/no-unsupported-features.js +1542 -0
  36. package/lib/rules/prefer-global/buffer.js +49 -0
  37. package/lib/rules/prefer-global/console.js +46 -0
  38. package/lib/rules/prefer-global/process.js +46 -0
  39. package/lib/rules/prefer-global/text-decoder.js +49 -0
  40. package/lib/rules/prefer-global/text-encoder.js +49 -0
  41. package/lib/rules/prefer-global/url-search-params.js +49 -0
  42. package/lib/rules/prefer-global/url.js +48 -0
  43. package/lib/rules/prefer-promises/dns.js +74 -0
  44. package/lib/rules/prefer-promises/fs.js +76 -0
  45. package/lib/rules/process-exit-as-throw.js +164 -0
  46. package/lib/rules/shebang.js +170 -0
  47. package/lib/util/cache.js +58 -0
  48. package/lib/util/check-existence.js +40 -0
  49. package/lib/util/check-extraneous.js +52 -0
  50. package/lib/util/check-prefer-global.js +63 -0
  51. package/lib/util/check-publish.js +71 -0
  52. package/lib/util/check-restricted.js +109 -0
  53. package/lib/util/check-unsupported-builtins.js +108 -0
  54. package/lib/util/enumerate-property-names.js +39 -0
  55. package/lib/util/exists.js +58 -0
  56. package/lib/util/get-allow-modules.js +47 -0
  57. package/lib/util/get-configured-node-version.js +39 -0
  58. package/lib/util/get-convert-path.js +189 -0
  59. package/lib/util/get-npmignore.js +184 -0
  60. package/lib/util/get-package-json.js +75 -0
  61. package/lib/util/get-resolve-paths.js +44 -0
  62. package/lib/util/get-semver-range.js +30 -0
  63. package/lib/util/get-try-extensions.js +47 -0
  64. package/lib/util/import-target.js +85 -0
  65. package/lib/util/merge-visitors-in-place.js +46 -0
  66. package/lib/util/strip-import-path-params.js +10 -0
  67. package/lib/util/visit-import.js +66 -0
  68. package/lib/util/visit-require.js +60 -0
  69. 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
+ }