Skip to content

Commit 8c33cb1

Browse files
Kocalweaverryan
authored andcommitted
Move from eslint-loader to eslint-webpack-plugin, close #847
1 parent 17ed0a8 commit 8c33cb1

File tree

14 files changed

+618
-81
lines changed

14 files changed

+618
-81
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
# CHANGELOG
22

3+
## [v1.7.1](https://github.com/symfony/webpack-encore/releases/tag/v1.7.0)
4+
5+
*Jan 20th, 2022*
6+
7+
### Bug Fix
8+
9+
- [#1069](https://github.com/symfony/webpack-encore/pull/1069) - Increased webpack-cli version constraint to v.4.9.1 - *@nspyke*
10+
311
## [v1.7.0](https://github.com/symfony/webpack-encore/releases/tag/v1.7.0)
412

513
*Dec 2nd, 2021*
614

715
Dependency changes:
816

917
* Official support for `ts-loader` 8 was dropped.
10-
* Official support for `typescript` 8 was dropped and minimum increased to 4.2.2.
18+
* Official support for `typescript` 3 was dropped and minimum increased to 4.2.2.
1119
* Official support for `vue` was bumped to 3.2.14 or higher.
1220
* Official support for `vue-loader` was bumped to 16.7.0 or higher.
1321

index.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,7 @@ class Encore {
12691269
* Configure the loader to lint `.vue` files
12701270
* ```
12711271
*
1272+
* @deprecated Prefer using "Encore.enableEslintPlugin()" instead.
12721273
* @param {string|object|function} eslintLoaderOptionsOrCallback
12731274
* @param {object} encoreOptions
12741275
* @returns {Encore}
@@ -1279,6 +1280,38 @@ class Encore {
12791280
return this;
12801281
}
12811282

1283+
/**
1284+
* If enabled, the eslint-webpack-plugin is enabled.
1285+
*
1286+
* https://github.com/webpack-contrib/eslint-webpack-plugin
1287+
*
1288+
* ```
1289+
* // enables the eslint plugin using the default eslint configuration.
1290+
* Encore.enableEslintPlugin();
1291+
*
1292+
* // You can also pass in an object of options
1293+
* // that will be passed on to the eslint-webpack-plugin
1294+
* Encore.enableEslintPlugin({
1295+
* emitWarning: false
1296+
* });
1297+
*
1298+
* // For a more advanced usage you can pass in a callback
1299+
* // https://github.com/webpack-contrib/eslint-webpack-plugin#options
1300+
* Encore.enableEslintPlugin((options) => {
1301+
* options.extensions.push('vue'); // to lint Vue files
1302+
* options.emitWarning = false;
1303+
* });
1304+
* ```
1305+
*
1306+
* @param {string|object|function} eslintPluginOptionsOrCallback
1307+
* @returns {Encore}
1308+
*/
1309+
enableEslintPlugin(eslintPluginOptionsOrCallback = () => {}) {
1310+
webpackConfig.enableEslintPlugin(eslintPluginOptionsOrCallback);
1311+
1312+
return this;
1313+
}
1314+
12821315
/**
12831316
* If enabled, display build notifications using
12841317
* webpack-notifier.

lib/WebpackConfig.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class WebpackConfig {
120120
this.usePreact = false;
121121
this.useVueLoader = false;
122122
this.useEslintLoader = false;
123+
this.useEslintPlugin = false;
123124
this.useTypeScriptLoader = false;
124125
this.useForkedTypeScriptTypeChecking = false;
125126
this.useBabelTypeScriptPreset = false;
@@ -171,6 +172,7 @@ class WebpackConfig {
171172
this.devServerOptionsConfigurationCallback = () => {};
172173
this.vueLoaderOptionsCallback = () => {};
173174
this.eslintLoaderOptionsCallback = () => {};
175+
this.eslintPluginOptionsCallback = () => {};
174176
this.tsConfigurationCallback = () => {};
175177
this.handlebarsConfigurationCallback = () => {};
176178
this.miniCssExtractLoaderConfigurationCallback = () => {};
@@ -803,6 +805,12 @@ class WebpackConfig {
803805
}
804806

805807
enableEslintLoader(eslintLoaderOptionsOrCallback = () => {}, eslintOptions = {}) {
808+
logger.deprecation('Encore.enableEslintLoader() is deprecated, please use Encore.enableEslintPlugin() instead.');
809+
810+
if (this.useEslintPlugin) {
811+
throw new Error('Encore.enableEslintLoader() can not be called when Encore.enableEslintPlugin() has been called.');
812+
}
813+
806814
this.useEslintLoader = true;
807815

808816
if (typeof eslintLoaderOptionsOrCallback === 'function') {
@@ -831,6 +839,24 @@ class WebpackConfig {
831839
this.eslintOptions = eslintOptions;
832840
}
833841

842+
enableEslintPlugin(eslintPluginOptionsOrCallback = () => {}) {
843+
if (this.useEslintLoader) {
844+
throw new Error('Encore.enableEslintPlugin() can not be called when Encore.enableEslintLoader() has been called.');
845+
}
846+
847+
this.useEslintPlugin = true;
848+
849+
if (typeof eslintPluginOptionsOrCallback === 'function') {
850+
this.eslintPluginOptionsCallback = eslintPluginOptionsOrCallback;
851+
} else if (typeof eslintPluginOptionsOrCallback === 'object') {
852+
this.eslintPluginOptionsCallback = (options) => {
853+
Object.assign(options, eslintPluginOptionsOrCallback);
854+
};
855+
} else {
856+
throw new Error('Argument 1 to enableEslintPlugin() must be either an object or callback function.');
857+
}
858+
}
859+
834860
enableBuildNotifications(enabled = true, notifierPluginOptionsCallback = () => {}) {
835861
if (typeof notifierPluginOptionsCallback !== 'function') {
836862
throw new Error('Argument 2 to enableBuildNotifications() must be a callback function.');

lib/config-generator.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const vuePluginUtil = require('./plugins/vue');
3737
const friendlyErrorPluginUtil = require('./plugins/friendly-errors');
3838
const assetOutputDisplay = require('./plugins/asset-output-display');
3939
const notifierPluginUtil = require('./plugins/notifier');
40+
const eslintPluginUtil = require('./plugins/eslint');
4041
const PluginPriorities = require('./plugins/plugin-priorities');
4142
const applyOptionsCallback = require('./utils/apply-options-callback');
4243
const copyEntryTmpName = require('./utils/copyEntryTmpName');
@@ -454,6 +455,8 @@ class ConfigGenerator {
454455

455456
vuePluginUtil(plugins, this.webpackConfig);
456457

458+
eslintPluginUtil(plugins, this.webpackConfig);
459+
457460
if (!this.webpackConfig.runtimeConfig.outputJson) {
458461
const friendlyErrorPlugin = friendlyErrorPluginUtil(this.webpackConfig);
459462
plugins.push({

lib/features.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ const features = {
126126
],
127127
description: 'Enable ESLint checks'
128128
},
129+
eslint_plugin: {
130+
method: 'enableEslintPlugin()',
131+
// eslint is needed so the end-user can do things
132+
packages: [
133+
{ name: 'eslint' },
134+
{ name: 'eslint-webpack-plugin', enforce_version: true },
135+
],
136+
description: 'Enable ESLint checks'
137+
},
129138
copy_files: {
130139
method: 'copyFiles()',
131140
packages: [

lib/loaders/eslint.js

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,12 @@
99

1010
'use strict';
1111

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

16-
function isMissingConfigError(e) {
17-
if (!e.message || !e.message.includes('No ESLint configuration found')) {
18-
return false;
19-
}
20-
21-
return true;
22-
}
23-
2418
module.exports = {
2519
/**
2620
* @param {WebpackConfig} webpackConfig
@@ -29,19 +23,11 @@ module.exports = {
2923
getOptions(webpackConfig) {
3024
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('eslint');
3125

32-
const eslint = require('eslint'); // eslint-disable-line node/no-unpublished-require
33-
const engine = new eslint.CLIEngine({
34-
cwd: webpackConfig.runtimeConfig.context,
35-
});
36-
37-
try {
38-
engine.getConfigForFile('webpack.config.js');
39-
} catch (e) {
40-
if (isMissingConfigError(e)) {
41-
const chalk = require('chalk');
42-
const packageHelper = require('../package-helper');
26+
if (!hasEslintConfiguration(webpackConfig)) {
27+
const chalk = require('chalk');
28+
const packageHelper = require('../package-helper');
4329

44-
const message = `No ESLint configration has been found.
30+
const message = `No ESLint configuration has been found.
4531
4632
${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.
4733
@@ -57,10 +43,7 @@ module.exports = {
5743
Install ${chalk.yellow('babel-eslint')} to prevent potential parsing issues: ${packageHelper.getInstallCommand([[{ name: 'babel-eslint' }]])}
5844
5945
`;
60-
throw new Error(message);
61-
}
62-
63-
throw e;
46+
throw new Error(message);
6447
}
6548

