diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe937a9e..0de90cd91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - Added an `optionalDependencies` option to [`no-extraneous-dependencies`] to allow/forbid optional dependencies ([#266], thanks [@jfmengels]). - Added `newlines-between` option to [`order`] rule ([#298], thanks [@singles]) - add [`no-mutable-exports`] rule ([#290], thanks [@josh]) +- [`import/extensions` setting]: a whitelist of file extensions to parse as modules + and search for `export`s. If unspecified, all extensions are considered valid (for now). + In v2, this will likely default to `['.js', MODULE_EXT]`,. ([#297], to fix [#267]) ### Fixed - [`extensions`]: fallback to source path for extension enforcement if imported @@ -180,6 +183,7 @@ for info on changes for earlier releases. [`import/cache` setting]: ./README.md#importcache [`import/ignore` setting]: ./README.md#importignore +[`import/extensions` setting]: ./README.md#importextensions [`no-unresolved`]: ./docs/rules/no-unresolved.md [`no-deprecated`]: ./docs/rules/no-deprecated.md @@ -198,6 +202,10 @@ for info on changes for earlier releases. [`no-mutable-exports`]: ./docs/rules/no-mutable-exports.md [#298]: https://github.com/benmosher/eslint-plugin-import/pull/298 +<<<<<<< 7775f344b90aa44c446d596e4e137d6a725bf5e8 +======= +[#297]: https://github.com/benmosher/eslint-plugin-import/pull/297 +>>>>>>> default to all extensions valid to avoid breaking change until semver-next [#296]: https://github.com/benmosher/eslint-plugin-import/pull/296 [#290]: https://github.com/benmosher/eslint-plugin-import/pull/290 [#289]: https://github.com/benmosher/eslint-plugin-import/pull/289 @@ -221,6 +229,7 @@ for info on changes for earlier releases. [#286]: https://github.com/benmosher/eslint-plugin-import/issues/286 [#281]: https://github.com/benmosher/eslint-plugin-import/issues/281 [#272]: https://github.com/benmosher/eslint-plugin-import/issues/272 +[#267]: https://github.com/benmosher/eslint-plugin-import/issues/267 [#266]: https://github.com/benmosher/eslint-plugin-import/issues/266 [#216]: https://github.com/benmosher/eslint-plugin-import/issues/216 [#214]: https://github.com/benmosher/eslint-plugin-import/issues/214 diff --git a/README.md b/README.md index 348d87d29..d36b064e0 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,21 @@ If you are interesting in writing a resolver, see the [spec](./resolvers/README. You may set the following settings in your `.eslintrc`: +#### `import/extensions` + +A whitelist of file extensions that will be parsed as modules and inspected for +`export`s. + +This will default to `['.js']` in the next major revision of this plugin, unless +you are using the `react` shared config, in which case it is specified as `['.js', '.jsx']`. + +Note that this is different from (and likely a subset of) any `import/resolver` +extensions settings, which may include `.json`, `.coffee`, etc. which will still +factor into the `no-unresolved` rule. + +Also, `import/ignore` patterns will overrule this whitelist, so `node_modules` that +end in `.js` will still be ignored by default. + #### `import/ignore` A list of regex strings that, if matched by a path, will diff --git a/config/react.js b/config/react.js new file mode 100644 index 000000000..c8bd7ade9 --- /dev/null +++ b/config/react.js @@ -0,0 +1,8 @@ +/** + * - adds `.jsx` as an extension + */ +module.exports = { + settings: { + 'import/extensions': ['.js', '.jsx'], + }, +} diff --git a/src/core/ignore.js b/src/core/ignore.js index ec992ca10..50fdc76d7 100644 --- a/src/core/ignore.js +++ b/src/core/ignore.js @@ -1,9 +1,33 @@ +import { extname } from 'path' +import Set from 'es6-set' + +// one-shot memoized +let cachedSet, lastSettings +function validExtensions({ settings }) { + if (cachedSet && settings === lastSettings) { + return cachedSet + } + + // todo: add 'mjs'? + lastSettings = settings + // breaking: default to '.js' + // cachedSet = new Set(settings['import/extensions'] || [ '.js' ]) + cachedSet = 'import/extensions' in settings + ? new Set(settings['import/extensions']) + : { has: () => true } // the set of all elements + + return cachedSet +} + export default function ignore(path, context) { // ignore node_modules by default - var ignoreStrings = context.settings['import/ignore'] + const ignoreStrings = context.settings['import/ignore'] ? [].concat(context.settings['import/ignore']) : ['node_modules'] + // check extension whitelist first (cheap) + if (!validExtensions(context).has(extname(path))) return true + if (ignoreStrings.length === 0) return false for (var i = 0; i < ignoreStrings.length; i++) { diff --git a/src/index.js b/src/index.js index c9c01b111..0d1eb15cf 100644 --- a/src/index.js +++ b/src/index.js @@ -28,6 +28,9 @@ export const configs = { 'errors': require('../config/errors'), 'warnings': require('../config/warnings'), + // useful stuff for folks using React + 'react': require('../config/react'), + // shhhh... work in progress "secret" rules 'stage-0': require('../config/stage-0'), } diff --git a/tests/files/data.json b/tests/files/data.json new file mode 100644 index 000000000..ca747236d --- /dev/null +++ b/tests/files/data.json @@ -0,0 +1 @@ +{ "foo": "bar" } \ No newline at end of file diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 4ad6a6cbc..590a62e8e 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -1,4 +1,4 @@ -import { test } from '../utils' +import { test, SYNTAX_CASES } from '../utils' import { RuleTester } from 'eslint' var ruleTester = new RuleTester() @@ -96,6 +96,7 @@ ruleTester.run('named', rule, { settings: { 'import/ignore': ['common'] }, }), + ...SYNTAX_CASES, ], invalid: [ @@ -166,6 +167,7 @@ ruleTester.run('named', rule, { // parse errors test({ code: "import { a } from './test.coffee';", + settings: { 'import/extensions': ['.js', '.coffee'] }, errors: [{ message: "Parse errors in imported module './test.coffee': Unexpected token > (1:20)", type: 'Literal', diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index 0e49bd8b2..1651a6949 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -1,4 +1,4 @@ -var test = require('../utils').test +import { test, SYNTAX_CASES } from '../utils' import { RuleTester } from 'eslint' var ruleTester = new RuleTester({ env: { es6: true }}) @@ -86,6 +86,7 @@ const valid = [ parser: 'babel-eslint', }), + ...SYNTAX_CASES, ] const invalid = [ diff --git a/tests/src/rules/no-named-as-default.js b/tests/src/rules/no-named-as-default.js index 4b12c56be..e3a843378 100644 --- a/tests/src/rules/no-named-as-default.js +++ b/tests/src/rules/no-named-as-default.js @@ -1,4 +1,4 @@ -import { test } from '../utils' +import { test, SYNTAX_CASES } from '../utils' import { RuleTester } from 'eslint' const ruleTester = new RuleTester() @@ -16,6 +16,8 @@ ruleTester.run('no-named-as-default', rule, { , parser: 'babel-eslint' }), test({ code: 'export bar from "./bar";' , parser: 'babel-eslint' }), + + ...SYNTAX_CASES, ], invalid: [ diff --git a/tests/src/utils.js b/tests/src/utils.js index 301b9ac1d..db5fed076 100644 --- a/tests/src/utils.js +++ b/tests/src/utils.js @@ -57,4 +57,10 @@ export const SYNTAX_CASES = [ test({ code: 'export default x' }), test({ code: 'export default class x {}' }), + // issue #267: parser whitelist + test({ + code: 'import json from "./data.json"', + settings: { 'import/extensions': ['.js'] }, // breaking: remove for v2 + }), + ]