Webpack --watch and launching nodemon?
Asked Answered
E

10

48

Thanks to an excellent answer by @McMath I now have webpack compiling both my client and my server. I'm now on to trying to make webpack --watch be useful. Ideally I'd like to have it spawn something like nodemon for my server process when that bundle changes, and some flavor of browsersync for when my client changes.

I realize it's a bundler/loader and not really a task runner, but is there some way to accomplish this? A lack of google results seems to indicate I'm trying something new, but this must have been done already..

I can always have webpack package to another directory and use gulp to watch it/copy it/browsersync-ify it, but that seems like a hack.. Is there a better way?

Epirus answered 22/2, 2016 at 2:57 Comment(0)
C
37

Faced the same problem and found the next solution - webpack-shell-plugin. It

allows you to run any shell commands before or after webpack builds

So, thats my scripts in package.json:

"scripts": {
      "clean": "rimraf build",
      "prestart": "npm run clean",
      "start": "webpack --config webpack.client.config.js",
      "poststart": "webpack --watch --config webpack.server.config.js",
}

If I run 'start' script it launches next script sequence: clean -> start -> poststart. And there is part of 'webpack.server.config.js':

var WebpackShellPlugin = require('webpack-shell-plugin');

...
if (process.env.NODE_ENV !== 'production') {
    config.plugins.push(new WebpackShellPlugin({onBuildEnd: ['nodemon build/server.js --watch build']}));
}
...

"onBuildEnd" event fires only once after first build, rebuilds are not trigger "onBuildEnd", so nodemon works as intended

Chagrin answered 7/4, 2017 at 8:14 Comment(3)
Can you share the rest of the file webpack.server.config.js?Nordau
Cause it didn't work when I tried these: ` "start": "cross-env NODE_ENV=development webpack --watch", "poststart": "cross-env NODE_ENV=development webpack --watch --config webpack.server.config.js"` ` var WebpackShellPlugin = require('webpack-shell-plugin') module.exports = { plugins: (process.env.NODE_ENV !== 'production') ? [ new WebpackShellPlugin({ onBuildEnd: [ 'cross-env NODE_ENV=development nodemon server/server.js --watch ./dist' ] }) ] : [] } `Nordau
Note that Webpack doesn't support this usage in newer versions. Use webpack-shell-plugin-next instead.Bathymetry
B
78
  1. Install the following dependencies:

npm install npm-run-all webpack nodemon

  1. Configure your package.json file to something as seen below:

package.json

{
  ...

  "scripts": {
    "start"        : "npm-run-all --parallel watch:server watch:build",
    "watch:build"  : "webpack --watch",
    "watch:server" : "nodemon \"./dist/index.js\" --watch \"./dist\""
  },

  ...

}

After doing so, you can easily run your project by using npm start.

Don't forget config WatchIgnorePlugin for webpack to ignore ./dist folder.

Dependencies

  1. npm-run-all - A CLI tool to run multiple npm-scripts in parallel or sequential.
  2. webpack - webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.
  3. nodemon - Simple monitor script for use during development of a node.js app.
Bookplate answered 19/9, 2016 at 14:5 Comment(4)
It works. But a bit buggy. If your folder dist does not exist while first run you can receive an error.Gallion
Don't forget the '--inspect ' flag.Copyright
@Bookplate This does not start two builds for client and server though.Hobbes
See my answer for a solution to the bugginess.Bathymetry
F
39

I like the simplicity of nodemon-webpack-plugin

webpack.config.js

const NodemonPlugin = require('nodemon-webpack-plugin')

module.exports = {
  plugins: [new NodemonPlugin()]
}

then just run webpack with the watch flag

webpack --watch
Flodden answered 12/4, 2018 at 5:55 Comment(0)
C
37

Faced the same problem and found the next solution - webpack-shell-plugin. It

allows you to run any shell commands before or after webpack builds

So, thats my scripts in package.json:

"scripts": {
      "clean": "rimraf build",
      "prestart": "npm run clean",
      "start": "webpack --config webpack.client.config.js",
      "poststart": "webpack --watch --config webpack.server.config.js",
}

If I run 'start' script it launches next script sequence: clean -> start -> poststart. And there is part of 'webpack.server.config.js':

var WebpackShellPlugin = require('webpack-shell-plugin');

...
if (process.env.NODE_ENV !== 'production') {
    config.plugins.push(new WebpackShellPlugin({onBuildEnd: ['nodemon build/server.js --watch build']}));
}
...

