forked from mysticatea/eslint-plugin-node
-
-
Notifications
You must be signed in to change notification settings - Fork 41
/
check-unsupported-builtins.js
118 lines (106 loc) · 3.88 KB
/
check-unsupported-builtins.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { Range, lt, major } = require("semver") // eslint-disable-line no-unused-vars
const { ReferenceTracker } = require("@eslint-community/eslint-utils")
const getConfiguredNodeVersion = require("./get-configured-node-version")
const getSemverRange = require("./get-semver-range")
const unprefixNodeColon = require("./unprefix-node-colon")
/**
* @typedef {Object} SupportInfo
* @property {string | null} supported The stably supported version. If `null` is present, it hasn't been supported yet.
* @property {string[]} [backported] The backported versions.
* @property {string} [experimental] The added version as experimental.
*/
/**
* Parses the options.
* @param {RuleContext} context The rule context.
* @returns {{version:Range,ignores:Set<string>}} Parsed value.
*/
function parseOptions(context) {
const raw = context.options[0] || {}
const version = getConfiguredNodeVersion(context)
const ignores = new Set(raw.ignores || [])
return Object.freeze({ version, ignores })
}
/**
* Check if it has been supported.
* @param {SupportInfo} info The support info.
* @param {Range} configured The configured version range.
*/
function isSupported({ backported, supported }, configured) {
if (
backported &&
backported.length >= 2 &&
!backported.every((v, i) => i === 0 || lt(backported[i - 1], v))
) {
throw new Error("Invalid BackportConfiguration")
}
if (supported == null) {
return false
}
if (backported == null || backported.length === 0) {
return !configured.intersects(getSemverRange(`<${supported}`))
}
return !configured.intersects(
getSemverRange(
[...backported, supported]
.map((v, i) => (i === 0 ? `<${v}` : `>=${major(v)}.0.0 <${v}`))
.join(" || ")
)
)
}
/**
* Get the formatted text of a given supported version.
* @param {SupportInfo} info The support info.
*/
function supportedVersionToString({ backported, supported }) {
if (supported == null) {
return "(none yet)"
}
if (backported == null || backported.length === 0) {
return supported
}
return `${supported} (backported: ^${backported.join(", ^")})`
}
/**
* Verify the code to report unsupported APIs.
* @param {RuleContext} context The rule context.
* @param {{modules:object,globals:object}} trackMap The map for APIs to report.
* @returns {void}
*/
module.exports.checkUnsupportedBuiltins = function checkUnsupportedBuiltins(
context,
trackMap
) {
const options = parseOptions(context)
const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
const scope = sourceCode.getScope?.(sourceCode.ast) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9
const tracker = new ReferenceTracker(scope, { mode: "legacy" })
const references = [
...tracker.iterateCjsReferences(trackMap.modules || {}),
...tracker.iterateEsmReferences(trackMap.modules || {}),
...tracker.iterateGlobalReferences(trackMap.globals || {}),
]
for (const { node, path, info } of references) {
const name = unprefixNodeColon(path.join("."))
const supported = isSupported(info, options.version)
if (!supported && !options.ignores.has(name)) {
context.report({
node,
messageId: "unsupported",
data: {
name,
supported: supportedVersionToString(info),
version: options.version.raw,
},
})
}
}
}
exports.messages = {
unsupported:
"The '{{name}}' is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
}