Skip to content

Commit

Permalink
Move from eslint-loader to eslint-webpack-plugin, close #847
Browse files Browse the repository at this point in the history
  • Loading branch information
Kocal authored and weaverryan committed Jan 21, 2022
1 parent 17ed0a8 commit 8c33cb1
Show file tree
Hide file tree
Showing 14 changed files with 618 additions and 81 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
# CHANGELOG

## [v1.7.1](https://github.com/symfony/webpack-encore/releases/tag/v1.7.0)

*Jan 20th, 2022*

### Bug Fix

- [#1069](https://github.com/symfony/webpack-encore/pull/1069) - Increased webpack-cli version constraint to v.4.9.1 - *@nspyke*

## [v1.7.0](https://github.com/symfony/webpack-encore/releases/tag/v1.7.0)

*Dec 2nd, 2021*

Dependency changes:

* Official support for `ts-loader` 8 was dropped.
* Official support for `typescript` 8 was dropped and minimum increased to 4.2.2.
* Official support for `typescript` 3 was dropped and minimum increased to 4.2.2.
* Official support for `vue` was bumped to 3.2.14 or higher.
* Official support for `vue-loader` was bumped to 16.7.0 or higher.

Expand Down
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 <[email protected]>
*
* 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 <[email protected]>
*
* 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;
};
};
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@symfony/webpack-encore",
"version": "1.7.0",
"version": "1.7.1",
"description": "Webpack Encore is a simpler way to integrate Webpack into your application",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -45,10 +45,11 @@
"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",
"webpack-cli": "^4",
"webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.0.0",
"yargs-parser": "^20.2.4"
},
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 8c33cb1

Please sign in to comment.