diff --git a/CHANGELOG.md b/CHANGELOG.md index 46699d7ce..fb467d3df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). +## [Unreleased] +### Fixed +- `export * from 'foo'` now properly ignores a `default` export from `foo`, if any. ([#328]/[#332], thanks [@jkimbo]) + This impacts all static analysis of imported names. ([`default`], [`named`], [`namespace`], [`export`]) + ## [1.8.0] - 2016-05-11 ### Added - [`prefer-default-export`], new rule. ([#308], thanks [@gavriguy]) @@ -206,10 +211,13 @@ for info on changes for earlier releases. [`no-nodejs-modules`]: ./docs/rules/no-nodejs-modules.md [`order`]: ./docs/rules/order.md [`named`]: ./docs/rules/named.md +[`default`]: ./docs/rules/default.md +[`export`]: ./docs/rules/export.md [`newline-after-import`]: ./docs/rules/newline-after-import.md [`no-mutable-exports`]: ./docs/rules/no-mutable-exports.md [`prefer-default-export`]: ./docs/rules/prefer-default-export.md +[#332]: https://github.com/benmosher/eslint-plugin-import/pull/332 [#322]: https://github.com/benmosher/eslint-plugin-import/pull/322 [#316]: https://github.com/benmosher/eslint-plugin-import/pull/316 [#308]: https://github.com/benmosher/eslint-plugin-import/pull/308 @@ -235,6 +243,7 @@ for info on changes for earlier releases. [#164]: https://github.com/benmosher/eslint-plugin-import/pull/164 [#157]: https://github.com/benmosher/eslint-plugin-import/pull/157 +[#328]: https://github.com/benmosher/eslint-plugin-import/issues/328 [#317]: https://github.com/benmosher/eslint-plugin-import/issues/317 [#286]: https://github.com/benmosher/eslint-plugin-import/issues/286 [#281]: https://github.com/benmosher/eslint-plugin-import/issues/281 @@ -290,3 +299,4 @@ for info on changes for earlier releases. [@josh]: https://github.com/josh [@borisyankov]: https://github.com/borisyankov [@gavriguy]: https://github.com/gavriguy +[@jkimbo]: https://github.com/jkimbo diff --git a/src/core/getExports.js b/src/core/getExports.js index e5d007790..befcc73d7 100644 --- a/src/core/getExports.js +++ b/src/core/getExports.js @@ -228,13 +228,16 @@ export default class ExportMap { if (this.namespace.has(name)) return true if (this.reexports.has(name)) return true - for (let dep of this.dependencies.values()) { - let innerMap = dep() + // default exports must be explicitly re-exported (#328) + if (name !== 'default') { + for (let dep of this.dependencies.values()) { + let innerMap = dep() - // todo: report as unresolved? - if (!innerMap) continue + // todo: report as unresolved? + if (!innerMap) continue - if (innerMap.has(name)) return true + if (innerMap.has(name)) return true + } } return false @@ -264,18 +267,22 @@ export default class ExportMap { return deep } - for (let dep of this.dependencies.values()) { - let innerMap = dep() - // todo: report as unresolved? - if (!innerMap) continue - // safeguard against cycles - if (innerMap.path === this.path) continue + // default exports must be explicitly re-exported (#328) + if (name !== 'default') { + for (let dep of this.dependencies.values()) { + let innerMap = dep() + // todo: report as unresolved? + if (!innerMap) continue + + // safeguard against cycles + if (innerMap.path === this.path) continue - let innerValue = innerMap.hasDeep(name) - if (innerValue.found) { - innerValue.path.unshift(this) - return innerValue + let innerValue = innerMap.hasDeep(name) + if (innerValue.found) { + innerValue.path.unshift(this) + return innerValue + } } } @@ -298,16 +305,19 @@ export default class ExportMap { return imported.get(local) } - for (let dep of this.dependencies.values()) { - let innerMap = dep() - // todo: report as unresolved? - if (!innerMap) continue + // default exports must be explicitly re-exported (#328) + if (name !== 'default') { + for (let dep of this.dependencies.values()) { + let innerMap = dep() + // todo: report as unresolved? + if (!innerMap) continue - // safeguard against cycles - if (innerMap.path === this.path) continue + // safeguard against cycles + if (innerMap.path === this.path) continue - let innerValue = innerMap.get(name) - if (innerValue !== undefined) return innerValue + let innerValue = innerMap.get(name) + if (innerValue !== undefined) return innerValue + } } return undefined @@ -321,7 +331,7 @@ export default class ExportMap { callback.call(thisArg, getImport().get(local), name, this)) this.dependencies.forEach(dep => dep().forEach((v, n) => - callback.call(thisArg, v, n, this))) + n !== 'default' && callback.call(thisArg, v, n, this))) } // todo: keys, values, entries? diff --git a/src/rules/export.js b/src/rules/export.js index ba1206430..9263b0ae3 100644 --- a/src/rules/export.js +++ b/src/rules/export.js @@ -50,7 +50,10 @@ module.exports = function (context) { return } let any = false - remoteExports.forEach((v, name) => (any = true) && addNamed(name, node)) + remoteExports.forEach((v, name) => + name !== 'default' && + (any = true) && // poor man's filter + addNamed(name, node)) if (!any) { context.report(node.source, diff --git a/tests/files/deep-es7/b.js b/tests/files/deep-es7/b.js index bd0fb92a6..68c9fd272 100644 --- a/tests/files/deep-es7/b.js +++ b/tests/files/deep-es7/b.js @@ -1 +1,2 @@ export * as c from './c' +export default 'b' diff --git a/tests/files/deep/b.js b/tests/files/deep/b.js index 0aa1bba58..10eab3796 100644 --- a/tests/files/deep/b.js +++ b/tests/files/deep/b.js @@ -1,2 +1,3 @@ import * as c from './c' -export { c } \ No newline at end of file +export { c } +export default 'b' diff --git a/tests/files/re-export.js b/tests/files/re-export.js index 74531b604..eba96a8e4 100644 --- a/tests/files/re-export.js +++ b/tests/files/re-export.js @@ -1,3 +1,6 @@ export const c = 'foo' export * from './named-exports' + +// #328: this exports only 'foo', not the default. +export * from './bar' diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index 9153f49c5..0e8205e4a 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -115,5 +115,11 @@ ruleTester.run('default', rule, { parser: 'babel-eslint', errors: ['No default export found in module.'], }), + + // #328: * exports do not include default + test({ + code: 'import barDefault from "./re-export"', + errors: [`No default export found in module.`], + }), ], }) diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index 0a559241a..3f101291c 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -21,6 +21,9 @@ ruleTester.run('export', rule, { test({ code: 'export { bar }; export * from "./export-all"' }), test({ code: 'export * from "./export-all"' }), test({ code: 'export * from "./does-not-exist"' }), + + // #328: "export * from" does not export a default + test({ code: 'export default foo; export * from "./bar"' }), ], invalid: [ @@ -29,10 +32,6 @@ ruleTester.run('export', rule, { code: 'export default foo; export default bar', errors: ['Multiple default exports.', 'Multiple default exports.'], }), - test({ - code: 'export default foo; export * from "./default-export"', - errors: ['Multiple default exports.', 'Multiple default exports.'], - }), test({ code: 'export default function foo() {}; ' + 'export default function bar() {}', @@ -99,5 +98,11 @@ ruleTester.run('export', rule, { 'Multiple exports of name \'bar\'.'], }), + + // #328: "export * from" does not export a default + test({ + code: 'export * from "./default-export"', + errors: [`No named exports found in module './default-export'.`], + }), ], }) diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 590a62e8e..caaed2a89 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -217,5 +217,11 @@ ruleTester.run('named', rule, { // todo: better error message errors: ["common not found via re-export-default.js -> common.js"], }), + + // #328: * exports do not include default + test({ + code: 'import { default as barDefault } from "./re-export"', + errors: [`default not found in './re-export'`], + }), ], }) diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index 1651a6949..43e0ea7d4 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -77,6 +77,7 @@ const valid = [ // names.default is valid export test({ code: "import * as names from './default-export';" }), + test({ code: "import * as names from './default-export'; console.log(names.default)" }), test({ code: 'export * as names from "./default-export"', parser: 'babel-eslint', @@ -164,6 +165,12 @@ const invalid = [ errors: [error('c', 'names')], }), + // #328: * exports do not include default + test({ + code: 'import * as ree from "./re-export"; console.log(ree.default)', + errors: [`'default' not found in imported namespace 'ree'.`], + }), + ] /////////////////////// @@ -177,6 +184,9 @@ const invalid = [ test({ parser, code: `import * as a from "./${folder}/a"; var {b:{c:{d:{e}}}} = a` }), test({ parser, code: `import { b } from "./${folder}/a"; var {c:{d:{e}}} = b` })) + // deep namespaces should include explicitly exported defaults + test({ parser, code: `import * as a from "./${folder}/a"; console.log(a.b.default)` }), + invalid.push( test({ parser,