diff --git a/README.md b/README.md index 4d650a4..b0c1cd0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,48 @@ # webpack-bundle-update-hook-plugin + Add a tapable 'bundle-update' hook to webpack. On bundle updates registered plugins get lists of new, changed and removed modules. + +This plugin can be useful in combination with [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) or [webpack-dev-server](https://github.com/webpack/webpack-dev-server). The plugin adds a new hook `bundle-update` to which other plugins can register. After each bundle update, registered plugins receive lists of new, changed and removed files. + +## Installation + + npm install --save-dev webpack-bundle-update-hook-plugin + +## Usage + +Add the plugin to your `webpack.config.js` file: + + var BundleUpdateHookPlugin = require('webpack-bundle-update-hook-plugin'); + + module.exports = { + + // ... + + plugins: [ + // ... + new BundleUpdateHookPlugin() + ] + + }; + +Then use [webpack's Node.js API](https://webpack.github.io/docs/node.js-api.html) to get a compiler with this configuration. The compiler exposes the [tapable API](https://github.com/webpack/tapable) for registering plugins. With the webpack-bundle-update-hook-plugin you can now create plugins for the hook `bundle-update`: + + var webpack = require('webpack'); + var webpackConfig = require('./webpack.config'); + + var compiler = webpack(webpackConfig); + + compiler.plugin('bundle-update', function (newModules, changedModules, removedModules, stats) { + // newModules, changedModules and removedModules are objects. The properties of the objects are module ids, the + // values of these properties are the corresponding module resources (= absolute module paths). + + // stats is passed as is from the compilers 'done' plugin hook. + + console.log('new modules: ', newModules); + console.log('changed modules: ', changedModules); + console.log('removed modules: ', removedModules); + }); + +## License + +MIT (see LICENSE file) \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..8c72682 --- /dev/null +++ b/index.js @@ -0,0 +1,88 @@ +/** + * Create a new BundleUpdateHookPlugin instance. + * @param {Object.} options Available options: { debug: true|false } (debug defaults to false) + * @constructor + */ +function BundleUpdateHookPlugin(options) { + options = options || {}; + this.debug = options.debug || false; +} + +/** + * Register the plugin on the 'done' hook. + * @param compiler + */ +BundleUpdateHookPlugin.prototype.apply = function(compiler) { + + var that = this; + var prevModules; + + // do our things after a compilation is done + compiler.plugin('done', function(stats) { + + var debug = that.debug; + + var currModules = {}; + var newModules = {}; + var changedModules = {}; + var removedModules = {}; + + stats.compilation.modules.forEach(function (module) { + // skip modules which don't have a resource property (e.g. main module 0) + if (!module.resource) return; + + currModules[module.id] = { + buildTimestamp: module.buildTimestamp, + resource: module.resource + }; + + // if prevModules exists, check if current module is new or has changed + if (prevModules) { + if (!prevModules[module.id]) { + // current module is new + if (debug) console.log('[webpack-bundle-update-hook-plugin] detected new module: ' + + module.resource); + newModules[module.id] = module.resource; + } else if (prevModules[module.id].buildTimestamp !== module.buildTimestamp) { + // current module has changed + if (debug) console.log('[webpack-bundle-update-hook-plugin] detected changed module: ' + + module.resource); + changedModules[module.id] = module.resource; + } + } + }); + + // if prevModules exists, check for removed modules + if (prevModules) { + Object.keys(prevModules).forEach(function (id) { + if (!currModules[id]) { + // module was removed + if (debug) console.log('[webpack-bundle-update-hook-plugin] detected removed module: ' + + prevModules[id].resource); + removedModules[id] = prevModules[id].resource; + } + }); + } + + prevModules = currModules; + + var newModulesCount = Object.getOwnPropertyNames(newModules).length; + var changedModulesCount = Object.getOwnPropertyNames(changedModules).length; + var removedModulesCount = Object.getOwnPropertyNames(removedModules).length; + + // if anything changed, emit bundle-update event + if (newModulesCount !== 0 || changedModulesCount !== 0 || removedModulesCount !== 0) { + if (debug) console.log('[webpack-bundle-update-hook-plugin] a new bundle was compiled (' + + newModulesCount + ' new, ' + changedModulesCount + ' changed, ' + removedModulesCount + + ' removed modules) - emitting bundle-update'); + this.applyPlugins('bundle-update', newModules, changedModules, removedModules, stats); + } else { + if (debug) console.log('[webpack-bundle-update-hook-plugin] a new bundle was compiled but nothing changed' + + ' (this will always apply to the first compilation)'); + } + + }); + +}; + +module.exports = BundleUpdateHookPlugin; diff --git a/package.json b/package.json new file mode 100644 index 0000000..372731f --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "webpack-bundle-update-hook-plugin", + "version": "1.0.0", + "description": "Add a tapable 'bundle-update' hook to webpack. On bundle updates registered plugins get lists of new, changed and removed modules.", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://github.com/skleeschulte/webpack-bundle-update-hook-plugin.git" + }, + "keywords": [ + "webpack", + "plugin", + "tapable", + "hook", + "bundle", + "update", + "new", + "changed", + "removed" + ], + "author": "Stefan Kleeschulte", + "license": "MIT" +}