Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 875279c

Browse files
rfermannljharb
authored andcommittedJul 2, 2019
[Fix] no-unused-modules: Exclude package "main"/"bin"/"browser" entry points
Fixes #1327.
1 parent 22d5440 commit 875279c

File tree

14 files changed

+124
-9
lines changed

14 files changed

+124
-9
lines changed
 

‎docs/rules/no-unused-modules.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ export function doAnything() {
105105
export default 5 // will not be reported
106106
```
107107

108-
108+
#### Important Note
109+
Exports from files listed as a main file (`main`, `browser`, or `bin` fields in `package.json`) will be ignored by default. This only applies if the `package.json` is not set to `private: true`
109110

110111
## When not to use
111112

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"eslint-module-utils": "^2.4.0",
8989
"has": "^1.0.3",
9090
"minimatch": "^3.0.4",
91+
"object.values": "^1.1.0",
9192
"read-pkg-up": "^2.0.0",
9293
"resolve": "^1.11.0"
9394
},

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

+66-7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
import Exports from '../ExportMap'
88
import resolve from 'eslint-module-utils/resolve'
99
import docsUrl from '../docsUrl'
10+
import { dirname, join } from 'path'
11+
import readPkgUp from 'read-pkg-up'
12+
import values from 'object.values'
1013

1114
// eslint/lib/util/glob-util has been moved to eslint/lib/util/glob-utils with version 5.3
1215
// and has been moved to eslint/lib/cli-engine/file-enumerator in version 6
@@ -80,7 +83,7 @@ const prepareImportsAndExports = (srcFiles, context) => {
8083
if (currentExports) {
8184
const { dependencies, reexports, imports: localImportList, namespace } = currentExports
8285

83-
// dependencies === export * from
86+
// dependencies === export * from
8487
const currentExportAll = new Set()
8588
dependencies.forEach(value => {
8689
currentExportAll.add(value().path)
@@ -146,7 +149,7 @@ const prepareImportsAndExports = (srcFiles, context) => {
146149
}
147150

148151
/**
149-
* traverse through all imports and add the respective path to the whereUsed-list
152+
* traverse through all imports and add the respective path to the whereUsed-list
150153
* of the corresponding export
151154
*/
152155
const determineUsage = () => {
@@ -201,6 +204,58 @@ const newNamespaceImportExists = specifiers =>
201204
const newDefaultImportExists = specifiers =>
202205
specifiers.some(({ type }) => type === IMPORT_DEFAULT_SPECIFIER)
203206

207+
const fileIsInPkg = file => {
208+
const { path, pkg } = readPkgUp.sync({cwd: file, normalize: false})
209+
const basePath = dirname(path)
210+
211+
const checkPkgFieldString = pkgField => {
212+
if (join(basePath, pkgField) === file) {
213+
return true
214+
}
215+
}
216+
217+
const checkPkgFieldObject = pkgField => {
218+
const pkgFieldFiles = values(pkgField).map(value => join(basePath, value))
219+
if (pkgFieldFiles.includes(file)) {
220+
return true
221+
}
222+
}
223+
224+
const checkPkgField = pkgField => {
225+
if (typeof pkgField === 'string') {
226+
return checkPkgFieldString(pkgField)
227+
}
228+
229+
if (typeof pkgField === 'object') {
230+
return checkPkgFieldObject(pkgField)
231+
}
232+
}
233+
234+
if (pkg.private === true) {
235+
return false
236+
}
237+
238+
if (pkg.bin) {
239+
if (checkPkgField(pkg.bin)) {
240+
return true
241+
}
242+
}
243+
244+
if (pkg.browser) {
245+
if (checkPkgField(pkg.browser)) {
246+
return true
247+
}
248+
}
249+
250+
if (pkg.main) {
251+
if (checkPkgFieldString(pkg.main)) {
252+
return true
253+
}
254+
}
255+
256+
return false
257+
}
258+
204259
module.exports = {
205260
meta: {
206261
docs: { url: docsUrl('no-unused-modules') },
@@ -315,6 +370,10 @@ module.exports = {
315370
return
316371
}
317372

373+
if (fileIsInPkg(file)) {
374+
return
375+
}
376+
318377
// refresh list of source files
319378
const srcFiles = resolveFiles(getSrc(src), ignoreExports)
320379

@@ -325,7 +384,7 @@ module.exports = {
325384

326385
exports = exportList.get(file)
327386

328-
// special case: export * from
387+
// special case: export * from
329388
const exportAll = exports.get(EXPORT_ALL_DECLARATION)
330389
if (typeof exportAll !== 'undefined' && exportedValue !== IMPORT_DEFAULT_SPECIFIER) {
331390
if (exportAll.whereUsed.size > 0) {
@@ -362,7 +421,7 @@ module.exports = {
362421

363422
/**
364423
* only useful for tools like vscode-eslint
365-
*
424+
*
366425
* update lists of existing exports during runtime
367426
*/
368427
const updateExportUsage = node => {
@@ -384,7 +443,7 @@ module.exports = {
384443
node.body.forEach(({ type, declaration, specifiers }) => {
385444
if (type === EXPORT_DEFAULT_DECLARATION) {
386445
newExportIdentifiers.add(IMPORT_DEFAULT_SPECIFIER)
387-
}
446+
}
388447
if (type === EXPORT_NAMED_DECLARATION) {
389448
if (specifiers.length > 0) {
390449
specifiers.forEach(specifier => {
@@ -399,7 +458,7 @@ module.exports = {
399458
declaration.type === CLASS_DECLARATION
400459
) {
401460
newExportIdentifiers.add(declaration.id.name)
402-
}
461+
}
403462
if (declaration.type === VARIABLE_DECLARATION) {
404463
declaration.declarations.forEach(({ id }) => {
405464
newExportIdentifiers.add(id.name)
@@ -438,7 +497,7 @@ module.exports = {
438497

439498
/**
440499
* only useful for tools like vscode-eslint
441-
*
500+
*
442501
* update lists of existing imports during runtime
443502
*/
444503
const updateImportUsage = node => {

‎tests/files/no-unused-modules/bin.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const bin = 'bin'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const binObject = 'binObject'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"bin": {
3+
"binObject": "./index.js"
4+
},
5+
"private": false
6+
}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const browser = 'browser'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const browserObject = 'browserObject'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"browser": {
3+
"browserObject": "./index.js"
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const main = 'main'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"bin": "./bin.js",
3+
"browser": "./browser.js",
4+
"main": "./main/index.js"
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const privatePkg = 'privatePkg'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"main": "./index.js",
3+
"private": true
4+
}

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

+29-1
Original file line numberDiff line numberDiff line change
@@ -610,4 +610,32 @@ ruleTester.run('no-unused-modules', rule, {
610610
filename: testFilePath('../jsx/named.jsx')}),
611611
],
612612
invalid: [],
613-
})
613+
})
614+
615+
describe('do not report unused export for files mentioned in package.json', () => {
616+
ruleTester.run('no-unused-modules', rule, {
617+
valid: [
618+
test({ options: unusedExportsOptions,
619+
code: 'export const bin = "bin"',
620+
filename: testFilePath('./no-unused-modules/bin.js')}),
621+
test({ options: unusedExportsOptions,
622+
code: 'export const binObject = "binObject"',
623+
filename: testFilePath('./no-unused-modules/binObject/index.js')}),
624+
test({ options: unusedExportsOptions,
625+
code: 'export const browser = "browser"',
626+
filename: testFilePath('./no-unused-modules/browser.js')}),
627+
test({ options: unusedExportsOptions,
628+
code: 'export const browserObject = "browserObject"',
629+
filename: testFilePath('./no-unused-modules/browserObject/index.js')}),
630+
test({ options: unusedExportsOptions,
631+
code: 'export const main = "main"',
632+
filename: testFilePath('./no-unused-modules/main/index.js')}),
633+
],
634+
invalid: [
635+
test({ options: unusedExportsOptions,
636+
code: 'export const privatePkg = "privatePkg"',
637+
filename: testFilePath('./no-unused-modules/privatePkg/index.js'),
638+
errors: [error(`exported declaration 'privatePkg' not used within other modules`)]}),
639+
],
640+
})
641+
})

0 commit comments

Comments
 (0)
Please sign in to comment.