Skip to content

Commit f064772

Browse files
nicolashenryljharb
andcommitted
[Fix] no-unused-modules: consider exported TypeScript interfaces, types and enums
Fixes #1680 Co-authored-by: e020873 <[email protected]> Co-authored-by: Jordan Harband <[email protected]>
1 parent ec5195e commit f064772

File tree

7 files changed

+116
-28
lines changed

7 files changed

+116
-28
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
66

77
## [Unreleased]
8+
### Fixed
9+
- [`no-unused-modules`]: consider exported TypeScript interfaces, types and enums ([#1819], thanks [@nicolashenry])
810

911
## [2.21.2] - 2020-06-09
1012
### Fixed
@@ -702,6 +704,7 @@ for info on changes for earlier releases.
702704

703705
[`memo-parser`]: ./memo-parser/README.md
704706

707+
[#1819]: https://github.com/benmosher/eslint-plugin-import/pull/1819
705708
[#1802]: https://github.com/benmosher/eslint-plugin-import/pull/1802
706709
[#1801]: https://github.com/benmosher/eslint-plugin-import/issues/1801
707710
[#1788]: https://github.com/benmosher/eslint-plugin-import/pull/1788
@@ -1216,3 +1219,4 @@ for info on changes for earlier releases.
12161219
[@adjerbetian]: https://github.com/adjerbetian
12171220
[@Maxim-Mazurok]: https://github.com/Maxim-Mazurok
12181221
[@malykhinvi]: https://github.com/malykhinvi
1222+
[@nicolashenry]: https://github.com/nicolashenry

src/rules/no-unused-modules.js

+31-26
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,33 @@ const IMPORT_DEFAULT_SPECIFIER = 'ImportDefaultSpecifier'
6363
const VARIABLE_DECLARATION = 'VariableDeclaration'
6464
const FUNCTION_DECLARATION = 'FunctionDeclaration'
6565
const CLASS_DECLARATION = 'ClassDeclaration'
66+
const INTERFACE_DECLARATION = 'InterfaceDeclaration'
67+
const TYPE_ALIAS = 'TypeAlias'
68+
const TS_INTERFACE_DECLARATION = 'TSInterfaceDeclaration'
69+
const TS_TYPE_ALIAS_DECLARATION = 'TSTypeAliasDeclaration'
70+
const TS_ENUM_DECLARATION = 'TSEnumDeclaration'
6671
const DEFAULT = 'default'
6772

73+
function forEachDeclarationIdentifier(declaration, cb) {
74+
if (declaration) {
75+
if (
76+
declaration.type === FUNCTION_DECLARATION ||
77+
declaration.type === CLASS_DECLARATION ||
78+
declaration.type === INTERFACE_DECLARATION ||
79+
declaration.type === TYPE_ALIAS ||
80+
declaration.type === TS_INTERFACE_DECLARATION ||
81+
declaration.type === TS_TYPE_ALIAS_DECLARATION ||
82+
declaration.type === TS_ENUM_DECLARATION
83+
) {
84+
cb(declaration.id.name)
85+
} else if (declaration.type === VARIABLE_DECLARATION) {
86+
declaration.declarations.forEach(({ id }) => {
87+
cb(id.name)
88+
})
89+
}
90+
}
91+
}
92+
6893
/**
6994
* List of imports per file.
7095
*
@@ -559,19 +584,9 @@ module.exports = {
559584
}
560585
})
561586
}
562-
if (declaration) {
563-
if (
564-
declaration.type === FUNCTION_DECLARATION ||
565-
declaration.type === CLASS_DECLARATION
566-
) {
567-
newExportIdentifiers.add(declaration.id.name)
568-
}
569-
if (declaration.type === VARIABLE_DECLARATION) {
570-
declaration.declarations.forEach(({ id }) => {
571-
newExportIdentifiers.add(id.name)
572-
})
573-
}
574-
}
587+
forEachDeclarationIdentifier(declaration, (name) => {
588+
newExportIdentifiers.add(name)
589+
})
575590
}
576591
})
577592

@@ -883,19 +898,9 @@ module.exports = {
883898
node.specifiers.forEach(specifier => {
884899
checkUsage(node, specifier.exported.name)
885900
})
886-
if (node.declaration) {
887-
if (
888-
node.declaration.type === FUNCTION_DECLARATION ||
889-
node.declaration.type === CLASS_DECLARATION
890-
) {
891-
checkUsage(node, node.declaration.id.name)
892-
}
893-
if (node.declaration.type === VARIABLE_DECLARATION) {
894-
node.declaration.declarations.forEach(declaration => {
895-
checkUsage(node, declaration.id.name)
896-
})
897-
}
898-
}
901+
forEachDeclarationIdentifier(node.declaration, (name) => {
902+
checkUsage(node, name)
903+
})
899904
},
900905
}
901906
},
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
import {b} from './file-ts-b';
2+
import {c} from './file-ts-c';
3+
import {d} from './file-ts-d';
4+
import {e} from './file-ts-e';
25

3-
export const a = b + 1;
6+
export const a = b + 1 + e.f;
7+
export const a2: c = {};
8+
export const a3: d = {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export interface c {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type d = {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export enum e { f };

tests/src/rules/no-unused-modules.js

+72-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { test, testFilePath } from '../utils'
1+
import { test, testFilePath, getTSParsers } from '../utils'
22
import jsxConfig from '../../../config/react'
33
import typescriptConfig from '../../../config/typescript'
44

@@ -736,10 +736,81 @@ describe('correctly work with Typescript only files', () => {
736736
error(`exported declaration 'b' not used within other modules`),
737737
],
738738
}),
739+
test({
740+
options: unusedExportsTypescriptOptions,
741+
code: `export interface c {};`,
742+
parser: require.resolve('babel-eslint'),
743+
filename: testFilePath('./no-unused-modules/typescript/file-ts-c.ts'),
744+
errors: [
745+
error(`exported declaration 'c' not used within other modules`),
746+
],
747+
}),
748+
test({
749+
options: unusedExportsTypescriptOptions,
750+
code: `export type d = {};`,
751+
parser: require.resolve('babel-eslint'),
752+
filename: testFilePath('./no-unused-modules/typescript/file-ts-d.ts'),
753+
errors: [
754+
error(`exported declaration 'd' not used within other modules`),
755+
],
756+
}),
739757
],
740758
})
741759
})
742760

761+
context('TypeScript', function () {
762+
getTSParsers().forEach((parser) => {
763+
typescriptRuleTester.run('no-unused-modules', rule, {
764+
valid: [
765+
test({
766+
options: unusedExportsTypescriptOptions,
767+
code: 'import a from "file-ts-a";',
768+
parser: parser,
769+
filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'),
770+
}),
771+
],
772+
invalid: [
773+
test({
774+
options: unusedExportsTypescriptOptions,
775+
code: `export const b = 2;`,
776+
parser: parser,
777+
filename: testFilePath('./no-unused-modules/typescript/file-ts-b.ts'),
778+
errors: [
779+
error(`exported declaration 'b' not used within other modules`),
780+
],
781+
}),
782+
test({
783+
options: unusedExportsTypescriptOptions,
784+
code: `export interface c {};`,
785+
parser: parser,
786+
filename: testFilePath('./no-unused-modules/typescript/file-ts-c.ts'),
787+
errors: [
788+
error(`exported declaration 'c' not used within other modules`),
789+
],
790+
}),
791+
test({
792+
options: unusedExportsTypescriptOptions,
793+
code: `export type d = {};`,
794+
parser: parser,
795+
filename: testFilePath('./no-unused-modules/typescript/file-ts-d.ts'),
796+
errors: [
797+
error(`exported declaration 'd' not used within other modules`),
798+
],
799+
}),
800+
test({
801+
options: unusedExportsTypescriptOptions,
802+
code: `export enum e { f };`,
803+
parser: parser,
804+
filename: testFilePath('./no-unused-modules/typescript/file-ts-e.ts'),
805+
errors: [
806+
error(`exported declaration 'e' not used within other modules`),
807+
],
808+
}),
809+
],
810+
})
811+
})
812+
})
813+
743814
describe('correctly work with JSX only files', () => {
744815
jsxRuleTester.run('no-unused-modules', rule, {
745816
valid: [

0 commit comments

Comments
 (0)