Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to HMR on server side? #45

Closed
jiyinyiyong opened this Issue Oct 20, 2015 · 28 comments

Comments

Projects
None yet
@jiyinyiyong
Copy link

commented Oct 20, 2015

It's mentioned on in the docs that Webpack also does HMR to server code, but how? Please can you add more details?
https://github.com/webpack/docs/wiki/hot-module-replacement


Updated:

Minimal example https://github.com/minimal-xyz/minimal-webpack-nodejs-hmr

@sokra

This comment has been minimized.

Copy link
Member

commented Oct 21, 2015

Here is the process in short:

  • Compile the server code with webpack
  • Use target: "node" or target: "async-node"
  • Enabled HMR via --hot or HotModuleReplacementPlugin
  • Use webpack/hot/poll or webpack/hot/signal
    • The first polls the fs for updates (easy to use)
    • The second listens for a process event to check for updates (you need a way to send the signal)
  • Run the bundle with node.
  • You can't use existing HMR loaders like react-hot-loader or style-loader because they make no sense in a server environment. Just add manuall replacement code at the correct location (i. e. accept request handler like in the example)
@jiyinyiyong

This comment has been minimized.

Copy link
Author

commented Oct 21, 2015

So the bundled code is still run with node and it connects to webpack-dev-server listening for updates via webpack/hot/signal like...

var backendConfig = config({
  entry: [
    'webpack/hot/signal.js',
    './src/main.js'
  ],
  target: 'node',
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'backend.js'
  },
  node: {
    __dirname: true,
    __filename: true
  },
  externals: nodeModules,
  recordsPath: path.join(__dirname, 'build/_records'),
  plugins: [
    new webpack.IgnorePlugin(/\.(css|less)$/),
    new webpack.BannerPlugin('require("source-map-support").install();',
                             { raw: true, entryOnly: false }),
    new webpack.HotModuleReplacementPlugin({ quiet: true })
  ]
});

And in @jlongster 's configs nodemon is actually useless since Webpack will reload code internally...
https://github.com/jlongster/backend-with-webpack/blob/master/gulpfile.js

gulp.task('run', ['backend-watch', 'frontend-watch'], function() {
  nodemon({
    execMap: {
      js: 'node'
    },
    script: path.join(__dirname, 'build/backend'),
    ignore: ['*'],
    watch: ['foo/'],
    ext: 'noop'
  }).on('restart', function() {
    console.log('Patched!');
  });
});

It all makes sense now.. Thanks for replying.

@sokra

This comment has been minimized.

Copy link
Member

commented Oct 21, 2015

You can't use the webpack-dev-server. It's a server which serves assets not a runner. Just run webpack --watch and node bundle.js. I would go the webpack/hot/poll?1000 route first. It's pretty easy to use and suitable for dev environments. For production (if you want to hot update your production server) the signal approach is better suited.

@jiyinyiyong

This comment has been minimized.

Copy link
Author

commented Oct 21, 2015

I'm quite confused on webpack --watch. I thought it's unlike dev-server and will compile the whole bundle on every update. How does Webpack know which modules are updated then?

@sokra

This comment has been minimized.

Copy link
Member

commented Oct 21, 2015

filesystem watcher. The same as the webpack-dev-server.

@jiyinyiyong

This comment has been minimized.

Copy link
Author

commented Oct 21, 2015

Feeling much better when I finally got it up and running.
https://github.com/jiyinyiyong/webpack-backend-HMR-demo

@jiyinyiyong

This comment has been minimized.

Copy link
Author

commented Oct 21, 2015

I heard someone talking about memory leaks in hot swapping system built on top of Node.js . Does Webpack fixed such issues?

@sokra

This comment has been minimized.

Copy link
Member

commented Oct 21, 2015

I heard someone talking about memory leaks in hot swapping system built on top of Node.js . Does Webpack fixed such issues?

I don't know about issues.

@jiyinyiyong

This comment has been minimized.

Copy link
Author

commented Oct 21, 2015

Thanks anyway.

@particle4dev

This comment has been minimized.

Copy link

commented Oct 31, 2015

@jiyinyiyong great demo, thanks!

@umidbekkarimov

This comment has been minimized.

Copy link

commented Aug 18, 2016

@jiyinyiyong just tried your example with webpack 2, works like charm, great development experience with just one tool

@laurenskling

This comment has been minimized.

Copy link

commented Aug 27, 2016

I'm getting:

