Skip to content

Commit c046376

Browse files
authoredMay 10, 2024··
feat: Add support for ignoring experemental features (#269)
1 parent c3e5a19 commit c046376

File tree

4 files changed

+111
-21
lines changed

4 files changed

+111
-21
lines changed
 

‎docs/rules/no-unsupported-features/node-builtins.md

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ But, you can overwrite the version by `version` option.
3636

3737
The `version` option accepts [the valid version range of `node-semver`](https://github.com/npm/node-semver#range-grammar).
3838

39+
#### allowExperimental
40+
41+
This allows you to enable experimental features that are available in your configured node version
42+
43+
The `"allowExperimental"` option accepts a boolean value (the default value is `false`).
44+
3945
#### ignores
4046

4147
If you are using transpilers, maybe you want to ignore the warnings about some features.

‎lib/rules/no-unsupported-features/node-builtins.js

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ module.exports = {
3737
type: "object",
3838
properties: {
3939
version: getConfiguredNodeVersion.schema,
40+
allowExperimental: { type: "boolean" },
4041
ignores: {
4142
type: "array",
4243
items: {

‎lib/util/check-unsupported-builtins.js

+65-21
Original file line numberDiff line numberDiff line change
@@ -15,51 +15,56 @@ const semverRangeSubset = require("semver/ranges/subset")
1515
* Parses the options.
1616
* @param {import('eslint').Rule.RuleContext} context The rule context.
1717
* @returns {Readonly<{
18-
* version: import('semver').Range,
19-
* ignores: Set<string>
18+
* version: import('semver').Range;
19+
* ignores: Set<string>;
20+
* allowExperimental: boolean;
2021
* }>} Parsed value.
2122
*/
2223
function parseOptions(context) {
2324
const raw = context.options[0] || {}
2425
const version = getConfiguredNodeVersion(context)
2526
const ignores = new Set(raw.ignores || [])
27+
const allowExperimental = raw.allowExperimental ?? false
2628

27-
return Object.freeze({ version, ignores })
29+
return Object.freeze({ version, ignores, allowExperimental })
2830
}
2931

3032
/**
3133
* Check if it has been supported.
32-
* @param {import('../unsupported-features/types.js').SupportInfo} info The support info.
33-
* @param {import('semver').Range} configured The configured version range.
34+
* @param {string[] | undefined} featureRange The target features supported range
35+
* @param {import('semver').Range} requestedRange The configured version range.
36+
* @returns {boolean}
3437
*/
35-
function isSupported({ supported }, configured) {
36-
if (supported == null || supported.length === 0) {
38+
function isInRange(featureRange, requestedRange) {
39+
if (featureRange == null || featureRange.length === 0) {
3740
return false
3841
}
3942

40-
const [latest] = rsort(supported)
43+
const [latest] = rsort(featureRange)
4144
const range = getSemverRange(
42-
[...supported.map(version => `^${version}`), `>= ${latest}`].join("||")
45+
[...featureRange.map(version => `^${version}`), `>= ${latest}`].join(
46+
"||"
47+
)
4348
)
4449

4550
if (range == null) {
4651
return false
4752
}
4853

49-
return semverRangeSubset(configured, range)
54+
return semverRangeSubset(requestedRange, range)
5055
}
5156

5257
/**
5358
* Get the formatted text of a given supported version.
54-
* @param {import('../unsupported-features/types.js').SupportInfo} info The support info.
59+
* @param {string[] | undefined} versions The support info.
5560
* @returns {string | undefined}
5661
*/
57-
function supportedVersionToString({ supported }) {
58-
if (supported == null || supported.length === 0) {
62+
function versionsToString(versions) {
63+
if (versions == null) {
5964
return
6065
}
6166

62-
const [latest, ...backported] = rsort(supported)
67+
const [latest, ...backported] = rsort(versions)
6368

6469
if (backported.length === 0) {
6570
return latest
@@ -92,27 +97,66 @@ module.exports.checkUnsupportedBuiltins = function checkUnsupportedBuiltins(
9297

9398
for (const { node, path, info } of references) {
9499
const name = unprefixNodeColon(path.join("."))
95-
const supported = isSupported(info, options.version)
96100

97-
if (supported === true || options.ignores.has(name)) {
101+
if (options.ignores.has(name)) {
98102
continue
99103
}
100-
const supportedVersion = supportedVersionToString(info)
104+
105+
if (options.allowExperimental) {
106+
if (isInRange(info.experimental, options.version)) {
107+
continue
108+
}
109+
110+
const experimentalVersion = versionsToString(info.experimental)
111+
if (experimentalVersion) {
112+
context.report({
113+
node,
114+
messageId: "not-experimental-till",
115+
data: {
116+
name: path.join("."),
117+
experimental: experimentalVersion,
118+
version: options.version.raw,
119+
},
120+
})
121+
continue
122+
}
123+
}
124+
125+
if (isInRange(info.supported, options.version)) {
126+
continue
127+
}
128+
129+
const supportedVersion = versionsToString(info.supported)
130+
if (supportedVersion) {
131+
context.report({
132+
node,
133+
messageId: "not-supported-till",
134+
data: {
135+
name: path.join("."),
136+
supported: supportedVersion,
137+
version: options.version.raw,
138+
},
139+
})
140+
continue
141+
}
142+
101143
context.report({
102144
node,
103-
messageId: supportedVersion
104-
? "not-supported-till"
105-
: "not-supported-yet",
145+
messageId: "not-supported-yet",
106146
data: {
107147
name: path.join("."),
108-
supported: /** @type string */ (supportedVersion),
109148
version: options.version.raw,
110149
},
111150
})
112151
}
113152
}
114153

115154
exports.messages = {
155+
"not-experimental-till": [
156+
"The '{{name}}' is not an experimental feature",
157+
"until Node.js {{experimental}}.",
158+
"The configured version range is '{{version}}'.",
159+
].join(" "),
116160
"not-supported-till": [
117161
"The '{{name}}' is still an experimental feature",
118162
"and is not supported until Node.js {{supported}}.",

‎tests/lib/rules/no-unsupported-features/node-builtins.js

+39
Original file line numberDiff line numberDiff line change
@@ -5371,5 +5371,44 @@ new RuleTester({ languageOptions: { sourceType: "module" } }).run(
53715371
},
53725372
],
53735373
},
5374+
5375+
{
5376+
valid: [
5377+
{
5378+
code: "fetch('/asd')",
5379+
options: [{ version: "16.16.0", allowExperimental: true }],
5380+
},
5381+
],
5382+
invalid: [
5383+
{
5384+
code: "fetch('/asd')",
5385+
options: [{ version: "16.0.0", allowExperimental: true }],
5386+
errors: [
5387+
{
5388+
messageId: "not-experimental-till",
5389+
data: {
5390+
name: "fetch",
5391+
experimental: "17.5.0 (backported: ^16.15.0)",
5392+
version: "16.0.0",
5393+
},
5394+
},
5395+
],
5396+
},
5397+
{
5398+
code: "fetch('/asd')",
5399+
options: [{ version: "16.16.0", allowExperimental: false }],
5400+
errors: [
5401+
{
5402+
messageId: "not-supported-till",
5403+
data: {
5404+
name: "fetch",
5405+
supported: "21.0.0",
5406+
version: "16.16.0",
5407+
},
5408+
},
5409+
],
5410+
},
5411+
],
5412+
},
53745413
])
53755414
)

0 commit comments

Comments
 (0)
Please sign in to comment.