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