no-object-constructor.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. /**
  2. * @fileoverview Rule to disallow calls to the `Object` constructor without an argument
  3. * @author Francesco Trotta
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const {
  10. getVariableByName,
  11. isArrowToken,
  12. isStartOfExpressionStatement,
  13. needsPrecedingSemicolon,
  14. } = require("./utils/ast-utils");
  15. //------------------------------------------------------------------------------
  16. // Rule Definition
  17. //------------------------------------------------------------------------------
  18. /** @type {import('../types').Rule.RuleModule} */
  19. module.exports = {
  20. meta: {
  21. type: "suggestion",
  22. docs: {
  23. description:
  24. "Disallow calls to the `Object` constructor without an argument",
  25. recommended: false,
  26. url: "https://eslint.org/docs/latest/rules/no-object-constructor",
  27. },
  28. hasSuggestions: true,
  29. schema: [],
  30. messages: {
  31. preferLiteral: "The object literal notation {} is preferable.",
  32. useLiteral: "Replace with '{{replacement}}'.",
  33. useLiteralAfterSemicolon:
  34. "Replace with '{{replacement}}', add preceding semicolon.",
  35. },
  36. },
  37. create(context) {
  38. const sourceCode = context.sourceCode;
  39. /**
  40. * Determines whether or not an object literal that replaces a specified node needs to be enclosed in parentheses.
  41. * @param {ASTNode} node The node to be replaced.
  42. * @returns {boolean} Whether or not parentheses around the object literal are required.
  43. */
  44. function needsParentheses(node) {
  45. if (isStartOfExpressionStatement(node)) {
  46. return true;
  47. }
  48. const prevToken = sourceCode.getTokenBefore(node);
  49. if (prevToken && isArrowToken(prevToken)) {
  50. return true;
  51. }
  52. return false;
  53. }
  54. /**
  55. * Reports on nodes where the `Object` constructor is called without arguments.
  56. * @param {ASTNode} node The node to evaluate.
  57. * @returns {void}
  58. */
  59. function check(node) {
  60. if (
  61. node.callee.type !== "Identifier" ||
  62. node.callee.name !== "Object" ||
  63. node.arguments.length
  64. ) {
  65. return;
  66. }
  67. const variable = getVariableByName(
  68. sourceCode.getScope(node),
  69. "Object",
  70. );
  71. if (variable && variable.identifiers.length === 0) {
  72. let replacement;
  73. let fixText;
  74. let messageId = "useLiteral";
  75. if (needsParentheses(node)) {
  76. replacement = "({})";
  77. if (needsPrecedingSemicolon(sourceCode, node)) {
  78. fixText = ";({})";
  79. messageId = "useLiteralAfterSemicolon";
  80. } else {
  81. fixText = "({})";
  82. }
  83. } else {
  84. replacement = fixText = "{}";
  85. }
  86. context.report({
  87. node,
  88. messageId: "preferLiteral",
  89. suggest: [
  90. {
  91. messageId,
  92. data: { replacement },
  93. fix: fixer => fixer.replaceText(node, fixText),
  94. },
  95. ],
  96. });
  97. }
  98. }
  99. return {
  100. CallExpression: check,
  101. NewExpression: check,
  102. };
  103. },
  104. };