no-restricted-globals.js 3.7 KB

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