/** * @fileoverview Restrict usage of duplicate imports. * @author Simen Bekkhus */ "use strict"; //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ /** * Returns the name of the module imported or re-exported. * * @param {ASTNode} node - A node to get. * @returns {string} the name of the module, or empty string if no name. */ function getValue(node) { if (node && node.source && node.source.value) { return node.source.value.trim(); } return ""; } /** * Checks if the name of the import or export exists in the given array, and reports if so. * * @param {RuleContext} context - The ESLint rule context object. * @param {ASTNode} node - A node to get. * @param {string} value - The name of the imported or exported module. * @param {string[]} array - The array containing other imports or exports in the file. * @param {string} messageId - A messageId to be reported after the name of the module * * @returns {void} No return value */ function checkAndReport(context, node, value, array, messageId) { if (array.indexOf(value) !== -1) { context.report({ node, messageId, data: { module: value } }); } } /** * @callback nodeCallback * @param {ASTNode} node - A node to handle. */ /** * Returns a function handling the imports of a given file * * @param {RuleContext} context - The ESLint rule context object. * @param {boolean} includeExports - Whether or not to check for exports in addition to imports. * @param {string[]} importsInFile - The array containing other imports in the file. * @param {string[]} exportsInFile - The array containing other exports in the file. * * @returns {nodeCallback} A function passed to ESLint to handle the statement. */ function handleImports(context, includeExports, importsInFile, exportsInFile) { return function(node) { const value = getValue(node); if (value) { checkAndReport(context, node, value, importsInFile, "import"); if (includeExports) { checkAndReport(context, node, value, exportsInFile, "importAs"); } importsInFile.push(value); } }; } /** * Returns a function handling the exports of a given file * * @param {RuleContext} context - The ESLint rule context object. * @param {string[]} importsInFile - The array containing other imports in the file. * @param {string[]} exportsInFile - The array containing other exports in the file. * * @returns {nodeCallback} A function passed to ESLint to handle the statement. */ function handleExports(context, importsInFile, exportsInFile) { return function(node) { const value = getValue(node); if (value) { checkAndReport(context, node, value, exportsInFile, "export"); checkAndReport(context, node, value, importsInFile, "exportAs"); exportsInFile.push(value); } }; } module.exports = { meta: { type: "problem", docs: { description: "disallow duplicate module imports", category: "ECMAScript 6", recommended: false, url: "https://eslint.org/docs/rules/no-duplicate-imports" }, schema: [{ type: "object", properties: { includeExports: { type: "boolean", default: false } }, additionalProperties: false }], messages: { import: "'{{module}}' import is duplicated.", importAs: "'{{module}}' import is duplicated as export.", export: "'{{module}}' export is duplicated.", exportAs: "'{{module}}' export is duplicated as import." } }, create(context) { const includeExports = (context.options[0] || {}).includeExports, importsInFile = [], exportsInFile = []; const handlers = { ImportDeclaration: handleImports(context, includeExports, importsInFile, exportsInFile) }; if (includeExports) { handlers.ExportNamedDeclaration = handleExports(context, importsInFile, exportsInFile); handlers.ExportAllDeclaration = handleExports(context, importsInFile, exportsInFile); } return handlers; } };