no-useless-computed-key.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /**
  2. * @fileoverview Rule to disallow unnecessary computed property keys in object literals
  3. * @author Burak Yigit Kaya
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("./utils/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Rule Definition
  12. //------------------------------------------------------------------------------
  13. module.exports = {
  14. meta: {
  15. type: "suggestion",
  16. docs: {
  17. description: "disallow unnecessary computed property keys in objects and classes",
  18. category: "ECMAScript 6",
  19. recommended: false,
  20. url: "https://eslint.org/docs/rules/no-useless-computed-key"
  21. },
  22. schema: [{
  23. type: "object",
  24. properties: {
  25. enforceForClassMembers: {
  26. type: "boolean",
  27. default: false
  28. }
  29. },
  30. additionalProperties: false
  31. }],
  32. fixable: "code",
  33. messages: {
  34. unnecessarilyComputedProperty: "Unnecessarily computed property [{{property}}] found."
  35. }
  36. },
  37. create(context) {
  38. const sourceCode = context.getSourceCode();
  39. const enforceForClassMembers = context.options[0] && context.options[0].enforceForClassMembers;
  40. /**
  41. * Reports a given node if it violated this rule.
  42. * @param {ASTNode} node The node to check.
  43. * @returns {void}
  44. */
  45. function check(node) {
  46. if (!node.computed) {
  47. return;
  48. }
  49. const key = node.key,
  50. nodeType = typeof key.value;
  51. let allowedKey;
  52. if (node.type === "MethodDefinition") {
  53. allowedKey = node.static ? "prototype" : "constructor";
  54. } else {
  55. allowedKey = "__proto__";
  56. }
  57. if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== allowedKey) {
  58. context.report({
  59. node,
  60. messageId: "unnecessarilyComputedProperty",
  61. data: { property: sourceCode.getText(key) },
  62. fix(fixer) {
  63. const leftSquareBracket = sourceCode.getTokenBefore(key, astUtils.isOpeningBracketToken);
  64. const rightSquareBracket = sourceCode.getTokenAfter(key, astUtils.isClosingBracketToken);
  65. // If there are comments between the brackets and the property name, don't do a fix.
  66. if (sourceCode.commentsExistBetween(leftSquareBracket, rightSquareBracket)) {
  67. return null;
  68. }
  69. const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket);
  70. // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} })
  71. const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] &&
  72. !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key));
  73. const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw;
  74. return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey);
  75. }
  76. });
  77. }
  78. }
  79. /**
  80. * A no-op function to act as placeholder for checking a node when the `enforceForClassMembers` option is `false`.
  81. * @returns {void}
  82. * @private
  83. */
  84. function noop() {}
  85. return {
  86. Property: check,
  87. MethodDefinition: enforceForClassMembers ? check : noop
  88. };
  89. }
  90. };