diff --git a/CHANGELOG.md b/CHANGELOG.md index 15a5bec7e..4698b4e02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - `recommended` shared config. Roughly `errors` and `warnings` mixed together, with some `parserOptions` in the mix. ([#402]) - `react` shared config: added `jsx: true` to `parserOptions.ecmaFeatures`. +- Added [`no-webpack-loader-syntax`] rule: forbid custom Webpack loader syntax in imports. ([#586], thanks [@fson]!) ### Breaking - [`import/extensions` setting] defaults to `['.js']`. ([#306]) @@ -331,7 +332,9 @@ for info on changes for earlier releases. [`max-dependencies`]: ./docs/rules/max-dependencies.md [`no-internal-modules`]: ./docs/rules/no-internal-modules.md [`no-dynamic-require`]: ./docs/rules/no-dynamic-require.md +[`no-webpack-loader-syntax`]: ./docs/rules/no-webpack-loader-syntax.md +[#586]: https://github.com/benmosher/eslint-plugin-import/pull/586 [#568]: https://github.com/benmosher/eslint-plugin-import/pull/568 [#555]: https://github.com/benmosher/eslint-plugin-import/pull/555 [#538]: https://github.com/benmosher/eslint-plugin-import/pull/538 @@ -488,3 +491,4 @@ for info on changes for earlier releases. [@spalger]: https://github.com/spalger [@preco21]: https://github.com/preco21 [@skyrpex]: https://github.com/skyrpex +[@fson]: https://github.com/fson diff --git a/README.md b/README.md index a4bcad5b5..e7977fd1b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a * Forbid import of modules using absolute paths ([`no-absolute-path`]) * Forbid `require()` calls with expressions ([`no-dynamic-require`]) * Prevent importing the submodules of other modules ([`no-internal-modules`]) +* Forbid Webpack loader syntax in imports ([`no-webpack-loader-syntax`]) [`no-unresolved`]: ./docs/rules/no-unresolved.md [`named`]: ./docs/rules/named.md @@ -31,6 +32,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a [`no-absolute-path`]: ./docs/rules/no-absolute-path.md [`no-dynamic-require`]: ./docs/rules/no-dynamic-require.md [`no-internal-modules`]: ./docs/rules/no-internal-modules.md +[`no-webpack-loader-syntax`]: ./docs/rules/no-webpack-loader-syntax.md **Helpful warnings:** diff --git a/docs/rules/no-webpack-loader-syntax.md b/docs/rules/no-webpack-loader-syntax.md new file mode 100644 index 000000000..a26012737 --- /dev/null +++ b/docs/rules/no-webpack-loader-syntax.md @@ -0,0 +1,36 @@ +# no-webpack-loader-syntax + +Forbid Webpack loader syntax in imports. + +[Webpack](http://webpack.github.io) allows specifying the [loaders](http://webpack.github.io/docs/loaders.html) to use in the import source string using a special syntax like this: +```js +var moduleWithOneLoader = require("my-loader!./my-awesome-module"); +``` + +This syntax is non-standard, so it couples the code to Webpack. The recommended way to specify Webpack loader configuration is in a [Webpack configuration file](http://webpack.github.io/docs/loaders.html#loaders-by-config). + +## Rule Details + +### Fail + +```js +import myModule from 'my-loader!my-module'; +import theme from 'style!css!./theme.css'; + +var myModule = require('my-loader!./my-module'); +var theme = require('style!css!./theme.css'); +``` + +### Pass + +```js +import myModule from 'my-module'; +import theme from './theme.css'; + +var myModule = require('my-module'); +var theme = require('./theme.css'); +``` + +## When Not To Use It + +If you have a project that doesn't use Webpack you can safely disable this rule. diff --git a/src/index.js b/src/index.js index bae40374a..ecdb30cbf 100644 --- a/src/index.js +++ b/src/index.js @@ -21,6 +21,7 @@ export const rules = { 'no-extraneous-dependencies': require('./rules/no-extraneous-dependencies'), 'no-absolute-path': require('./rules/no-absolute-path'), 'no-nodejs-modules': require('./rules/no-nodejs-modules'), + 'no-webpack-loader-syntax': require('./rules/no-webpack-loader-syntax'), 'order': require('./rules/order'), 'newline-after-import': require('./rules/newline-after-import'), 'prefer-default-export': require('./rules/prefer-default-export'), diff --git a/src/rules/no-webpack-loader-syntax.js b/src/rules/no-webpack-loader-syntax.js new file mode 100644 index 000000000..3d9ba0034 --- /dev/null +++ b/src/rules/no-webpack-loader-syntax.js @@ -0,0 +1,28 @@ +import isStaticRequire from '../core/staticRequire' + +function reportIfNonStandard(context, node, name) { + if (name.indexOf('!') !== -1) { + context.report(node, `Unexpected '!' in '${name}'. ` + + 'Do not use import syntax to configure webpack loaders.' + ) + } +} + +module.exports = { + meta: { + docs: {}, + }, + + create: function (context) { + return { + ImportDeclaration: function handleImports(node) { + reportIfNonStandard(context, node, node.source.value) + }, + CallExpression: function handleRequires(node) { + if (isStaticRequire(node)) { + reportIfNonStandard(context, node, node.arguments[0].value) + } + }, + } + }, +} diff --git a/tests/src/rules/no-webpack-loader-syntax.js b/tests/src/rules/no-webpack-loader-syntax.js new file mode 100644 index 000000000..23a1190fb --- /dev/null +++ b/tests/src/rules/no-webpack-loader-syntax.js @@ -0,0 +1,74 @@ +import { test } from '../utils' + +import { RuleTester } from 'eslint' + +const ruleTester = new RuleTester() + , rule = require('rules/no-webpack-loader-syntax') + +const message = 'Do not use import syntax to configure webpack loaders.' + +ruleTester.run('no-webpack-loader-syntax', rule, { + valid: [ + test({ code: 'import _ from "lodash"'}), + test({ code: 'import find from "lodash.find"'}), + test({ code: 'import foo from "./foo.css"'}), + test({ code: 'import data from "@scope/my-package/data.json"'}), + test({ code: 'var _ = require("lodash")'}), + test({ code: 'var find = require("lodash.find")'}), + test({ code: 'var foo = require("./foo")'}), + test({ code: 'var foo = require("../foo")'}), + test({ code: 'var foo = require("foo")'}), + test({ code: 'var foo = require("./")'}), + test({ code: 'var foo = require("@scope/foo")'}), + ], + invalid: [ + test({ + code: 'import _ from "babel!lodash"', + errors: [ + { message: `Unexpected '!' in 'babel!lodash'. ${message}` }, + ], + }), + test({ + code: 'import find from "-babel-loader!lodash.find"', + errors: [ + { message: `Unexpected '!' in '-babel-loader!lodash.find'. ${message}` }, + ], + }), + test({ + code: 'import foo from "style!css!./foo.css"', + errors: [ + { message: `Unexpected '!' in 'style!css!./foo.css'. ${message}` }, + ], + }), + test({ + code: 'import data from "json!@scope/my-package/data.json"', + errors: [ + { message: `Unexpected '!' in 'json!@scope/my-package/data.json'. ${message}` }, + ], + }), + test({ + code: 'var _ = require("babel!lodash")', + errors: [ + { message: `Unexpected '!' in 'babel!lodash'. ${message}` }, + ], + }), + test({ + code: 'var find = require("-babel-loader!lodash.find")', + errors: [ + { message: `Unexpected '!' in '-babel-loader!lodash.find'. ${message}` }, + ], + }), + test({ + code: 'var foo = require("style!css!./foo.css")', + errors: [ + { message: `Unexpected '!' in 'style!css!./foo.css'. ${message}` }, + ], + }), + test({ + code: 'var data = require("json!@scope/my-package/data.json")', + errors: [ + { message: `Unexpected '!' in 'json!@scope/my-package/data.json'. ${message}` }, + ], + }), + ], +})