Skip to content

Commit 6731139

Browse files
committed
Move from eslint-loader to eslint-webpack-plugin, close #847
1 parent 972c882 commit 6731139

File tree

12 files changed

+235
-360
lines changed

12 files changed

+235
-360
lines changed

index.js

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,47 +1234,33 @@ class Encore {
12341234
}
12351235

12361236
/**
1237-
* If enabled, the eslint-loader is enabled.
1237+
* If enabled, the eslint-webpack-plugin is enabled.
12381238
*
1239-
* https://github.com/MoOx/eslint-loader
1239+
* https://github.com/webpack-contrib/eslint-webpack-plugin
12401240
*
12411241
* ```
1242-
* // enables the eslint loaded using the default eslint configuration.
1243-
* Encore.enableEslintLoader();
1242+
* // enables the eslint plugin using the default eslint configuration.
1243+
* Encore.enableEslintPlugin();
12441244
*
12451245
* // You can also pass in an object of options
1246-
* // that will be passed on to the eslint-loader
1247-
* Encore.enableEslintLoader({
1246+
* // that will be passed on to the eslint-webpack-plugin
1247+
* Encore.enableEslintPlugin({
12481248
* emitWarning: false
12491249
* });
12501250
*
12511251
* // For a more advanced usage you can pass in a callback
1252-
* // https://github.com/webpack-contrib/eslint-loader#options
1253-
* Encore.enableEslintLoader((options) => {
1254-
* options.extends = 'airbnb';
1252+
* // https://github.com/webpack-contrib/eslint-webpack-plugin#options
1253+
* Encore.enableEslintPlugin((options) => {
1254+
* options.extensions.push('vue'); // to lint Vue files
12551255
* options.emitWarning = false;
12561256
* });
1257-
*
1258-
* // configure Encore-specific options
1259-
* Encore.enableEslintLoader(() => {}, {
1260-
* // set optional Encore-specific options, for instance:
1261-
*
1262-
* // lint `.vue` files
1263-
* lintVue: true
1264-
* });
12651257
* ```
12661258
*
1267-
* Supported options:
1268-
* * {boolean} lintVue (default=false)
1269-
* Configure the loader to lint `.vue` files
1270-
* ```
1271-
*
1272-
* @param {string|object|function} eslintLoaderOptionsOrCallback
1273-
* @param {object} encoreOptions
1259+
* @param {string|object|function} eslintPluginOptionsOrCallback
12741260
* @returns {Encore}
12751261
*/
1276-
enableEslintLoader(eslintLoaderOptionsOrCallback = () => {}, encoreOptions = {}) {
1277-
webpackConfig.enableEslintLoader(eslintLoaderOptionsOrCallback, encoreOptions);
1262+
enableEslintPlugin(eslintPluginOptionsOrCallback = () => {}) {
1263+
webpackConfig.enableEslintPlugin(eslintPluginOptionsOrCallback);
12781264

12791265
return this;
12801266
}
@@ -1491,7 +1477,7 @@ class Encore {
14911477
*
14921478
* ```
14931479
* Encore
1494-
* .enableEslintLoader()
1480+
* .enableEslintPlugin()
14951481
* .enableVueLoader()
14961482
* .configureLoaderRule('eslint', (loaderRule) => {
14971483
* loaderRule.test = /\.(jsx?|vue)/;

lib/WebpackConfig.js

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class WebpackConfig {
119119
this.useReact = false;
120120
this.usePreact = false;
121121
this.useVueLoader = false;
122-
this.useEslintLoader = false;
122+
this.useEslintPlugin = false;
123123
this.useTypeScriptLoader = false;
124124
this.useForkedTypeScriptTypeChecking = false;
125125
this.useBabelTypeScriptPreset = false;
@@ -150,9 +150,6 @@ class WebpackConfig {
150150
version: null,
151151
runtimeCompilerBuild: null
152152
};
153-
this.eslintOptions = {
154-
lintVue: false,
155-
};
156153
this.persistentCacheBuildDependencies = {};
157154

158155
// Features/Loaders options callbacks
@@ -170,7 +167,7 @@ class WebpackConfig {
170167
this.watchOptionsConfigurationCallback = () => {};
171168
this.devServerOptionsConfigurationCallback = () => {};
172169
this.vueLoaderOptionsCallback = () => {};
173-
this.eslintLoaderOptionsCallback = () => {};
170+
this.eslintPluginOptionsCallback = () => {};
174171
this.tsConfigurationCallback = () => {};
175172
this.handlebarsConfigurationCallback = () => {};
176173
this.miniCssExtractLoaderConfigurationCallback = () => {};
@@ -184,7 +181,6 @@ class WebpackConfig {
184181
less: () => {},
185182
stylus: () => {},
186183
vue: () => {},
187-
eslint: () => {},
188184
typescript: () => {},
189185
handlebars: () => {},
190186
};
@@ -802,33 +798,18 @@ class WebpackConfig {
802798
}
803799
}
804800

805-
enableEslintLoader(eslintLoaderOptionsOrCallback = () => {}, eslintOptions = {}) {
806-
this.useEslintLoader = true;
807-
808-
if (typeof eslintLoaderOptionsOrCallback === 'function') {
809-
this.eslintLoaderOptionsCallback = eslintLoaderOptionsOrCallback;
810-
} else if (typeof eslintLoaderOptionsOrCallback === 'string') {
811-
logger.deprecation('enableEslintLoader: Extending from a configuration is deprecated, please use a configuration file instead. See https://eslint.org/docs/user-guide/configuring for more information.');
801+
enableEslintPlugin(eslintPluginOptionsOrCallback = () => {}) {
802+
this.useEslintPlugin = true;
812803

813-
this.eslintLoaderOptionsCallback = (options) => {
814-
options.extends = eslintLoaderOptionsOrCallback;
815-
};
816-
} else if (typeof eslintLoaderOptionsOrCallback === 'object') {
817-
this.eslintLoaderOptionsCallback = (options) => {
818-
Object.assign(options, eslintLoaderOptionsOrCallback);
804+
if (typeof eslintPluginOptionsOrCallback === 'function') {
805+
this.eslintPluginOptionsCallback = eslintPluginOptionsOrCallback;
806+
} else if (typeof eslintPluginOptionsOrCallback === 'object') {
807+
this.eslintPluginOptionsCallback = (options) => {
808+
Object.assign(options, eslintPluginOptionsOrCallback);
819809
};
820810
} else {
821-
throw new Error('Argument 1 to enableEslintLoader() must be either a string, object or callback function.');
822-
}
823-
824-
// Check allowed keys
825-
for (const key of Object.keys(eslintOptions)) {
826-
if (!(key in this.eslintOptions)) {
827-
throw new Error(`"${key}" is not a valid key for enableEslintLoader(). Valid keys: ${Object.keys(this.eslintOptions).join(', ')}.`);
828-
}
811+
throw new Error('Argument 1 to enableEslintPlugin() must be either an object or callback function.');
829812
}
830-
831-
this.eslintOptions = eslintOptions;
832813
}
833814

834815
enableBuildNotifications(enabled = true, notifierPluginOptionsCallback = () => {}) {

lib/config-generator.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ const babelLoaderUtil = require('./loaders/babel');
2222
const tsLoaderUtil = require('./loaders/typescript');
2323
const vueLoaderUtil = require('./loaders/vue');
2424
const handlebarsLoaderUtil = require('./loaders/handlebars');
25-
const eslintLoaderUtil = require('./loaders/eslint');
2625
// plugins utils
2726
const miniCssExtractPluginUtil = require('./plugins/mini-css-extract');
2827
const deleteUnusedEntriesPluginUtil = require('./plugins/delete-unused-entries');
@@ -37,6 +36,7 @@ const vuePluginUtil = require('./plugins/vue');
3736
const friendlyErrorPluginUtil = require('./plugins/friendly-errors');
3837
const assetOutputDisplay = require('./plugins/asset-output-display');
3938
const notifierPluginUtil = require('./plugins/notifier');
39+
const eslintPluginUtil = require('./plugins/eslint');
4040
const PluginPriorities = require('./plugins/plugin-priorities');
4141
const stimulusBridge = require('./plugins/stimulus-bridge');
4242
const applyOptionsCallback = require('./utils/apply-options-callback');
@@ -400,16 +400,6 @@ class ConfigGenerator {
400400
}));
401401
}
402402

403-
if (this.webpackConfig.useEslintLoader) {
404-
rules.push(applyRuleConfigurationCallback('eslint', {
405-
test: eslintLoaderUtil.getTest(this.webpackConfig),
406-
loader: require.resolve('eslint-loader'), //eslint-disable-line node/no-unpublished-require
407-
exclude: /node_modules/,
408-
enforce: 'pre',
409-
options: eslintLoaderUtil.getOptions(this.webpackConfig)
410-
}));
411-
}
412-
413403
if (this.webpackConfig.useTypeScriptLoader) {
414404
rules.push(applyRuleConfigurationCallback('typescript', {
415405
test: /\.tsx?$/,
@@ -457,6 +447,8 @@ class ConfigGenerator {
457447

458448
vuePluginUtil(plugins, this.webpackConfig);
459449

450+
eslintPluginUtil(plugins, this.webpackConfig);
451+
460452
if (!this.webpackConfig.runtimeConfig.outputJson) {
461453
const friendlyErrorPlugin = friendlyErrorPluginUtil(this.webpackConfig);
462454
plugins.push({

lib/features.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ const features = {
118118
description: 'use Vue with JSX support'
119119
},
120120
eslint: {
121-
method: 'enableEslintLoader()',
121+
method: 'enableEslintPlugin()',
122122
// eslint is needed so the end-user can do things
123123
packages: [
124124
{ name: 'eslint' },
125-
{ name: 'eslint-loader', enforce_version: true },
125+
{ name: 'eslint-webpack-plugin', enforce_version: true },
126126
],
127127
description: 'Enable ESLint checks'
128128
},

lib/loaders/eslint.js renamed to lib/plugins/eslint.js

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
'use strict';
1111

1212
const WebpackConfig = require('../WebpackConfig'); //eslint-disable-line no-unused-vars
13-
const loaderFeatures = require('../features');
13+
const EslintPlugin = require('eslint-webpack-plugin'); //eslint-disable-line node/no-unpublished-require
1414
const applyOptionsCallback = require('../utils/apply-options-callback');
15+
const pluginFeatures = require('../features');
1516

1617
function isMissingConfigError(e) {
1718
if (!e.message || !e.message.includes('No ESLint configuration found')) {
@@ -21,27 +22,32 @@ function isMissingConfigError(e) {
2122
return true;
2223
}
2324

24-
module.exports = {
25-
/**
26-
* @param {WebpackConfig} webpackConfig
27-
* @return {Object} of options to use for eslint-loader options.
28-
*/
29-
getOptions(webpackConfig) {
30-
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('eslint');
31-
32-
const eslint = require('eslint'); // eslint-disable-line node/no-unpublished-require
33-
const engine = new eslint.CLIEngine({
25+
/**
26+
* Support for ESLint.
27+
*
28+
* @param {Array} plugins
29+
* @param {WebpackConfig} webpackConfig
30+
* @return {void}
31+
*/
32+
module.exports = function(plugins, webpackConfig) {
33+
if (webpackConfig.useEslintPlugin) {
34+
pluginFeatures.ensurePackagesExistAndAreCorrectVersion('eslint');
35+
36+
const { ESLint } = require('eslint'); // eslint-disable-line node/no-unpublished-require
37+
const eslint = new ESLint({
3438
cwd: webpackConfig.runtimeConfig.context,
3539
});
3640

3741
try {
38-
engine.getConfigForFile('webpack.config.js');
42+
(async function() {
43+
await eslint.calculateConfigForFile('webpack.config.js');
44+
})();
3945
} catch (e) {
4046
if (isMissingConfigError(e)) {
4147
const chalk = require('chalk');
4248
const packageHelper = require('../package-helper');
4349

44-
const message = `No ESLint configration has been found.
50+
const message = `No ESLint configuration has been found.
4551
4652
${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.
4753
@@ -63,25 +69,15 @@ Install ${chalk.yellow('babel-eslint')} to prevent potential parsing issues: ${p
6369
throw e;
6470
}
6571

66-
const eslintLoaderOptions = {
67-
cache: true,
68-
emitWarning: true
72+
const eslintPluginOptions = {
73+
emitWarning: true,
74+
extensions: ['js', 'jsx'],
6975
};
7076

71-
return applyOptionsCallback(webpackConfig.eslintLoaderOptionsCallback, eslintLoaderOptions);
72-
},
73-
74-
/**
75-
* @param {WebpackConfig} webpackConfig
76-
* @return {RegExp} to use for eslint-loader `test` rule
77-
*/
78-
getTest(webpackConfig) {
79-
const extensions = ['jsx?'];
80-
81-
if (webpackConfig.eslintOptions.lintVue) {
82-
extensions.push('vue');
83-
}
84-
85-
return new RegExp(`\\.(${extensions.join('|')})$`);
77+
plugins.push({
78+
plugin: new EslintPlugin(
79+
applyOptionsCallback(webpackConfig.eslintPluginOptionsCallback, eslintPluginOptions)
80+
),
81+
});
8682
}
8783
};

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@
6666
"chai-fs": "^2.0.0",
6767
"chai-subset": "^1.6.0",
6868
"core-js": "^3.0.0",
69-
"eslint": "^6.7.0 || ^7.0.0",
70-
"eslint-loader": "^4.0.0",
69+
"eslint": "^7.0.0",
7170
"eslint-plugin-header": "^3.0.0",
7271
"eslint-plugin-import": "^2.8.0",
7372
"eslint-plugin-node": "^11.1.0",
73+
"eslint-webpack-plugin": "^2.5.4",
7474
"file-loader": "^6.0.0",
7575
"fork-ts-checker-webpack-plugin": "^5.0.0 || ^6.0.0",
7676
"fs-extra": "^9.0.0",

test/WebpackConfig.js

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,29 +1437,29 @@ describe('WebpackConfig object', () => {
14371437
const config = createConfig();
14381438
const callback = (loader) => {};
14391439

1440-
expect(config.loaderConfigurationCallbacks['eslint']).to.not.equal(callback);
1440+
expect(config.loaderConfigurationCallbacks['vue']).to.not.equal(callback);
14411441

1442-
config.configureLoaderRule('eslint', callback);
1443-
expect(config.loaderConfigurationCallbacks['eslint']).to.equal(callback);
1442+
config.configureLoaderRule('vue', callback);
1443+
expect(config.loaderConfigurationCallbacks['vue']).to.equal(callback);
14441444
});
14451445

14461446
it('Call method with a not supported loader', () => {
14471447
const config = createConfig();
14481448

14491449
expect(() => {
14501450
config.configureLoaderRule('reason');
1451-
}).to.throw('Loader "reason" is not configurable. Valid loaders are "javascript", "css", "images", "fonts", "sass", "less", "stylus", "vue", "eslint", "typescript", "handlebars" and the aliases "js", "ts", "scss".');
1451+
}).to.throw('Loader "reason" is not configurable. Valid loaders are "javascript", "css", "images", "fonts", "sass", "less", "stylus", "vue", "typescript", "handlebars" and the aliases "js", "ts", "scss".');
14521452
});
14531453

14541454
it('Call method with not a valid callback', () => {
14551455
const config = createConfig();
14561456

14571457
expect(() => {
1458-
config.configureLoaderRule('eslint');
1458+
config.configureLoaderRule('vue');
14591459
}).to.throw('Argument 2 to configureLoaderRule() must be a callback function.');
14601460

14611461
expect(() => {
1462-
config.configureLoaderRule('eslint', {});
1462+
config.configureLoaderRule('vue', {});
14631463
}).to.throw('Argument 2 to configureLoaderRule() must be a callback function.');
14641464
});
14651465
});
@@ -1501,16 +1501,4 @@ describe('WebpackConfig object', () => {
15011501
expect(() => config.enableIntegrityHashes(true, ['sha1', 'foo', 'sha256'])).to.throw('Invalid hash algorithm "foo"');
15021502
});
15031503
});
1504-
1505-
describe('enableEslintLoader', () => {
1506-
it('Should validate Encore-specific options', () => {
1507-
const config = createConfig();
1508-
1509-
expect(() => {
1510-
config.enableEslintLoader(() => {}, {
1511-
notExisting: false,
1512-
});
1513-
}).to.throw('"notExisting" is not a valid key for enableEslintLoader(). Valid keys: lintVue.');
1514-
});
1515-
});
15161504
});

0 commit comments

Comments
 (0)