Skip to content

Commit 9aa7e8d

Browse files
committed
Breaking: add ES2016/2017 features to no-unsupported-features (fixes #46)
1 parent a6f90e1 commit 9aa7e8d

File tree

4 files changed

+281
-30
lines changed

4 files changed

+281
-30
lines changed

docs/rules/no-unsupported-features.md

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,38 @@
11
# Disallow unsupported ECMAScript features on the specified version (no-unsupported-features)
22

3-
Node.js v0.12, v4, and v5 don't support all ECMAScript 2015 (ES6) features.
4-
This rule reports when you used unsupported ECMAScript 2015 features on the specified Node version.
3+
Node.js doesn't support all ECMAScript standard features.
4+
This rule reports when you used unsupported ECMAScript 2015-2017 features on the specified Node.js version.
55

6-
## Rule Details
6+
> ※ About ECMAScript 2017, this rule reports only features which have arrived at stage 4 until 2016-10-31.
7+
> It needs a major version bump in order to cover newer features.
78
8-
**This rule expects to be used with `"env": {"es6": true}` configuration.**
9+
## Rule Details
910

10-
This rule requires a Node version.
11-
For Example:
11+
:warning: This rule expects to be used with the following configuration:
1212

1313
```json
1414
{
15-
"node/no-unsupported-features": ["error", {"version": 4}]
15+
"env": {"es6": true},
16+
"parserOptions": {"ecmaVersion": 2017}
1617
}
1718
```
1819

19-
This rule accepts the following version number:
20+
:warning: This rule reads the [engines] field of `package.json` to detect Node.js version.
2021

21-
- `0.10`
22-
- `0.12`
23-
- `4`
24-
- `5`
25-
- `6`
22+
I recommend a use of the [engines] field since it's the official way to indicate what Node.js versions your module is supporting.
23+
For example of `package.json`:
24+
25+
```json
26+
{
27+
"name": "your-module",
28+
"version": "1.0.0",
29+
"engines": {
30+
"node": ">=4.0.0"
31+
}
32+
}
33+
```
2634

27-
If the version was omitted, this rule will read the [engines](https://docs.npmjs.com/files/package.json#engines) field of `package.json`.
28-
If both the `version` option and the `engines` field don't exist, this rule will use the minimum version Node community is maintaining (It's `0.10` currently).
35+
If the [engines] field is omitted, this rule chooses `0.12` since it's the minimum version the community is maintaining.
2936

3037
Examples of :-1: **incorrect** code for this rule:
3138

@@ -98,16 +105,33 @@ var p = new Promise((resolve, reject) => {
98105

99106
## Options
100107

101-
This rule has `"ignores"` option to ignore to use the specified features.
102-
103108
```json
104109
{
105-
"node/no-unsupported-features": ["error", {"version": 4, "ignores": []}]
110+
"node/no-unsupported-features": ["error", {
111+
"version": 4,
112+
"ignores": []
113+
}]
106114
}
107115
```
108116

109-
Features which are specified by this `"ignores"` option are not warned.
110-
This `"ignores"` option accepts an array of the following strings.
117+
### version
118+
119+
As mentioned above, this rule reads the [engines] field of `package.json` to detect Node.js version.
120+
Also, you can overwrite the version by `version` option.
121+
The `version` option accepts the following version number:
122+
123+
- `0.10`
124+
- `0.12`
125+
- `4`
126+
- `5`
127+
- `6`
128+
- `7`
129+
130+
### ignores
131+
132+
If you are using transpilers, maybe you want to ignore the warnings about some features.
133+
You can use this `ignores` option to ignore the given features.
134+
The `"ignores"` option accepts an array of the following strings.
111135

112136
- `"syntax"` (group)
113137
- `"defaultParameters"`
@@ -130,6 +154,9 @@ This `"ignores"` option accepts an array of the following strings.
130154
- `"generatorFunctions"`
131155
- `"classes"`
132156
- `"modules"`
157+
- `"exponentialOperators"`
158+
- `"asyncAwait"`
159+
- `"trailingCommasInFunctionSyntax"`
133160
- `"runtime"` (group)
134161
- `"globalObjects"` (group)
135162
- `"typedArrays"` (group)
@@ -157,6 +184,9 @@ This `"ignores"` option accepts an array of the following strings.
157184
- `"Object.is"`
158185
- `"Object.getOwnPropertySymbols"`
159186
- `"Object.setPrototypeOf"`
187+
- `"Object.values"`
188+
- `"Object.entries"`
189+
- `"Object.getOwnPropertyDescriptors"`
160190
- `"String.*"` (group)
161191
- `"String.raw"`
162192
- `"String.fromCodePoint"`
@@ -211,6 +241,7 @@ This `"ignores"` option accepts an array of the following strings.
211241
- `"extendsString"`
212242
- `"extendsMap"`
213243
- `"extendsSet"`
244+
- `"extendsNull"`
214245

215246
If a group value is given, all sub items of the value are ignored.
216247
e.g. if `"String.*"` is given then `"String.raw"` and `"String.fromCodePoint"` are ignored.
@@ -226,7 +257,14 @@ function foo(a = 1) {
226257
}
227258
```
228259

260+
## Known Limitations
261+
262+
This rule cannot report non-static things.
263+
E.g., a use of instance methods.
264+
229265
## Further Reading
230266

231267
- http://node.green/
232268
- http://kangax.github.io/compat-table/es6/
269+
270+
[engines]: https://docs.npmjs.com/files/package.json#engines

lib/rules/no-unsupported-features.js

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var getValueIfString = require("../util/get-value-if-string")
1818
// Helpers
1919
//------------------------------------------------------------------------------
2020

21-
var VERSIONS = [0.10, 0.12, 4, 5, 6]
21+
var VERSIONS = [0.10, 0.12, 4, 5, 6, 7]
2222
var OPTIONS = Object.keys(features)
2323
var FUNC_TYPE = /^(?:Arrow)?Function(?:Declaration|Expression)$/
2424
var CLASS_TYPE = /^Class(?:Declaration|Expression)$/
@@ -37,7 +37,10 @@ var SUBCLASSING_TEST_TARGETS = [
3737
"Map", "Set",
3838
]
3939
var PROPERTY_TEST_TARGETS = {
40-
Object: ["assign", "is", "getOwnPropertySymbols", "setPrototypeOf"],
40+
Object: [
41+
"assign", "is", "getOwnPropertySymbols", "setPrototypeOf", "values",
42+
"entries", "getOwnPropertyDescriptors",
43+
],
4144
String: ["raw", "fromCodePoint"],
4245
Array: ["from", "of"],
4346
Number: [
@@ -294,6 +297,48 @@ module.exports = function(context) {
294297
return scope.isStrict
295298
}
296299

300+
/**
301+
* Checks whether the given function has trailing commas or not.
302+
*
303+
* @param {ASTNode} node - The function node to check.
304+
* @returns {boolean} `true` if the function has trailing commas.
305+
*/
306+
function hasTrailingCommaForFunction(node) {
307+
var length = node.params.length
308+
309+
return (
310+
length >= 1 &&
311+
sourceCode.getTokenAfter(node.params[length - 1]).value === ","
312+
)
313+
}
314+
315+
/**
316+
* Checks whether the given call expression has trailing commas or not.
317+
*
318+
* @param {ASTNode} node - The call expression node to check.
319+
* @returns {boolean} `true` if the call expression has trailing commas.
320+
*/
321+
function hasTrailingCommaForCall(node) {
322+
return (
323+
node.arguments.length >= 1 &&
324+
sourceCode.getLastToken(node, 1).value === ","
325+
)
326+
}
327+
328+
/**
329+
* Checks whether the given class extends from null or not.
330+
*
331+
* @param {ASTNode} node - The class node to check.
332+
* @returns {boolean} `true` if the class extends from null.
333+
*/
334+
function extendsNull(node) {
335+
return (
336+
node.superClass != null &&
337+
node.superClass.type === "Literal" &&
338+
node.superClass.value === null
339+
)
340+
}
341+
297342
/**
298343
* Reports a given node if the specified feature is not supported.
299344
*
@@ -387,6 +432,12 @@ module.exports = function(context) {
387432

388433
"ArrowFunctionExpression": function(node) {
389434
report(node, "arrowFunctions")
435+
if (node.async) {
436+
report(node, "asyncAwait")
437+
}
438+
if (hasTrailingCommaForFunction(node)) {
439+
report(node, "trailingCommasInFunctionSyntax")
440+
}
390441
},
391442

392443
"AssignmentPattern": function(node) {
@@ -403,12 +454,24 @@ module.exports = function(context) {
403454
if (node.generator) {
404455
report(node, "generatorFunctions")
405456
}
457+
if (node.async) {
458+
report(node, "asyncAwait")
459+
}
460+
if (hasTrailingCommaForFunction(node)) {
461+
report(node, "trailingCommasInFunctionSyntax")
462+
}
406463
},
407464

408465
"FunctionExpression": function(node) {
409466
if (node.generator) {
410467
report(node, "generatorFunctions")
411468
}
469+
if (node.async) {
470+
report(node, "asyncAwait")
471+
}
472+
if (hasTrailingCommaForFunction(node)) {
473+
report(node, "trailingCommasInFunctionSyntax")
474+
}
412475
},
413476

414477
"MetaProperty": function(node) {
@@ -431,10 +494,18 @@ module.exports = function(context) {
431494

432495
"ClassDeclaration": function(node) {
433496
report(node, "classes")
497+
498+
if (extendsNull(node)) {
499+
report(node, "extendsNull")
500+
}
434501
},
435502

436503
"ClassExpression": function(node) {
437504
report(node, "classes")
505+
506+
if (extendsNull(node)) {
507+
report(node, "extendsNull")
508+
}
438509
},
439510

440511
//----------------------------------------------------------------------
@@ -464,6 +535,28 @@ module.exports = function(context) {
464535
}
465536
},
466537

538+
"AssignmentExpression": function(node) {
539+
if (node.operator === "**=") {
540+
report(node, "exponentialOperators")
541+
}
542+
},
543+
544+
"AwaitExpression": function(node) {
545+
report(node, "asyncAwait")
546+
},
547+
548+
"BinaryExpression": function(node) {
549+
if (node.operator === "**") {
550+
report(node, "exponentialOperators")
551+
}
552+
},
553+
554+
"CallExpression": function(node) {
555+
if (hasTrailingCommaForCall(node)) {
556+
report(node, "trailingCommasInFunctionSyntax")
557+
}
558+
},
559+
467560
"Identifier": function(node) {
468561
var raw = sourceCode.getText(node)
469562
if (hasUnicodeCodePointEscape(raw)) {
@@ -509,6 +602,9 @@ module.exports = function(context) {
509602
report(node, "regexpU")
510603
}
511604
}
605+
if (hasTrailingCommaForCall(node)) {
606+
report(node, "trailingCommasInFunctionSyntax")
607+
}
512608
},
513609

514610
"ObjectPattern": function(node) {
@@ -563,7 +659,7 @@ module.exports = function(context) {
563659
module.exports.schema = [
564660
{
565661
oneOf: [
566-
{enum: VERSIONS},
662+
{enum: VERSIONS},
567663
{
568664
type: "object",
569665
properties: {
@@ -575,7 +671,6 @@ module.exports.schema = [
575671
},
576672
},
577673
additionalProperties: false,
578-
required: ["version"],
579674
},
580675
],
581676
},

lib/util/features.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,22 @@ module.exports = {
129129
"modules": {
130130
alias: ["syntax"],
131131
name: "Import and Export Declarations",
132-
node: 6,
132+
node: NaN,
133+
},
134+
"exponentialOperators": {
135+
alias: ["syntax"],
136+
name: "Exponential Operators (**)",
137+
node: 7,
138+
},
139+
"asyncAwait": {
140+
alias: ["syntax"],
141+
name: "Async Functions",
142+
node: NaN,
143+
},
144+
"trailingCommasInFunctionSyntax": {
145+
alias: ["syntax"],
146+
name: "Trailing Commas in Function Syntax",
147+
node: NaN,
133148
},
134149

135150
//--------------------------------------------------------------------------
@@ -269,6 +284,24 @@ module.exports = {
269284
singular: true,
270285
node: 0.12,
271286
},
287+
"Object.values": {
288+
alias: ["runtime", "staticMethods", "Object.*"],
289+
name: "'Object.values'",
290+
singular: true,
291+
node: 7,
292+
},
293+
"Object.entries": {
294+
alias: ["runtime", "staticMethods", "Object.*"],
295+
name: "'Object.entries'",
296+
singular: true,
297+
node: 7,
298+
},
299+
"Object.getOwnPropertyDescriptors": {
300+
alias: ["runtime", "staticMethods", "Object.*"],
301+
name: "'Object.getOwnPropertyDescriptors'",
302+
singular: true,
303+
node: 7,
304+
},
272305

273306
"String.raw": {
274307
alias: ["runtime", "staticMethods", "String.*"],
@@ -563,4 +596,10 @@ module.exports = {
563596
singular: true,
564597
node: 4,
565598
},
599+
"extendsNull": {
600+
alias: ["runtime", "extends"],
601+
name: "'extends null'",
602+
singular: true,
603+
node: NaN,
604+
},
566605
}

0 commit comments

Comments
 (0)