no-useless-rename.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /**
  2. * @fileoverview Disallow renaming import, export, and destructured assignments to the same name.
  3. * @author Kai Cataldo
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("./utils/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Rule Definition
  12. //------------------------------------------------------------------------------
  13. /** @type {import('../shared/types').Rule} */
  14. module.exports = {
  15. meta: {
  16. type: "suggestion",
  17. defaultOptions: [{
  18. ignoreDestructuring: false,
  19. ignoreImport: false,
  20. ignoreExport: false
  21. }],
  22. docs: {
  23. description: "Disallow renaming import, export, and destructured assignments to the same name",
  24. recommended: false,
  25. url: "https://eslint.org/docs/latest/rules/no-useless-rename"
  26. },
  27. fixable: "code",
  28. schema: [
  29. {
  30. type: "object",
  31. properties: {
  32. ignoreDestructuring: { type: "boolean" },
  33. ignoreImport: { type: "boolean" },
  34. ignoreExport: { type: "boolean" }
  35. },
  36. additionalProperties: false
  37. }
  38. ],
  39. messages: {
  40. unnecessarilyRenamed: "{{type}} {{name}} unnecessarily renamed."
  41. }
  42. },
  43. create(context) {
  44. const sourceCode = context.sourceCode;
  45. const [{ ignoreDestructuring, ignoreImport, ignoreExport }] = context.options;
  46. //--------------------------------------------------------------------------
  47. // Helpers
  48. //--------------------------------------------------------------------------
  49. /**
  50. * Reports error for unnecessarily renamed assignments
  51. * @param {ASTNode} node node to report
  52. * @param {ASTNode} initial node with initial name value
  53. * @param {string} type the type of the offending node
  54. * @returns {void}
  55. */
  56. function reportError(node, initial, type) {
  57. const name = initial.type === "Identifier" ? initial.name : initial.value;
  58. return context.report({
  59. node,
  60. messageId: "unnecessarilyRenamed",
  61. data: {
  62. name,
  63. type
  64. },
  65. fix(fixer) {
  66. const replacementNode = node.type === "Property" ? node.value : node.local;
  67. if (sourceCode.getCommentsInside(node).length > sourceCode.getCommentsInside(replacementNode).length) {
  68. return null;
  69. }
  70. // Don't autofix code such as `({foo: (foo) = a} = obj);`, parens are not allowed in shorthand properties.
  71. if (
  72. replacementNode.type === "AssignmentPattern" &&
  73. astUtils.isParenthesised(sourceCode, replacementNode.left)
  74. ) {
  75. return null;
  76. }
  77. return fixer.replaceText(node, sourceCode.getText(replacementNode));
  78. }
  79. });
  80. }
  81. /**
  82. * Checks whether a destructured assignment is unnecessarily renamed
  83. * @param {ASTNode} node node to check
  84. * @returns {void}
  85. */
  86. function checkDestructured(node) {
  87. if (ignoreDestructuring) {
  88. return;
  89. }
  90. for (const property of node.properties) {
  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.type !== "Property" || property.shorthand || 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, "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 (
  115. node.imported.range[0] !== node.local.range[0] &&
  116. astUtils.getModuleExportName(node.imported) === node.local.name
  117. ) {
  118. reportError(node, node.imported, "Import");
  119. }
  120. }
  121. /**
  122. * Checks whether an export is unnecessarily renamed
  123. * @param {ASTNode} node node to check
  124. * @returns {void}
  125. */
  126. function checkExport(node) {
  127. if (ignoreExport) {
  128. return;
  129. }
  130. if (
  131. node.local.range[0] !== node.exported.range[0] &&
  132. astUtils.getModuleExportName(node.local) === astUtils.getModuleExportName(node.exported)
  133. ) {
  134. reportError(node, node.local, "Export");
  135. }
  136. }
  137. //--------------------------------------------------------------------------
  138. // Public
  139. //--------------------------------------------------------------------------
  140. return {
  141. ObjectPattern: checkDestructured,
  142. ImportSpecifier: checkImport,
  143. ExportSpecifier: checkExport
  144. };
  145. }
  146. };