no-useless-rename.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /**
  2. * @fileoverview Disallow renaming import, export, and destructured assignments to the same name.
  3. * @author Kai Cataldo
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. module.exports = {
  10. meta: {
  11. type: "suggestion",
  12. docs: {
  13. description: "disallow renaming import, export, and destructured assignments to the same name",
  14. category: "ECMAScript 6",
  15. recommended: false,
  16. url: "https://eslint.org/docs/rules/no-useless-rename"
  17. },
  18. fixable: "code",
  19. schema: [
  20. {
  21. type: "object",
  22. properties: {
  23. ignoreDestructuring: { type: "boolean", default: false },
  24. ignoreImport: { type: "boolean", default: false },
  25. ignoreExport: { type: "boolean", default: false }
  26. },
  27. additionalProperties: false
  28. }
  29. ],
  30. messages: {
  31. unnecessarilyRenamed: "{{type}} {{name}} unnecessarily renamed."
  32. }
  33. },
  34. create(context) {
  35. const sourceCode = context.getSourceCode(),
  36. options = context.options[0] || {},
  37. ignoreDestructuring = options.ignoreDestructuring === true,
  38. ignoreImport = options.ignoreImport === true,
  39. ignoreExport = options.ignoreExport === true;
  40. //--------------------------------------------------------------------------
  41. // Helpers
  42. //--------------------------------------------------------------------------
  43. /**
  44. * Reports error for unnecessarily renamed assignments
  45. * @param {ASTNode} node node to report
  46. * @param {ASTNode} initial node with initial name value
  47. * @param {ASTNode} result node with new name value
  48. * @param {string} type the type of the offending node
  49. * @returns {void}
  50. */
  51. function reportError(node, initial, result, type) {
  52. const name = initial.type === "Identifier" ? initial.name : initial.value;
  53. return context.report({
  54. node,
  55. messageId: "unnecessarilyRenamed",
  56. data: {
  57. name,
  58. type
  59. },
  60. fix(fixer) {
  61. if (sourceCode.commentsExistBetween(initial, result)) {
  62. return null;
  63. }
  64. const replacementText = result.type === "AssignmentPattern"
  65. ? sourceCode.getText(result)
  66. : name;
  67. return fixer.replaceTextRange([
  68. initial.range[0],
  69. result.range[1]
  70. ], replacementText);
  71. }
  72. });
  73. }
  74. /**
  75. * Checks whether a destructured assignment is unnecessarily renamed
  76. * @param {ASTNode} node node to check
  77. * @returns {void}
  78. */
  79. function checkDestructured(node) {
  80. if (ignoreDestructuring) {
  81. return;
  82. }
  83. for (const property of node.properties) {
  84. /*
  85. * TODO: Remove after babel-eslint removes ExperimentalRestProperty
  86. * https://github.com/eslint/eslint/issues/12335
  87. */
  88. if (property.type === "ExperimentalRestProperty") {
  89. continue;
  90. }
  91. /**
  92. * Properties using shorthand syntax and rest elements can not be renamed.
  93. * If the property is computed, we have no idea if a rename is useless or not.
  94. */
  95. if (property.shorthand || property.type === "RestElement" || property.computed) {
  96. continue;
  97. }
  98. const key = (property.key.type === "Identifier" && property.key.name) || (property.key.type === "Literal" && property.key.value);
  99. const renamedKey = property.value.type === "AssignmentPattern" ? property.value.left.name : property.value.name;
  100. if (key === renamedKey) {
  101. reportError(property, property.key, property.value, "Destructuring assignment");
  102. }
  103. }
  104. }
  105. /**
  106. * Checks whether an import is unnecessarily renamed
  107. * @param {ASTNode} node node to check
  108. * @returns {void}
  109. */
  110. function checkImport(node) {
  111. if (ignoreImport) {
  112. return;
  113. }
  114. if (node.imported.name === node.local.name &&
  115. node.imported.range[0] !== node.local.range[0]) {
  116. reportError(node, node.imported, node.local, "Import");
  117. }
  118. }
  119. /**
  120. * Checks whether an export is unnecessarily renamed
  121. * @param {ASTNode} node node to check
  122. * @returns {void}
  123. */
  124. function checkExport(node) {
  125. if (ignoreExport) {
  126. return;
  127. }
  128. if (node.local.name === node.exported.name &&
  129. node.local.range[0] !== node.exported.range[0]) {
  130. reportError(node, node.local, node.exported, "Export");
  131. }
  132. }
  133. //--------------------------------------------------------------------------
  134. // Public
  135. //--------------------------------------------------------------------------
  136. return {
  137. ObjectPattern: checkDestructured,
  138. ImportSpecifier: checkImport,
  139. ExportSpecifier: checkExport
  140. };
  141. }
  142. };