Skip to content

Commit

Permalink
[[FEAT]] Add esversion option
Browse files Browse the repository at this point in the history
This option (which may be set to 3, 5, or 6 / 2015) should replace `es3`, `es5` and `esnext`. This:
- precludes invalid configurations like `es3: false, es5: true`
- scales better for future versions of ECMAScript
- eliminates the ambiguity of `esnext` (whose meaning has changed now that ES6 has been finalized)

This commit partially fixes #2124
  • Loading branch information
nicolo-ribaudo authored and jugglinmike committed Aug 15, 2015
1 parent 7f5806f commit cf5a699
Show file tree
Hide file tree
Showing 9 changed files with 1,057 additions and 878 deletions.
181 changes: 123 additions & 58 deletions src/jshint.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/lex.js
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ Lexer.prototype = {
code: "W119",
line: this.line,
character: this.char,
data: [ "Octal integer literal" ]
data: [ "Octal integer literal", "6" ]
});
}

Expand All @@ -814,7 +814,7 @@ Lexer.prototype = {
code: "W119",
line: this.line,
character: this.char,
data: [ "Binary integer literal" ]
data: [ "Binary integer literal", "6" ]
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ var warnings = {
W101: "Line is too long.",
W102: null,
W103: "The '{a}' property is deprecated.",
W104: "'{a}' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).",
W104: "'{a}' is available in ES{b} (use 'esversion: {b}') or Mozilla JS extensions (use moz).",
W105: "Unexpected {a} in '{b}'.",
W106: "Identifier '{a}' is not in camel case.",
W107: "Script URL.",
Expand All @@ -198,7 +198,7 @@ var warnings = {
W116: "Expected '{a}' and instead saw '{b}'.",
W117: "'{a}' is not defined.",
W118: "'{a}' is only available in Mozilla JavaScript extensions (use moz option).",
W119: "'{a}' is only available in ES6 (use esnext option).",
W119: "'{a}' is only available in ES{b} (use 'esversion: {b}').",
W120: "You might be leaking a variable ({a}) here.",
W121: "Extending prototype of native object: '{a}'.",
W122: "Invalid typeof value '{a}'",
Expand Down
30 changes: 26 additions & 4 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,17 @@ exports.bool = {
* specification. Use this option if you need your program to be executable
* in older browsers—such as Internet Explorer 6/7/8/9—and other legacy
* JavaScript environments.
*
* @deprecated Use `esversion: 3` instead.
*/
es3 : true,

/**
* This option enables syntax first defined in [the ECMAScript 5.1
* specification](http://es5.github.io/). This includes allowing reserved
* keywords as object properties.
*
* @deprecated Use `esversion: 5` instead.
*/
es5 : true,

Expand Down Expand Up @@ -542,8 +546,10 @@ exports.bool = {
*
* More info:
*
* * [Draft Specification for ES.next (ECMA-262 Ed.
* 6)](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts)
* * [Specification for ECMAScript
* 6](http://www.ecma-international.org/ecma-262/6.0/index.html)
*
* @deprecated Use `esversion: 6` instead.
*/
esnext : true,

Expand Down Expand Up @@ -937,8 +943,24 @@ exports.val = {
// end - stop ignoring lines, starting on the next line
// line - ignore warnings / errors for just a single line
// (this option does not bypass the lexer)
ignoreDelimiters: false // array of start/end delimiters used to ignore
// certain chunks from code

ignoreDelimiters: false, // array of start/end delimiters used to ignore
// certain chunks from code

/**
* This option is used to specify the ECMAScript version to which the code
* must adhere. It can assume one of the following values:
* - `3` - If you need your program to be executable
* in older browsers—such as Internet Explorer 6/7/8/9—and other legacy
* JavaScript environments
* - `5` - To enable syntax first defined in [the ECMAScript 5.1
* specification](http://www.ecma-international.org/ecma-262/5.1/index.html).
* This includes allowing reserved keywords as object properties.
* - `6` - To tell JSHint that your code uses [ECMAScript
* 6](http://www.ecma-international.org/ecma-262/6.0/index.html) specific
* syntax. Note that not all browsers implement them.
*/
esversion: 5
};

// These are JSHint boolean options which are shared with JSLint
Expand Down
31 changes: 17 additions & 14 deletions src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,32 @@ var state = {
this.option.module || this.option.strict === "implied";
},

// Assumption: chronologically ES3 < ES5 < ES6/ESNext < Moz
// Assumption: chronologically ES3 < ES5 < ES6 < Moz

inMoz: function() {
return this.option.moz && !this.option.esnext;
return this.option.moz;
},

/**
* @param {object} options
* @param {boolean} options.strict - When `true`, only consider ESNext when
* in "esnext" code and *not* in "moz".
* @param {boolean} strict - When `true`, only consider ES6 when in
* "esversion: 6" code and *not* in "moz".
*/
inESNext: function(strict) {
inES6: function(strict) {
if (strict) {
return !this.option.moz && this.option.esnext;
return !this.option.moz && this.option.esversion === 6;
}
return this.option.moz || this.option.esnext;
},

inES5: function() {
return !this.option.es3;
return this.option.moz || this.option.esversion >= 6;
},

inES3: function() {
return this.option.es3;
/**
* @param {boolean} strict - When `true`, return `true` only when
* esversion is exactly 5
*/
inES5: function(strict) {
if (strict) {
return (!this.option.esversion || this.option.esversion === 5) && !this.option.moz;
}
return !this.option.esversion || this.option.esversion >= 5 || this.option.moz;
},


Expand Down
2 changes: 1 addition & 1 deletion src/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ exports.register = function(linter) {
linter.warn("W103", {
line: data.line,
char: data.char,
data: [ data.name ]
data: [ data.name, "6" ]
});
}
});
Expand Down
86 changes: 43 additions & 43 deletions tests/unit/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -810,37 +810,37 @@ exports.testES6Modules = function (test) {
testRun.test(src, {esnext: true});

testRun = TestRun(test)
.addError(3, "'import' is only available in ES6 (use esnext option).")
.addError(4, "'import' is only available in ES6 (use esnext option).")
.addError(5, "'import' is only available in ES6 (use esnext option).")
.addError(6, "'import' is only available in ES6 (use esnext option).")
.addError(7, "'import' is only available in ES6 (use esnext option).")
.addError(8, "'import' is only available in ES6 (use esnext option).")
.addError(9, "'import' is only available in ES6 (use esnext option).")
.addError(10, "'import' is only available in ES6 (use esnext option).")
.addError(11, "'import' is only available in ES6 (use esnext option).")
.addError(22, "'export' is only available in ES6 (use esnext option).")
.addError(26, "'export' is only available in ES6 (use esnext option).")
.addError(30, "'export' is only available in ES6 (use esnext option).")
.addError(31, "'export' is only available in ES6 (use esnext option).")
.addError(32, "'export' is only available in ES6 (use esnext option).")
.addError(36, "'export' is only available in ES6 (use esnext option).")
.addError(40, "'export' is only available in ES6 (use esnext option).")
.addError(44, "'export' is only available in ES6 (use esnext option).")
.addError(46, "'export' is only available in ES6 (use esnext option).")
.addError(47, "'class' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).")
.addError(48, "'export' is only available in ES6 (use esnext option).")
.addError(48, "'class' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).")
.addError(47, "'export' is only available in ES6 (use esnext option).")
.addError(46, "'class' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).")
.addError(57, "'import' is only available in ES6 (use esnext option).")
.addError(58, "'import' is only available in ES6 (use esnext option).")
.addError(59, "'import' is only available in ES6 (use esnext option).")
.addError(60, "'import' is only available in ES6 (use esnext option).")
.addError(65, "'import' is only available in ES6 (use esnext option).")
.addError(67, "'export' is only available in ES6 (use esnext option).")
.addError(67, "'function*' is only available in ES6 (use esnext option).")
.addError(67, "'yield' is available in ES6 (use esnext option) or Mozilla JS extensions (use moz).");
.addError(3, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(4, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(5, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(6, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(7, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(8, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(9, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(10, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(11, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(22, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(26, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(30, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(31, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(32, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(36, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(40, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(44, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(46, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(47, "'class' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).")
.addError(48, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(48, "'class' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).")
.addError(47, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(46, "'class' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).")
.addError(57, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(58, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(59, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(60, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(65, "'import' is only available in ES6 (use 'esversion: 6').")
.addError(67, "'export' is only available in ES6 (use 'esversion: 6').")
.addError(67, "'function*' is only available in ES6 (use 'esversion: 6').")
.addError(67, "'yield' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz).");
importConstErrors.forEach(function(error) { testRun.addError.apply(testRun, error); });
testRun.test(src, {});

Expand Down Expand Up @@ -1473,21 +1473,21 @@ exports.testDefaultArguments = function (test) {
.test(src, { moz: true });

TestRun(test)
.addError(7, "'default parameters' is only available in ES6 (use esnext option).")
.addError(11, "'default parameters' is only available in ES6 (use esnext option).")
.addError(12, "'default parameters' is only available in ES6 (use esnext option).")
.addError(13, "'default parameters' is only available in ES6 (use esnext option).")
.addError(14, "'default parameters' is only available in ES6 (use esnext option).")
.addError(7, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(11, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(12, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(13, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(14, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(14, "'num3' was used before it was declared, which is illegal for 'param' variables.")
.addError(15, "'default parameters' is only available in ES6 (use esnext option).")
.addError(15, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(15, "'num4' was used before it was declared, which is illegal for 'param' variables.")
.addError(18, "'default parameters' is only available in ES6 (use esnext option).")
.addError(18, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(18, "Regular parameters should not come after default parameters.")
.addError(26, "'default parameters' is only available in ES6 (use esnext option).")
.addError(31, "'default parameters' is only available in ES6 (use esnext option).")
.addError(33, "'default parameters' is only available in ES6 (use esnext option).")
.addError(35, "'default parameters' is only available in ES6 (use esnext option).")
.addError(36, "'default parameters' is only available in ES6 (use esnext option).")
.addError(26, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(31, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(33, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(35, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(36, "'default parameters' is only available in ES6 (use 'esversion: 6').")
.addError(36, "'e' was used before it was declared, which is illegal for 'param' variables.")
.test(src, { });

Expand Down
121 changes: 121 additions & 0 deletions tests/unit/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -3166,3 +3166,124 @@ exports.module.declarationRestrictions = function( test ) {

test.done();
};

exports.esversion = function(test) {
var code = [
"// jshint esversion: 3",
"// jshint esversion: 4",
"// jshint esversion: 5",
"// jshint esversion: 6",
"// jshint esversion: 2015"
];

TestRun(test, "Value")
.addError(2, "Bad option value.")
.test(code);

var es5code = [
"var a = {",
" get b() {}",
"};"
];

TestRun(test, "ES5 syntax as ES3")
.addError(2, "get/set are ES5 features.")
.test(es5code, { esversion: 3 });

TestRun(test, "ES5 syntax as ES5")
.test(es5code); // esversion: 5 (default)

TestRun(test, "ES5 syntax as ES6")
.test(es5code, { esversion: 6 });

var es6code = [
"var a = {",
" ['b']: 1",
"};"
];

TestRun(test, "ES6 syntax as ES3")
.addError(2, "'computed property names' is only available in ES6 (use 'esversion: 6').")
.test(es6code, { esversion: 3 });

TestRun(test, "ES6 syntax as ES5")
.addError(2, "'computed property names' is only available in ES6 (use 'esversion: 6').")
.test(es6code); // esversion: 5 (default)

TestRun(test, "ES6 syntax as ES6")
.test(es6code, { esversion: 6 });

// Array comprehensions aren't defined in ECMAScript 6,
// but they can be enabled using the `esnext` option
var arrayComprehension = [
"var a = [ 1, 2, 3 ];",
"var b = [ for (i of a) i ];"
];

TestRun(test, "array comprehensions - esversion: 6")
.addError(2, "'array comprehension' is only available in Mozilla JavaScript extensions " +
"(use moz option).")
.test(arrayComprehension, { esversion: 6 });

TestRun(test, "array comprehensions - esnext: true")
.test(arrayComprehension, { esnext: true });


TestRun(test, "precedence over `es3`") // TODO: Remove in JSHint 3
.test(es6code, { esversion: 6, es3: true });

TestRun(test, "precedence over `es5`") // TODO: Remove in JSHint 3
.addError(0, "ES5 option is now set per default")
.test(es6code, { esversion: 6, es5: true });

TestRun(test, "precedence over `esnext`") // TODO: Remove in JSHint 3
.addError(2, "'computed property names' is only available in ES6 (use 'esversion: 6').")
.test(es6code, { esversion: 3, esnext: true });

var code2 = [ // TODO: Remove in JSHint 3
"/* jshint esversion: 3, esnext: true */"
].concat(es6code);

TestRun(test, "the last has the precedence (inline configuration)") // TODO: Remove in JSHint 3
.test(code2);

var code3 = [
"var someCode;",
"// jshint esversion: 3"
];

TestRun(test, "cannot be set after any executable code")
.addError(2, "The 'esversion' option cannot be set after any executable code.")
.test(code3);

var code4 = [
"#!/usr/bin/env node",
"/**",
" * License",
" */",
"// jshint esversion: 3",
"// jshint esversion: 6"
];

TestRun(test, "can follow shebang or comments")
.test(code4);

var code5 = [
"// jshint moz: true",
"// jshint esversion: 3",
"var x = {",
" get a() {}",
"};",
"// jshint moz: true",
"var x = {",
" get a() {}",
"};"
];

TestRun(test, "correctly swap between moz and esversion")
.addError(4, "get/set are ES5 features.")
.test(code5);


test.done();
};
Loading

0 comments on commit cf5a699

Please sign in to comment.