diff --git a/docs/rules/no-restricted-globals.md b/docs/rules/no-restricted-globals.md index 751f5d66de6..c9df9d3391d 100644 --- a/docs/rules/no-restricted-globals.md +++ b/docs/rules/no-restricted-globals.md @@ -13,7 +13,35 @@ This rule allows you to specify global variable names that you don't want to use ## Options -This rule takes a list of strings which are the global variable names. +This rule takes a list of strings, where each string is a global to be restricted: + +```json +{ + "rules": { + "no-restricted-globals": ["error", "event", "fdescribe"] + } +} +``` + +Alternatively, the rule also accepts objects, where the global name and an optional custom message are specified: + +```json +{ + "rules": { + "no-restricted-globals": [ + "error", + { + "name": "event", + "message": "Use local parameter instead." + }, + { + "name": "fdescribe", + "message": "Do not commit fdescribe. Use describe instead." + } + ] + } +} +``` Examples of **incorrect** code for sample `"event", "fdescribe"` global variable names: @@ -45,6 +73,17 @@ import event from "event-module"; var event = 1; ``` +Examples of **incorrect** code for a sample `"event"` global variable name, along with a custom error message: + +```js +/*global event*/ +/* eslint no-restricted-globals: [{ name: "error", message: "Use local parameter instead." }] */ + +function onClick() { + console.log(event); // Unexpected global variable 'event'. Use local parameter instead. +} +``` + ## Related Rules * [no-restricted-properties](no-restricted-properties.md) diff --git a/lib/rules/no-restricted-globals.js b/lib/rules/no-restricted-globals.js index 603a6b2d376..75428fc1747 100644 --- a/lib/rules/no-restricted-globals.js +++ b/lib/rules/no-restricted-globals.js @@ -4,6 +4,13 @@ */ "use strict"; +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const DEFAULT_MESSAGE_TEMPLATE = "Unexpected use of '{{name}}'.", + CUSTOM_MESSAGE_TEMPLATE = "Unexpected use of '{{name}}'. {{customMessage}}"; + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -19,20 +26,43 @@ module.exports = { schema: { type: "array", items: { - type: "string" + oneOf: [ + { + type: "string" + }, + { + type: "object", + properties: { + name: { type: "string" }, + message: { type: "string" } + }, + required: ["name"], + additionalProperties: false + } + ] }, - uniqueItems: true + uniqueItems: true, + minItems: 0 } }, create(context) { - const restrictedGlobals = context.options; - // if no globals are restricted we don't need to check - if (restrictedGlobals.length === 0) { + // If no globals are restricted, we don't need to do anything + if (context.options.length === 0) { return {}; } + const restrictedGlobalMessages = context.options.reduce((memo, option) => { + if (typeof option === "string") { + memo[option] = null; + } else { + memo[option.name] = option.message; + } + + return memo; + }, {}); + /** * Report a variable to be used as a restricted global. * @param {Reference} reference the variable reference @@ -40,9 +70,20 @@ module.exports = { * @private */ function reportReference(reference) { - context.report({ node: reference.identifier, message: "Unexpected use of '{{name}}'.", data: { - name: reference.identifier.name - } }); + const name = reference.identifier.name, + customMessage = restrictedGlobalMessages[name], + message = customMessage + ? CUSTOM_MESSAGE_TEMPLATE + : DEFAULT_MESSAGE_TEMPLATE; + + context.report({ + node: reference.identifier, + message, + data: { + name, + customMessage + } + }); } /** @@ -52,7 +93,7 @@ module.exports = { * @private */ function isRestricted(name) { - return restrictedGlobals.indexOf(name) >= 0; + return restrictedGlobalMessages.hasOwnProperty(name); } return { diff --git a/tests/lib/rules/no-restricted-globals.js b/tests/lib/rules/no-restricted-globals.js index df9d6e4ffb0..6ce7b9cd5c5 100644 --- a/tests/lib/rules/no-restricted-globals.js +++ b/tests/lib/rules/no-restricted-globals.js @@ -20,46 +20,158 @@ const ruleTester = new RuleTester(); ruleTester.run("no-restricted-globals", rule, { valid: [ - { code: "foo" }, - { code: "foo", options: ["bar"] }, - { code: "var foo = 1;", options: ["foo"] }, - { code: "event", env: { browser: true }, options: ["bar"] }, - { code: "import foo from 'bar';", options: ["foo"], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, - { code: "function foo() {}", options: ["foo"] }, - { code: "function fn() { var foo; }", options: ["foo"] }, - { code: "foo.bar", options: ["bar"] } + { + code: "foo" + }, + { + code: "foo", + options: ["bar"] + }, + { + code: "var foo = 1;", + options: ["foo"] + }, + { + code: "event", + env: { browser: true }, + options: ["bar"] + }, + { + code: "import foo from 'bar';", + options: ["foo"], + parserOptions: { ecmaVersion: 6, sourceType: "module" } + }, + { + code: "function foo() {}", + options: ["foo"] + }, + { + code: "function fn() { var foo; }", + options: ["foo"] + }, + { + code: "foo.bar", + options: ["bar"] + }, + { + code: "foo", + options: [{ name: "bar", message: "Use baz instead." }] + } ], invalid: [ { - code: "foo", options: ["foo"], + code: "foo", + options: ["foo"], + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] + }, + { + code: "function fn() { foo; }", + options: ["foo"], + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] + }, + { + code: "function fn() { foo; }", + options: ["foo"], + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] + }, + { + code: "event", + options: ["foo", "event"], + env: { browser: true }, + errors: [{ message: "Unexpected use of 'event'.", type: "Identifier" }] + }, + { + code: "foo", + options: ["foo"], + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] + }, + { + code: "foo()", + options: ["foo"], errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { - code: "function fn() { foo; }", options: ["foo"], + code: "foo.bar()", + options: ["foo"], errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { - code: "function fn() { foo; }", options: ["foo"], + code: "foo", + options: [{ name: "foo" }], + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] + }, + { + code: "function fn() { foo; }", + options: [{ name: "foo" }], + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] + }, + { + code: "function fn() { foo; }", + options: [{ name: "foo" }], globals: { foo: false }, errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { - code: "event", options: ["foo", "event"], + code: "event", + options: ["foo", { name: "event" }], env: { browser: true }, errors: [{ message: "Unexpected use of 'event'.", type: "Identifier" }] }, { - code: "foo", options: ["foo"], + code: "foo", + options: [{ name: "foo" }], globals: { foo: false }, errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { - code: "foo()", options: ["foo"], + code: "foo()", + options: [{ name: "foo" }], errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { - code: "foo.bar()", options: ["foo"], + code: "foo.bar()", + options: [{ name: "foo" }], errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] + }, + { + code: "foo", + options: [{ name: "foo", message: "Use bar instead." }], + errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }] + }, + { + code: "function fn() { foo; }", + options: [{ name: "foo", message: "Use bar instead." }], + errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }] + }, + { + code: "function fn() { foo; }", + options: [{ name: "foo", message: "Use bar instead." }], + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }] + }, + { + code: "event", + options: ["foo", { name: "event", message: "Use local event parameter." }], + env: { browser: true }, + errors: [{ message: "Unexpected use of 'event'. Use local event parameter.", type: "Identifier" }] + }, + { + code: "foo", + options: [{ name: "foo", message: "Use bar instead." }], + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }] + }, + { + code: "foo()", + options: [{ name: "foo", message: "Use bar instead." }], + errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }] + }, + { + code: "foo.bar()", + options: [{ name: "foo", message: "Use bar instead." }], + errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }] } ] });