diff --git a/conf/eslint.json b/conf/eslint.json index 593e58527f8..729a2040dff 100755 --- a/conf/eslint.json +++ b/conf/eslint.json @@ -136,7 +136,7 @@ "max-statements": [0, 10], "new-cap": 2, "new-parens": 2, - "newline-after-var": 0, + "newline-after-var": 0, "one-var": 0, "operator-assignment": [0, "always"], "padded-blocks": 0, diff --git a/lib/eslint.js b/lib/eslint.js index e3715a8d646..d431cf859ea 100755 --- a/lib/eslint.js +++ b/lib/eslint.js @@ -18,7 +18,8 @@ var estraverse = require("estraverse-fb"), timing = require("./timing"), createTokenStore = require("./token-store.js"), EventEmitter = require("events").EventEmitter, - escapeRegExp = require("escape-string-regexp"); + escapeRegExp = require("escape-string-regexp"), + validate = require("./validate-options"); //------------------------------------------------------------------------------ // Helpers @@ -632,9 +633,22 @@ module.exports = (function() { var ruleCreator = rules.get(key), severity = getRuleSeverity(config.rules[key]), options = getRuleOptions(config.rules[key]), - rule; + rule, valid; if (ruleCreator) { + if (ruleCreator.schema) { + valid = validate(key, ruleCreator.schema, options); + if (valid !== true) { + messages.push({ + ruleId: key, + fatal: true, + severity: severity, + message: "Invalid configuration: " + JSON.stringify(options) + " " + valid[0].message + }); + return messages; + } + } + try { rule = ruleCreator(new RuleContext( key, api, severity, options, diff --git a/lib/rules/brace-style.js b/lib/rules/brace-style.js index 599e86f68b9..6bf2ea6c582 100644 --- a/lib/rules/brace-style.js +++ b/lib/rules/brace-style.js @@ -202,3 +202,23 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["1tbs", "stroustrup"] + }, + { + "type": "object", + "properties": { + "allowSingleLine": { + "type": "boolean" + } + }, + "additionalProperties": false + } + ], + "maxItems": 2 +}; diff --git a/lib/rules/camelcase.js b/lib/rules/camelcase.js index d523aee5454..b9566a47a4b 100644 --- a/lib/rules/camelcase.js +++ b/lib/rules/camelcase.js @@ -92,3 +92,20 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "properties": { + "type": "string", + "enum": ["always", "never"] + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/comma-dangle.js b/lib/rules/comma-dangle.js index da3776d4243..152ad51a9f0 100644 --- a/lib/rules/comma-dangle.js +++ b/lib/rules/comma-dangle.js @@ -56,3 +56,14 @@ module.exports = function (context) { "ArrayExpression": checkForTrailingComma }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["always", "always-multiline", "never"] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/comma-spacing.js b/lib/rules/comma-spacing.js index 32136a35239..8b2602a5954 100644 --- a/lib/rules/comma-spacing.js +++ b/lib/rules/comma-spacing.js @@ -157,3 +157,22 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "before": { + "type": "boolean" + }, + "after": { + "type": "boolean" + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/curly.js b/lib/rules/curly.js index 2bfcfb27982..7c1e1504efa 100644 --- a/lib/rules/curly.js +++ b/lib/rules/curly.js @@ -101,3 +101,14 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["all", "multi", "multi-line"] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/dot-notation.js b/lib/rules/dot-notation.js index bf89cbfdf8e..5c9d0309784 100644 --- a/lib/rules/dot-notation.js +++ b/lib/rules/dot-notation.js @@ -102,3 +102,22 @@ module.exports = function(context) { } }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "allowKeywords": { + "type": "boolean" + }, + "allowPattern": { + "type": "string" + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/eqeqeq.js b/lib/rules/eqeqeq.js index e847a8327eb..9f0dcdecd50 100644 --- a/lib/rules/eqeqeq.js +++ b/lib/rules/eqeqeq.js @@ -88,3 +88,14 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["smart", "allow-null"] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/key-spacing.js b/lib/rules/key-spacing.js index a6270239c57..dd312fe4f66 100644 --- a/lib/rules/key-spacing.js +++ b/lib/rules/key-spacing.js @@ -218,3 +218,26 @@ module.exports = function(context) { } }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "align": { + "type": "string", + "enum": ["colon", "value"] + }, + "beforeColon": { + "type": "boolean" + }, + "afterColon": { + "type": "boolean" + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/max-len.js b/lib/rules/max-len.js index a36dd73b1bb..2d2290f4343 100644 --- a/lib/rules/max-len.js +++ b/lib/rules/max-len.js @@ -63,3 +63,19 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "integer", + "minimum": 0 + } + ], + "minItems": 2, + "maxItems": 2 +}; diff --git a/lib/rules/new-cap.js b/lib/rules/new-cap.js index 1474fc80289..0e8254b8f21 100644 --- a/lib/rules/new-cap.js +++ b/lib/rules/new-cap.js @@ -195,3 +195,34 @@ module.exports = function(context) { return listeners; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "newIsCap": { + "type": "boolean" + }, + "capIsNew": { + "type": "boolean" + }, + "newIsCapExceptions": { + "type": "array", + "items": { + "type": "string" + } + }, + "capIsNewExceptions": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/no-cond-assign.js b/lib/rules/no-cond-assign.js index 9b00fe4e32b..74ef92f6c17 100644 --- a/lib/rules/no-cond-assign.js +++ b/lib/rules/no-cond-assign.js @@ -115,3 +115,14 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["except-parens", "always"] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/no-inner-declarations.js b/lib/rules/no-inner-declarations.js index 664639d575d..fe66b41e390 100644 --- a/lib/rules/no-inner-declarations.js +++ b/lib/rules/no-inner-declarations.js @@ -70,3 +70,14 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["functions", "both"] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/no-mixed-spaces-and-tabs.js b/lib/rules/no-mixed-spaces-and-tabs.js index 7b76d76db99..f1d2ce03fe7 100644 --- a/lib/rules/no-mixed-spaces-and-tabs.js +++ b/lib/rules/no-mixed-spaces-and-tabs.js @@ -66,3 +66,14 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["smart-tabs", true, false] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/no-multi-spaces.js b/lib/rules/no-multi-spaces.js index 9e247c1533d..657a12ceaf4 100644 --- a/lib/rules/no-multi-spaces.js +++ b/lib/rules/no-multi-spaces.js @@ -99,3 +99,25 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "exceptions": { + "type": "object", + "patternProperties": { + "^([A-Z][a-z]*)+$": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js index 7363cac6187..8c84cef7d0c 100644 --- a/lib/rules/no-unused-vars.js +++ b/lib/rules/no-unused-vars.js @@ -170,3 +170,28 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "oneOf": [ + { + "enum": ["all", "local"] + }, + { + "type": "object", + "properties": { + "vars": { + "enum": ["all", "local"] + }, + "args": { + "enum": ["all", "after-used", "none"] + } + } + } + ] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/no-use-before-define.js b/lib/rules/no-use-before-define.js index 3f175dc78b6..190ded332f9 100644 --- a/lib/rules/no-use-before-define.js +++ b/lib/rules/no-use-before-define.js @@ -65,3 +65,13 @@ module.exports = function(context) { "ArrowFunctionExpression": findVariables }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "enum": ["nofunc"] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/quotes.js b/lib/rules/quotes.js index 1ed86910f69..732d8f7d8ff 100644 --- a/lib/rules/quotes.js +++ b/lib/rules/quotes.js @@ -76,3 +76,19 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["single", "double"] + }, + { + "type": "string", + "enum": ["avoid-escape"] + } + ], + "minItems": 1, + "maxItems": 2 +}; diff --git a/lib/rules/semi-spacing.js b/lib/rules/semi-spacing.js index 05ced445934..38d46605f2c 100644 --- a/lib/rules/semi-spacing.js +++ b/lib/rules/semi-spacing.js @@ -150,3 +150,22 @@ module.exports = function (context) { } }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "before": { + "type": "boolean" + }, + "after": { + "type": "boolean" + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/semi.js b/lib/rules/semi.js index f7c8637ea5c..0aac15f8382 100644 --- a/lib/rules/semi.js +++ b/lib/rules/semi.js @@ -106,3 +106,13 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "enum": ["always", "never"] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/space-infix-ops.js b/lib/rules/space-infix-ops.js index 4165987967f..64509a5923c 100644 --- a/lib/rules/space-infix-ops.js +++ b/lib/rules/space-infix-ops.js @@ -66,3 +66,19 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "int32Hint": { + "type": "boolean" + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/space-unary-ops.js b/lib/rules/space-unary-ops.js index 2fe8a3f4063..4c611f4fea2 100644 --- a/lib/rules/space-unary-ops.js +++ b/lib/rules/space-unary-ops.js @@ -116,3 +116,22 @@ module.exports = function(context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "words": { + "type": "boolean" + }, + "nonwords": { + "type": "boolean" + } + }, + "additionalProperties": false + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/strict.js b/lib/rules/strict.js index 16d8612891e..80ed97869cf 100644 --- a/lib/rules/strict.js +++ b/lib/rules/strict.js @@ -234,3 +234,14 @@ module.exports = function(context) { return modes[mode || "deprecated"]; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["never", "global", "function"] + } + ], + "maxItems": 1 +}; diff --git a/lib/rules/yoda.js b/lib/rules/yoda.js index cdb5bdad083..84b57d350b2 100644 --- a/lib/rules/yoda.js +++ b/lib/rules/yoda.js @@ -208,3 +208,23 @@ module.exports = function (context) { }; }; + +module.exports.schema = { + "type": "array", + "items": [ + { + "type": "string", + "enum": ["always", "never"] + }, + { + "type": "object", + "properties": { + "exceptRange": { + "type": "boolean" + } + }, + "additionalProperties": false + } + ], + "maxItems": 2 +}; diff --git a/lib/validate-options.js b/lib/validate-options.js new file mode 100644 index 00000000000..b488e6dbb1e --- /dev/null +++ b/lib/validate-options.js @@ -0,0 +1,35 @@ +/** + * @fileoverview Validates rule options. + * @author Brandon Mills + * @copyright 2015 Brandon Mills + */ + +"use strict"; + +var validator = require("is-my-json-valid"); + +var validators = Object.create(null); // Cache generated schema validators + +/** + * Validates a rule's options against a schema. + * @param {string} id The rule's unique name. + * @param {object} schema JSON schema for the rule's options. + * @param {object} options Current options for the rule. + * @return {boolean|object[]} True if options are valid, or array of errors if not. + */ +module.exports = function (id, schema, options) { + var validate = validators[id], + result; + + if (!validate) { + validate = validators[id] = validator(schema, { verbose: true }); + } + + result = validate(options); + + if (!result) { + result = validate.errors; + } + + return result; +}; diff --git a/package.json b/package.json index 0211858e639..00f818c3d0f 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "estraverse": "^2.0.0", "estraverse-fb": "^1.3.1", "globals": "^6.1.0", + "is-my-json-valid": "^2.10.0", "js-yaml": "^3.2.5", "minimatch": "^2.0.1", "mkdirp": "^0.5.0", diff --git a/tests/lib/rules/comma-dangle.js b/tests/lib/rules/comma-dangle.js index 8dfdab658ee..a95513a83ca 100644 --- a/tests/lib/rules/comma-dangle.js +++ b/tests/lib/rules/comma-dangle.js @@ -33,9 +33,9 @@ eslintTester.addRuleTest("lib/rules/comma-dangle", { "[]", "[\n]", - { code: "var foo = { bar: 'baz' }", options: ["foo"] }, - { code: "var foo = {\nbar: 'baz'\n}", options: ["bar"] }, - { code: "var foo = [ 'baz' ]", options: ["baz"] }, + { code: "var foo = { bar: 'baz' }", options: ["never"] }, + { code: "var foo = {\nbar: 'baz'\n}", options: ["never"] }, + { code: "var foo = [ 'baz' ]", options: ["never"] }, { code: "var foo = { bar: 'baz', }", options: [ "always" ] }, { code: "var foo = {\nbar: 'baz',\n}", options: [ "always" ] }, @@ -143,7 +143,7 @@ eslintTester.addRuleTest("lib/rules/comma-dangle", { { code: "var foo = { bar: 'baz', }", - options: [ "foo" ], + options: [ "never" ], errors: [ { message: "Unexpected trailing comma.", @@ -155,7 +155,7 @@ eslintTester.addRuleTest("lib/rules/comma-dangle", { }, { code: "var foo = {\nbar: 'baz',\n}", - options: [ "bar" ], + options: [ "never" ], errors: [ { message: "Unexpected trailing comma.", @@ -167,7 +167,7 @@ eslintTester.addRuleTest("lib/rules/comma-dangle", { }, { code: "foo({ bar: 'baz', qux: 'quux', });", - options: [ "baz" ], + options: [ "never" ], errors: [ { message: "Unexpected trailing comma.", diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js index 9180060a152..1e1e134d546 100644 --- a/tests/lib/rules/no-unused-vars.js +++ b/tests/lib/rules/no-unused-vars.js @@ -59,7 +59,7 @@ eslintTester.addRuleTest("lib/rules/no-unused-vars", { { code: "function g(bar, baz) { return baz; }; g();", args: [1, {"vars": "all", "args": "after-used"}] }, { code: "function g(bar, baz) { return bar; }; g();", args: [1, {"vars": "all", "args": "none"}] }, { code: "function g(bar, baz) { return 2; }; g();", args: [1, {"vars": "all", "args": "none"}] }, - { code: "function g(bar, baz) { return bar + baz; }; g();", args: [1, {"vars": "locals", "args": "all"}] }, + { code: "function g(bar, baz) { return bar + baz; }; g();", args: [1, {"vars": "local", "args": "all"}] }, { code: "var g = function (bar, baz) { return 2; }; g();", args: [1, {"vars": "all", "args": "none"}] }, "(function z() { z(); })();", { code: " ", globals: {a: true} }, diff --git a/tests/lib/rules/quotes.js b/tests/lib/rules/quotes.js index eaf746be704..c1a91720dcd 100644 --- a/tests/lib/rules/quotes.js +++ b/tests/lib/rules/quotes.js @@ -24,7 +24,7 @@ eslintTester.addRuleTest("lib/rules/quotes", { { code: "var foo =
Hello world
;", args: [1, "single"], ecmaFeatures: { jsx: true } }, { code: "var foo =
;", args: [1, "single"], ecmaFeatures: { jsx: true } }, { code: "var foo =
Hello world
;", args: [1, "double"], ecmaFeatures: { jsx: true } }, - { code: "var foo =
Hello world
;", args: [1, "avoid-escape"], ecmaFeatures: { jsx: true } } + { code: "var foo =
Hello world
;", args: [1, "single", "avoid-escape"], ecmaFeatures: { jsx: true } } ], invalid: [ { code: "var foo = \"bar\";",