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

Dynamic hostname and port of hot update manifest #262

Closed
kentor opened this issue Sep 14, 2015 · 22 comments
Closed

Dynamic hostname and port of hot update manifest #262

kentor opened this issue Sep 14, 2015 · 22 comments

Comments

@kentor
Copy link

kentor commented Sep 14, 2015

Say my app is served on port 3000, and I have webpack dev server running on port 45537. I'm only using the dev server for assets/hot reloading, not serving the html files, that is taken care of by something else (django, rails, whatever). The thing is, I may access this app using different hostnames or IPs (e.g. localhost, 10.0.2.2, the internal network IP of the server) so I want the hot reloading stuff to work no matter how/where I am accessing the app.

Command used to start webpack-dev-server:

webpack-dev-server --config webpack.hot.config.js --host 0.0.0.0 --port 45537 --hot --no-info

conifg:

const path = require('path');

module.exports = {
  entry: [
    'webpack-dev-server/client?http://0.0.0.0:45537',
    'webpack/hot/only-dev-server',
    path.join(__dirname, 'src', 'js', 'app.js'),
  ],
  module: {
    loaders: [
      {
        exclude: /node_modules/,
        loaders: ['react-hot', 'babel-loader'],
        test: /\.js$/,
      },
    ],
  },
  output: {
    filename: 'app.js',
    publicPath: '/',
  },
};

In my html file, I have this at the bottom to load webpack-dev-server.js and my app bundle:

    <script>
      !function() {
        var host = location.host.split(':')[0];
        document.write(
          '<script src="http://' + host + ':45537/app.js"></' + 'script>'
        );
      }()
    </script>

The problem is when I reload a file, it tries to grab the manifest from port 3000 instead of 45537:

[WDS] App updated. Recompiling...
[WDS] App hot update...
[HMR] Checking for updates on the server...
GET http://192.168.1.45:3000/0f047ad04fb3ea0a8b54.hot-update.json 404 (Not Found)

Of course, specifing the publicPath as http://localhost:45537/ would only work if I am on the same computer as the dev server, so that's not what I am looking for.

@sokra
Copy link
Member

sokra commented Sep 15, 2015

You can set the publicPath at runtime (in webpack compiled modules):

__webpack_public_path__ = 'http://' + host + ':45537/js/'

@kentor
Copy link
Author

kentor commented Sep 16, 2015

I don't understand. I followed http://webpack.github.io/docs/configuration.html#output-publicpath, (the warning about the Hot Module Replace Plugin needing to be disabled scares me, if I can't use HMR then what's the point of this?) and created a file called webpack.init.js and in it put

var host = window.location.host.split(':')[0];
__webpack_public_path__ = 'http://' + host + ':45537/';

and then I modified my entry list to include it:

 entry: [
    './webpack.init.js',
    'webpack/hot/only-dev-server',
    path.join(__dirname, 'src', 'js', 'app.js'),
  ],

That didn't seem to affect anything.

@sokra
Copy link
Member

sokra commented Sep 16, 2015

the warning about the Hot Module Replace Plugin needing to be disabled scares me, if I can't use HMR then what's the point of this?

The note is wrong, I removed it.


hmmm... it should work that way.

@kentor
Copy link
Author

kentor commented Sep 19, 2015

Yeah I see the source code says

