diff --git a/lib/config/config-file.js b/lib/config/config-file.js index 492800b7a3c..80344f50973 100644 --- a/lib/config/config-file.js +++ b/lib/config/config-file.js @@ -541,7 +541,7 @@ function loadFromDisk(resolvedPath, configContext) { const ruleMap = configContext.linterContext.getRules(); // validate the configuration before continuing - validator.validate(config, resolvedPath.configFullName, ruleMap.get.bind(ruleMap), configContext.linterContext.environments); + validator.validate(config, ruleMap.get.bind(ruleMap), configContext.linterContext.environments, resolvedPath.configFullName); /* * If an `extends` property is defined, it represents a configuration file to use as diff --git a/lib/config/config-validator.js b/lib/config/config-validator.js index 2345b28893c..386db777502 100644 --- a/lib/config/config-validator.js +++ b/lib/config/config-validator.js @@ -116,7 +116,7 @@ function validateRuleSchema(rule, localOptions) { * no source is prepended to the message. * @returns {void} */ -function validateRuleOptions(rule, ruleId, options, source) { +function validateRuleOptions(rule, ruleId, options, source = null) { if (!rule) { return; } @@ -140,11 +140,11 @@ function validateRuleOptions(rule, ruleId, options, source) { /** * Validates an environment object * @param {Object} environment The environment config object to validate. - * @param {string} source The name of the configuration source to report in any errors. * @param {Environments} envContext Env context + * @param {string} source The name of the configuration source to report in any errors. * @returns {void} */ -function validateEnvironment(environment, source, envContext) { +function validateEnvironment(environment, envContext, source = null) { // not having an environment is ok if (!environment) { @@ -163,11 +163,11 @@ function validateEnvironment(environment, source, envContext) { /** * Validates a rules config object * @param {Object} rulesConfig The rules config object to validate. - * @param {string} source The name of the configuration source to report in any errors. * @param {function(string): {create: Function}} ruleMapper A mapper function from strings to loaded rules + * @param {string} source The name of the configuration source to report in any errors. * @returns {void} */ -function validateRules(rulesConfig, source, ruleMapper) { +function validateRules(rulesConfig, ruleMapper, source = null) { if (!rulesConfig) { return; } @@ -228,7 +228,7 @@ const emitDeprecationWarning = lodash.memoize((source, errorCode) => { * @param {string} source The name of the configuration source to report in any errors. * @returns {void} */ -function validateConfigSchema(config, source) { +function validateConfigSchema(config, source = null) { validateSchema = validateSchema || ajv.compile(configSchema); if (!validateSchema(config)) { @@ -252,19 +252,19 @@ function validateConfigSchema(config, source) { /** * Validates an entire config object. * @param {Object} config The config object to validate. - * @param {string} source The name of the configuration source to report in any errors. * @param {function(string): {create: Function}} ruleMapper A mapper function from rule IDs to defined rules * @param {Environments} envContext The env context + * @param {string} source The name of the configuration source to report in any errors. * @returns {void} */ -function validate(config, source, ruleMapper, envContext) { +function validate(config, ruleMapper, envContext, source = null) { validateConfigSchema(config, source); - validateRules(config.rules, source, ruleMapper); - validateEnvironment(config.env, source, envContext); + validateRules(config.rules, ruleMapper, source); + validateEnvironment(config.env, envContext, source); for (const override of config.overrides || []) { - validateRules(override.rules, source, ruleMapper); - validateEnvironment(override.env, source, envContext); + validateRules(override.rules, ruleMapper, source); + validateEnvironment(override.env, envContext, source); } } diff --git a/lib/testers/rule-tester.js b/lib/testers/rule-tester.js index 6d1bba989fd..f1d91553132 100644 --- a/lib/testers/rule-tester.js +++ b/lib/testers/rule-tester.js @@ -379,7 +379,7 @@ class RuleTester { } } - validator.validate(config, "rule-tester", ruleMap.get.bind(ruleMap), new Environments()); + validator.validate(config, ruleMap.get.bind(ruleMap), new Environments(), "rule-tester"); return { messages: linter.verify(code, config, filename, true), diff --git a/tests/lib/config/config-validator.js b/tests/lib/config/config-validator.js index 721533c1030..9d4c39baf76 100644 --- a/tests/lib/config/config-validator.js +++ b/tests/lib/config/config-validator.js @@ -108,7 +108,7 @@ describe("Validator", () => { describe("validate", () => { it("should do nothing with an empty config", () => { - validator.validate({}, "tests", ruleMapper, linter.environments); + validator.validate({}, ruleMapper, linter.environments, "tests"); }); it("should do nothing with a valid eslint config", () => { @@ -124,9 +124,9 @@ describe("Validator", () => { parserOptions: { foo: "bar" }, rules: {} }, - "tests", ruleMapper, - linter.environments + linter.environments, + "tests" ); }); @@ -136,9 +136,9 @@ describe("Validator", () => { { foo: true }, - "tests", ruleMapper, - linter.environments + linter.environments, + "tests" ); assert.throws(fn, "Unexpected top-level property \"foo\"."); @@ -146,13 +146,13 @@ describe("Validator", () => { describe("root", () => { it("should throw with a string value", () => { - const fn = validator.validate.bind(null, { root: "true" }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { root: "true" }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"root\" is the wrong type (expected boolean but got `\"true\"`)."); }); it("should throw with a numeric value", () => { - const fn = validator.validate.bind(null, { root: 0 }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { root: 0 }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"root\" is the wrong type (expected boolean but got `0`)."); }); @@ -160,13 +160,13 @@ describe("Validator", () => { describe("globals", () => { it("should throw with a string value", () => { - const fn = validator.validate.bind(null, { globals: "jQuery" }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { globals: "jQuery" }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"globals\" is the wrong type (expected object but got `\"jQuery\"`)."); }); it("should throw with an array value", () => { - const fn = validator.validate.bind(null, { globals: ["jQuery"] }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { globals: ["jQuery"] }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"globals\" is the wrong type (expected object but got `[\"jQuery\"]`)."); }); @@ -174,32 +174,32 @@ describe("Validator", () => { describe("parser", () => { it("should not throw with a null value", () => { - validator.validate({ parser: null }, null, ruleMapper, linter.environments); + validator.validate({ parser: null }, ruleMapper, linter.environments, null); }); }); describe("env", () => { it("should throw with an array environment", () => { - const fn = validator.validate.bind(null, { env: [] }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { env: [] }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"env\" is the wrong type (expected object but got `[]`)."); }); it("should throw with a primitive environment", () => { - const fn = validator.validate.bind(null, { env: 1 }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { env: 1 }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"env\" is the wrong type (expected object but got `1`)."); }); it("should catch invalid environments", () => { - const fn = validator.validate.bind(null, { env: { browser: true, invalid: true } }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { env: { browser: true, invalid: true } }, ruleMapper, linter.environments, null); assert.throws(fn, "Environment key \"invalid\" is unknown\n"); }); it("should catch disabled invalid environments", () => { - const fn = validator.validate.bind(null, { env: { browser: true, invalid: false } }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { env: { browser: true, invalid: false } }, ruleMapper, linter.environments, null); assert.throws(fn, "Environment key \"invalid\" is unknown\n"); }); @@ -212,11 +212,11 @@ describe("Validator", () => { describe("plugins", () => { it("should not throw with an empty array", () => { - validator.validate({ plugins: [] }, null, ruleMapper, linter.environments); + validator.validate({ plugins: [] }, ruleMapper, linter.environments, null); }); it("should throw with a string", () => { - const fn = validator.validate.bind(null, { plugins: "react" }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { plugins: "react" }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"plugins\" is the wrong type (expected array but got `\"react\"`)."); }); @@ -224,11 +224,11 @@ describe("Validator", () => { describe("settings", () => { it("should not throw with an empty object", () => { - validator.validate({ settings: {} }, null, ruleMapper, linter.environments); + validator.validate({ settings: {} }, ruleMapper, linter.environments, null); }); it("should throw with an array", () => { - const fn = validator.validate.bind(null, { settings: ["foo"] }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { settings: ["foo"] }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"settings\" is the wrong type (expected object but got `[\"foo\"]`)."); }); @@ -236,15 +236,15 @@ describe("Validator", () => { describe("extends", () => { it("should not throw with an empty array", () => { - validator.validate({ extends: [] }, null, ruleMapper, linter.environments); + validator.validate({ extends: [] }, ruleMapper, linter.environments, null); }); it("should not throw with a string", () => { - validator.validate({ extends: "react" }, null, ruleMapper, linter.environments); + validator.validate({ extends: "react" }, ruleMapper, linter.environments, null); }); it("should throw with an object", () => { - const fn = validator.validate.bind(null, { extends: {} }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { extends: {} }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"extends\" is the wrong type (expected string/array but got `{}`)."); }); @@ -252,11 +252,11 @@ describe("Validator", () => { describe("parserOptions", () => { it("should not throw with an empty object", () => { - validator.validate({ parserOptions: {} }, null, ruleMapper, linter.environments); + validator.validate({ parserOptions: {} }, ruleMapper, linter.environments, null); }); it("should throw with an array", () => { - const fn = validator.validate.bind(null, { parserOptions: ["foo"] }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { parserOptions: ["foo"] }, ruleMapper, linter.environments, null); assert.throws(fn, "Property \"parserOptions\" is the wrong type (expected object but got `[\"foo\"]`)."); }); @@ -265,47 +265,47 @@ describe("Validator", () => { describe("rules", () => { it("should do nothing with an empty rules object", () => { - validator.validate({ rules: {} }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: {} }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with a valid config with rules", () => { - validator.validate({ rules: { "mock-rule": [2, "second"] } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-rule": [2, "second"] } }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with a valid config when severity is off", () => { - validator.validate({ rules: { "mock-rule": ["off", "second"] } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-rule": ["off", "second"] } }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with an invalid config when severity is off", () => { - validator.validate({ rules: { "mock-required-options-rule": "off" } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-required-options-rule": "off" } }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with an invalid config when severity is an array with 'off'", () => { - validator.validate({ rules: { "mock-required-options-rule": ["off"] } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-required-options-rule": ["off"] } }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with a valid config when severity is warn", () => { - validator.validate({ rules: { "mock-rule": ["warn", "second"] } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-rule": ["warn", "second"] } }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with a valid config when severity is error", () => { - validator.validate({ rules: { "mock-rule": ["error", "second"] } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-rule": ["error", "second"] } }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with a valid config when severity is Off", () => { - validator.validate({ rules: { "mock-rule": ["Off", "second"] } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-rule": ["Off", "second"] } }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with a valid config when severity is Warn", () => { - validator.validate({ rules: { "mock-rule": ["Warn", "second"] } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-rule": ["Warn", "second"] } }, ruleMapper, linter.environments, "tests"); }); it("should do nothing with a valid config when severity is Error", () => { - validator.validate({ rules: { "mock-rule": ["Error", "second"] } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-rule": ["Error", "second"] } }, ruleMapper, linter.environments, "tests"); }); it("should catch invalid rule options", () => { - const fn = validator.validate.bind(null, { rules: { "mock-rule": [3, "third"] } }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { rules: { "mock-rule": [3, "third"] } }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n"); }); @@ -313,13 +313,13 @@ describe("Validator", () => { it("should allow for rules with no options", () => { linter.defineRule("mock-no-options-rule", mockNoOptionsRule); - validator.validate({ rules: { "mock-no-options-rule": 2 } }, "tests", ruleMapper, linter.environments); + validator.validate({ rules: { "mock-no-options-rule": 2 } }, ruleMapper, linter.environments, "tests"); }); it("should not allow options for rules with no options", () => { linter.defineRule("mock-no-options-rule", mockNoOptionsRule); - const fn = validator.validate.bind(null, { rules: { "mock-no-options-rule": [2, "extra"] } }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { rules: { "mock-no-options-rule": [2, "extra"] } }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-no-options-rule\" is invalid:\n\tValue [\"extra\"] should NOT have more than 0 items.\n"); }); @@ -327,39 +327,39 @@ describe("Validator", () => { describe("overrides", () => { it("should not throw with an empty overrides array", () => { - validator.validate({ overrides: [] }, "tests", ruleMapper, linter.environments); + validator.validate({ overrides: [] }, ruleMapper, linter.environments, "tests"); }); it("should not throw with a valid overrides array", () => { - validator.validate({ overrides: [{ files: "*", rules: {} }] }, "tests", ruleMapper, linter.environments); + validator.validate({ overrides: [{ files: "*", rules: {} }] }, ruleMapper, linter.environments, "tests"); }); it("should throw if override does not specify files", () => { - const fn = validator.validate.bind(null, { overrides: [{ rules: {} }] }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ rules: {} }] }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- \"overrides[0]\" should have required property 'files'. Value: {\"rules\":{}}.\n"); }); it("should throw if override has an empty files array", () => { - const fn = validator.validate.bind(null, { overrides: [{ files: [] }] }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ files: [] }] }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Property \"overrides[0].files\" is the wrong type (expected string but got `[]`).\n\t- \"overrides[0].files\" should NOT have fewer than 1 items. Value: [].\n\t- \"overrides[0].files\" should match exactly one schema in oneOf. Value: [].\n"); }); it("should throw if override has nested overrides", () => { - const fn = validator.validate.bind(null, { overrides: [{ files: "*", overrides: [{ files: "*", rules: {} }] }] }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ files: "*", overrides: [{ files: "*", rules: {} }] }] }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].overrides\".\n"); }); it("should throw if override extends", () => { - const fn = validator.validate.bind(null, { overrides: [{ files: "*", extends: "eslint-recommended" }] }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ files: "*", extends: "eslint-recommended" }] }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].extends\".\n"); }); it("should throw if override tries to set root", () => { - const fn = validator.validate.bind(null, { overrides: [{ files: "*", root: "true" }] }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ files: "*", root: "true" }] }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "ESLint configuration in tests is invalid:\n\t- Unexpected top-level property \"overrides[0].root\".\n"); }); @@ -367,13 +367,13 @@ describe("Validator", () => { describe("env", () => { it("should catch invalid environments", () => { - const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: true } }] }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: true } }] }, ruleMapper, linter.environments, null); assert.throws(fn, "Environment key \"invalid\" is unknown\n"); }); it("should catch disabled invalid environments", () => { - const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: false } }] }, null, ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ files: "*", env: { browser: true, invalid: false } }] }, ruleMapper, linter.environments, null); assert.throws(fn, "Environment key \"invalid\" is unknown\n"); }); @@ -383,7 +383,7 @@ describe("Validator", () => { describe("rules", () => { it("should catch invalid rule options", () => { - const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-rule": [3, "third"] } }] }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-rule": [3, "third"] } }] }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-rule\" is invalid:\n\tSeverity should be one of the following: 0 = off, 1 = warn, 2 = error (you passed '3').\n"); }); @@ -391,7 +391,7 @@ describe("Validator", () => { it("should not allow options for rules with no options", () => { linter.defineRule("mock-no-options-rule", mockNoOptionsRule); - const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-no-options-rule": [2, "extra"] } }] }, "tests", ruleMapper, linter.environments); + const fn = validator.validate.bind(null, { overrides: [{ files: "*", rules: { "mock-no-options-rule": [2, "extra"] } }] }, ruleMapper, linter.environments, "tests"); assert.throws(fn, "tests:\n\tConfiguration for rule \"mock-no-options-rule\" is invalid:\n\tValue [\"extra\"] should NOT have more than 0 items.\n"); });