"onBuildEnd" event fires only once after first build, rebuilds are not trigger "onBuildEnd", so nodemon works as intended

Chagrin answered 7/4, 2017 at 8:14 Comment(3)
Can you share the rest of the file webpack.server.config.js?Nordau
Cause it didn't work when I tried these: ` "start": "cross-env NODE_ENV=development webpack --watch", "poststart": "cross-env NODE_ENV=development webpack --watch --config webpack.server.config.js"` ` var WebpackShellPlugin = require('webpack-shell-plugin') module.exports = { plugins: (process.env.NODE_ENV !== 'production') ? [ new WebpackShellPlugin({ onBuildEnd: [ 'cross-env NODE_ENV=development nodemon server/server.js --watch ./dist' ] }) ] : [] } `Nordau
Note that Webpack doesn't support this usage in newer versions. Use webpack-shell-plugin-next instead.Bathymetry
H
5

In addition to @Ling's good answer:

If you want to build your project once, before you watch it with nodemon, you can use a webpack compiler hook. The plugin's code triggers nodemon in the done hook once after webpack has finished its compilation (see also this helpful post).

const { spawn } = require("child_process")

function OnFirstBuildDonePlugin() {
  let isInitialBuild = true
  return {
    apply: compiler => {
      compiler.hooks.done.tap("OnFirstBuildDonePlugin", compilation => {
        if (isInitialBuild) {
          isInitialBuild = false
          spawn("nodemon dist/index.js --watch dist", {
            stdio: "inherit",
            shell: true
          })
        }
      })
    }
  }
}

webpack.config.js:

  module.exports = {
    ... 
    plugins: [
      ... 
      OnFirstBuildDonePlugin()
    ]
  })

package.json:

"scripts": {
  "dev"  : "webpack --watch"
},

Hope, it helps.

Houdini answered 13/7, 2019 at 17:56 Comment(1)
This is the best solution to the problem IMHO since it works in all the use cases, for instance I wanted to launch electron after the first build completed.Pilcher
G
2

There's no need to use plugins here. You could try running multiple nodemon instances like below. Try modifying the following script for your use case, and see if it works for you:

"scripts": {
    "start": "nodemon --ignore './public/' ./bin/www & nodemon --ignore './public/' --exec 'yarn webpack'",
    "webpack": "webpack --config frontend/webpack.config.js"
}
Gerontocracy answered 26/9, 2017 at 22:48 Comment(0)
D
1

You don't need any plugins to use webpack and nodemon, just use this scripts on your package.json

"scripts": {
  "start": "nodemon --ignore './client/dist' -e js,ejs,html,css --exec 'npm run watch'",
  "watch": "npm run build && node ./server/index.js",
  "build": "rimraf ./client/dist && webpack --bail --progress --profile"
},
Disepalous answered 18/4, 2017 at 6:48 Comment(1)
What if I need to buil client and server, and server depends on client files cause it's universal application (build client first, after that build server with 'watch' key and run it)? This way won't work.Chagrin
M
1

@Ling has an answer very close to being correct. But it errors the first time somebody runs watch. You'll need to modify the solution as so to prevent errors.

  1. Run npm install npm-run-all webpack nodemon

  2. Create a file called watch-shim.js in your root. Add the following contents, which will create a dummy file and directory if they're missing.

    var fs = require('fs');
    
    if (!fs.existsSync('./dist')) {
        fs.mkdir('./dist');
        fs.writeFileSync('./dist/bundle.js', '');
    }
    
  3. Setup your scripts as so in package.json. This will only run watch if the watch-shim.js file runs successfully. Thereby preventing Nodemon from crashing due to missing files on the first run.

    {
        ...
        "scripts": {
            "start": "npm run watch",
            "watch": "node watch-shim.js && npm-run-all --parallel watch:server watch:build",
            "watch:build": "webpack --progress --colors --watch",
            "watch:server": "nodemon \"./dist/bundle.js\" --watch \"./dist/*\""
        }
        ...
    },
    
Medellin answered 31/12, 2017 at 23:45 Comment(0)
N
1

I tried most of the solution provided above. I believe the best one is to use nodemon-webpack-plugin .

It is very simple to use i.e. just add const NodemonPlugin = require('nodemon-webpack-plugin') to webpack file with new NodemonPlugin() as your plugin.