function hotDownloadManifest(callback) { // eslint-disable-line no-unused-vars
/******/        if(typeof XMLHttpRequest === "undefined")
/******/            return callback(new Error("No browser support"));
/******/        try {
/******/            var request = new XMLHttpRequest();
/******/            var requestPath = __webpack_require__.p + "" + hotCurrentHash + ".hot-update.json";

and with my webpack.init.js in the entry file I see in the compiled output

/* 1 */
/***/ function(module, exports, __webpack_require__) {

    /* WEBPACK VAR INJECTION */(function(module) {/* REACT HOT LOADER */ if (true) { (function () { var ReactHotAPI = __webpack_require__(3), RootInstanceProvider = __webpack_require__(11), ReactMount = __webpack_require__(13), React = __webpack_require__(67); module.makeHot = module.hot.data ? module.hot.data.makeHot : ReactHotAPI(function () { return RootInstanceProvider.getRootInstances(ReactMount); }, React); })(); } try { (function () {

    'use strict';

    var host = window.location.host.split(':')[0];
    __webpack_require__.p = 'http://' + host + ':45537/';

Seems like it should work. But __webpack_require__ is a different object in the 1 module than the __webpack_require__ in the hotDownloadManifest function.

@kentor
Copy link
Author

kentor commented Sep 19, 2015

I think I need to modify __webpack_public_path__ in module 0 instead of any other later module because of this:

/******/    function hotCreateRequire(moduleId) { // eslint-disable-line no-unused-vars
/******/        var me = installedModules[moduleId];
/******/        if(!me) return __webpack_require__;
/******/        var fn = function(request) {

Whatever me is is undefined only the first time we go through this path when moduleId is 0. So.. how do I do that? (Maybe that warning about HMR was right after all?)

@kentor kentor changed the title Dynamic hostname of hot update manifest Dynamic hostname and port of hot update manifest Sep 19, 2015
@aaronjensen
Copy link

My config is a bit different than yours. I just run webpack-dev-server --config webpack.hot.config.js

Here are the relevant parts of my config:

  entry: [
      'webpack/hot/only-dev-server',
      './js/application.js',
  ],
    output: {
      path: path.join(__dirname, 'public'),
      filename: path.join('assets', 'js', 'application.js'),
      chunkFilename: path.join('assets', 'js', '[id].js'),
      sourceMapFilename: path.join('assets', 'js', '[file].map'),
      publicPath: '/',
    },

  devServer: {
    contentBase: './public',
    historyApiFallback: true,
    host: '0.0.0.0',
    hot: true,
    port: 4001,
    publicPath: '/',
    noInfo: true,
    stats: { colors: true },
    watchOptions: {
      aggregateTimeout: 50,
    },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],

  },

@kentor
Copy link
Author

kentor commented Sep 19, 2015

Thanks @aaronjensen. I think I see why it's working for you but not for me. You are actually accessing the html page served by the dev-server (so same port as the assets) whereas I am only using the dev-server to serve assets and I'm using some other app (and port) to serve the html, so hot reloading would work in your case because the url of the manifest that webpack tries to grab will have the correct port.

@aaronjensen
Copy link

Hi @kentor, good, hope that helps you get sorted. FWIW, we actually are on different ports than the browser sees as well because of browser-sync, so I do this:

    <script>
      var devServer = document.createElement('script');
      devServer.setAttribute('src', 'http://' + window.location.host.replace('4000', '4001') + '/webpack-dev-server.js');
      document.body.appendChild(devServer);
    </script>

Note the .replace(...)

@kentor
Copy link
Author

kentor commented Sep 19, 2015

@aaronjensen Ah ok. I can load webpack-dev-server.js fine. I'm not sure if you're using react-hot-loader, but the thing that's broken for me is when I save a react component module then webpack doesn't use the correct port to get the manifest file. This is what I see when I save a react component file:

screen shot 2015-09-19 at 2 42 34 pm

In your case, it would try port 4000 instead of 4001 to grab the manifest file, no?

@aaronjensen
Copy link

@kentor yes, you're right. And since browser-sync proxies to webpack-dev-server, this works for me. If your server isn't proxying, that won't work 😦 This whole thing is a bit more complicated than it seems it should be.

@kentor
Copy link
Author

kentor commented Sep 29, 2015

I'm using browserify-hmr now since that allows changing the websocket url at runtime so I'm going to close this. Thanks for the help anyway!

@kentor kentor closed this as completed Sep 29, 2015
@mkristo
Copy link

mkristo commented Dec 8, 2015

I'm having the same problem, though my setup is a bit different from what @kentor was using.

I'm using Cordova and serving my HTML file directly from the file system and requests JS from the devserver. By specifying the host to the devservers IP I managed to get the polling working. But when downloading the manifest it tries to download the JSON from the Android device:

file:///android_asset/www/5d9b9c8a0afa9f1bb843.hot-update.json

It would be really nice if this could be solved somehow. Hot reloading when working with mobile devices would be really neat.

Thanks!

@gavinwilliams
Copy link

I have the same problem here, I'm using Symfony as a front end and webpack to serve assets.

dbrugne added a commit to dbrugne/cineclub that referenced this issue May 27, 2016
@AlexKvazos
Copy link

Has anyone found a solution for this? I am also having this issue where webpack is only serving static files and the application is being served by node/python/ruby etc...

@mareklaco
Copy link

mareklaco commented Aug 24, 2016

Had same issue and was able to get over it using this advice from https://github.com/gaearon/react-hot-loader/blob/master/docs/Troubleshooting.md

404 errors for hot-update.json files
This config variable must also match publicPath option specified when creating WebpackDevServer instance.

Also, note the CORS headers webpack-dev-server config.

  headers: {
    "Access-Control-Allow-Origin": "*",
  }

@nathanmarks
Copy link

nathanmarks commented Sep 15, 2016

Try this:

  output: {
    path: path.resolve(ROOT_PATH, 'public/assets'),
    publicPath: `http://0.0.0.0:${DEV_SERVER_PORT}/assets/`,
    filename: '[name].js',
  },

@nathanmarks
Copy link

@alexkuz ^^^ the above works while running alongside a rails app served separately

@alexkuz
Copy link
Contributor

alexkuz commented Sep 15, 2016

@nathanmarks thank you for reference, but I think this is another case.
What I need is to hot-reload proxy target, like this:

devServer: {
  proxy: {
    '/api/*': {
      target: config.proxyBaseUrl, // this should be reloadable
      secure: false,
      changeOrigin: true,
      alterProxyOptions: true
    }
  }
}

If I get it right, the above is not related to this.

@thasmo
Copy link

thasmo commented Apr 30, 2017

In my case (for browser-sync) I had to use publicPath: 'http://localhost:3000/assets/'.

@chriscant
Copy link

For Cordova the app runs at file:///android_asset/www/ in Android and at something like this in iOS: /var/containers/Bundle/Applications/XXXX/AppNameapp/www/

In the webpack config I have output publicPath: '' ie empty. This means that the generated CSS files don't have an awkward path prefix. There are various generated files referred to in the CSS which must be moved into the css directory at the end of the build.

Finally in the entry JS file I have something like this:

var root = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1);
_webpack_public_path__ = root;

@thenewguy
Copy link
Contributor

So what was the resolution here. @kentor do you remember how you overcame this issue?

@seeliang
Copy link

@thenewguy you will need

Try this:

  output: {
    path: path.resolve(ROOT_PATH, 'public/assets'),
    publicPath: `http://0.0.0.0:${DEV_SERVER_PORT}/assets/`,
    filename: '[name].js',
  },

and you may need

Had same issue and was able to get over it using this advice from https://github.com/gaearon/react-hot-loader/blob/master/docs/Troubleshooting.md

404 errors for hot-update.json files
This config variable must also match publicPath option specified when creating WebpackDevServer instance.

Also, note the CORS headers webpack-dev-server config.

  headers: {
    "Access-Control-Allow-Origin": "*",
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests