diff --git a/index.js b/index.js index edd09959..6f7a5deb 100644 --- a/index.js +++ b/index.js @@ -102,14 +102,90 @@ const publicApi = { return this; }, + /** + * Allows you to configure the options passed to the DefinePlugin. + * A list of available options can be found at https://webpack.js.org/plugins/define-plugin/ + * + * For example: + * + * Encore.configureDefinePlugin((options) => { + * options.VERSION = JSON.stringify('1.0.0'); + * }) + * + * @param {function} definePluginOptionsCallback + * @returns {exports} + */ + configureDefinePlugin(definePluginOptionsCallback = () => {}) { + webpackConfig.configureDefinePlugin(definePluginOptionsCallback); + + return this; + }, + + /** + * Allows you to configure the options passed to the extract-text-webpack-plugin. + * A list of available options can be found at https://github.com/webpack-contrib/extract-text-webpack-plugin + * + * For example: + * + * Encore.configureExtractTextPlugin((options) => { + * options.ignoreOrder = true; + * }) + * + * @param {function} extractTextPluginOptionsCallback + * @returns {exports} + */ + configureExtractTextPlugin(extractTextPluginOptionsCallback = () => {}) { + webpackConfig.configureExtractTextPlugin(extractTextPluginOptionsCallback); + + return this; + }, + + /** + * Allows you to configure the options passed to the friendly-errors-webpack-plugin. + * A list of available options can be found at https://github.com/geowarin/friendly-errors-webpack-plugin + * + * For example: + * + * Encore.configureFriendlyErrorsPlugin((options) => { + * options.clearConsole = true; + * }) + * + * @param {function} friendlyErrorsPluginOptionsCallback + * @returns {exports} + */ + configureFriendlyErrorsPlugin(friendlyErrorsPluginOptionsCallback = () => {}) { + webpackConfig.configureFriendlyErrorsPlugin(friendlyErrorsPluginOptionsCallback); + + return this; + }, + + /** + * Allows you to configure the options passed to the LoaderOptionsPlugins. + * A list of available options can be found at https://webpack.js.org/plugins/loader-options-plugin/ + * + * For example: + * + * Encore.configureLoaderOptionsPlugin((options) => { + * options.minimize = true; + * }) + * + * @param {function} loaderOptionsPluginOptionsCallback + * @returns {exports} + */ + configureLoaderOptionsPlugin(loaderOptionsPluginOptionsCallback = () => {}) { + webpackConfig.configureLoaderOptionsPlugin(loaderOptionsPluginOptionsCallback); + + return this; + }, + /** * Allows you to configure the options passed to webpack-manifest-plugin. * A list of available options can be found at https://github.com/danethurber/webpack-manifest-plugin * * For example: * - * Encore.configureManifestPlugin(function(options){ - * options.fileName: '../../var/assets/manifest.json' + * Encore.configureManifestPlugin((options) => { + * options.fileName = '../../var/assets/manifest.json'; * }) * * @param {function} manifestPluginOptionsCallback @@ -121,6 +197,26 @@ const publicApi = { return this; }, + /** + * Allows you to configure the options passed to the uglifyjs-webpack-plugin. + * A list of available options can be found at https://github.com/webpack-contrib/uglifyjs-webpack-plugin/tree/v0.4.6 + * + * For example: + * + * Encore.configureUglifyJsPlugin((options) => { + * options.compress = false; + * options.beautify = true; + * }) + * + * @param {function} uglifyJsPluginOptionsCallback + * @returns {exports} + */ + configureUglifyJsPlugin(uglifyJsPluginOptionsCallback = () => {}) { + webpackConfig.configureUglifyJsPlugin(uglifyJsPluginOptionsCallback); + + return this; + }, + /** * Adds a JavaScript file that should be webpacked: * @@ -519,13 +615,22 @@ const publicApi = { }, /** - * If enabled, the output directory is emptied between - * each build (to remove old files). + * If enabled, the output directory is emptied between each build (to remove old files). + * + * A list of available options can be found at https://github.com/johnagan/clean-webpack-plugin + * + * For example: + * + * Encore.cleanupOutputBeforeBuild(['*.js'], (options) => { + * options.dry = true; + * }) * + * @param {Array} paths Paths that should be cleaned, relative to the "root" option + * @param {function} cleanWebpackPluginOptionsCallback * @returns {exports} */ - cleanupOutputBeforeBuild() { - webpackConfig.cleanupOutputBeforeBuild(); + cleanupOutputBeforeBuild(paths = ['**/*'], cleanWebpackPluginOptionsCallback = () => {}) { + webpackConfig.cleanupOutputBeforeBuild(paths, cleanWebpackPluginOptionsCallback); return this; }, diff --git a/lib/WebpackConfig.js b/lib/WebpackConfig.js index 9b399fb6..bfe4302a 100644 --- a/lib/WebpackConfig.js +++ b/lib/WebpackConfig.js @@ -30,40 +30,60 @@ function validateRuntimeConfig(runtimeConfig) { class WebpackConfig { constructor(runtimeConfig) { validateRuntimeConfig(runtimeConfig); + this.runtimeConfig = runtimeConfig; - this.outputPath = null; - this.publicPath = null; - this.manifestKeyPrefix = null; this.entries = new Map(); this.styleEntries = new Map(); this.plugins = []; + this.loaders = []; + + // Global settings + this.outputPath = null; + this.publicPath = null; + this.manifestKeyPrefix = null; + this.sharedCommonsEntryName = null; + this.providedVariables = {}; + this.configuredFilenames = {}; + + // Features/Loaders flags this.useVersioning = false; this.useSourceMaps = false; + this.cleanupOutput = false; + this.useImagesLoader = true; + this.useFontsLoader = true; this.usePostCssLoader = false; - this.postCssLoaderOptionsCallback = function() {}; - this.useSassLoader = false; - this.sassLoaderOptionsCallback = function() {}; - this.sassOptions = { - resolve_url_loader: true - }; this.useLessLoader = false; - this.lessLoaderOptionsCallback = function() {}; - this.cleanupOutput = false; - this.sharedCommonsEntryName = null; - this.providedVariables = {}; - this.babelConfigurationCallback = function() {}; + this.useSassLoader = false; this.useReact = false; this.useVueLoader = false; - this.vueLoaderOptionsCallback = () => {}; - this.loaders = []; this.useTypeScriptLoader = false; - this.tsConfigurationCallback = function() {}; this.useForkedTypeScriptTypeChecking = false; + + // Features/Loaders options + this.sassOptions = { + resolve_url_loader: true + }; + + // Features/Loaders options callbacks + this.postCssLoaderOptionsCallback = () => {}; + this.sassLoaderOptionsCallback = () => {}; + this.lessLoaderOptionsCallback = () => {}; + this.babelConfigurationCallback = () => {}; + this.vueLoaderOptionsCallback = () => {}; + this.tsConfigurationCallback = () => {}; + + // Plugins options + this.cleanWebpackPluginPaths = ['**/*']; + + // Plugins callbacks + this.cleanWebpackPluginOptionsCallback = () => {}; + this.definePluginOptionsCallback = () => {}; + this.extractTextPluginOptionsCallback = () => {}; this.forkedTypeScriptTypesCheckOptionsCallback = () => {}; - this.useImagesLoader = true; - this.useFontsLoader = true; - this.configuredFilenames = {}; - this.manifestPluginOptionsCallback = function() {}; + this.friendlyErrorsPluginOptionsCallback = () => {}; + this.loaderOptionsPluginOptionsCallback = () => {}; + this.manifestPluginOptionsCallback = () => {}; + this.uglifyJsPluginOptionsCallback = () => {}; } getContext() { @@ -118,6 +138,38 @@ class WebpackConfig { this.manifestKeyPrefix = manifestKeyPrefix; } + configureDefinePlugin(definePluginOptionsCallback = () => {}) { + if (typeof definePluginOptionsCallback !== 'function') { + throw new Error('Argument 1 to configureDefinePlugin() must be a callback function'); + } + + this.definePluginOptionsCallback = definePluginOptionsCallback; + } + + configureExtractTextPlugin(extractTextPluginOptionsCallback = () => {}) { + if (typeof extractTextPluginOptionsCallback !== 'function') { + throw new Error('Argument 1 to configureExtractTextPlugin() must be a callback function'); + } + + this.extractTextPluginOptionsCallback = extractTextPluginOptionsCallback; + } + + configureFriendlyErrorsPlugin(friendlyErrorsPluginOptionsCallback = () => {}) { + if (typeof friendlyErrorsPluginOptionsCallback !== 'function') { + throw new Error('Argument 1 to configureFriendlyErrorsPlugin() must be a callback function'); + } + + this.friendlyErrorsPluginOptionsCallback = friendlyErrorsPluginOptionsCallback; + } + + configureLoaderOptionsPlugin(loaderOptionsPluginOptionsCallback = () => {}) { + if (typeof loaderOptionsPluginOptionsCallback !== 'function') { + throw new Error('Argument 1 to configureLoaderOptionsPlugin() must be a callback function'); + } + + this.loaderOptionsPluginOptionsCallback = loaderOptionsPluginOptionsCallback; + } + configureManifestPlugin(manifestPluginOptionsCallback = () => {}) { if (typeof manifestPluginOptionsCallback !== 'function') { throw new Error('Argument 1 to configureManifestPlugin() must be a callback function'); @@ -126,6 +178,14 @@ class WebpackConfig { this.manifestPluginOptionsCallback = manifestPluginOptionsCallback; } + configureUglifyJsPlugin(uglifyJsPluginOptionsCallback = () => {}) { + if (typeof uglifyJsPluginOptionsCallback !== 'function') { + throw new Error('Argument 1 to configureUglifyJsPlugin() must be a callback function'); + } + + this.uglifyJsPluginOptionsCallback = uglifyJsPluginOptionsCallback; + } + /** * Returns the value that should be used as the publicPath, * which can be overridden by enabling the webpackDevServer @@ -311,8 +371,18 @@ class WebpackConfig { this.configuredFilenames = configuredFilenames; } - cleanupOutputBeforeBuild() { + cleanupOutputBeforeBuild(paths = ['**/*'], cleanWebpackPluginOptionsCallback = () => {}) { + if (!Array.isArray(paths)) { + throw new Error('Argument 1 to cleanupOutputBeforeBuild() must be an Array of paths - e.g. [\'**/*\']'); + } + + if (typeof cleanWebpackPluginOptionsCallback !== 'function') { + throw new Error('Argument 2 to cleanupOutputBeforeBuild() must be a callback function'); + } + this.cleanupOutput = true; + this.cleanWebpackPluginPaths = paths; + this.cleanWebpackPluginOptionsCallback = cleanWebpackPluginOptionsCallback; } autoProvideVariables(variables) { diff --git a/lib/config-generator.js b/lib/config-generator.js index 1440dfd4..16528b1f 100644 --- a/lib/config-generator.js +++ b/lib/config-generator.js @@ -26,7 +26,7 @@ const loaderOptionsPluginUtil = require('./plugins/loader-options'); const versioningPluginUtil = require('./plugins/versioning'); const variableProviderPluginUtil = require('./plugins/variable-provider'); const cleanPluginUtil = require('./plugins/clean'); -const commonChunksPluginUtil = require('./plugins/common-chunks'); +const commonsChunksPluginUtil = require('./plugins/commons-chunks'); const definePluginUtil = require('./plugins/define'); const uglifyPluginUtil = require('./plugins/uglify'); const friendlyErrorPluginUtil = require('./plugins/friendly-errors'); @@ -218,15 +218,15 @@ class ConfigGenerator { variableProviderPluginUtil(plugins, this.webpackConfig); - cleanPluginUtil(plugins, this.webpackConfig, ['**/*']); + cleanPluginUtil(plugins, this.webpackConfig); - commonChunksPluginUtil(plugins, this.webpackConfig); + commonsChunksPluginUtil(plugins, this.webpackConfig); - // todo - options here should be configurable definePluginUtil(plugins, this.webpackConfig); + uglifyPluginUtil(plugins, this.webpackConfig); - let friendlyErrorPlugin = friendlyErrorPluginUtil(); + const friendlyErrorPlugin = friendlyErrorPluginUtil(this.webpackConfig); plugins.push(friendlyErrorPlugin); assetOutputDisplay(plugins, this.webpackConfig, friendlyErrorPlugin); diff --git a/lib/plugins/clean.js b/lib/plugins/clean.js index 190d6e7e..adb269f2 100644 --- a/lib/plugins/clean.js +++ b/lib/plugins/clean.js @@ -16,20 +16,26 @@ const CleanWebpackPlugin = require('clean-webpack-plugin'); * * @param {Array} plugins to push to * @param {WebpackConfig} webpackConfig read only variable - * @param {Array} paths to clean - * @param {Object} cleanUpOptions * @return {void} */ -module.exports = function(plugins, webpackConfig, paths, cleanUpOptions = {}) { +module.exports = function(plugins, webpackConfig) { if (!webpackConfig.cleanupOutput) { return; } - let config = Object.assign({}, cleanUpOptions, { + const cleanWebpackPluginOptions = { root: webpackConfig.outputPath, verbose: false, - }); + }; - plugins.push(new CleanWebpackPlugin(paths, config)); + webpackConfig.cleanWebpackPluginOptionsCallback.apply( + cleanWebpackPluginOptions, + [cleanWebpackPluginOptions] + ); + + plugins.push(new CleanWebpackPlugin( + webpackConfig.cleanWebpackPluginPaths, + cleanWebpackPluginOptions + )); }; diff --git a/lib/plugins/common-chunks.js b/lib/plugins/commons-chunks.js similarity index 100% rename from lib/plugins/common-chunks.js rename to lib/plugins/commons-chunks.js diff --git a/lib/plugins/define.js b/lib/plugins/define.js index a1102b90..fda58c72 100644 --- a/lib/plugins/define.js +++ b/lib/plugins/define.js @@ -14,21 +14,24 @@ const webpack = require('webpack'); /** * @param {Array} plugins * @param {WebpackConfig} webpackConfig - * @param {Object} defineOptions * @return {void} */ -module.exports = function(plugins, webpackConfig, defineOptions = {}) { +module.exports = function(plugins, webpackConfig) { if (!webpackConfig.isProduction()) { return; } - let defineConfig = Object.assign({}, defineOptions, { + const definePluginOptions = { 'process.env': { NODE_ENV: '"production"' } - }); - let define = new webpack.DefinePlugin(defineConfig); + }; - plugins.push(define); + webpackConfig.definePluginOptionsCallback.apply( + definePluginOptions, + [definePluginOptions] + ); + + plugins.push(new webpack.DefinePlugin(definePluginOptions)); }; diff --git a/lib/plugins/extract-text.js b/lib/plugins/extract-text.js index 1f8242f2..4ab2fdbb 100644 --- a/lib/plugins/extract-text.js +++ b/lib/plugins/extract-text.js @@ -14,10 +14,9 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin'); /** * @param {Array} plugins * @param {WebpackConfig} webpackConfig - * @param {Object} extractTextOptions Options to pass to the plugin * @return {void} */ -module.exports = function(plugins, webpackConfig, extractTextOptions = {}) { +module.exports = function(plugins, webpackConfig) { /* * All CSS/SCSS content (due to the loaders above) will be @@ -37,13 +36,18 @@ module.exports = function(plugins, webpackConfig, extractTextOptions = {}) { filename = webpackConfig.configuredFilenames.css; } - let config = Object.assign({}, extractTextOptions, { + const extractTextPluginOptions = { filename: filename, // if true, async CSS (e.g. loaded via require.ensure()) // is extracted to the entry point CSS. If false, it's // inlined in the AJAX-loaded .js file. allChunks: false - }); + }; - plugins.push(new ExtractTextPlugin(config)); + webpackConfig.extractTextPluginOptionsCallback.apply( + extractTextPluginOptions, + [extractTextPluginOptions] + ); + + plugins.push(new ExtractTextPlugin(extractTextPluginOptions)); }; diff --git a/lib/plugins/friendly-errors.js b/lib/plugins/friendly-errors.js index c10e9eb4..3d256d2f 100644 --- a/lib/plugins/friendly-errors.js +++ b/lib/plugins/friendly-errors.js @@ -18,10 +18,11 @@ const vueUnactivatedLoaderTransformer = require('../friendly-errors/transformers const vueUnactivatedLoaderFormatter = require('../friendly-errors/formatters/vue-unactivated-loader-error'); /** + * @param {WebpackConfig} webpackConfig * @return {FriendlyErrorsWebpackPlugin} */ -module.exports = function() { - return new FriendlyErrorsWebpackPlugin({ +module.exports = function(webpackConfig) { + const friendlyErrorsPluginOptions = { clearConsole: false, additionalTransformers: [ missingLoaderTransformer, @@ -36,5 +37,12 @@ module.exports = function() { compilationSuccessInfo: { messages: [] } - }); + }; + + webpackConfig.friendlyErrorsPluginOptionsCallback.apply( + friendlyErrorsPluginOptions, + [friendlyErrorsPluginOptions] + ); + + return new FriendlyErrorsWebpackPlugin(friendlyErrorsPluginOptions); }; diff --git a/lib/plugins/loader-options.js b/lib/plugins/loader-options.js index 06f120dd..345d08a3 100644 --- a/lib/plugins/loader-options.js +++ b/lib/plugins/loader-options.js @@ -17,7 +17,6 @@ const webpack = require('webpack'); * @return {void} */ module.exports = function(plugins, webpackConfig) { - /* * This section is a bit mysterious. The "minimize" * true is read and used to minify the CSS. @@ -27,11 +26,18 @@ module.exports = function(plugins, webpackConfig) { * not totally sure what's going on here * https://github.com/jtangelder/sass-loader/issues/285 */ - plugins.push(new webpack.LoaderOptionsPlugin({ + const loaderOptionsPluginOptions = { debug: !webpackConfig.isProduction(), options: { context: webpackConfig.getContext(), output: { path: webpackConfig.outputPath } } - })); + }; + + webpackConfig.loaderOptionsPluginOptionsCallback.apply( + loaderOptionsPluginOptions, + [loaderOptionsPluginOptions] + ); + + plugins.push(new webpack.LoaderOptionsPlugin(loaderOptionsPluginOptions)); }; diff --git a/lib/plugins/uglify.js b/lib/plugins/uglify.js index 84686f20..cc7b89f3 100644 --- a/lib/plugins/uglify.js +++ b/lib/plugins/uglify.js @@ -14,19 +14,22 @@ const webpack = require('webpack'); /** * @param {Array} plugins * @param {WebpackConfig} webpackConfig - * @param {Object} uglifyOptions * @return {void} */ -module.exports = function(plugins, webpackConfig, uglifyOptions = {}) { +module.exports = function(plugins, webpackConfig) { if (!webpackConfig.isProduction()) { return; } - let uglifyConfig = Object.assign({}, uglifyOptions, { + const uglifyJsPluginOptions = { sourceMap: webpackConfig.useSourceMaps - }); - let uglify = new webpack.optimize.UglifyJsPlugin(uglifyConfig); + }; - plugins.push(uglify); + webpackConfig.uglifyJsPluginOptionsCallback.apply( + uglifyJsPluginOptions, + [uglifyJsPluginOptions] + ); + + plugins.push(new webpack.optimize.UglifyJsPlugin(uglifyJsPluginOptions)); }; diff --git a/test/WebpackConfig.js b/test/WebpackConfig.js index 91920a06..fe799c04 100644 --- a/test/WebpackConfig.js +++ b/test/WebpackConfig.js @@ -163,17 +163,124 @@ describe('WebpackConfig object', () => { }); }); + describe('cleanupOutputBeforeBuild', () => { + it('Enabling it with default settings', () => { + const config = createConfig(); + config.cleanupOutputBeforeBuild(); + + expect(config.cleanupOutput).to.be.true; + expect(config.cleanWebpackPluginPaths).to.deep.equal(['**/*']); + }); + + it('Setting paths and callback', () => { + const config = createConfig(); + const callback = () => {}; + config.cleanupOutputBeforeBuild(['**/*.js', '**/*.css'], callback); + + expect(config.cleanupOutput).to.be.true; + expect(config.cleanWebpackPluginPaths).to.deep.equal(['**/*.js', '**/*.css']); + expect(config.cleanWebpackPluginOptionsCallback).to.equal(callback); + }); + + it('Setting invalid paths argument', () => { + const config = createConfig(); + + expect(() => { + config.cleanupOutputBeforeBuild('foo', () => {}); + }).to.throw('Argument 1 to cleanupOutputBeforeBuild() must be an Array of paths'); + }); + + it('Setting invalid callback argument', () => { + const config = createConfig(); + + expect(() => { + config.cleanupOutputBeforeBuild(['**/*'], 'foo'); + }).to.throw('Argument 2 to cleanupOutputBeforeBuild() must be a callback function'); + }); + }); + + describe('configureDefinePlugin', () => { + it('Setting callback', () => { + const config = createConfig(); + const callback = () => {}; + config.configureDefinePlugin(callback); + + expect(config.definePluginOptionsCallback).to.equal(callback); + }); + + it('Setting invalid callback argument', () => { + const config = createConfig(); + + expect(() => { + config.configureDefinePlugin('foo'); + }).to.throw('Argument 1 to configureDefinePlugin() must be a callback function'); + }); + }); + + describe('configureExtractTextPlugin', () => { + it('Setting callback', () => { + const config = createConfig(); + const callback = () => {}; + config.configureExtractTextPlugin(callback); + + expect(config.extractTextPluginOptionsCallback).to.equal(callback); + }); + + it('Setting invalid callback argument', () => { + const config = createConfig(); + + expect(() => { + config.configureExtractTextPlugin('foo'); + }).to.throw('Argument 1 to configureExtractTextPlugin() must be a callback function'); + }); + }); + + describe('configureFriendlyErrorsPlugin', () => { + it('Setting callback', () => { + const config = createConfig(); + const callback = () => {}; + config.configureFriendlyErrorsPlugin(callback); + + expect(config.friendlyErrorsPluginOptionsCallback).to.equal(callback); + }); + + it('Setting invalid callback argument', () => { + const config = createConfig(); + + expect(() => { + config.configureFriendlyErrorsPlugin('foo'); + }).to.throw('Argument 1 to configureFriendlyErrorsPlugin() must be a callback function'); + }); + }); + + describe('configureLoaderOptionsPlugin', () => { + it('Setting callback', () => { + const config = createConfig(); + const callback = () => {}; + config.configureLoaderOptionsPlugin(callback); + + expect(config.loaderOptionsPluginOptionsCallback).to.equal(callback); + }); + + it('Setting invalid callback argument', () => { + const config = createConfig(); + + expect(() => { + config.configureLoaderOptionsPlugin('foo'); + }).to.throw('Argument 1 to configureLoaderOptionsPlugin() must be a callback function'); + }); + }); + describe('configureManifestPlugin', () => { - it('Setting custom options', () => { + it('Setting callback', () => { const config = createConfig(); const callback = () => {}; config.configureManifestPlugin(callback); - // fileName option overridden expect(config.manifestPluginOptionsCallback).to.equal(callback); }); - it('Setting invalid custom options argument', () => { + it('Setting invalid callback argument', () => { const config = createConfig(); const callback = 'invalid'; @@ -183,6 +290,24 @@ describe('WebpackConfig object', () => { }); }); + describe('configureUglifyJsPlugin', () => { + it('Setting callback', () => { + const config = createConfig(); + const callback = () => {}; + config.configureUglifyJsPlugin(callback); + + expect(config.uglifyJsPluginOptionsCallback).to.equal(callback); + }); + + it('Setting invalid callback argument', () => { + const config = createConfig(); + + expect(() => { + config.configureUglifyJsPlugin('foo'); + }).to.throw('Argument 1 to configureUglifyJsPlugin() must be a callback function'); + }); + }); + describe('addEntry', () => { it('Calling with a duplicate name throws an error', () => { const config = createConfig(); diff --git a/test/index.js b/test/index.js index 881653c2..b5255ce7 100644 --- a/test/index.js +++ b/test/index.js @@ -251,6 +251,60 @@ describe('Public API', () => { }); + describe('configureDefinePlugin', () => { + + it('should return the API object', () => { + const returnedValue = api.configureDefinePlugin(() => {}); + expect(returnedValue).to.equal(api); + }); + + }); + + describe('configureExtractTextPlugin', () => { + + it('should return the API object', () => { + const returnedValue = api.configureExtractTextPlugin(() => {}); + expect(returnedValue).to.equal(api); + }); + + }); + + describe('configureFriendlyErrorsPlugin', () => { + + it('should return the API object', () => { + const returnedValue = api.configureFriendlyErrorsPlugin(() => {}); + expect(returnedValue).to.equal(api); + }); + + }); + + describe('configureLoaderOptionsPlugin', () => { + + it('should return the API object', () => { + const returnedValue = api.configureLoaderOptionsPlugin(() => {}); + expect(returnedValue).to.equal(api); + }); + + }); + + describe('configureManifestPlugin', () => { + + it('should return the API object', () => { + const returnedValue = api.configureManifestPlugin(() => {}); + expect(returnedValue).to.equal(api); + }); + + }); + + describe('configureUglifyJsPlugin', () => { + + it('should return the API object', () => { + const returnedValue = api.configureUglifyJsPlugin(() => {}); + expect(returnedValue).to.equal(api); + }); + + }); + describe('Runtime environment proxy', () => { beforeEach(() => { api.clearRuntimeEnvironment(); diff --git a/test/plugins/clean.js b/test/plugins/clean.js new file mode 100644 index 00000000..dd0045a4 --- /dev/null +++ b/test/plugins/clean.js @@ -0,0 +1,62 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const expect = require('chai').expect; +const CleanWebpackPlugin = require('clean-webpack-plugin'); +const WebpackConfig = require('../../lib/WebpackConfig'); +const RuntimeConfig = require('../../lib/config/RuntimeConfig'); +const cleanPluginUtil = require('../../lib/plugins/clean'); + +function createConfig() { + const runtimeConfig = new RuntimeConfig(); + runtimeConfig.context = __dirname; + runtimeConfig.babelRcFileExists = false; + + return new WebpackConfig(runtimeConfig); +} + +describe('plugins/clean', () => { + it('disabled', () => { + const config = createConfig(); + const plugins = []; + + cleanPluginUtil(plugins, config); + expect(plugins.length).to.equal(0); + }); + + it('enabled with default settings', () => { + const config = createConfig(); + const plugins = []; + + config.cleanupOutputBeforeBuild(); + + cleanPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(CleanWebpackPlugin); + expect(plugins[0].paths).to.deep.equal(['**/*']); + expect(plugins[0].options.dry).to.equal(false); + }); + + it('enabled with custom paths and options callback', () => { + const config = createConfig(); + const plugins = []; + + config.cleanupOutputBeforeBuild(['**/*.js', '**/*.css'], (options) => { + options.dry = true; + }); + + cleanPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(CleanWebpackPlugin); + expect(plugins[0].paths).to.deep.equal(['**/*.js', '**/*.css']); + expect(plugins[0].options.dry).to.equal(true); + }); +}); diff --git a/test/plugins/define.js b/test/plugins/define.js new file mode 100644 index 00000000..3e0c5f9e --- /dev/null +++ b/test/plugins/define.js @@ -0,0 +1,65 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const expect = require('chai').expect; +const webpack = require('webpack'); +const WebpackConfig = require('../../lib/WebpackConfig'); +const RuntimeConfig = require('../../lib/config/RuntimeConfig'); +const definePluginUtil = require('../../lib/plugins/define'); + +function createConfig(environment = 'production') { + const runtimeConfig = new RuntimeConfig(); + runtimeConfig.context = __dirname; + runtimeConfig.babelRcFileExists = false; + runtimeConfig.environment = environment; + + return new WebpackConfig(runtimeConfig); +} + +describe('plugins/define', () => { + it('dev environment', () => { + const config = createConfig('dev'); + const plugins = []; + + definePluginUtil(plugins, config); + expect(plugins.length).to.equal(0); + }); + + it('production environment with default settings', () => { + const config = createConfig(); + const plugins = []; + + definePluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(webpack.DefinePlugin); + }); + + it('production environment with options callback', () => { + const config = createConfig(); + const plugins = []; + + config.configureDefinePlugin((options) => { + options['foo'] = true; + options['process.env'].bar = true; + }); + + definePluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(webpack.DefinePlugin); + + // Allows to add new definitions + expect(plugins[0].definitions.foo).to.equal(true); + expect(plugins[0].definitions['process.env'].bar).to.equal(true); + + // Doesn't remove default definitions + expect(plugins[0].definitions['process.env'].NODE_ENV).to.equal(JSON.stringify('production')); + }); +}); diff --git a/test/plugins/extract-text.js b/test/plugins/extract-text.js new file mode 100644 index 00000000..ea3fead8 --- /dev/null +++ b/test/plugins/extract-text.js @@ -0,0 +1,73 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const expect = require('chai').expect; +const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const WebpackConfig = require('../../lib/WebpackConfig'); +const RuntimeConfig = require('../../lib/config/RuntimeConfig'); +const extractTextPluginUtil = require('../../lib/plugins/extract-text'); + +function createConfig() { + const runtimeConfig = new RuntimeConfig(); + runtimeConfig.context = __dirname; + runtimeConfig.babelRcFileExists = false; + + return new WebpackConfig(runtimeConfig); +} + +describe('plugins/extract-text', () => { + it('with default settings and versioning disabled', () => { + const config = createConfig(); + const plugins = []; + + extractTextPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(ExtractTextPlugin); + expect(plugins[0].filename).to.equal('[name].css'); + expect(plugins[0].options.allChunks).to.equal(false); + }); + + it('with default settings and versioning enabled', () => { + const config = createConfig(); + const plugins = []; + + config.enableVersioning(); + + extractTextPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(ExtractTextPlugin); + expect(plugins[0].filename).to.equal('[name].[contenthash].css'); + expect(plugins[0].options.allChunks).to.equal(false); + }); + + it('with options callback', () => { + const config = createConfig(); + const plugins = []; + + config.configureExtractTextPlugin((options) => { + options.disable = true; + options.filename = 'bar'; + }); + + extractTextPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(ExtractTextPlugin); + + // Allows to add new options + expect(plugins[0].options.disable).to.equal(true); + + // Allows to override default options + expect(plugins[0].filename).to.equal('bar'); + + // Doesn't remove default options + expect(plugins[0].options.allChunks).to.equal(false); + }); +}); diff --git a/test/plugins/friendly-errors.js b/test/plugins/friendly-errors.js new file mode 100644 index 00000000..2d337eea --- /dev/null +++ b/test/plugins/friendly-errors.js @@ -0,0 +1,51 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const expect = require('chai').expect; +const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin'); +const WebpackConfig = require('../../lib/WebpackConfig'); +const RuntimeConfig = require('../../lib/config/RuntimeConfig'); +const friendlyErrorsPluginUtil = require('../../lib/plugins/friendly-errors'); + +function createConfig() { + const runtimeConfig = new RuntimeConfig(); + runtimeConfig.context = __dirname; + runtimeConfig.babelRcFileExists = false; + + return new WebpackConfig(runtimeConfig); +} + +describe('plugins/friendly-errors', () => { + it('with default settings', () => { + const config = createConfig(); + + const plugin = friendlyErrorsPluginUtil(config); + expect(plugin).to.be.instanceof(FriendlyErrorsWebpackPlugin); + expect(plugin.shouldClearConsole).to.equal(false); + expect(plugin.formatters.length).to.equal(6); + expect(plugin.transformers.length).to.equal(6); + }); + + it('with options callback', () => { + const config = createConfig(); + + config.configureFriendlyErrorsPlugin((options) => { + options.clearConsole = true; + options.additionalFormatters = []; + }); + + const plugin = friendlyErrorsPluginUtil(config); + expect(plugin).to.be.instanceof(FriendlyErrorsWebpackPlugin); + expect(plugin.shouldClearConsole).to.equal(true); + expect(plugin.formatters.length).to.equal(3); + expect(plugin.transformers.length).to.equal(6); + }); +}); diff --git a/test/plugins/loader-options.js b/test/plugins/loader-options.js new file mode 100644 index 00000000..c3794c36 --- /dev/null +++ b/test/plugins/loader-options.js @@ -0,0 +1,66 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const expect = require('chai').expect; +const webpack = require('webpack'); +const WebpackConfig = require('../../lib/WebpackConfig'); +const RuntimeConfig = require('../../lib/config/RuntimeConfig'); +const loaderOptionsPluginUtil = require('../../lib/plugins/loader-options'); + +function createConfig(environment = 'production') { + const runtimeConfig = new RuntimeConfig(); + runtimeConfig.context = __dirname; + runtimeConfig.babelRcFileExists = false; + runtimeConfig.environment = environment; + + return new WebpackConfig(runtimeConfig); +} + +describe('plugins/loader-options', () => { + it('dev environment with default settings', () => { + const config = createConfig('dev'); + const plugins = []; + + loaderOptionsPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(webpack.LoaderOptionsPlugin); + expect(plugins[0].options.debug).to.equal(true); + }); + + it('production environment with default settings', () => { + const config = createConfig(); + const plugins = []; + + loaderOptionsPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(webpack.LoaderOptionsPlugin); + expect(plugins[0].options.debug).to.equal(false); + }); + + it('production environment with options callback', () => { + const config = createConfig(); + const plugins = []; + + config.configureLoaderOptionsPlugin((options) => { + options.debug = true; + }); + + loaderOptionsPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(webpack.LoaderOptionsPlugin); + + // Allows to override default options + expect(plugins[0].options.debug).to.equal(true); + + // Doesn't remove default options + expect(plugins[0].options.options.context).to.equal(config.getContext()); + }); +}); diff --git a/test/plugins/manifest.js b/test/plugins/manifest.js new file mode 100644 index 00000000..159e34ea --- /dev/null +++ b/test/plugins/manifest.js @@ -0,0 +1,58 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const expect = require('chai').expect; +const ManifestPlugin = require('../../lib/webpack/webpack-manifest-plugin'); +const WebpackConfig = require('../../lib/WebpackConfig'); +const RuntimeConfig = require('../../lib/config/RuntimeConfig'); +const manifestPluginUtil = require('../../lib/plugins/manifest'); + +function createConfig() { + const runtimeConfig = new RuntimeConfig(); + runtimeConfig.context = __dirname; + runtimeConfig.babelRcFileExists = false; + + const config = new WebpackConfig(runtimeConfig); + config.setPublicPath('/foo'); + return config; +} + +describe('plugins/manifest', () => { + it('default settings', () => { + const config = createConfig(); + const plugins = []; + + manifestPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(ManifestPlugin); + expect(plugins[0].opts.fileName).to.equal('manifest.json'); + expect(plugins[0].opts.publicPath).to.equal('/foo/'); + }); + + it('with options callback', () => { + const config = createConfig(); + const plugins = []; + + config.configureManifestPlugin((options) => { + options.fileName = 'bar'; + }); + + manifestPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(ManifestPlugin); + + // Allows to override default options + expect(plugins[0].opts.fileName).to.equal('bar'); + + // Doesn't remove default options + expect(plugins[0].opts.publicPath).to.equal('/foo/'); + }); +}); diff --git a/test/plugins/uglify.js b/test/plugins/uglify.js new file mode 100644 index 00000000..0ef2cec5 --- /dev/null +++ b/test/plugins/uglify.js @@ -0,0 +1,64 @@ +/* + * This file is part of the Symfony Webpack Encore package. + * + * (c) Fabien Potencier + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +'use strict'; + +const expect = require('chai').expect; +const webpack = require('webpack'); +const WebpackConfig = require('../../lib/WebpackConfig'); +const RuntimeConfig = require('../../lib/config/RuntimeConfig'); +const uglifyPluginUtil = require('../../lib/plugins/uglify'); + +function createConfig(environment = 'production') { + const runtimeConfig = new RuntimeConfig(); + runtimeConfig.context = __dirname; + runtimeConfig.babelRcFileExists = false; + runtimeConfig.environment = environment; + + return new WebpackConfig(runtimeConfig); +} + +describe('plugins/uglify', () => { + it('dev environment default settings', () => { + const config = createConfig('dev'); + const plugins = []; + + uglifyPluginUtil(plugins, config); + expect(plugins.length).to.equal(0); + }); + + it('production environment default settings', () => { + const config = createConfig(); + const plugins = []; + + uglifyPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(webpack.optimize.UglifyJsPlugin); + expect(plugins[0].options.sourceMap).to.equal(false); + }); + + it('with options callback', () => { + const config = createConfig(); + const plugins = []; + + config.configureUglifyJsPlugin((options) => { + options.beautify = true; + }); + + uglifyPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0]).to.be.instanceof(webpack.optimize.UglifyJsPlugin); + + // Allows to override default options + expect(plugins[0].options.beautify).to.equal(true); + + // Doesn't remove default options + expect(plugins[0].options.sourceMap).to.equal(false); + }); +});