diff --git a/index.js b/index.js index 35b43c54..ddf5c799 100644 --- a/index.js +++ b/index.js @@ -608,6 +608,30 @@ const publicApi = { return this; }, + /** + * If enabled, display build notifications using + * webpack-notifier. + * + * https://github.com/Turbo87/webpack-notifier + * + * Encore.enableBuildNotifications(); + * + * // or configure the webpack-notifier options + * // https://github.com/Turbo87/webpack-notifier#configuration + * Encore.enableBuildNotifications(true, function(options) { + * options.title = 'Webpack build'; + * }); + * + * @param {boolean} enabled + * @param {function} notifierPluginOptionsCallback + * @returns {exports} + */ + enableBuildNotifications(enabled = true, notifierPluginOptionsCallback = () => {}) { + webpackConfig.enableBuildNotifications(enabled, notifierPluginOptionsCallback); + + return this; + }, + /** * Call this if you wish to disable the default * images loader. diff --git a/lib/WebpackConfig.js b/lib/WebpackConfig.js index 5b859b0c..89b7b54e 100644 --- a/lib/WebpackConfig.js +++ b/lib/WebpackConfig.js @@ -60,6 +60,7 @@ class WebpackConfig { this.useVueLoader = false; this.useTypeScriptLoader = false; this.useForkedTypeScriptTypeChecking = false; + this.useWebpackNotifier = false; // Features/Loaders options this.sassOptions = { @@ -89,6 +90,7 @@ class WebpackConfig { this.loaderOptionsPluginOptionsCallback = () => {}; this.manifestPluginOptionsCallback = () => {}; this.uglifyJsPluginOptionsCallback = () => {}; + this.notifierPluginOptionsCallback = () => {}; } getContext() { @@ -389,6 +391,15 @@ class WebpackConfig { this.vueLoaderOptionsCallback = vueLoaderOptionsCallback; } + enableBuildNotifications(enabled = true, notifierPluginOptionsCallback = () => {}) { + if (typeof notifierPluginOptionsCallback !== 'function') { + throw new Error('Argument 2 to enableBuildNotifications() must be a callback function.'); + } + + this.useWebpackNotifier = enabled; + this.notifierPluginOptionsCallback = notifierPluginOptionsCallback; + } + disableImagesLoader() { this.useImagesLoader = false; } diff --git a/lib/config-generator.js b/lib/config-generator.js index 5b634c40..2287e940 100644 --- a/lib/config-generator.js +++ b/lib/config-generator.js @@ -31,6 +31,7 @@ const definePluginUtil = require('./plugins/define'); const uglifyPluginUtil = require('./plugins/uglify'); const friendlyErrorPluginUtil = require('./plugins/friendly-errors'); const assetOutputDisplay = require('./plugins/asset-output-display'); +const notifierPluginUtil = require('./plugins/notifier'); const PluginPriorities = require('./plugins/plugin-priorities'); class ConfigGenerator { @@ -232,6 +233,8 @@ class ConfigGenerator { uglifyPluginUtil(plugins, this.webpackConfig); + notifierPluginUtil(plugins, this.webpackConfig); + const friendlyErrorPlugin = friendlyErrorPluginUtil(this.webpackConfig); plugins.push({ plugin: friendlyErrorPlugin, diff --git a/lib/features.js b/lib/features.js index 6d419dea..e1c361d2 100644 --- a/lib/features.js +++ b/lib/features.js @@ -57,6 +57,11 @@ const features = { // vue-template-compiler is a peer dep of vue-loader packages: ['vue', 'vue-loader', 'vue-template-compiler'], description: 'load VUE files' + }, + notifier: { + method: 'enableBuildNotifications()', + packages: ['webpack-notifier'], + description: 'display build notifications' } }; diff --git a/lib/plugins/notifier.js b/lib/plugins/notifier.js new file mode 100644 index 00000000..838bd424 --- /dev/null +++ b/lib/plugins/notifier.js @@ -0,0 +1,41 @@ +/* + * 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 pluginFeatures = require('../features'); +const PluginPriorities = require('./plugin-priorities'); + +/** + * @param {Array} plugins + * @param {WebpackConfig} webpackConfig + * @return {void} + */ +module.exports = function(plugins, webpackConfig) { + if (!webpackConfig.useWebpackNotifier) { + return; + } + + pluginFeatures.ensurePackagesExist('notifier'); + + const notifierPluginOptions = { + title: 'Webpack Encore' + }; + + webpackConfig.notifierPluginOptionsCallback.apply( + notifierPluginOptions, + [notifierPluginOptions] + ); + + const WebpackNotifier = require('webpack-notifier'); // eslint-disable-line + plugins.push({ + plugin: new WebpackNotifier(notifierPluginOptions), + priority: PluginPriorities.WebpackNotifier + }); +}; diff --git a/lib/plugins/plugin-priorities.js b/lib/plugins/plugin-priorities.js index 3d397a0d..41098c71 100644 --- a/lib/plugins/plugin-priorities.js +++ b/lib/plugins/plugin-priorities.js @@ -25,4 +25,5 @@ module.exports = { HashedModuleIdsPlugin: 0, NamedModulesPlugin: 0, WebpackChunkHash: 0, + WebpackNotifier: 0, }; diff --git a/package.json b/package.json index 58ce6f75..b9a93b28 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "vue": "^2.3.4", "vue-loader": "^12.2.1", "vue-template-compiler": "^2.3.4", + "webpack-notifier": "^1.5.0", "zombie": "^5.0.5" } } diff --git a/test/WebpackConfig.js b/test/WebpackConfig.js index b1425156..872ae985 100644 --- a/test/WebpackConfig.js +++ b/test/WebpackConfig.js @@ -604,6 +604,38 @@ describe('WebpackConfig object', () => { }); }); + + describe('enableBuildNotifications', () => { + it('Calling method with default values', () => { + const config = createConfig(); + config.enableBuildNotifications(); + + expect(config.useWebpackNotifier).to.be.true; + }); + + it('Calling method without enabling it', () => { + const config = createConfig(); + config.enableBuildNotifications(false); + + expect(config.useWebpackNotifier).to.be.false; + }); + + it('Calling method with options callback', () => { + const config = createConfig(); + const callback = () => {}; + config.enableBuildNotifications(true, callback); + + expect(config.useWebpackNotifier).to.be.true; + expect(config.notifierPluginOptionsCallback).to.equal(callback); + }); + + it('Calling method with invalid options callback', () => { + const config = createConfig(); + + expect(() => config.enableBuildNotifications(true, 'FOO')).to.throw('must be a callback function'); + }); + }); + describe('addPlugin', () => { it('extends the current registered plugins', () => { const config = createConfig(); diff --git a/test/index.js b/test/index.js index 5c8ce33c..bc63c6f8 100644 --- a/test/index.js +++ b/test/index.js @@ -215,6 +215,15 @@ describe('Public API', () => { }); + describe('enableBuildNotifications', () => { + + it('must return the API object', () => { + const returnedValue = api.enableBuildNotifications(); + expect(returnedValue).to.equal(api); + }); + + }); + describe('disableImagesLoader', () => { it('must return the API object', () => { diff --git a/test/plugins/notifier.js b/test/plugins/notifier.js new file mode 100644 index 00000000..347ea3d8 --- /dev/null +++ b/test/plugins/notifier.js @@ -0,0 +1,70 @@ +/* + * 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 WebpackNotifier = require('webpack-notifier'); +const WebpackConfig = require('../../lib/WebpackConfig'); +const RuntimeConfig = require('../../lib/config/RuntimeConfig'); +const notifierPluginUtil = require('../../lib/plugins/notifier'); + +function createConfig() { + const runtimeConfig = new RuntimeConfig(); + runtimeConfig.context = __dirname; + runtimeConfig.babelRcFileExists = false; + + return new WebpackConfig(runtimeConfig); +} + +describe('plugins/notifier', () => { + it('disabled by default', () => { + const config = createConfig(); + const plugins = []; + + notifierPluginUtil(plugins, config); + expect(plugins.length).to.equal(0); + }); + + it('explicitly disabled', () => { + const config = createConfig(); + const plugins = []; + + config.enableBuildNotifications(false); + + notifierPluginUtil(plugins, config); + expect(plugins.length).to.equal(0); + }); + + it('enabled with default settings', () => { + const config = createConfig(); + const plugins = []; + + config.enableBuildNotifications(); + + notifierPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0].plugin).to.be.instanceof(WebpackNotifier); + expect(plugins[0].plugin.options.title).to.equal('Webpack Encore'); + }); + + it('enabled with options callback', () => { + const config = createConfig(); + const plugins = []; + + config.enableBuildNotifications(true, (options) => { + options.title = 'foo'; + }); + + notifierPluginUtil(plugins, config); + expect(plugins.length).to.equal(1); + expect(plugins[0].plugin).to.be.instanceof(WebpackNotifier); + expect(plugins[0].plugin.options.title).to.equal('foo'); + }); +}); diff --git a/yarn.lock b/yarn.lock index 85a358b7..f08334ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -136,6 +136,10 @@ ansi-styles@^3.1.0: dependencies: color-convert "^1.9.0" +ansicolors@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" + anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" @@ -1161,6 +1165,13 @@ caniuse-lite@^1.0.30000704: version "1.0.30000708" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000708.tgz#71dbf388c57f379b1bb66c89a890edc04c2509b6" +cardinal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-1.0.0.tgz#50e21c1b0aa37729f9377def196b5a9cec932ee9" + dependencies: + ansicolors "~0.2.1" + redeyed "~1.0.0" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -1255,6 +1266,13 @@ cli-table@^0.3.1: dependencies: colors "1.0.3" +cli-usage@^0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/cli-usage/-/cli-usage-0.1.4.tgz#7c01e0dc706c234b39c933838c8e20b2175776e2" + dependencies: + marked "^0.3.6" + marked-terminal "^1.6.2" + cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" @@ -2115,6 +2133,10 @@ esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" +esprima@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.0.0.tgz#53cf247acda77313e551c3aa2e73342d3fb4f7d9" + esquery@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" @@ -2607,6 +2629,10 @@ growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" +growly@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" @@ -3325,6 +3351,14 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +lodash._arraycopy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" + +lodash._arrayeach@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz#bab156b2a90d3f1bbd5c653403349e5e5933ef9e" + lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -3332,6 +3366,17 @@ lodash._baseassign@^3.0.0: lodash._basecopy "^3.0.0" lodash.keys "^3.0.0" +lodash._baseclone@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz#303519bf6393fe7e42f34d8b630ef7794e3542b7" + dependencies: + lodash._arraycopy "^3.0.0" + lodash._arrayeach "^3.0.0" + lodash._baseassign "^3.0.0" + lodash._basefor "^3.0.0" + lodash.isarray "^3.0.0" + lodash.keys "^3.0.0" + lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -3340,6 +3385,10 @@ lodash._basecreate@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" +lodash._basefor@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" + lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" @@ -3376,6 +3425,13 @@ lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" +lodash.clonedeep@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-3.0.2.tgz#a0a1e40d82a5ea89ff5b147b8444ed63d92827db" + dependencies: + lodash._baseclone "^3.0.0" + lodash._bindcallback "^3.0.0" + lodash.clonedeep@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -3447,6 +3503,10 @@ lodash.tail@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" +lodash.toarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -3511,6 +3571,20 @@ map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" +marked-terminal@^1.6.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-1.7.0.tgz#c8c460881c772c7604b64367007ee5f77f125904" + dependencies: + cardinal "^1.0.0" + chalk "^1.1.3" + cli-table "^0.3.1" + lodash.assign "^4.2.0" + node-emoji "^1.4.1" + +marked@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7" + math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" @@ -3620,7 +3694,7 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -3704,6 +3778,12 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +node-emoji@^1.4.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.8.1.tgz#6eec6bfb07421e2148c75c6bba72421f8530a826" + dependencies: + lodash.toarray "^4.4.0" + node-fetch@^1.0.1: version "1.7.2" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.2.tgz#c54e9aac57e432875233525f3c891c4159ffefd7" @@ -3761,6 +3841,18 @@ node-libs-browser@^2.0.0: util "^0.10.3" vm-browserify "0.0.4" +node-notifier@^4.1.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-4.6.1.tgz#056d14244f3dcc1ceadfe68af9cff0c5473a33f3" + dependencies: + cli-usage "^0.1.1" + growly "^1.2.0" + lodash.clonedeep "^3.0.0" + minimist "^1.1.1" + semver "^5.1.0" + shellwords "^0.1.0" + which "^1.0.5" + node-pre-gyp@^0.6.36: version "0.6.36" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" @@ -4724,6 +4816,12 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +redeyed@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-1.0.1.tgz#e96c193b40c0816b00aec842698e61185e55498a" + dependencies: + esprima "~3.0.0" + reduce-css-calc@^1.2.6: version "1.3.0" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" @@ -5016,7 +5114,7 @@ selfsigned@^1.9.1: dependencies: node-forge "0.6.33" -"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.3.0: +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -5116,6 +5214,10 @@ shelljs@^0.7.5: interpret "^1.0.0" rechoir "^0.6.2" +shellwords@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + sigmund@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" @@ -5843,6 +5945,14 @@ webpack-dev-server@^2.4.5: webpack-dev-middleware "^1.11.0" yargs "^6.0.0" +webpack-notifier@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/webpack-notifier/-/webpack-notifier-1.5.0.tgz#c010007d448cebc34defc99ecf288fa5e8c6baf6" + dependencies: + node-notifier "^4.1.0" + object-assign "^4.1.0" + strip-ansi "^3.0.1" + webpack-sources@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf" @@ -5915,6 +6025,12 @@ which@1, which@^1.2.9: dependencies: isexe "^2.0.0" +which@^1.0.5: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"