no-duplicate-imports.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /**
  2. * @fileoverview Restrict usage of duplicate imports.
  3. * @author Simen Bekkhus
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. /**
  10. * Returns the name of the module imported or re-exported.
  11. * @param {ASTNode} node A node to get.
  12. * @returns {string} the name of the module, or empty string if no name.
  13. */
  14. function getValue(node) {
  15. if (node && node.source && node.source.value) {
  16. return node.source.value.trim();
  17. }
  18. return "";
  19. }
  20. /**
  21. * Checks if the name of the import or export exists in the given array, and reports if so.
  22. * @param {RuleContext} context The ESLint rule context object.
  23. * @param {ASTNode} node A node to get.
  24. * @param {string} value The name of the imported or exported module.
  25. * @param {string[]} array The array containing other imports or exports in the file.
  26. * @param {string} messageId A messageId to be reported after the name of the module
  27. *
  28. * @returns {void} No return value
  29. */
  30. function checkAndReport(context, node, value, array, messageId) {
  31. if (array.indexOf(value) !== -1) {
  32. context.report({
  33. node,
  34. messageId,
  35. data: {
  36. module: value
  37. }
  38. });
  39. }
  40. }
  41. /**
  42. * @callback nodeCallback
  43. * @param {ASTNode} node A node to handle.
  44. */
  45. /**
  46. * Returns a function handling the imports of a given file
  47. * @param {RuleContext} context The ESLint rule context object.
  48. * @param {boolean} includeExports Whether or not to check for exports in addition to imports.
  49. * @param {string[]} importsInFile The array containing other imports in the file.
  50. * @param {string[]} exportsInFile The array containing other exports in the file.
  51. *
  52. * @returns {nodeCallback} A function passed to ESLint to handle the statement.
  53. */
  54. function handleImports(context, includeExports, importsInFile, exportsInFile) {
  55. return function(node) {
  56. const value = getValue(node);
  57. if (value) {
  58. checkAndReport(context, node, value, importsInFile, "import");
  59. if (includeExports) {
  60. checkAndReport(context, node, value, exportsInFile, "importAs");
  61. }
  62. importsInFile.push(value);
  63. }
  64. };
  65. }
  66. /**
  67. * Returns a function handling the exports of a given file
  68. * @param {RuleContext} context The ESLint rule context object.
  69. * @param {string[]} importsInFile The array containing other imports in the file.
  70. * @param {string[]} exportsInFile The array containing other exports in the file.
  71. *
  72. * @returns {nodeCallback} A function passed to ESLint to handle the statement.
  73. */
  74. function handleExports(context, importsInFile, exportsInFile) {
  75. return function(node) {
  76. const value = getValue(node);
  77. if (value) {
  78. checkAndReport(context, node, value, exportsInFile, "export");
  79. checkAndReport(context, node, value, importsInFile, "exportAs");
  80. exportsInFile.push(value);
  81. }
  82. };
  83. }
  84. module.exports = {
  85. meta: {
  86. type: "problem",
  87. docs: {
  88. description: "disallow duplicate module imports",
  89. category: "ECMAScript 6",
  90. recommended: false,
  91. url: "https://eslint.org/docs/rules/no-duplicate-imports"
  92. },
  93. schema: [{
  94. type: "object",
  95. properties: {
  96. includeExports: {
  97. type: "boolean",
  98. default: false
  99. }
  100. },
  101. additionalProperties: false
  102. }],
  103. messages: {
  104. import: "'{{module}}' import is duplicated.",
  105. importAs: "'{{module}}' import is duplicated as export.",
  106. export: "'{{module}}' export is duplicated.",
  107. exportAs: "'{{module}}' export is duplicated as import."
  108. }
  109. },
  110. create(context) {
  111. const includeExports = (context.options[0] || {}).includeExports,
  112. importsInFile = [],
  113. exportsInFile = [];
  114. const handlers = {
  115. ImportDeclaration: handleImports(context, includeExports, importsInFile, exportsInFile)
  116. };
  117. if (includeExports) {
  118. handlers.ExportNamedDeclaration = handleExports(context, importsInFile, exportsInFile);
  119. handlers.ExportAllDeclaration = handleExports(context, importsInFile, exportsInFile);
  120. }
  121. return handlers;
  122. }
  123. };