├── .nvmrc ├── .gitignore ├── test ├── mocha.opts └── StartServerPlugin.test.js ├── .babelrc ├── .travis.yml ├── LICENSE ├── package.json ├── README.md └── src └── StartServerPlugin.js /.nvmrc: -------------------------------------------------------------------------------- 1 | v5.10.1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --compilers js:babel-register 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | notifications: 7 | email: false 8 | branches: 9 | except: 10 | - /^v[0-9]/ 11 | deploy: 12 | provider: script 13 | script: npm run release 14 | skip_cleanup: true 15 | on: 16 | branch: master 17 | -------------------------------------------------------------------------------- /test/StartServerPlugin.test.js: -------------------------------------------------------------------------------- 1 | import expect from "expect"; 2 | import Plugin from ".."; 3 | 4 | describe("StartServerPlugin", function() { 5 | it("should be `import`-able", function() { 6 | expect(Plugin).toBeA(Function); 7 | }); 8 | 9 | it("should be `require`-able", function() { 10 | expect(require("..")).toBe(Plugin); 11 | }); 12 | 13 | it("should accept a string name", function() { 14 | const p = new Plugin('test'); 15 | expect(p.options.name).toBe('test'); 16 | }); 17 | 18 | it("should accept an options object", function() { 19 | const p = new Plugin({whee: true}); 20 | expect(p.options.whee).toBe(true); 21 | }); 22 | 23 | it("should calculate arguments", function() { 24 | const p = new Plugin({nodeArgs: ['meep'], args: ['moop']}); 25 | const args = p._getArgs(); 26 | expect(args.filter(a => a === 'meep').length).toBe(1); 27 | expect(args.slice(-2)).toEqual(['--', 'moop']); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "start-server-webpack-plugin", 3 | "version": "2.2.0", 4 | "description": "Automatically start your server once Webpack's build completes.", 5 | "main": "dist/StartServerPlugin.js", 6 | "files": [ 7 | "dist" 8 | ], 9 | "scripts": { 10 | "prebuild": "rimraf dist", 11 | "build": "babel src -d dist", 12 | "release": "github-semantic-version", 13 | "prepublish": "npm run build", 14 | "pretest": "npm run build", 15 | "test": "mocha" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/ericclemmons/start-server-webpack-plugin.git" 20 | }, 21 | "keywords": [ 22 | "webpack", 23 | "server", 24 | "start", 25 | "watch", 26 | "restart", 27 | "express" 28 | ], 29 | "author": "Eric Clemmons ", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/ericclemmons/start-server-webpack-plugin/issues" 33 | }, 34 | "homepage": "https://github.com/ericclemmons/start-server-webpack-plugin#readme", 35 | "devDependencies": { 36 | "babel-cli": "6.7.5", 37 | "babel-preset-es2015": "6.6.0", 38 | "babel-register": "6.7.2", 39 | "expect": "1.16.0", 40 | "github-semantic-version": "4.0.13", 41 | "mocha": "2.4.5", 42 | "rimraf": "2.5.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # start-server-webpack-plugin 2 | 3 | > Automatically start your server once Webpack's build completes. 4 | 5 | [![travis build](https://img.shields.io/travis/ericclemmons/start-server-webpack-plugin.svg)](https://travis-ci.org/ericclemmons/start-server-webpack-plugin) 6 | [![version](https://img.shields.io/npm/v/start-server-webpack-plugin.svg)](http://npm.im/start-server-webpack-plugin) 7 | [![downloads](https://img.shields.io/npm/dm/start-server-webpack-plugin.svg)](http://npm-stat.com/charts.html?package=start-server-webpack-plugin) 8 | [![MIT License](https://img.shields.io/npm/l/start-server-webpack-plugin.svg)](http://opensource.org/licenses/MIT) 9 | 10 | ### Installation 11 | 12 | ```shell 13 | $ npm install --save-dev start-server-webpack-plugin 14 | ``` 15 | 16 | ### Usage 17 | 18 | In `webpack.config.server.babel.js`: 19 | 20 | ```js 21 | import StartServerPlugin from "start-server-webpack-plugin"; 22 | 23 | export default { 24 | // This script will be ran after building 25 | entry: { 26 | server: ... 27 | }, 28 | ... 29 | plugins: [ 30 | ... 31 | // Only use this in DEVELOPMENT 32 | new StartServerPlugin({ 33 | name: 'server.js', 34 | nodeArgs: ['--inspect'], // allow debugging 35 | args: ['scriptArgument1', 'scriptArgument2'], // pass args to script 36 | }), 37 | ... 38 | ], 39 | ... 40 | } 41 | ``` 42 | 43 | The `name` argument in `new StartServerPlugin(name)` refers to the built asset, which is named by the output options of webpack (in the example the entry `server` becomes `server.js`. This way, the plugin knows which entry to start in case there are several. 44 | 45 | If you don't pass a name, the plugin will tell you the available names. 46 | 47 | You can use `nodeArgs` and `args` to pass arguments to node and your script, respectively. For example, you can use this to use the node debugger. 48 | 49 | ### License 50 | 51 | > MIT License 2016 © Eric Clemmons 52 | -------------------------------------------------------------------------------- /src/StartServerPlugin.js: -------------------------------------------------------------------------------- 1 | import cluster from "cluster"; 2 | 3 | export default class StartServerPlugin { 4 | constructor(options) { 5 | if (options == null) { 6 | options = {}; 7 | } 8 | if (typeof options === 'string') { 9 | options = {name: options}; 10 | } 11 | this.options = options; 12 | this.afterEmit = this.afterEmit.bind(this); 13 | this.apply = this.apply.bind(this); 14 | this.startServer = this.startServer.bind(this); 15 | 16 | this.worker = null; 17 | } 18 | 19 | _getArgs() { 20 | const {options} = this; 21 | const execArgv = (options.nodeArgs || []).concat(process.execArgv); 22 | if (options.args) { 23 | execArgv.push('--'); 24 | execArgv.push.apply(execArgv, options.args); 25 | } 26 | return execArgv; 27 | } 28 | 29 | afterEmit(compilation, callback) { 30 | if (this.worker && this.worker.isConnected()) { 31 | return callback(); 32 | } 33 | 34 | this.startServer(compilation, callback); 35 | } 36 | 37 | apply(compiler) { 38 | compiler.plugin("after-emit", this.afterEmit); 39 | } 40 | 41 | startServer(compilation, callback) { 42 | const {options} = this; 43 | let name; 44 | const names = Object.keys(compilation.assets); 45 | if (options.name) { 46 | name = options.name; 47 | if (!compilation.assets[name]) { 48 | console.error("Entry " + name + " not found. Try one of: " + names.join(" ")); 49 | } 50 | } else { 51 | name = names[0]; 52 | if (names.length > 1) { 53 | console.log("More than one entry built, selected " + name + ". All names: " + names.join(" ")); 54 | } 55 | } 56 | const { existsAt } = compilation.assets[name]; 57 | const execArgv = this._getArgs(); 58 | 59 | cluster.setupMaster({ exec: existsAt, execArgv }); 60 | 61 | cluster.on("online", (worker) => { 62 | this.worker = worker; 63 | callback(); 64 | }); 65 | 66 | cluster.fork(); 67 | } 68 | } 69 | 70 | module.exports = StartServerPlugin; 71 | --------------------------------------------------------------------------------