6649
const eslintLoaderOptions = {

lib/plugins/eslint.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* This file is part of the Symfony Webpack Encore package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const forceSync = require('sync-rpc');
13+
const hasEslintConfiguration = forceSync(require.resolve('../utils/has-eslint-configuration'));
14+
const WebpackConfig = require('../WebpackConfig'); //eslint-disable-line no-unused-vars
15+
const EslintPlugin = require('eslint-webpack-plugin'); //eslint-disable-line node/no-unpublished-require
16+
const applyOptionsCallback = require('../utils/apply-options-callback');
17+
const pluginFeatures = require('../features');
18+
19+
/**
20+
* Support for ESLint.
21+
*
22+
* @param {Array} plugins
23+
* @param {WebpackConfig} webpackConfig
24+
* @return {void}
25+
*/
26+
module.exports = function(plugins, webpackConfig) {
27+
if (webpackConfig.useEslintPlugin) {
28+
pluginFeatures.ensurePackagesExistAndAreCorrectVersion('eslint_plugin');
29+
30+
if (!hasEslintConfiguration(webpackConfig)) {
31+
const chalk = require('chalk');
32+
const packageHelper = require('../package-helper');
33+
34+
const message = `No ESLint configuration has been found.
35+
36+
${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.
37+
38+
If you prefer to create a ${chalk.yellow('.eslintrc.js')} file by yourself, here is an example to get you started:
39+
40+
${chalk.yellow(`// .eslintrc.js
41+
module.exports = {
42+
parser: 'babel-eslint',
43+
extends: ['eslint:recommended'],
44+
}
45+
`)}
46+
47+
Install ${chalk.yellow('babel-eslint')} to prevent potential parsing issues: ${packageHelper.getInstallCommand([[{ name: 'babel-eslint' }]])}
48+
49+
`;
50+
throw new Error(message);
51+
}
52+
53+
const eslintPluginOptions = {
54+
emitWarning: true,
55+
extensions: ['js', 'jsx'],
56+
};
57+
58+
plugins.push({
59+
plugin: new EslintPlugin(
60+
applyOptionsCallback(webpackConfig.eslintPluginOptionsCallback, eslintPluginOptions)
61+
),
62+
});
63+
}
64+
};

lib/utils/has-eslint-configuration.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This file is part of the Symfony Webpack Encore package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
13+
function isMissingConfigError(e) {
14+
if (!e.message || !e.message.includes('No ESLint configuration found')) {
15+
return false;
16+
}
17+
18+
return true;
19+
}
20+
21+
/**
22+
* @returns {Promise<boolean>}
23+
*/
24+
module.exports = async function() {
25+
/**
26+
* @param {WebpackConfig} webpackConfig
27+
* @returns {Promise<boolean>}
28+
*/
29+
return async function(webpackConfig) {
30+
const { ESLint } = require('eslint'); // eslint-disable-line node/no-unpublished-require
31+
const eslint = new ESLint({
32+
cwd: webpackConfig.runtimeConfig.context,
33+
});
34+
35+
try {
36+
await eslint.calculateConfigForFile('webpack.config.js');
37+
} catch (e) {
38+
return !isMissingConfigError(e);
39+
}
40+
41+
return true;
42+
};
43+
};

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@symfony/webpack-encore",
3-
"version": "1.7.0",
3+
"version": "1.7.1",
44
"description": "Webpack Encore is a simpler way to integrate Webpack into your application",
55
"main": "index.js",
66
"scripts": {
@@ -45,10 +45,11 @@
4545
"resolve-url-loader": "^3.1.2",
4646
"semver": "^7.3.2",
4747
"style-loader": "^2.0.0",
48+
"sync-rpc": "^1.3.6",
4849
"terser-webpack-plugin": "^5.1.1",
4950
"tmp": "^0.2.1",
5051
"webpack": "^5.35",
51-
"webpack-cli": "^4",
52+
"webpack-cli": "^4.9.1",
5253
"webpack-dev-server": "^4.0.0",
5354
"yargs-parser": "^20.2.4"
5455
},
@@ -69,11 +70,12 @@
6970
"chai-fs": "^2.0.0",
7071
"chai-subset": "^1.6.0",
7172
"core-js": "^3.0.0",
72-
"eslint": "^6.7.0 || ^7.0.0",
73+
"eslint": "^7.0.0",
7374
"eslint-loader": "^4.0.0",
7475
"eslint-plugin-header": "^3.0.0",
7576
"eslint-plugin-import": "^2.8.0",
7677
"eslint-plugin-node": "^11.1.0",
78+
"eslint-webpack-plugin": "^2.5.4",
7779
"file-loader": "^6.0.0",
7880
"fork-ts-checker-webpack-plugin": "^5.0.0 || ^6.0.0",
7981
"fs-extra": "^9.0.0",

test/WebpackConfig.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,5 +1512,24 @@ describe('WebpackConfig object', () => {
15121512
});
15131513
}).to.throw('"notExisting" is not a valid key for enableEslintLoader(). Valid keys: lintVue.');
15141514
});
1515+
1516+
it('ESLint loader can not be enabled if ESLint Webpack Plugin is already enabled', () => {
1517+
const config = createConfig();
1518+
config.enableEslintPlugin();
1519+
1520+
expect(function() {
1521+
config.enableEslintLoader();
1522+
}).to.throw('Encore.enableEslintLoader() can not be called when Encore.enableEslintPlugin() has been called.');
1523+
});
1524+
});
1525+
describe('enableEslintPlugin', () => {
1526+
it('ESLint loader can not be enabled if ESLint Webpack Plugin is already enabled', () => {
1527+
const config = createConfig();
1528+
config.enableEslintLoader();
1529+
1530+
expect(function() {
1531+
config.enableEslintPlugin();
1532+
}).to.throw('Encore.enableEslintPlugin() can not be called when Encore.enableEslintLoader() has been called.');
1533+
});
15151534
});
15161535
});

0 commit comments

Comments
 (0)