Skip to content

Commit

Permalink
feature #985 Move from eslint-loader to eslint-webpack-plugin, close #…
Browse files Browse the repository at this point in the history
…847 (Kocal, weaverryan)

This PR was squashed before being merged into the main branch.

Discussion
----------

Move from eslint-loader to eslint-webpack-plugin, close #847

Hi 👋

This PR is a proposal for #847 which add the eslint-webpack-plugin, since the eslint-loader is deprecated.

There are a lot of changes:
- ~**BC** method `.enableEslintLoader()` has been removed, in favor of `.enableEslintPlugin()`. It accepts an object or a function.~ `.enableEslintLoader()` has not been removed, so it's not a BC anymore
- **BC** ESLint <7 support has been dropped, since the plugin requires ESLint >= 7.
- **BC, I guess** No more Encore-specific options, the only option `lintVue` was too specific and not futur proof (if you want to lint GraphQL, JSON or whatever you want, with ESLint, see last § of #574 (comment)). Extensions can easily be configured through `Encore.enableEslintPlugin(options => { options.extensions.push('vue'); })`
- `eslint.CLIEngine` is not used anymore to detect if an ESLint configuration exists, because this class is deprecated since ESLint 7.

~However, some tests are failing but I don't know why:~
```
[
  {
    moduleIdentifier: '/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js??ruleSet[1].rules[0].use[0]!/tmp/err9jr/js/eslint.js',
    moduleName: './js/eslint.js',
    message: 'Module build failed (from ../../home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js):\n' +
      "Error: Cannot find module '`@babel`/plugin-syntax-dynamic-import'\n" +
      'Require stack:\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/plugins.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/index.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/index.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/lib/config/parse-runtime.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/test/helpers/setup.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/test/bin/encore.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/lib/esm-utils.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/lib/mocha.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/lib/cli/one-and-dones.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/lib/cli/options.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/bin/mocha\n' +
      '    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:831:15)\n' +
      '    at resolve (internal/modules/cjs/helpers.js:80:19)\n' +
      '    at resolveStandardizedName (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/plugins.js:96:7)\n' +
      '    at resolvePlugin (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/plugins.js:40:10)\n' +
      '    at loadPlugin (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/plugins.js:48:20)\n' +
      '    at loadPlugin.next (<anonymous>)\n' +
      '    at createDescriptor (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-descriptors.js:179:16)\n' +
      '    at createDescriptor.next (<anonymous>)\n' +
      '    at step (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:261:32)\n' +
      '    at evaluateAsync (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:291:5)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:44:11\n' +
      '    at Array.forEach (<anonymous>)\n' +
      '    at Function.async (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:43:15)\n' +
      '    at Function.all (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:216:13)\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at createDescriptors (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-descriptors.js:134:38)\n' +
      '    at createDescriptors.next (<anonymous>)\n' +
      '    at createPluginDescriptors (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-descriptors.js:130:17)\n' +
      '    at createPluginDescriptors.next (<anonymous>)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-descriptors.js:86:32\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at Function.<anonymous> (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/gensync-utils/async.js:16:3)\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at step (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:269:25)\n' +
      '    at evaluateAsync (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:291:5)\n' +
      '    at Function.errback (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:113:7)\n' +
      '    at errback (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/gensync-utils/async.js:60:18)\n' +
      '    at async (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:188:31)\n' +
      '    at onFirstPause (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:216:13)\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at cachedFunction (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/caching.js:58:46)\n' +
      '    at cachedFunction.next (<anonymous>)\n' +
      '    at mergeChainOpts (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-chain.js:405:34)\n' +
      '    at mergeChainOpts.next (<anonymous>)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-chain.js:364:14\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at buildRootChain (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-chain.js:57:36)\n' +
      '    at buildRootChain.next (<anonymous>)\n' +
      '    at loadPrivatePartialConfig (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/partial.js:85:62)\n' +
      '    at loadPrivatePartialConfig.next (<anonymous>)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/partial.js:131:25\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at step (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:269:25)\n' +
      '    at evaluateAsync (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:291:5)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:93:9\n' +
      '    at new Promise (<anonymous>)\n' +
      '    at async (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:92:14)\n' +
      '    at Object.<anonymous> (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:155:26)\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at asyncGeneratorStep (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:3:103)\n' +
      '    at _next (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:5:194)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:5:364\n' +
      '    at new Promise (<anonymous>)\n' +
      '    at Object.<anonymous> (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:5:97)\n' +
      '    at Object.loader (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:64:18)\n' +
      '    at Object.<anonymous> (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:59:12)',
    moduleId: './js/eslint.js',
    moduleTrace: [],
    details: undefined,
    stack: 'ModuleBuildError: Module build failed (from ../../home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js):\n' +
      "Error: Cannot find module '`@babel`/plugin-syntax-dynamic-import'\n" +
      'Require stack:\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/plugins.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/index.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/index.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/lib/config/parse-runtime.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/test/helpers/setup.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/test/bin/encore.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/lib/esm-utils.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/lib/mocha.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/lib/cli/one-and-dones.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/lib/cli/options.js\n' +
      '- /home/hugo/workspace-os/webpack-encore/node_modules/mocha/bin/mocha\n' +
      '    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:831:15)\n' +
      '    at resolve (internal/modules/cjs/helpers.js:80:19)\n' +
      '    at resolveStandardizedName (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/plugins.js:96:7)\n' +
      '    at resolvePlugin (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/plugins.js:40:10)\n' +
      '    at loadPlugin (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/files/plugins.js:48:20)\n' +
      '    at loadPlugin.next (<anonymous>)\n' +
      '    at createDescriptor (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-descriptors.js:179:16)\n' +
      '    at createDescriptor.next (<anonymous>)\n' +
      '    at step (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:261:32)\n' +
      '    at evaluateAsync (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:291:5)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:44:11\n' +
      '    at Array.forEach (<anonymous>)\n' +
      '    at Function.async (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:43:15)\n' +
      '    at Function.all (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:216:13)\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at createDescriptors (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-descriptors.js:134:38)\n' +
      '    at createDescriptors.next (<anonymous>)\n' +
      '    at createPluginDescriptors (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-descriptors.js:130:17)\n' +
      '    at createPluginDescriptors.next (<anonymous>)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-descriptors.js:86:32\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at Function.<anonymous> (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/gensync-utils/async.js:16:3)\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at step (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:269:25)\n' +
      '    at evaluateAsync (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:291:5)\n' +
      '    at Function.errback (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:113:7)\n' +
      '    at errback (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/gensync-utils/async.js:60:18)\n' +
      '    at async (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:188:31)\n' +
      '    at onFirstPause (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:216:13)\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at cachedFunction (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/caching.js:58:46)\n' +
      '    at cachedFunction.next (<anonymous>)\n' +
      '    at mergeChainOpts (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-chain.js:405:34)\n' +
      '    at mergeChainOpts.next (<anonymous>)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-chain.js:364:14\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at buildRootChain (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/config-chain.js:57:36)\n' +
      '    at buildRootChain.next (<anonymous>)\n' +
      '    at loadPrivatePartialConfig (/home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/partial.js:85:62)\n' +
      '    at loadPrivatePartialConfig.next (<anonymous>)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/`@babel`/core/lib/config/partial.js:131:25\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at step (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:269:25)\n' +
      '    at evaluateAsync (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:291:5)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:93:9\n' +
      '    at new Promise (<anonymous>)\n' +
      '    at async (/home/hugo/workspace-os/webpack-encore/node_modules/gensync/index.js:92:14)\n' +
      '    at Object.<anonymous> (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:155:26)\n' +
      '    at Generator.next (<anonymous>)\n' +
      '    at asyncGeneratorStep (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:3:103)\n' +
      '    at _next (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:5:194)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:5:364\n' +
      '    at new Promise (<anonymous>)\n' +
      '    at Object.<anonymous> (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:5:97)\n' +
      '    at Object.loader (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:64:18)\n' +
      '    at Object.<anonymous> (/home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:59:12)\n' +
      '    at processResult (/home/hugo/workspace-os/webpack-encore/node_modules/webpack/lib/NormalModule.js:676:19)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/webpack/lib/NormalModule.js:778:5\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/loader-runner/lib/LoaderRunner.js:399:11\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/loader-runner/lib/LoaderRunner.js:251:18\n' +
      '    at context.callback (/home/hugo/workspace-os/webpack-encore/node_modules/loader-runner/lib/LoaderRunner.js:124:13)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/babel-loader/lib/index.js:59:103'
  },
  {
    message: 'No ESLint configuration found in /tmp/err9jr/js.',
    details: undefined,
    stack: 'Error: No ESLint configuration found in /tmp/err9jr/js.\n' +
      '    at CascadingConfigArrayFactory._finalizeConfigArray (/home/hugo/workspace-os/webpack-encore/node_modules/`@eslint`/eslintrc/lib/cascading-config-array-factory.js:508:19)\n' +
      '    at CascadingConfigArrayFactory.getConfigArrayForFile (/home/hugo/workspace-os/webpack-encore/node_modules/`@eslint`/eslintrc/lib/cascading-config-array-factory.js:299:21)\n' +
      '    at FileEnumerator._iterateFilesWithFile (/home/hugo/workspace-os/webpack-encore/node_modules/eslint/lib/cli-engine/file-enumerator.js:365:43)\n' +
      '    at FileEnumerator._iterateFiles (/home/hugo/workspace-os/webpack-encore/node_modules/eslint/lib/cli-engine/file-enumerator.js:346:25)\n' +
      '    at FileEnumerator.iterateFiles (/home/hugo/workspace-os/webpack-encore/node_modules/eslint/lib/cli-engine/file-enumerator.js:296:59)\n' +
      '    at iterateFiles.next (<anonymous>)\n' +
      '    at CLIEngine.executeOnFiles (/home/hugo/workspace-os/webpack-encore/node_modules/eslint/lib/cli-engine/cli-engine.js:765:48)\n' +
      '    at ESLint.lintFiles (/home/hugo/workspace-os/webpack-encore/node_modules/eslint/lib/eslint/eslint.js:530:23)\n' +
      '    at lintFiles (/home/hugo/workspace-os/webpack-encore/node_modules/eslint-webpack-plugin/dist/getESLint.js:57:36)\n' +
      '    at lint (/home/hugo/workspace-os/webpack-encore/node_modules/eslint-webpack-plugin/dist/linter.js:93:21)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/eslint-webpack-plugin/dist/index.js:138:11\n' +
      '    at Hook.eval [as callAsync] (eval at create (/home/hugo/workspace-os/webpack-encore/node_modules/webpack/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:16:1)\n' +
      '    at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (/home/hugo/workspace-os/webpack-encore/node_modules/webpack/node_modules/tapable/lib/Hook.js:18:14)\n' +
      '    at Compilation.finish (/home/hugo/workspace-os/webpack-encore/node_modules/webpack/lib/Compilation.js:2155:28)\n' +
      '    at /home/hugo/workspace-os/webpack-encore/node_modules/webpack/lib/Compiler.js:1075:19\n' +
      '    at processTicksAndRejections (internal/process/task_queues.js:79:11)'
  }
]
      2) Code splitting with dynamic import
 ERROR  Failed to compile with 1 errors                                                                                                                                       15:54:37

 error  in ./test_tmp/tsnh3r/js/code_splitting_dynamic_import.js                                                                                                              15:54:37
```

~The dependency ``@babel`/plugin-syntax-dynamic-import` is present in Encore's `package.json`, so I don't really understand the issue... 😬~
~Any help would be appreciated! :heart:~
**EDIT:** fixed, see #985 (comment)

WDYT? Thanks!

Commits
-------

8c33cb1 Move from eslint-loader to eslint-webpack-plugin, close #847
  • Loading branch information
weaverryan committed Jan 21, 2022
2 parents fbb0bb6 + 8c33cb1 commit ba177af
Show file tree
Hide file tree
Showing 13 changed files with 504 additions and 38 deletions.
33 changes: 33 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,7 @@ class Encore {
* Configure the loader to lint `.vue` files
* ```
*
* @deprecated Prefer using "Encore.enableEslintPlugin()" instead.
* @param {string|object|function} eslintLoaderOptionsOrCallback
* @param {object} encoreOptions
* @returns {Encore}
Expand All @@ -1279,6 +1280,38 @@ class Encore {
return this;
}

/**
* If enabled, the eslint-webpack-plugin is enabled.
*
* https://github.com/webpack-contrib/eslint-webpack-plugin
*
* ```
* // enables the eslint plugin using the default eslint configuration.
* Encore.enableEslintPlugin();
*
* // You can also pass in an object of options
* // that will be passed on to the eslint-webpack-plugin
* Encore.enableEslintPlugin({
* emitWarning: false
* });
*
* // For a more advanced usage you can pass in a callback
* // https://github.com/webpack-contrib/eslint-webpack-plugin#options
* Encore.enableEslintPlugin((options) => {
* options.extensions.push('vue'); // to lint Vue files
* options.emitWarning = false;
* });
* ```
*
* @param {string|object|function} eslintPluginOptionsOrCallback
* @returns {Encore}
*/
enableEslintPlugin(eslintPluginOptionsOrCallback = () => {}) {
webpackConfig.enableEslintPlugin(eslintPluginOptionsOrCallback);

return this;
}

/**
* If enabled, display build notifications using
* webpack-notifier.
Expand Down
26 changes: 26 additions & 0 deletions lib/WebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class WebpackConfig {
this.usePreact = false;
this.useVueLoader = false;
this.useEslintLoader = false;
this.useEslintPlugin = false;
this.useTypeScriptLoader = false;
this.useForkedTypeScriptTypeChecking = false;
this.useBabelTypeScriptPreset = false;
Expand Down Expand Up @@ -171,6 +172,7 @@ class WebpackConfig {
this.devServerOptionsConfigurationCallback = () => {};
this.vueLoaderOptionsCallback = () => {};
this.eslintLoaderOptionsCallback = () => {};
this.eslintPluginOptionsCallback = () => {};
this.tsConfigurationCallback = () => {};
this.handlebarsConfigurationCallback = () => {};
this.miniCssExtractLoaderConfigurationCallback = () => {};
Expand Down Expand Up @@ -803,6 +805,12 @@ class WebpackConfig {
}

enableEslintLoader(eslintLoaderOptionsOrCallback = () => {}, eslintOptions = {}) {
logger.deprecation('Encore.enableEslintLoader() is deprecated, please use Encore.enableEslintPlugin() instead.');

if (this.useEslintPlugin) {
throw new Error('Encore.enableEslintLoader() can not be called when Encore.enableEslintPlugin() has been called.');
}

this.useEslintLoader = true;

if (typeof eslintLoaderOptionsOrCallback === 'function') {
Expand Down Expand Up @@ -831,6 +839,24 @@ class WebpackConfig {
this.eslintOptions = eslintOptions;
}

enableEslintPlugin(eslintPluginOptionsOrCallback = () => {}) {
if (this.useEslintLoader) {
throw new Error('Encore.enableEslintPlugin() can not be called when Encore.enableEslintLoader() has been called.');
}

this.useEslintPlugin = true;

if (typeof eslintPluginOptionsOrCallback === 'function') {
this.eslintPluginOptionsCallback = eslintPluginOptionsOrCallback;
} else if (typeof eslintPluginOptionsOrCallback === 'object') {
this.eslintPluginOptionsCallback = (options) => {
Object.assign(options, eslintPluginOptionsOrCallback);
};
} else {
throw new Error('Argument 1 to enableEslintPlugin() must be either an object or callback function.');
}
}

enableBuildNotifications(enabled = true, notifierPluginOptionsCallback = () => {}) {
if (typeof notifierPluginOptionsCallback !== 'function') {
throw new Error('Argument 2 to enableBuildNotifications() must be a callback function.');
Expand Down
3 changes: 3 additions & 0 deletions lib/config-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const vuePluginUtil = require('./plugins/vue');
const friendlyErrorPluginUtil = require('./plugins/friendly-errors');
const assetOutputDisplay = require('./plugins/asset-output-display');
const notifierPluginUtil = require('./plugins/notifier');
const eslintPluginUtil = require('./plugins/eslint');
const PluginPriorities = require('./plugins/plugin-priorities');
const applyOptionsCallback = require('./utils/apply-options-callback');
const copyEntryTmpName = require('./utils/copyEntryTmpName');
Expand Down Expand Up @@ -454,6 +455,8 @@ class ConfigGenerator {

vuePluginUtil(plugins, this.webpackConfig);

eslintPluginUtil(plugins, this.webpackConfig);

if (!this.webpackConfig.runtimeConfig.outputJson) {
const friendlyErrorPlugin = friendlyErrorPluginUtil(this.webpackConfig);
plugins.push({
Expand Down
9 changes: 9 additions & 0 deletions lib/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ const features = {
],
description: 'Enable ESLint checks'
},
eslint_plugin: {
method: 'enableEslintPlugin()',
// eslint is needed so the end-user can do things
packages: [
{ name: 'eslint' },
{ name: 'eslint-webpack-plugin', enforce_version: true },
],
description: 'Enable ESLint checks'
},
copy_files: {
method: 'copyFiles()',
packages: [
Expand Down
31 changes: 7 additions & 24 deletions lib/loaders/eslint.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,12 @@

'use strict';

const forceSync = require('sync-rpc');
const hasEslintConfiguration = forceSync(require.resolve('../utils/has-eslint-configuration'));
const WebpackConfig = require('../WebpackConfig'); //eslint-disable-line no-unused-vars
const loaderFeatures = require('../features');
const applyOptionsCallback = require('../utils/apply-options-callback');

function isMissingConfigError(e) {
if (!e.message || !e.message.includes('No ESLint configuration found')) {
return false;
}

return true;
}

module.exports = {
/**
* @param {WebpackConfig} webpackConfig
Expand All @@ -29,19 +23,11 @@ module.exports = {
getOptions(webpackConfig) {
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('eslint');

const eslint = require('eslint'); // eslint-disable-line node/no-unpublished-require
const engine = new eslint.CLIEngine({
cwd: webpackConfig.runtimeConfig.context,
});

try {
engine.getConfigForFile('webpack.config.js');
} catch (e) {
if (isMissingConfigError(e)) {
const chalk = require('chalk');
const packageHelper = require('../package-helper');
if (!hasEslintConfiguration(webpackConfig)) {
const chalk = require('chalk');
const packageHelper = require('../package-helper');

const message = `No ESLint configration has been found.
const message = `No ESLint configuration has been found.
${chalk.bgGreen.black('', 'FIX', '')} Run command ${chalk.yellow('./node_modules/.bin/eslint --init')} or manually create a ${chalk.yellow('.eslintrc.js')} file at the root of your project.
Expand All @@ -57,10 +43,7 @@ module.exports = {
Install ${chalk.yellow('babel-eslint')} to prevent potential parsing issues: ${packageHelper.getInstallCommand([[{ name: 'babel-eslint' }]])}
`;
throw new Error(message);
}

throw e;
throw new Error(message);
}