Below are the scripts to use it:

"scripts": {
    "watch:webpack-build-dev": "webpack --watch --mode development",
    "clean-db": "rm -rf ./db && mkdir -p ./db",
    "local-dev": "npm run clean-db && npm run watch:webpack-build-dev"
    ...
}

After this you can simply run npm run local-dev.

Adding a module to development is usually not as bad as adding to a production one. And mostly you will be using it for the development anyway.

This also doesn't require any additional package like nodemon or npm-run-all etc.

Also nodemon-webpack-plugin only works in watch mode.

Nan answered 12/5, 2021 at 18:9 Comment(0)
M
0

Assuming nodemon server.js touch the server.js file afterEmit:

// webpack.config.js

module.exports = {
  // ...
  plugins: [
    // ...,

    // 👇
    apply: (compiler) => {
      compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {

        require('child_process').execSync('touch server.js') // $ touch server.js
      });
    }
  ]
}
Moeller answered 3/3, 2020 at 9:56 Comment(1)
I'm really not sure why this answer was downvoted twice, it worked perfectly fine for me and solved all my issues. What it does is that after webpack output is emitted (aka everything is compiled to disk) it runs the command specified in execSync call. What I use it for is to run yalc push which updates my local registry of packages. Works like a charm including in watch mode!Marta
B
0

As pointed out by @aprisniak, Ling's answer is sufficient but Nodemon throws an error if the specified watch file doesn't exist. Although there is an issue to watch for a non-existent file on Nodemon's GitHub page, the feature doesn't exist. My workaround is similar to Ling's answer but I use concurrently instead of npm-run-all.

To fix the issue, introduce a script to touch your watch file before Nodemon starts up.

package.json

"scripts": {
    "_stopNodemonCrash": "mkdir -p dist && touch ./dist/server.js",
    "_nodemon": "yarn _stopNodemonCrash && nodemon ./dist/server.js",
    "dev": "rm -rf dist && concurrently -n webpack,nodemon \"webpack\" \"yarn _nodemon\""
}

In your webpack config, make sure that you have watch set to true and that you include whatever specific watch options you need.

webpack.config.js

{
    mode: 'development',
    entry: {
      server: './src/server/server.js',
    },
    output: {
      path: path.join(__dirname, '..', 'dist'),
      publicPath: '/',
      filename: '[name].js',
    },
    watch: true,
    watchOptions: {
      poll: 1000,
      ignored: /node_modules/,
      aggregateTimeout: 300,
    },
    ...
}

Here's what happens when you run yarn dev:

  1. The build directory is cleared
  2. Webpack runs and bundles all your code into the build directory WHILE Nodemon starts up and watches the bundled server.js file for changes (without crashing)
  3. You get to see a nice little output as shown below
[nodemon] [nodemon] 2.0.21
[nodemon] [nodemon] to restart at any time, enter `rs`
[nodemon] [nodemon] or send SIGHUP to 68591 to restart
[nodemon] [nodemon] watching path(s): dist/server.js
[nodemon] [nodemon] starting `node ./dist/server.js`
[nodemon] [nodemon] forking
[nodemon] [nodemon] child pid: 68599
[nodemon] [nodemon] watching 1 file
[nodemon] [nodemon] clean exit - waiting for changes before restart
[webpack] asset server.js 41.8 KiB [emitted] (name: server)
[webpack] asset src_server_modules_config_js.js 38.4 KiB [emitted]
[webpack] asset src_server_modules_hmr_js.js 16.8 KiB [emitted]
[webpack] asset webpack_webpack_client_dev_config_js.js 4.21 KiB [emitted]
[webpack] cached modules 63.5 KiB (javascript) 5.46 KiB (runtime) [cached] 46 modules
[webpack] webpack 5.75.0 compiled successfully in 1967 ms
[nodemon] [nodemon] files triggering change check: dist/server.js
[nodemon] [nodemon] matched rule: **/dist/server.js
[nodemon] [nodemon] changes after filters (before/after): 1/1
[nodemon] [nodemon] restarting due to changes...
[nodemon] [nodemon] dist/server.js
[nodemon] 
[nodemon] [nodemon] starting `node ./dist/server.js`
[nodemon] [nodemon] forking
[nodemon] [nodemon] child pid: 68611
[nodemon] 
[nodemon] --------------listening on: 8080-----------------
Bathymetry answered 20/3, 2023 at 21:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.