no-useless-computed-key.js 3.9 KB

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