UNPKG

5.36 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to check for implicit global variables, functions and classes.
3 * @author Joshua Peek
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12module.exports = {
13 meta: {
14 type: "suggestion",
15
16 docs: {
17 description: "disallow declarations in the global scope",
18 category: "Best Practices",
19 recommended: false,
20 url: "https://eslint.org/docs/rules/no-implicit-globals"
21 },
22
23 schema: [{
24 type: "object",
25 properties: {
26 lexicalBindings: {
27 type: "boolean",
28 default: false
29 }
30 },
31 additionalProperties: false
32 }],
33
34 messages: {
35 globalNonLexicalBinding: "Unexpected {{kind}} declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable.",
36 globalLexicalBinding: "Unexpected {{kind}} declaration in the global scope, wrap in a block or in an IIFE.",
37 globalVariableLeak: "Global variable leak, declare the variable if it is intended to be local.",
38 assignmentToReadonlyGlobal: "Unexpected assignment to read-only global variable.",
39 redeclarationOfReadonlyGlobal: "Unexpected redeclaration of read-only global variable."
40 }
41 },
42
43 create(context) {
44
45 const checkLexicalBindings = context.options[0] && context.options[0].lexicalBindings === true;
46
47 /**
48 * Reports the node.
49 * @param {ASTNode} node Node to report.
50 * @param {string} messageId Id of the message to report.
51 * @param {string|undefined} kind Declaration kind, can be 'var', 'const', 'let', function or class.
52 * @returns {void}
53 */
54 function report(node, messageId, kind) {
55 context.report({
56 node,
57 messageId,
58 data: {
59 kind
60 }
61 });
62 }
63
64 return {
65 Program() {
66 const scope = context.getScope();
67
68 scope.variables.forEach(variable => {
69
70 // Only ESLint global variables have the `writable` key.
71 const isReadonlyEslintGlobalVariable = variable.writeable === false;
72 const isWritableEslintGlobalVariable = variable.writeable === true;
73
74 if (isWritableEslintGlobalVariable) {
75
76 // Everything is allowed with writable ESLint global variables.
77 return;
78 }
79
80 variable.defs.forEach(def => {
81 const defNode = def.node;
82
83 if (def.type === "FunctionName" || (def.type === "Variable" && def.parent.kind === "var")) {
84 if (isReadonlyEslintGlobalVariable) {
85 report(defNode, "redeclarationOfReadonlyGlobal");
86 } else {
87 report(
88 defNode,
89 "globalNonLexicalBinding",
90 def.type === "FunctionName" ? "function" : `'${def.parent.kind}'`
91 );
92 }
93 }
94
95 if (checkLexicalBindings) {
96 if (def.type === "ClassName" ||
97 (def.type === "Variable" && (def.parent.kind === "let" || def.parent.kind === "const"))) {
98 if (isReadonlyEslintGlobalVariable) {
99 report(defNode, "redeclarationOfReadonlyGlobal");
100 } else {
101 report(
102 defNode,
103 "globalLexicalBinding",
104 def.type === "ClassName" ? "class" : `'${def.parent.kind}'`
105 );
106 }
107 }
108 }
109 });
110 });
111
112 // Undeclared assigned variables.
113 scope.implicit.variables.forEach(variable => {
114 const scopeVariable = scope.set.get(variable.name);
115 let messageId;
116
117 if (scopeVariable) {
118
119 // ESLint global variable
120 if (scopeVariable.writeable) {
121 return;
122 }
123 messageId = "assignmentToReadonlyGlobal";
124
125 } else {
126
127 // Reference to an unknown variable, possible global leak.
128 messageId = "globalVariableLeak";
129 }
130
131 // def.node is an AssignmentExpression, ForInStatement or ForOfStatement.
132 variable.defs.forEach(def => {
133 report(def.node, messageId);
134 });
135 });
136 }
137 };
138
139 }
140};