From 1caa402f47b4080051a8d761b9daf2f1a290e9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fermann?= Date: Tue, 2 Jul 2019 11:43:43 +0200 Subject: [PATCH] [Fix] `no-unused-modules`: Exclude package "main"/"bin"/"browser" entry points Fixes #1327. --- docs/rules/no-unused-modules.md | 3 +- package.json | 1 + src/rules/no-unused-modules.js | 74 +++++++++++++++++-- tests/files/no-unused-modules/bin.js | 1 + .../no-unused-modules/binObject/index.js | 1 + .../no-unused-modules/binObject/package.json | 6 ++ tests/files/no-unused-modules/browser.js | 1 + .../no-unused-modules/browserObject/index.js | 1 + .../browserObject/package.json | 5 ++ tests/files/no-unused-modules/main/index.js | 1 + tests/files/no-unused-modules/package.json | 5 ++ .../no-unused-modules/privatePkg/index.js | 1 + .../no-unused-modules/privatePkg/package.json | 4 + tests/src/rules/no-unused-modules.js | 30 +++++++- 14 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 tests/files/no-unused-modules/bin.js create mode 100644 tests/files/no-unused-modules/binObject/index.js create mode 100644 tests/files/no-unused-modules/binObject/package.json create mode 100644 tests/files/no-unused-modules/browser.js create mode 100644 tests/files/no-unused-modules/browserObject/index.js create mode 100644 tests/files/no-unused-modules/browserObject/package.json create mode 100644 tests/files/no-unused-modules/main/index.js create mode 100644 tests/files/no-unused-modules/package.json create mode 100644 tests/files/no-unused-modules/privatePkg/index.js create mode 100644 tests/files/no-unused-modules/privatePkg/package.json diff --git a/docs/rules/no-unused-modules.md b/docs/rules/no-unused-modules.md index 32db6465f..4302bc845 100644 --- a/docs/rules/no-unused-modules.md +++ b/docs/rules/no-unused-modules.md @@ -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 diff --git a/package.json b/package.json index f7989b2d7..488a441a1 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index 16130d47d..47cd11c0d 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -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 @@ -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) @@ -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 = () => { @@ -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) { + 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') }, @@ -315,6 +371,10 @@ module.exports = { return } + if (fileIsInPkg(file)) { + return + } + // refresh list of source files const srcFiles = resolveFiles(getSrc(src), ignoreExports) @@ -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) { @@ -362,7 +422,7 @@ module.exports = { /** * only useful for tools like vscode-eslint - * + * * update lists of existing exports during runtime */ const updateExportUsage = node => { @@ -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 => { @@ -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) @@ -438,7 +498,7 @@ module.exports = { /** * only useful for tools like vscode-eslint - * + * * update lists of existing imports during runtime */ const updateImportUsage = node => { diff --git a/tests/files/no-unused-modules/bin.js b/tests/files/no-unused-modules/bin.js new file mode 100644 index 000000000..c755d1402 --- /dev/null +++ b/tests/files/no-unused-modules/bin.js @@ -0,0 +1 @@ +export const bin = 'bin' diff --git a/tests/files/no-unused-modules/binObject/index.js b/tests/files/no-unused-modules/binObject/index.js new file mode 100644 index 000000000..53cc33821 --- /dev/null +++ b/tests/files/no-unused-modules/binObject/index.js @@ -0,0 +1 @@ +export const binObject = 'binObject' diff --git a/tests/files/no-unused-modules/binObject/package.json b/tests/files/no-unused-modules/binObject/package.json new file mode 100644 index 000000000..fa9c85326 --- /dev/null +++ b/tests/files/no-unused-modules/binObject/package.json @@ -0,0 +1,6 @@ +{ + "bin": { + "binObject": "./index.js" + }, + "private": false +} diff --git a/tests/files/no-unused-modules/browser.js b/tests/files/no-unused-modules/browser.js new file mode 100644 index 000000000..daad8d294 --- /dev/null +++ b/tests/files/no-unused-modules/browser.js @@ -0,0 +1 @@ +export const browser = 'browser' diff --git a/tests/files/no-unused-modules/browserObject/index.js b/tests/files/no-unused-modules/browserObject/index.js new file mode 100644 index 000000000..92d09f8f1 --- /dev/null +++ b/tests/files/no-unused-modules/browserObject/index.js @@ -0,0 +1 @@ +export const browserObject = 'browserObject' diff --git a/tests/files/no-unused-modules/browserObject/package.json b/tests/files/no-unused-modules/browserObject/package.json new file mode 100644 index 000000000..28272c6fe --- /dev/null +++ b/tests/files/no-unused-modules/browserObject/package.json @@ -0,0 +1,5 @@ +{ + "browser": { + "browserObject": "./index.js" + } +} diff --git a/tests/files/no-unused-modules/main/index.js b/tests/files/no-unused-modules/main/index.js new file mode 100644 index 000000000..9eb0aade1 --- /dev/null +++ b/tests/files/no-unused-modules/main/index.js @@ -0,0 +1 @@ +export const main = 'main' diff --git a/tests/files/no-unused-modules/package.json b/tests/files/no-unused-modules/package.json new file mode 100644 index 000000000..5aae42b81 --- /dev/null +++ b/tests/files/no-unused-modules/package.json @@ -0,0 +1,5 @@ +{ + "bin": "./bin.js", + "browser": "./browser.js", + "main": "./main/index.js" +} diff --git a/tests/files/no-unused-modules/privatePkg/index.js b/tests/files/no-unused-modules/privatePkg/index.js new file mode 100644 index 000000000..c936cd493 --- /dev/null +++ b/tests/files/no-unused-modules/privatePkg/index.js @@ -0,0 +1 @@ +export const privatePkg = 'privatePkg' diff --git a/tests/files/no-unused-modules/privatePkg/package.json b/tests/files/no-unused-modules/privatePkg/package.json new file mode 100644 index 000000000..618c66d18 --- /dev/null +++ b/tests/files/no-unused-modules/privatePkg/package.json @@ -0,0 +1,4 @@ +{ + "main": "./index.js", + "private": true +} diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index cefa6ff21..8050b5693 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -610,4 +610,32 @@ ruleTester.run('no-unused-modules', rule, { filename: testFilePath('../jsx/named.jsx')}), ], invalid: [], -}) \ No newline at end of file +}) + +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`)]}), + ], + }) +})