no-implicit-globals.js 5.4 KB

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