From ef1d1dd359e0fd05c9bf633bd081a85dc9995e7e Mon Sep 17 00:00:00 2001 From: Noureldin Shaker <43860275+Pandemic1617@users.noreply.github.com> Date: Sun, 26 Nov 2023 17:44:50 +0000 Subject: [PATCH 1/3] added require tests for no-duplicates rule --- tests/src/rules/no-duplicates.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index d61fda86e..388b092df 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -49,8 +49,27 @@ ruleTester.run('no-duplicates', rule, { test({ code: "import {y} from './foo'; import * as ns from './foo'", }), + + // require imports + test({ + code: "const a = require('./foo');", + }), + test({ + code: "const a = require('./foo'); const b = require('./bar');", + }), ], invalid: [ + // require imports + test({ + code: "const a = require('./foo'); const b = require('./foo');", + errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'], + }), + + test({ + code: "const a = require('./foo'); const b = require('./bar'); const c = require('./foo');", + errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'], + }), + test({ code: "import { x } from './foo'; import { y } from './foo'", output: "import { x , y } from './foo'; ", From 98d4eabeb974bb0afe44d0faa2c16759d7aef388 Mon Sep 17 00:00:00 2001 From: Noureldin Shaker <43860275+Pandemic1617@users.noreply.github.com> Date: Sun, 26 Nov 2023 17:46:12 +0000 Subject: [PATCH 2/3] added require compatibility to no-duplicates --- src/rules/no-duplicates.js | 48 +++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 6b4f4d559..e247379ed 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -9,6 +9,10 @@ try { typescriptPkg = require('typescript/package.json'); // eslint-disable-line import/no-extraneous-dependencies } catch (e) { /**/ } +function getSourceFromNode(node) { + return node.callee ? node.arguments[0] : node.source; +} + function checkImports(imported, context) { for (const [module, nodes] of imported.entries()) { if (nodes.length > 1) { @@ -18,14 +22,14 @@ function checkImports(imported, context) { const fix = getFix(first, rest, sourceCode, context); context.report({ - node: first.source, + node: getSourceFromNode(first), message, fix, // Attach the autofix (if any) to the first import. }); for (const node of rest) { context.report({ - node: node.source, + node: getSourceFromNode(node), message, }); } @@ -44,6 +48,12 @@ function getFix(first, rest, sourceCode, context) { return undefined; } + // if there is a require call in any of the duplicates, return undefined + // this could be implemented in the future + if ([first, ...rest].some(node => "callee" in node)) { + return undefined; + } + // Adjusting the first import might make it multiline, which could break // `eslint-disable-next-line` comments and similar, so bail if the first // import has comments. Also, if the first import is `import * as ns from @@ -307,16 +317,27 @@ module.exports = { const moduleMaps = new Map(); - function getImportMap(n) { - if (!moduleMaps.has(n.parent)) { - moduleMaps.set(n.parent, { + function getPrimalRef(n) { + if (n.type === "Program" || n.type === "DeclareModule" || n.type === "TSModuleBlock") return n; + return getPrimalRef(n.parent); + } + + function getModuleMaps(n) { + const marker = getPrimalRef(n); + if (!moduleMaps.has(marker)) { + moduleMaps.set(marker, { imported: new Map(), nsImported: new Map(), defaultTypesImported: new Map(), namedTypesImported: new Map(), }); } - const map = moduleMaps.get(n.parent); + const map = moduleMaps.get(marker); + return map; + } + + function getImportMap(n) { + const map = getModuleMaps(n) const preferInline = context.options[0] && context.options[0]['prefer-inline']; if (!preferInline && n.importKind === 'type') { return n.specifiers.length > 0 && n.specifiers[0].type === 'ImportDefaultSpecifier' ? map.defaultTypesImported : map.namedTypesImported; @@ -341,6 +362,21 @@ module.exports = { } }, + CallExpression: (call) => { + if (call.callee.name !== "require" || call.callee.type !== 'Identifier' || call.arguments.length !== 1) return; + const modulePath = call.arguments[0]; + if (modulePath.type !== 'Literal') return; + + const resolvedPath = resolver(modulePath.value); + const importMap = getModuleMaps(call).imported; + + if (importMap.has(resolvedPath)) { + importMap.get(resolvedPath).push(call); + } else { + importMap.set(resolvedPath, [call]); + } + }, + 'Program:exit'() { for (const map of moduleMaps.values()) { checkImports(map.imported, context); From 61754185aae4278d78d18caaf99b7459dabbb1bf Mon Sep 17 00:00:00 2001 From: Noureldin Shaker <43860275+Pandemic1617@users.noreply.github.com> Date: Sun, 26 Nov 2023 22:21:36 +0000 Subject: [PATCH 3/3] formatting fix --- src/rules/no-duplicates.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index e247379ed..4d226b1be 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -50,7 +50,7 @@ function getFix(first, rest, sourceCode, context) { // if there is a require call in any of the duplicates, return undefined // this could be implemented in the future - if ([first, ...rest].some(node => "callee" in node)) { + if ([first, ...rest].some((node) => 'callee' in node)) { return undefined; } @@ -318,7 +318,7 @@ module.exports = { const moduleMaps = new Map(); function getPrimalRef(n) { - if (n.type === "Program" || n.type === "DeclareModule" || n.type === "TSModuleBlock") return n; + if (n.type === 'Program' || n.type === 'DeclareModule' || n.type === 'TSModuleBlock') { return n; } return getPrimalRef(n.parent); } @@ -337,7 +337,7 @@ module.exports = { } function getImportMap(n) { - const map = getModuleMaps(n) + const map = getModuleMaps(n); const preferInline = context.options[0] && context.options[0]['prefer-inline']; if (!preferInline && n.importKind === 'type') { return n.specifiers.length > 0 && n.specifiers[0].type === 'ImportDefaultSpecifier' ? map.defaultTypesImported : map.namedTypesImported; @@ -362,10 +362,10 @@ module.exports = { } }, - CallExpression: (call) => { - if (call.callee.name !== "require" || call.callee.type !== 'Identifier' || call.arguments.length !== 1) return; + CallExpression(call) { + if (call.callee.name !== 'require' || call.callee.type !== 'Identifier' || call.arguments.length !== 1) { return; } const modulePath = call.arguments[0]; - if (modulePath.type !== 'Literal') return; + if (modulePath.type !== 'Literal') { return; } const resolvedPath = resolver(modulePath.value); const importMap = getModuleMaps(call).imported;