• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**
2 * @fileoverview Restrict usage of specified globals.
3 * @author Benoît Zugmeyer
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Rule Definition
9//------------------------------------------------------------------------------
10
11module.exports = {
12    meta: {
13        type: "suggestion",
14
15        docs: {
16            description: "disallow specified global variables",
17            category: "Variables",
18            recommended: false,
19            url: "https://eslint.org/docs/rules/no-restricted-globals"
20        },
21
22        schema: {
23            type: "array",
24            items: {
25                oneOf: [
26                    {
27                        type: "string"
28                    },
29                    {
30                        type: "object",
31                        properties: {
32                            name: { type: "string" },
33                            message: { type: "string" }
34                        },
35                        required: ["name"],
36                        additionalProperties: false
37                    }
38                ]
39            },
40            uniqueItems: true,
41            minItems: 0
42        },
43
44        messages: {
45            defaultMessage: "Unexpected use of '{{name}}'.",
46            // eslint-disable-next-line eslint-plugin/report-message-format
47            customMessage: "Unexpected use of '{{name}}'. {{customMessage}}"
48        }
49    },
50
51    create(context) {
52
53        // If no globals are restricted, we don't need to do anything
54        if (context.options.length === 0) {
55            return {};
56        }
57
58        const restrictedGlobalMessages = context.options.reduce((memo, option) => {
59            if (typeof option === "string") {
60                memo[option] = null;
61            } else {
62                memo[option.name] = option.message;
63            }
64
65            return memo;
66        }, {});
67
68        /**
69         * Report a variable to be used as a restricted global.
70         * @param {Reference} reference the variable reference
71         * @returns {void}
72         * @private
73         */
74        function reportReference(reference) {
75            const name = reference.identifier.name,
76                customMessage = restrictedGlobalMessages[name],
77                messageId = customMessage
78                    ? "customMessage"
79                    : "defaultMessage";
80
81            context.report({
82                node: reference.identifier,
83                messageId,
84                data: {
85                    name,
86                    customMessage
87                }
88            });
89        }
90
91        /**
92         * Check if the given name is a restricted global name.
93         * @param {string} name name of a variable
94         * @returns {boolean} whether the variable is a restricted global or not
95         * @private
96         */
97        function isRestricted(name) {
98            return Object.prototype.hasOwnProperty.call(restrictedGlobalMessages, name);
99        }
100
101        return {
102            Program() {
103                const scope = context.getScope();
104
105                // Report variables declared elsewhere (ex: variables defined as "global" by eslint)
106                scope.variables.forEach(variable => {
107                    if (!variable.defs.length && isRestricted(variable.name)) {
108                        variable.references.forEach(reportReference);
109                    }
110                });
111
112                // Report variables not declared at all
113                scope.through.forEach(reference => {
114                    if (isRestricted(reference.identifier.name)) {
115                        reportReference(reference);
116                    }
117                });
118
119            }
120        };
121    }
122};
123