Skip to content

Commit 287129e

Browse files
committed
minor #99 Refactor plugin configuration (davidmpaz)
This PR was squashed before being merged into the master branch (closes #99). Discussion ---------- Refactor plugin configuration Extract encore built in plugin configuration logic into plugin utils. First step toward allowing to be able to configure plugins through options and have more modular integration for plugins inside encore. From now on only public API with proper options is needed, for most cases. This changes are internals and tries to improve plugin architecture inside encore. Please take a look on it and feedback are welcome :). So far I see improvements for several issues: * #2 - Point .2 ...for plugins we should do the same as for loaders, from point of view of module split (utilities). * #63 - Better (more modular) integration of the plugin as well its configuration. * #65 - Same as #63 * #79 - Now it should be possible to pass options and/or check for flags to apply plugin or not. * #87 - Some small steps to make clean plugin more configurable. Public API still need to be changed though. thanks in advance. Commits ------- 65adfdc Refactor plugin configuration
2 parents 8cc36e0 + 65adfdc commit 287129e

13 files changed

+461
-172
lines changed

lib/config-generator.js

+31-172
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,28 @@
99

1010
'use strict';
1111

12-
const webpack = require('webpack');
13-
const ExtractTextPlugin = require('extract-text-webpack-plugin');
1412
const extractText = require('./loaders/extract-text');
15-
const ManifestPlugin = require('./webpack/webpack-manifest-plugin');
16-
const DeleteUnusedEntriesJSPlugin = require('./webpack/delete-unused-entries-js-plugin');
17-
const AssetOutputDisplayPlugin = require('./friendly-errors/asset-output-display-plugin');
18-
const CleanWebpackPlugin = require('clean-webpack-plugin');
19-
const WebpackChunkHash = require('webpack-chunk-hash');
20-
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
21-
const missingLoaderTransformer = require('./friendly-errors/transformers/missing-loader');
22-
const missingLoaderFormatter = require('./friendly-errors/formatters/missing-loader');
23-
const missingPostCssConfigTransformer = require('./friendly-errors/transformers/missing-postcss-config');
24-
const missingPostCssConfigFormatter = require('./friendly-errors/formatters/missing-postcss-config');
25-
const vueUnactivatedLoaderTransformer = require('./friendly-errors/transformers/vue-unactivated-loader-error');
26-
const vueUnactivatedLoaderFormatter = require('./friendly-errors/formatters/vue-unactivated-loader-error');
2713
const pathUtil = require('./config/path-util');
14+
// loaders utils
2815
const cssLoaderUtil = require('./loaders/css');
2916
const sassLoaderUtil = require('./loaders/sass');
3017
const lessLoaderUtil = require('./loaders/less');
3118
const babelLoaderUtil = require('./loaders/babel');
3219
const tsLoaderUtil = require('./loaders/typescript');
3320
const vueLoaderUtil = require('./loaders/vue');
21+
// plugins utils
22+
const extractTextPluginUtil = require('./plugins/extract-text');
23+
const deleteUnusedEntriesPluginUtil = require('./plugins/delete-unused-entries');
24+
const manifestPluginUtil = require('./plugins/manifest');
25+
const loaderOptionsPluginUtil = require('./plugins/loader-options');
26+
const versioningPluginUtil = require('./plugins/versioning');
27+
const variableProviderPluginUtil = require('./plugins/variable-provider');
28+
const cleanPluginUtil = require('./plugins/clean');
29+
const commonChunksPluginUtil = require('./plugins/common-chunks');
30+
const definePluginUtil = require('./plugins/define');
31+
const uglifyPluginUtil = require('./plugins/uglify');
32+
const friendlyErrorPluginUtil = require('./plugins/friendly-errors');
33+
const assetOutputDisplay = require('./plugins/asset-output-display');
3434

3535
class ConfigGenerator {
3636
/**
@@ -180,173 +180,32 @@ class ConfigGenerator {
180180
buildPluginsConfig() {
181181
let plugins = [];
182182

183-
/*
184-
* All CSS/SCSS content (due to the loaders above) will be
185-
* extracted into an [entrypointname].css files. The result
186-
* is that NO css will be inlined, *except* CSS that is required
187-
* in an async way (e.g. via require.ensure()).
188-
*
189-
* This may not be ideal in some cases, but it's at least
190-
* predictable. It means that you must manually add a
191-
* link tag for an entry point's CSS (unless no CSS file
192-
* was imported - in which case no CSS file will be dumped).
193-
*/
194-
plugins.push(new ExtractTextPlugin({
195-
filename: this.webpackConfig.useVersioning ? '[name].[contenthash].css' : '[name].css',
196-
// if true, async CSS (e.g. loaded via require.ensure())
197-
// is extracted to the entry point CSS. If false, it's
198-
// inlined in the AJAX-loaded .js file.
199-
allChunks: false
200-
}));
183+
extractTextPluginUtil(plugins, this.webpackConfig);
201184

202185
// register the pure-style entries that should be deleted
203-
plugins.push(new DeleteUnusedEntriesJSPlugin(
204-
// transform into an Array
205-
[... this.webpackConfig.styleEntries.keys()]
206-
));
207-
208-
/*
209-
* Dump the manifest.json file
210-
*/
211-
let manifestPrefix = this.webpackConfig.manifestKeyPrefix;
212-
if (null === manifestPrefix) {
213-
// by convention, we remove the opening slash on the manifest keys
214-
manifestPrefix = this.webpackConfig.publicPath.replace(/^\//,'');
215-
}
216-
plugins.push(new ManifestPlugin({
217-
basePath: manifestPrefix,
218-
// guarantee the value uses the public path (or CDN public path)
219-
publicPath: this.webpackConfig.getRealPublicPath(),
220-
// always write a manifest.json file, even with webpack-dev-server
221-
writeToFileEmit: true,
222-
}));
223-
224-
/*
225-
* This section is a bit mysterious. The "minimize"
226-
* true is read and used to minify the CSS.
227-
* But as soon as this plugin is included
228-
* at all, SASS begins to have errors, until the context
229-
* and output options are specified. At this time, I'm
230-
* not totally sure what's going on here
231-
* https://github.com/jtangelder/sass-loader/issues/285
232-
*/
233-
plugins.push(new webpack.LoaderOptionsPlugin({
234-
debug: !this.webpackConfig.isProduction(),
235-
options: {
236-
context: this.webpackConfig.getContext(),
237-
output: { path: this.webpackConfig.outputPath }
238-
}
239-
}));
240-
241-
/*
242-
* With versioning, the "chunkhash" used in the filenames and
243-
* the module ids (i.e. the internal names of modules that
244-
* are required) become important. Specifically:
245-
*
246-
* 1) If the contents of a module don't change, then you don't want its
247-
* internal module id to change. Otherwise, whatever file holds the
248-
* webpack "manifest" will change because the module id will change.
249-
* Solved by HashedModuleIdsPlugin or NamedModulesPlugin
250-
*
251-
* 2) Similarly, if the final contents of a file don't change,
252-
* then we also don't want that file to have a new filename.
253-
* The WebpackChunkHash() handles this, by making sure that
254-
* the chunkhash is based off of the file contents.
255-
*
256-
* Even in the webpack community, the ideal setup seems to be
257-
* a bit of a mystery:
258-
* * https://github.com/webpack/webpack/issues/1315
259-
* * https://github.com/webpack/webpack.js.org/issues/652#issuecomment-273324529
260-
* * https://webpack.js.org/guides/caching/#deterministic-hashes
261-
*/
262-
if (this.webpackConfig.isProduction()) {
263-
// shorter, and obfuscated module ids (versus NamedModulesPlugin)
264-
// makes the final assets *slightly* larger, but prevents contents
265-
// from sometimes changing when nothing really changed
266-
plugins.push(new webpack.HashedModuleIdsPlugin());
267-
} else {
268-
// human-readable module names, helps debug in HMR
269-
// enable always when not in production for consistency
270-
plugins.push(new webpack.NamedModulesPlugin());
271-
}
186+
deleteUnusedEntriesPluginUtil(plugins, this.webpackConfig);
272187

273-
if (this.webpackConfig.useVersioning) {
274-
// enables the [chunkhash] ability
275-
plugins.push(new WebpackChunkHash());
276-
}
188+
// Dump the manifest.json file
189+
manifestPluginUtil(plugins, this.webpackConfig);
277190

278-
if (Object.keys(this.webpackConfig.providedVariables).length > 0) {
279-
plugins = plugins.concat([
280-
new webpack.ProvidePlugin(this.webpackConfig.providedVariables)
281-
]);
282-
}
191+
loaderOptionsPluginUtil(plugins, this.webpackConfig);
283192

284-
if (this.webpackConfig.cleanupOutput) {
285-
plugins.push(
286-
new CleanWebpackPlugin(['**/*'], {
287-
root: this.webpackConfig.outputPath,
288-
verbose: false,
289-
})
290-
);
291-
}
193+
versioningPluginUtil(plugins, this.webpackConfig);
292194

293-
// if we're extracting a vendor chunk, set it up!
294-
if (this.webpackConfig.sharedCommonsEntryName) {
295-
plugins = plugins.concat([
296-
new webpack.optimize.CommonsChunkPlugin({
297-
name: [
298-
this.webpackConfig.sharedCommonsEntryName,
299-
/*
300-
* Always dump a 2nd file - manifest.json that
301-
* will contain the webpack manifest information.
302-
* This changes frequently, and without this line,
303-
* it would be packaged inside the "shared commons entry"
304-
* file - e.g. vendor.js, which would prevent long-term caching.
305-
*/
306-
'manifest'
307-
],
308-
minChunks: Infinity,
309-
}),
310-
]);
311-
}
195+
variableProviderPluginUtil(plugins, this.webpackConfig);
312196

313-
if (this.webpackConfig.isProduction()) {
314-
plugins = plugins.concat([
315-
new webpack.DefinePlugin({
316-
'process.env': {
317-
NODE_ENV: '"production"'
318-
}
319-
}),
320-
321-
// todo - options here should be configurable
322-
new webpack.optimize.UglifyJsPlugin({
323-
sourceMap: this.webpackConfig.useSourceMaps
324-
})
325-
]);
326-
}
197+
cleanPluginUtil(plugins, this.webpackConfig, ['**/*']);
327198

328-
const friendlyErrorsPlugin = new FriendlyErrorsWebpackPlugin({
329-
clearConsole: false,
330-
additionalTransformers: [
331-
missingLoaderTransformer,
332-
missingPostCssConfigTransformer,
333-
vueUnactivatedLoaderTransformer
334-
],
335-
additionalFormatters: [
336-
missingLoaderFormatter,
337-
missingPostCssConfigFormatter,
338-
vueUnactivatedLoaderFormatter
339-
],
340-
compilationSuccessInfo: {
341-
messages: []
342-
}
343-
});
344-
plugins.push(friendlyErrorsPlugin);
199+
commonChunksPluginUtil(plugins, this.webpackConfig);
345200

346-
if (!this.webpackConfig.useDevServer()) {
347-
const outputPath = pathUtil.getRelativeOutputPath(this.webpackConfig);
348-
plugins.push(new AssetOutputDisplayPlugin(outputPath, friendlyErrorsPlugin));
349-
}
201+
// todo - options here should be configurable
202+
definePluginUtil(plugins, this.webpackConfig);
203+
uglifyPluginUtil(plugins, this.webpackConfig);
204+
205+
let friendlyErrorPlugin = friendlyErrorPluginUtil();
206+
plugins.push(friendlyErrorPlugin);
207+
208+
assetOutputDisplay(plugins, this.webpackConfig, friendlyErrorPlugin);
350209

351210
this.webpackConfig.plugins.forEach(function(plugin) {
352211
plugins.push(plugin);

lib/plugins/asset-output-display.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* This file is part of the Symfony 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 pathUtil = require('../config/path-util');
13+
const AssetOutputDisplayPlugin = require('../friendly-errors/asset-output-display-plugin');
14+
15+
/**
16+
* Updates plugins array passed adding AssetOutputDisplayPlugin instance
17+
*
18+
* @param {Array} plugins
19+
* @param {WebpackConfig} webpackConfig
20+
* @param {FriendlyErrorsWebpackPlugin} friendlyErrorsPlugin
21+
* @return {void}
22+
*/
23+
module.exports = function(plugins, webpackConfig, friendlyErrorsPlugin) {
24+
if (webpackConfig.useDevServer()) {
25+
return;
26+
}
27+
28+
const outputPath = pathUtil.getRelativeOutputPath(webpackConfig);
29+
plugins.push(new AssetOutputDisplayPlugin(outputPath, friendlyErrorsPlugin));
30+
};

lib/plugins/clean.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* This file is part of the Symfony 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 CleanWebpackPlugin = require('clean-webpack-plugin');
13+
14+
/**
15+
* Updates plugins array passed adding CleanWebpackPlugin instance
16+
*
17+
* @param {Array} plugins to push to
18+
* @param {WebpackConfig} webpackConfig read only variable
19+
* @param {Array} paths to clean
20+
* @param {Object} cleanUpOptions
21+
* @return {void}
22+
*/
23+
module.exports = function(plugins, webpackConfig, paths, cleanUpOptions = {}) {
24+
25+
if (!webpackConfig.cleanupOutput) {
26+
return;
27+
}
28+
29+
let config = Object.assign({}, cleanUpOptions, {
30+
root: webpackConfig.outputPath,
31+
verbose: false,
32+
});
33+
34+
plugins.push(new CleanWebpackPlugin(paths, config));
35+
};

lib/plugins/common-chunks.js

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* This file is part of the Symfony 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 webpack = require('webpack');
13+
14+
/**
15+
* @param {Array} plugins
16+
* @param {WebpackConfig} webpackConfig
17+
* @return {void}
18+
*/
19+
module.exports = function(plugins, webpackConfig) {
20+
21+
if (!webpackConfig.sharedCommonsEntryName) {
22+
return;
23+
}
24+
25+
// if we're extracting a vendor chunk, set it up!
26+
plugins.push(new webpack.optimize.CommonsChunkPlugin({
27+
name: [
28+
webpackConfig.sharedCommonsEntryName,
29+
/*
30+
* Always dump a 2nd file - manifest.json that
31+
* will contain the webpack manifest information.
32+
* This changes frequently, and without this line,
33+
* it would be packaged inside the "shared commons entry"
34+
* file - e.g. vendor.js, which would prevent long-term caching.
35+
*/
36+
'manifest'
37+
],
38+
minChunks: Infinity,
39+
}));
40+
};

lib/plugins/define.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of the Symfony 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 webpack = require('webpack');
13+
14+
/**
15+
* @param {Array} plugins
16+
* @param {WebpackConfig} webpackConfig
17+
* @param {Object} defineOptions
18+
* @return {void}
19+
*/
20+
module.exports = function(plugins, webpackConfig, defineOptions = {}) {
21+
22+
if (!webpackConfig.isProduction()) {
23+
return;
24+
}
25+
26+
let defineConfig = Object.assign({}, defineOptions, {
27+
'process.env': {
28+
NODE_ENV: '"production"'
29+
}
30+
});
31+
let define = new webpack.DefinePlugin(defineConfig);
32+
33+
plugins.push(define);
34+
};

0 commit comments

Comments
 (0)