no-implicit-globals.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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. module.exports = {
  10. meta: {
  11. type: "suggestion",
  12. docs: {
  13. description: "disallow declarations in the global scope",
  14. category: "Best Practices",
  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. variable.defs.forEach(def => {
  66. const defNode = def.node;
  67. if (def.type === "FunctionName" || (def.type === "Variable" && def.parent.kind === "var")) {
  68. if (isReadonlyEslintGlobalVariable) {
  69. report(defNode, "redeclarationOfReadonlyGlobal");
  70. } else {
  71. report(
  72. defNode,
  73. "globalNonLexicalBinding",
  74. def.type === "FunctionName" ? "function" : `'${def.parent.kind}'`
  75. );
  76. }
  77. }
  78. if (checkLexicalBindings) {
  79. if (def.type === "ClassName" ||
  80. (def.type === "Variable" && (def.parent.kind === "let" || def.parent.kind === "const"))) {
  81. if (isReadonlyEslintGlobalVariable) {
  82. report(defNode, "redeclarationOfReadonlyGlobal");
  83. } else {
  84. report(
  85. defNode,
  86. "globalLexicalBinding",
  87. def.type === "ClassName" ? "class" : `'${def.parent.kind}'`
  88. );
  89. }
  90. }
  91. }
  92. });
  93. });
  94. // Undeclared assigned variables.
  95. scope.implicit.variables.forEach(variable => {
  96. const scopeVariable = scope.set.get(variable.name);
  97. let messageId;
  98. if (scopeVariable) {
  99. // ESLint global variable
  100. if (scopeVariable.writeable) {
  101. return;
  102. }
  103. messageId = "assignmentToReadonlyGlobal";
  104. } else {
  105. // Reference to an unknown variable, possible global leak.
  106. messageId = "globalVariableLeak";
  107. }
  108. // def.node is an AssignmentExpression, ForInStatement or ForOfStatement.
  109. variable.defs.forEach(def => {
  110. report(def.node, messageId);
  111. });
  112. });
  113. }
  114. };
  115. }
  116. };