Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude pkg main files from no-unused-modules #1404

Merged
merged 1 commit into from
Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/rules/no-unused-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ export function doAnything() {
export default 5 // will not be reported
```


#### Important Note
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`

## When not to use

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"eslint-module-utils": "^2.4.0",
"has": "^1.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.0",
"read-pkg-up": "^2.0.0",
"resolve": "^1.11.0"
},
Expand Down
74 changes: 67 additions & 7 deletions src/rules/no-unused-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import Exports from '../ExportMap'
import resolve from 'eslint-module-utils/resolve'
import docsUrl from '../docsUrl'
import { dirname, join } from 'path'
import readPkgUp from 'read-pkg-up'
import values from 'object.values'
import includes from 'array-includes'

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

// dependencies === export * from
// dependencies === export * from
const currentExportAll = new Set()
dependencies.forEach(value => {
currentExportAll.add(value().path)
Expand Down Expand Up @@ -146,7 +150,7 @@ const prepareImportsAndExports = (srcFiles, context) => {
}

/**
* traverse through all imports and add the respective path to the whereUsed-list
* traverse through all imports and add the respective path to the whereUsed-list
* of the corresponding export
*/
const determineUsage = () => {
Expand Down Expand Up @@ -201,6 +205,58 @@ const newNamespaceImportExists = specifiers =>
const newDefaultImportExists = specifiers =>
specifiers.some(({ type }) => type === IMPORT_DEFAULT_SPECIFIER)

const fileIsInPkg = file => {
const { path, pkg } = readPkgUp.sync({cwd: file, normalize: false})
const basePath = dirname(path)

const checkPkgFieldString = pkgField => {
if (join(basePath, pkgField) === file) {
return true
}
}

const checkPkgFieldObject = pkgField => {
const pkgFieldFiles = values(pkgField).map(value => join(basePath, value))
if (includes(pkgFieldFiles, file)) {
return true
}
}

const checkPkgField = pkgField => {
if (typeof pkgField === 'string') {
return checkPkgFieldString(pkgField)
}

if (typeof pkgField === 'object') {
return checkPkgFieldObject(pkgField)
}
}

if (pkg.private === true) {
return false
}

if (pkg.bin) {
if (checkPkgField(pkg.bin)) {
return true
}
}

if (pkg.browser) {
rfermann marked this conversation as resolved.
Show resolved Hide resolved
if (checkPkgField(pkg.browser)) {
return true
}
}

if (pkg.main) {
if (checkPkgFieldString(pkg.main)) {
return true
}
}

return false
}

module.exports = {
meta: {
docs: { url: docsUrl('no-unused-modules') },
Expand Down Expand Up @@ -315,6 +371,10 @@ module.exports = {
return
}

if (fileIsInPkg(file)) {
return
}

// refresh list of source files
const srcFiles = resolveFiles(getSrc(src), ignoreExports)

Expand All @@ -325,7 +385,7 @@ module.exports = {

exports = exportList.get(file)

// special case: export * from
// special case: export * from
const exportAll = exports.get(EXPORT_ALL_DECLARATION)
if (typeof exportAll !== 'undefined' && exportedValue !== IMPORT_DEFAULT_SPECIFIER) {
if (exportAll.whereUsed.size > 0) {
Expand Down Expand Up @@ -362,7 +422,7 @@ module.exports = {

/**
* only useful for tools like vscode-eslint
*
*
* update lists of existing exports during runtime
*/
const updateExportUsage = node => {
Expand All @@ -384,7 +444,7 @@ module.exports = {
node.body.forEach(({ type, declaration, specifiers }) => {
if (type === EXPORT_DEFAULT_DECLARATION) {
newExportIdentifiers.add(IMPORT_DEFAULT_SPECIFIER)
}
}
if (type === EXPORT_NAMED_DECLARATION) {
if (specifiers.length > 0) {
specifiers.forEach(specifier => {
Expand All @@ -399,7 +459,7 @@ module.exports = {
declaration.type === CLASS_DECLARATION
) {
newExportIdentifiers.add(declaration.id.name)
}
}
if (declaration.type === VARIABLE_DECLARATION) {
declaration.declarations.forEach(({ id }) => {
newExportIdentifiers.add(id.name)
Expand Down Expand Up @@ -438,7 +498,7 @@ module.exports = {

/**
* only useful for tools like vscode-eslint
*
*
* update lists of existing imports during runtime
*/
const updateImportUsage = node => {
Expand Down
1 change: 1 addition & 0 deletions tests/files/no-unused-modules/bin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const bin = 'bin'
1 change: 1 addition & 0 deletions tests/files/no-unused-modules/binObject/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const binObject = 'binObject'
6 changes: 6 additions & 0 deletions tests/files/no-unused-modules/binObject/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"bin": {
"binObject": "./index.js"
},
"private": false
}
1 change: 1 addition & 0 deletions tests/files/no-unused-modules/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const browser = 'browser'
1 change: 1 addition & 0 deletions tests/files/no-unused-modules/browserObject/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const browserObject = 'browserObject'
5 changes: 5 additions & 0 deletions tests/files/no-unused-modules/browserObject/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"browser": {
"browserObject": "./index.js"
}
}
1 change: 1 addition & 0 deletions tests/files/no-unused-modules/main/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const main = 'main'
5 changes: 5 additions & 0 deletions tests/files/no-unused-modules/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"bin": "./bin.js",
"browser": "./browser.js",
"main": "./main/index.js"
}
1 change: 1 addition & 0 deletions tests/files/no-unused-modules/privatePkg/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const privatePkg = 'privatePkg'
4 changes: 4 additions & 0 deletions tests/files/no-unused-modules/privatePkg/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"main": "./index.js",
"private": true
}
30 changes: 29 additions & 1 deletion tests/src/rules/no-unused-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -610,4 +610,32 @@ ruleTester.run('no-unused-modules', rule, {
filename: testFilePath('../jsx/named.jsx')}),
],
invalid: [],
})
})

describe('do not report unused export for files mentioned in package.json', () => {
ruleTester.run('no-unused-modules', rule, {
valid: [
test({ options: unusedExportsOptions,
code: 'export const bin = "bin"',
filename: testFilePath('./no-unused-modules/bin.js')}),
test({ options: unusedExportsOptions,
code: 'export const binObject = "binObject"',
filename: testFilePath('./no-unused-modules/binObject/index.js')}),
test({ options: unusedExportsOptions,
code: 'export const browser = "browser"',
filename: testFilePath('./no-unused-modules/browser.js')}),
test({ options: unusedExportsOptions,
code: 'export const browserObject = "browserObject"',
filename: testFilePath('./no-unused-modules/browserObject/index.js')}),
test({ options: unusedExportsOptions,
code: 'export const main = "main"',
filename: testFilePath('./no-unused-modules/main/index.js')}),
],
invalid: [
test({ options: unusedExportsOptions,
code: 'export const privatePkg = "privatePkg"',
filename: testFilePath('./no-unused-modules/privatePkg/index.js'),
errors: [error(`exported declaration 'privatePkg' not used within other modules`)]}),
],
})
})