[HMR] Cannot apply update.
[HMR] Error: Aborted because 6 is not accepted
    at hotApply (/Library/WebServer/Documents/nondejus.nl/build/backend.js:343:31)
    at hotUpdateDownloaded (/Library/WebServer/Documents/nondejus.nl/build/backend.js:256:13)
    at hotAddUpdateChunk (/Library/WebServer/Documents/nondejus.nl/build/backend.js:236:13)
    at hotDownloadUpdateChunk (/Library/WebServer/Documents/nondejus.nl/build/backend.js:5:12)
    at hotEnsureUpdateChunk (/Library/WebServer/Documents/nondejus.nl/build/backend.js:246:13)
    at /Library/WebServer/Documents/nondejus.nl/build/backend.js:218:14
    at hotDownloadManifest (/Library/WebServer/Documents/nondejus.nl/build/backend.js:14:12)
    at Object.hotCheck [as check] (/Library/WebServer/Documents/nondejus.nl/build/backend.js:197:12)
    at Timeout.checkForUpdate [as _repeat] (/Library/WebServer/Documents/nondejus.nl/build/backend.js:556:16)
    at Timeout.wrapper [as _onTimeout] (timers.js:417:11)

[HMR] You need to restart the application!

my config is like yours, here it is:

var webpack = require('webpack');
var path = require('path');
var fs = require('fs');

var nodeModules = {};
fs.readdirSync('node_modules')
  .filter(function(x) {
    return ['.bin'].indexOf(x) === -1;
  })
  .forEach(function(mod) {
    nodeModules[mod] = 'commonjs ' + mod;
  });

module.exports = {
  context: path.join(__dirname),

  entry: [
    'webpack/hot/poll?1000',
    './preprocessor.js',
  ],

  target: 'node',

  node: {
    __dirname: true,
    __filename: true,
  },

  output: {
    path: path.join(__dirname, 'build'),
    filename: 'backend.js'
  },

  externals: nodeModules,

  resolve: {
    root: [
      path.resolve(__dirname),
    ],
    modulesDirectories: ['node_modules'],
    extensions: ['', '.js'],
  },

  plugins: [
    new webpack.IgnorePlugin(/\.(css|less)$/),
    new webpack.BannerPlugin(
      'require("source-map-support").install();',
      { raw: true, entryOnly: false }
    ),
    new webpack.HotModuleReplacementPlugin(),
  ],

  module: {
        loaders: [
        {
            test: /\.js?$/,
            exclude: /(node_modules|bower_components)/,
            loaders: ['babel'],
        },
    ],
  },
};

any ideas?

@laurenskling

This comment has been minimized.

Copy link

commented Aug 27, 2016

Found it myself. I had to module.hot.accept() a module for it to work.

@Download

This comment has been minimized.

Copy link

commented Oct 25, 2016

Not only do you have to accept it, you often have to make sure it is actually required again after it has been accepted and that the old module is replaced with the new module in your code. You can see this in example 1: hot replace request handler of http server on the wiki:

// accept update of dependency
module.hot.accept("./handler.js", function() {
  // replace request handler of server
  server.removeListener("request", requestHandler);
  requestHandler = require("./handler.js");
  server.on("request", requestHandler);
});

Notice how it's removing the old listener and attaching the new one. It depends on what module is changed how to handle this so Webpack cannot do it for you.

If a module is not accepted for replacement, Webpack will invalidate the parent(s) of the module: those modules that have required the changed module. Now all these modules are considered changed and it will attempt to replace these, all the way to the top. If even the top module is not accepted, the error message is displayed.

@DylanPiercey

This comment has been minimized.

Copy link

commented Feb 11, 2017

Is there a way to use the memory-fs in conjunction with hot reloading server side? I was playing around with a setup similar to https://www.npmjs.com/package/webpack-to-memory but couldn't think of a way to trigger hot reloads.

Any one have any ideas?

@ankitduseja

This comment has been minimized.

Copy link

commented Apr 6, 2017

Can webpack do HMR on a non-node server (eg. a django server)?

UPDATE: Well, I found this: https://medium.com/@rajaraodv/webpacks-hmr-react-hot-loader-the-missing-manual-232336dc0d96

The solution is to use proxy in webpack. Scroll down to App Scenario 3 in the above link.

@mnpenner

This comment has been minimized.

Copy link

commented May 3, 2017

I keep getting

/home/me/Projects/rs2/node_modules/webpack/hot/poll.js:32
	throw new Error("[HMR] Hot Module Replacement is disabled.");

Whether I use node_modules/.bin/webpack --watch --hot or new HotModuleReplacementPlugin(). How come?

@Download

This comment has been minimized.

Copy link

commented May 3, 2017

@mnpenner I think you need webpack-dev-server.
Webpack is like a compiler/builder.
Webpack dev server is an HTTP server that compiles stuff with Webpack and serves the results over HTTP.

The poll module is part of webpack but you need to start a webpack dev server so it can actually poll something.

@mnpenner

This comment has been minimized.

Copy link

commented May 3, 2017

@Download Sokra specifically said

You can't use the webpack-dev-server. It's a server which serves assets not a runner. Just run webpack --watch and node bundle.js

