no-implicit-globals.js 5.5 KB

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