├── .babelrc ├── .gitignore ├── .npmrc ├── .nvmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json ├── src └── ReloadServerPlugin.js └── test ├── ReloadServerPlugin.test.js ├── index.test.js └── mocha.opts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": [ 4 | "add-module-exports" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | .nyc_output 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 29 | node_modules 30 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save=true 2 | save-exact=false 3 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v5.1.0 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | notifications: 7 | email: false 8 | node_js: 9 | - "5" 10 | - "4" 11 | - "0.12" 12 | before_script: 13 | - npm prune 14 | after_success: npm run coverage 15 | branches: 16 | except: 17 | - "/^v\\d+\\.\\d+\\.\\d+$/" 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [1.0.1](https://github.com/ericclemmons/reload-server-webpack-plugin/tree/1.0.1) (2015-12-30) 4 | [Full Changelog](https://github.com/ericclemmons/reload-server-webpack-plugin/compare/v1.0.0...1.0.1) 5 | 6 | **Fixed bugs:** 7 | 8 | - Export to module.exports [\#2](https://github.com/ericclemmons/reload-server-webpack-plugin/pull/2) ([ericclemmons](https://github.com/ericclemmons)) 9 | 10 | ## [v1.0.0](https://github.com/ericclemmons/reload-server-webpack-plugin/tree/v1.0.0) (2015-12-30) 11 | **Implemented enhancements:** 12 | 13 | - Initial Release [\#1](https://github.com/ericclemmons/reload-server-webpack-plugin/pull/1) ([ericclemmons](https://github.com/ericclemmons)) 14 | 15 | 16 | 17 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Eric Clemmons 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # reload-server-webpack-plugin 2 | 3 | > Webpack plugin that automatically (re)starts your server between builds. 4 | 5 | [![travis build](https://img.shields.io/travis/ericclemmons/reload-server-webpack-plugin.svg)](https://travis-ci.org/ericclemmons/reload-server-webpack-plugin) 6 | [![Coverage Status](https://coveralls.io/repos/ericclemmons/reload-server-webpack-plugin/badge.svg?branch=master&service=github)](https://coveralls.io/github/ericclemmons/reload-server-webpack-plugin?branch=master) 7 | [![version](https://img.shields.io/npm/v/reload-server-webpack-plugin.svg)](http://npm.im/reload-server-webpack-plugin) 8 | [![downloads](https://img.shields.io/npm/dm/reload-server-webpack-plugin.svg)](http://npm-stat.com/charts.html?package=reload-server-webpack-plugin) 9 | [![MIT License](https://img.shields.io/npm/l/reload-server-webpack-plugin.svg)](http://opensource.org/licenses/MIT) 10 | 11 | - - - 12 | 13 | ### Why? 14 | 15 | - Remove your dependency on `nodemon`, `forever`, `pm2`, or similar. 16 | - This works better from a "cold start" when your server hasn't been built yet. 17 | - Fewer issues with websockets & hot-module reloading. 18 | 19 | ### Installation 20 | 21 | ```shell 22 | $ npm install --save-dev reload-server-webpack-plugin 23 | ``` 24 | 25 | ### Usage 26 | 27 | Update your `webpack.config.js`: 28 | 29 | ```js 30 | module.exports = { 31 | ... 32 | plugins: [ 33 | new ReloadServerPlugin({ 34 | // Defaults to process.cwd() + "/server.js" 35 | script: "path/to/server.js", 36 | }), 37 | ], 38 | ... 39 | }; 40 | ``` 41 | 42 | ### license 43 | 44 | > MIT License 2015 © Eric Clemmons 45 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./build/ReloadServerPlugin.js"); 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reload-server-webpack-plugin", 3 | "version": "1.0.1", 4 | "description": "Webpack plugin that automatically (re)starts your server between builds.", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js", 8 | "build" 9 | ], 10 | "config": { 11 | "nyc": { 12 | "exclude": [ 13 | "build" 14 | ] 15 | } 16 | }, 17 | "scripts": { 18 | "build": "babel src -d build", 19 | "changelog": "npm run changelog:generate && npm run changelog:add", 20 | "changelog:add": "git add CHANGELOG.md", 21 | "changelog:generate": "github_changelog_generator --future-release $npm_package_version", 22 | "coverage": "NODE_ENV=test npm test && nyc report --reporter=text-lcov | coveralls", 23 | "preversion": "npm run build", 24 | "postversion": "npm run version:amend && git push origin master --tags && npm publish", 25 | "pretest": "npm run build", 26 | "test": "nyc mocha", 27 | "version": "npm run changelog", 28 | "version:amend": "git commit --amend -m \"Release v${npm_package_version}\"" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/ericclemmons/reload-server-webpack-plugin.git" 33 | }, 34 | "keywords": [ 35 | "webpack", 36 | "webpack-plugin", 37 | "restart", 38 | "server", 39 | "start", 40 | "cluster" 41 | ], 42 | "author": "Eric Clemmons ", 43 | "license": "MIT", 44 | "bugs": { 45 | "url": "https://github.com/ericclemmons/reload-server-webpack-plugin/issues" 46 | }, 47 | "homepage": "https://github.com/ericclemmons/reload-server-webpack-plugin#readme", 48 | "devDependencies": { 49 | "babel-cli": "^6.3.17", 50 | "babel-plugin-add-module-exports": "^0.1.2", 51 | "babel-preset-es2015": "^6.3.13", 52 | "babel-register": "^6.3.13", 53 | "coveralls": "^2.11.6", 54 | "expect": "^1.13.4", 55 | "mocha": "^2.3.4", 56 | "nyc": "^5.0.1" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/ReloadServerPlugin.js: -------------------------------------------------------------------------------- 1 | import cluster from "cluster"; 2 | import path from "path"; 3 | 4 | const defaultOptions = { 5 | script: "server.js", 6 | }; 7 | 8 | export default class ReloadServerPlugin { 9 | constructor({ script } = defaultOptions) { 10 | this.done = null; 11 | this.workers = []; 12 | 13 | cluster.setupMaster({ 14 | exec: path.resolve(process.cwd(), script), 15 | }); 16 | 17 | cluster.on("online", (worker) => { 18 | this.workers.push(worker); 19 | 20 | if (this.done) { 21 | this.done(); 22 | } 23 | }); 24 | } 25 | 26 | apply(compiler) { 27 | compiler.hooks.afterEmit.tap({ 28 | name: 'reload-server', 29 | }, (compilation, callback) => { 30 | this.done = callback; 31 | this.workers.forEach((worker) => { 32 | try { 33 | process.kill(worker.process.pid, "SIGTERM"); 34 | } catch (e) { 35 | console.warn(`Unable to kill process #${worker.process.pid}`); 36 | } 37 | }); 38 | 39 | this.workers = []; 40 | 41 | cluster.fork(); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/ReloadServerPlugin.test.js: -------------------------------------------------------------------------------- 1 | import cluster from "cluster"; 2 | import expect from "expect"; 3 | import path from "path"; 4 | import Plugin from "../src/ReloadServerPlugin"; 5 | 6 | describe("ReloadServerPlugin", function() { 7 | beforeEach(function() { 8 | this.fork = expect.spyOn(cluster, "fork"); 9 | this.on = expect.spyOn(cluster, "on").andCallThrough(); 10 | this.setupMaster = expect.spyOn(cluster, "setupMaster"); 11 | 12 | this.plugin = new Plugin(); 13 | }); 14 | 15 | afterEach(function() { 16 | this.fork.restore(); 17 | this.on.restore(); 18 | this.setupMaster.restore(); 19 | 20 | cluster.removeAllListeners(); 21 | }); 22 | 23 | describe(".constructor", function() { 24 | it("should not have .done set", function() { 25 | expect(this.plugin.done).toEqual(null); 26 | }); 27 | 28 | it("should not have any .workers", function() { 29 | expect(this.plugin.workers).toEqual([]); 30 | }); 31 | 32 | it("should setupMaster", function() { 33 | expect(this.setupMaster).toHaveBeenCalled(); 34 | }); 35 | 36 | it("should default to server.js", function() { 37 | expect(this.setupMaster.calls[0].arguments).toEqual([ 38 | { exec: path.join(process.cwd(), "server.js") }, 39 | ]); 40 | }); 41 | 42 | it("should listen to the cluster", function() { 43 | expect(this.on).toHaveBeenCalled(); 44 | }); 45 | 46 | it("should listen to online events", function() { 47 | expect(this.on.calls[0].arguments[0]).toEqual("online"); 48 | }); 49 | }); 50 | 51 | describe(".apply", function() { 52 | beforeEach(function() { 53 | this.compiler = { 54 | hooks: { 55 | afterEmit: { 56 | tap: expect.createSpy() 57 | }, 58 | } 59 | }; 60 | 61 | this.plugin.apply(this.compiler); 62 | }); 63 | 64 | it("should add an after-emit hook", function() { 65 | expect(this.compiler.hooks.afterEmit.tap).toHaveBeenCalled(); 66 | expect(this.compiler.hooks.afterEmit.tap.calls[0].arguments[0]).toEqual({ name: 'reload-server' }); 67 | }); 68 | }); 69 | 70 | context("when after-emit is fired", function() { 71 | beforeEach(function() { 72 | this.callback = expect.createSpy(); 73 | this.compiler = { 74 | hooks: { 75 | afterEmit: { 76 | tap: (event, callback) => { 77 | expect(event.name).toEqual('reload-server'); 78 | 79 | callback({}, this.callback); 80 | }, 81 | }, 82 | } 83 | }; 84 | 85 | this.kill = expect.spyOn(process, "kill"); 86 | 87 | this.plugin.workers = [ 88 | { 89 | process: { 90 | pid: "PID" 91 | } 92 | } 93 | ]; 94 | 95 | this.plugin.apply(this.compiler); 96 | }); 97 | 98 | afterEach(function() { 99 | this.callback.restore(); 100 | this.kill.restore(); 101 | }); 102 | 103 | it("should set .done to the callback", function() { 104 | expect(this.plugin.done).toEqual(this.callback); 105 | }); 106 | 107 | it("should kill each worker", function() { 108 | expect(this.kill).toHaveBeenCalled(); 109 | expect(this.kill.calls.length).toEqual(1); 110 | expect(this.kill).toHaveBeenCalledWith("PID", "SIGTERM"); 111 | }); 112 | 113 | it("should clear out the worker queue", function() { 114 | expect(this.plugin.workers).toEqual([]); 115 | }); 116 | 117 | it("should fork a new worker", function() { 118 | expect(this.fork).toHaveBeenCalled(); 119 | }); 120 | }); 121 | 122 | context("when worker is online", function() { 123 | beforeEach(function() { 124 | this.plugin.done = expect.createSpy(); 125 | this.worker = { id: 1 }; 126 | 127 | cluster.emit("online", this.worker); 128 | }); 129 | 130 | afterEach(function() { 131 | this.plugin.done.restore(); 132 | }); 133 | 134 | it("should add online workers to the queue", function() { 135 | expect(this.plugin.workers).toEqual([this.worker]); 136 | }); 137 | 138 | it("should call .done", function() { 139 | expect(this.plugin.done).toHaveBeenCalled(); 140 | }); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | import expect from "expect"; 2 | import imported from ".."; 3 | 4 | const Plugin = require("../build/ReloadServerPlugin"); 5 | const required = require(".."); 6 | 7 | describe("ReloadServerPlugin", function() { 8 | it("should be import-able", function() { 9 | expect(imported).toEqual(Plugin); 10 | }); 11 | 12 | it("should be require-able", function() { 13 | expect(required).toEqual(Plugin); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers js:babel-register 2 | --------------------------------------------------------------------------------