@Download

This comment has been minimized.

Copy link

commented May 3, 2017

@mnpenner My apologies! You are right.

@mnpenner

This comment has been minimized.

Copy link

commented May 3, 2017

I think the problem I was having was with webpack-node-externals. It was excluding webpack/hot/poll.

webpack2-externals-plugin seems to work better:

{
    plugins: [
        new HotModuleReplacementPlugin(),
        new ExternalsPlugin({
            type: 'commonjs',
            include: `${projectRoot}/node_modules/`,
            exclude: `${projectRoot}/node_modules/webpack/hot`
        }),
    ]
}
@figalex

This comment has been minimized.

Copy link

commented May 24, 2017

@mnpenner is there a way I can see your complete webpack config please? I'm trying to setup webpack2 for nodejs code with react server side rendering but it getting this kind of errors when server tries to render some of my components:

Module build failed: ReferenceError: Unknown plugin "add-module-exports" specified in "/Users/alex/Development/muub/one/website/node_modules/react-load-script/.babelrc" at 0, attempted to resolve relative to "/Users/alex/Development/muub/one/website/node_modules/react-load-script"
    at /Users/alex/Development/muub/one/website/node_modules/babel-core/lib/transformation/file/options/option-manager.js:180:17
    at Array.map (native)
    at Function.normalisePlugins (/Users/alex/Development/muub/one/website/node_modules/babel-core/lib/transformation/file/options/option-manager.js:158:20)
    at OptionManager.mergeOptions (/Users/alex/Development/muub/one/website/node_modules/babel-core/lib/transformation/file/options/option-manager.js:234:36)
    at OptionManager.init (/Users/alex/Development/muub/one/website/node_modules/babel-core/lib/transformation/file/options/option-manager.js:368:12)
    at File.initOptions (/Users/alex/Development/muub/one/website/node_modules/babel-core/lib/transformation/file/index.js:212:65)
    at new File (/Users/alex/Development/muub/one/website/node_modules/babel-core/lib/transformation/file/index.js:135:24)
    at Pipeline.transform (/Users/alex/Development/muub/one/website/node_modules/babel-core/lib/transformation/pipeline.js:46:16)
    at transpile (/Users/alex/Development/muub/one/website/node_modules/babel-loader/lib/index.js:46:20)
    at Object.module.exports (/Users/alex/Development/muub/one/website/node_modules/babel-loader/lib/index.js:163:20)
 @ ./App/Containers/Publish/Payment/Payment.jsx 18:0-39
 @ ./App/Containers/Publish/Payment/index.js
 @ ./App/Containers/Publish/Publish.jsx
 @ ./App/Containers/Publish/index.js
 @ ./App/Routes/index.js
 @ ./App/Containers/App.jsx
 @ ./App/index.js
 @ multi webpack-hot-middleware/client react-hot-loader/patch ./App

This is just one of many. I also get errors like this one for other 3rd party npm packages.

@jiyinyiyong

This comment has been minimized.

Copy link
Author

commented May 24, 2017

Updated description of this issue, added my demo here https://github.com/minimal-xyz/minimal-webpack-nodejs-hmr

@mnpenner

This comment has been minimized.

Copy link

commented May 24, 2017

@figalex Uhhh... I ended up scrapping my server-side HMR. It's doable, but you have to decide how to handle each incoming file. Wasn't worth the hassle. I'm just using node-dev now. Only takes half a second to restart the whole server, and there's no configuration required. The only downside is that I have to run 3 separate processes: webpack-dev-server for client-side HMR, webpack --watch for server-side rebuilding, and then node-dev for when webpack/watch is done.

@jiyinyiyong

This comment has been minimized.

Copy link
Author

commented May 25, 2017

@mnpenner it's useful when you are developing a WebSocket server, and only in such cases you which you can't afford the state recreating.

@WataruKay

This comment has been minimized.

Copy link

commented Oct 17, 2017

@mnpenner I ended up with the same conclusion, but was using nodemon. node-dev seems like a better solution. Thank you for sharing your solution 👍

@vlazh

This comment has been minimized.

Copy link

commented Oct 18, 2017

Try the node-hot-loader.

@mancontr

This comment has been minimized.

Copy link

commented Dec 8, 2017

@DylanPiercey I'm also trying to do HMR server-side while keeping the files in memory.
I can reload the whole thing manually from memory without restarting node (reading the file from the memory file system and using require-from-string), but can't get HMR to work the same way.

I use webpack/hot/signal, and send the signal on compiler updates, but the problem is that HMR code does a require when trying to load the updates, and it won't find the files. The offending code is at NodeMainTemplate.runtime.js; I'm working on trying to override these functions so they use the in-memory file system instead, but have had no luck with it yet.

If I manage to do it, I'll post the solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.