const eslintLoaderOptions = {
Expand Down
64 changes: 64 additions & 0 deletions lib/plugins/eslint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* This file is part of the Symfony Webpack Encore package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

'use strict';

const forceSync = require('sync-rpc');
const hasEslintConfiguration = forceSync(require.resolve('../utils/has-eslint-configuration'));
const WebpackConfig = require('../WebpackConfig'); //eslint-disable-line no-unused-vars
const EslintPlugin = require('eslint-webpack-plugin'); //eslint-disable-line node/no-unpublished-require
const applyOptionsCallback = require('../utils/apply-options-callback');
const pluginFeatures = require('../features');

/**
* Support for ESLint.
*
* @param {Array} plugins
* @param {WebpackConfig} webpackConfig
* @return {void}
*/
module.exports = function(plugins, webpackConfig) {
if (webpackConfig.useEslintPlugin) {
pluginFeatures.ensurePackagesExistAndAreCorrectVersion('eslint_plugin');

if (!hasEslintConfiguration(webpackConfig)) {
const chalk = require('chalk');
const packageHelper = require('../package-helper');

const message = `No ESLint configuration has been found.
${chalk.bgGreen.black('', 'FIX', '')} Run command ${chalk.yellow('./node_modules/.bin/eslint --init')} or manually create a ${chalk.yellow('.eslintrc.js')} file at the root of your project.
If you prefer to create a ${chalk.yellow('.eslintrc.js')} file by yourself, here is an example to get you started:
${chalk.yellow(`// .eslintrc.js
module.exports = {
parser: 'babel-eslint',
extends: ['eslint:recommended'],
}
`)}
Install ${chalk.yellow('babel-eslint')} to prevent potential parsing issues: ${packageHelper.getInstallCommand([[{ name: 'babel-eslint' }]])}
`;
throw new Error(message);
}

const eslintPluginOptions = {
emitWarning: true,
extensions: ['js', 'jsx'],
};

plugins.push({
plugin: new EslintPlugin(
applyOptionsCallback(webpackConfig.eslintPluginOptionsCallback, eslintPluginOptions)
),
});
}
};
43 changes: 43 additions & 0 deletions lib/utils/has-eslint-configuration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* This file is part of the Symfony Webpack Encore package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

'use strict';


function isMissingConfigError(e) {
if (!e.message || !e.message.includes('No ESLint configuration found')) {
return false;
}

return true;
}

/**
* @returns {Promise<boolean>}
*/
module.exports = async function() {
/**
* @param {WebpackConfig} webpackConfig
* @returns {Promise<boolean>}
*/
return async function(webpackConfig) {
const { ESLint } = require('eslint'); // eslint-disable-line node/no-unpublished-require
const eslint = new ESLint({
cwd: webpackConfig.runtimeConfig.context,
});

try {
await eslint.calculateConfigForFile('webpack.config.js');
} catch (e) {
return !isMissingConfigError(e);
}

return true;
};
};
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"resolve-url-loader": "^3.1.2",
"semver": "^7.3.2",
"style-loader": "^2.0.0",
"sync-rpc": "^1.3.6",
"terser-webpack-plugin": "^5.1.1",
"tmp": "^0.2.1",
"webpack": "^5.35",
Expand All @@ -69,11 +70,12 @@
"chai-fs": "^2.0.0",
"chai-subset": "^1.6.0",
"core-js": "^3.0.0",
"eslint": "^6.7.0 || ^7.0.0",
"eslint": "^7.0.0",
"eslint-loader": "^4.0.0",
"eslint-plugin-header": "^3.0.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-node": "^11.1.0",
"eslint-webpack-plugin": "^2.5.4",
"file-loader": "^6.0.0",
"fork-ts-checker-webpack-plugin": "^5.0.0 || ^6.0.0",
"fs-extra": "^9.0.0",
Expand Down
19 changes: 19 additions & 0 deletions test/WebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -1512,5 +1512,24 @@ describe('WebpackConfig object', () => {
});
}).to.throw('"notExisting" is not a valid key for enableEslintLoader(). Valid keys: lintVue.');
});

it('ESLint loader can not be enabled if ESLint Webpack Plugin is already enabled', () => {
const config = createConfig();
config.enableEslintPlugin();

expect(function() {
config.enableEslintLoader();
}).to.throw('Encore.enableEslintLoader() can not be called when Encore.enableEslintPlugin() has been called.');
});
});
describe('enableEslintPlugin', () => {
it('ESLint loader can not be enabled if ESLint Webpack Plugin is already enabled', () => {
const config = createConfig();
config.enableEslintLoader();

expect(function() {
config.enableEslintPlugin();
}).to.throw('Encore.enableEslintPlugin() can not be called when Encore.enableEslintLoader() has been called.');
});
});
});
Loading

0 comments on commit ba177af

Please sign in to comment.