Skip to content

Commit

Permalink
[Fix] no-unused-modules: handle export { default } from syntax
Browse files Browse the repository at this point in the history
Fixes #1631
  • Loading branch information
richardxia authored and ljharb committed Feb 2, 2020
1 parent adbced7 commit efd6be1
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
- [`order`]: fix `isExternalModule` detect on windows ([#1651], thanks [@fisker])
- [`order`]: recognize ".." as a "parent" path ([#1658], thanks [@golopot])
- [`no-duplicates`]: fix fixer on cases with default import ([#1666], thanks [@golopot])
- [`no-unused-modules`]: Handle `export { default } from` syntax ([#1631], thanks [@richardxia])

## [2.20.1] - 2020-02-01
### Fixed
Expand Down Expand Up @@ -660,6 +661,7 @@ for info on changes for earlier releases.
[#1658]: https://github.com/benmosher/eslint-plugin-import/pull/1658
[#1651]: https://github.com/benmosher/eslint-plugin-import/pull/1651
[#1635]: https://github.com/benmosher/eslint-plugin-import/issues/1635
[#1631]: https://github.com/benmosher/eslint-plugin-import/issues/1631
[#1625]: https://github.com/benmosher/eslint-plugin-import/pull/1625
[#1620]: https://github.com/benmosher/eslint-plugin-import/pull/1620
[#1619]: https://github.com/benmosher/eslint-plugin-import/pull/1619
Expand Down Expand Up @@ -1113,3 +1115,4 @@ for info on changes for earlier releases.
[@IvanGoncharov]: https://github.com/IvanGoncharov
[@wschurman]: https://github.com/wschurman
[@fisker]: https://github.com/fisker
[@richardxia]: https://github.com/richardxia
53 changes: 51 additions & 2 deletions src/rules/no-unused-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,54 @@ const CLASS_DECLARATION = 'ClassDeclaration'
const DEFAULT = 'default'
const TYPE_ALIAS = 'TypeAlias'

/**
* List of imports per file.
*
* Represented by a two-level Map to a Set of identifiers. The upper-level Map
* keys are the paths to the modules containing the imports, while the
* lower-level Map keys are the paths to the files which are being imported
* from. Lastly, the Set of identifiers contains either names being imported
* or a special AST node name listed above (e.g ImportDefaultSpecifier).
*
* For example, if we have a file named foo.js containing:
*
* import { o2 } from './bar.js';
*
* Then we will have a structure that looks like:
*
* Map { 'foo.js' => Map { 'bar.js' => Set { 'o2' } } }
*
* @type {Map<string, Map<string, Set<string>>>}
*/
const importList = new Map()

/**
* List of exports per file.
*
* Represented by a two-level Map to an object of metadata. The upper-level Map
* keys are the paths to the modules containing the exports, while the
* lower-level Map keys are the specific identifiers or special AST node names
* being exported. The leaf-level metadata object at the moment only contains a
* `whereUsed` propoerty, which contains a Set of paths to modules that import
* the name.
*
* For example, if we have a file named bar.js containing the following exports:
*
* const o2 = 'bar';
* export { o2 };
*
* And a file named foo.js containing the following import:
*
* import { o2 } from './bar.js';
*
* Then we will have a structure that looks like:
*
* Map { 'bar.js' => Map { 'o2' => { whereUsed: Set { 'foo.js' } } } }
*
* @type {Map<string, Map<string, object>>}
*/
const exportList = new Map()

const ignoredFiles = new Set()
const filesOutsideSrc = new Set()

Expand Down Expand Up @@ -453,9 +499,12 @@ module.exports = {
}
}

const exportStatement = exports.get(exportedValue)
// exportsList will always map any imported value of 'default' to 'ImportDefaultSpecifier'
const exportsKey = exportedValue === DEFAULT ? IMPORT_DEFAULT_SPECIFIER : exportedValue

const exportStatement = exports.get(exportsKey)

const value = exportedValue === IMPORT_DEFAULT_SPECIFIER ? DEFAULT : exportedValue
const value = exportsKey === IMPORT_DEFAULT_SPECIFIER ? DEFAULT : exportsKey

if (typeof exportStatement !== 'undefined'){
if (exportStatement.whereUsed.size < 1) {
Expand Down
1 change: 1 addition & 0 deletions tests/files/no-unused-modules/file-0.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ import {q} from './file-q'
export * from './file-n'
export { default, o0, o3 } from './file-o'
export { p } from './file-p'
import s from './file-s'
1 change: 1 addition & 0 deletions tests/files/no-unused-modules/file-s.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './file-o'
9 changes: 7 additions & 2 deletions tests/src/rules/no-unused-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ ruleTester.run('no-unused-modules', rule, {
import * as l from './file-l'
export * from './file-n'
export { default, o0, o3 } from './file-o'
export { p } from './file-p'`,
export { p } from './file-p'
import s from './file-s'`,
filename: testFilePath('./no-unused-modules/file-0.js'),
errors: [
error(`exported declaration 'default' not used within other modules`),
Expand Down Expand Up @@ -165,7 +166,11 @@ ruleTester.run('no-unused-modules', rule, {

// // test for export from
ruleTester.run('no-unused-modules', rule, {
valid: [],
valid: [
test({ options: unusedExportsOptions,
code: `export { default } from './file-o'`,
filename: testFilePath('./no-unused-modules/file-s.js')}),
],
invalid: [
test({ options: unusedExportsOptions,
code: `export { k } from '${testFilePath('./no-unused-modules/file-k.js')}'`,
Expand Down

0 comments on commit efd6be1

Please sign in to comment.