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

Sending more information in socket message for (hot) CSS reloading #542

Closed
jeroenransijn opened this issue Jul 31, 2016 · 10 comments
Closed

Comments

@jeroenransijn
Copy link

When using Webpack Dev Server for building CSS with ExtractTextPlugin there is no hot reloading. Which makes sense because there is no JS to actually trigger this.

There is a way to eaves drop on the client about Webpack updates by listening to the postMessage call when updates come in through SockJS. This script listens to all updates and refreshes the CSS (link elements):

/**
 * Live CSS reloader *only use in Development*
 * ---
 * This script is connected to the Webpack Dev Server
 * and reloads every css file if there is an update.
 *
 * Note that the event triggered by Webpack doesn't describe
 * what file is updated. This mean that on every update, js or css,
 * onWebpackMessage is called.
 */
window.addEventListener('message', function onWebpackMessage (e) {
  if (e.data.search('webpackHotUpdate') === -1) return;

  var links = document.querySelectorAll('link');

  for (var i = 0, link; link = links[i]; i++) {

    if (link.href.indexOf('localhost') > -1) {
      var linkHref = link.href;

      if (linkHref.indexOf('style-reloader--safe-to-ignore') > -1) {
        link.href = linkHref;
      } else {
        link.href = linkHref + '?style-reloader--safe-to-ignore';
      }
    }
  }
}, false);

This actually works fine but is triggered on any update, JS or CSS. The annoying part is that it dirties up your server logs and updates on no good reason if it's JS. It would be cool to make eaves dropping an actual feature where the event object contains more data about the update. The most important one would be file name including extension.

This is partially the webpack config I am using.

var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var settings = require('./settings');

var entries = {};

settings.css.entryFiles.forEach(function (filePath) {
  entries['css/' + path.basename(filePath)] = [filePath];
});

module.exports = {
  devtool: 'source-map',
  entry: entries,
  output: {
    path: settings.outputPath,
    filename: '[name]',
    publicPath: '/',
  },
  module: {
    loaders: [
      {
        test:   /\.css$/,
        loader: ExtractTextPlugin.extract('style-loader', 'css-loader?minimize!postcss-loader')
      }
    ],
  },
  postcss: function (bundler) {
    return {
      defaults: [
        // Transfer @import rule by inlining content, e.g. @import 'normalize.css'
        // https://github.com/postcss/postcss-import
        require('postcss-import')({ addDependencyTo: bundler }),
        require('postcss-cssnext')({
          browsers: ['last 1 version'],
          warnForDuplicates: false
        })
      ]
    };
  },
  plugins: [
    new ExtractTextPlugin('[name]'),
    new webpack.HotModuleReplacementPlugin()
  ]
};

I am aware this is probably more a feature request than an issue.

@SpaceK33z
Copy link
Member

SpaceK33z commented Aug 2, 2016

I'm interested in this feature as well, at my work I use css reloading almost full-time, would be awesome if it works with hot reloading.

@elisechant
Copy link

You can't hot reload with ExtractTextPlugin. Webpack-dev-server works because its in memory not because of writing to file.

Hot styles are emitted from your bundles js file. Delete your local ref to your css file, remove the extract text plugin.

Your sass loader for dev mode should look something like this:

{
        test: /\.(scss)$/,
                loader: 'style!css?&sourceMap!postcss!resolve-url!sass?sourceMap'
            },

instead of :

{
        test: /\.(scss|css)$/,
        loader: ExtractSass.extract('style', `css?${DEBUG ? 'sourceMap': ''}!postcss!resolve-url!sass?sourceMap`)
      },

@SpaceK33z
Copy link
Member

@elisechant, while it is certainly true that you can use style-loader instead of ExtractTextPlugin in dev, I can understand the need to make the dev environment look like production as much as possible.

Sending the files that changed in the websocket could be interesting, also for other purposes.

@jeroenransijn, would you be willing to do a PR for this? I don't know for sure where exactly you can get the changed files, but they should be somewhere in the stats object.

@SpaceK33z
Copy link
Member

SpaceK33z commented Nov 5, 2016

I experimented a bit with this, it's possible to list the files that were changed. in lib/Server.js, in Server.prototype._sendStats, you can add this:

var changedModules = stats.modules.filter(function(mod) {
    return mod.built;
}).map(function(mod) {
    return mod.name;
});
console.log('changed', changedModules);

If you now change a CSS file or something, you'll see that only that file gets listed.

I'm still not sure if we should include this in the WebSocket though.

@msuperina
Copy link

msuperina commented Jan 27, 2017

Hi I am looking at this one right now and have a few questions... Please forgive me if I am missing things as I am new to the webpack ecosystem and I have not looked at the whole source code of webpack-dev-server:

  • I was thinking of sending a stringified array of files that have changed. Would you rather keep the "hash" as name of the message or change it so something more generic as the message would contain both the hash and the changed values ?
  • client-side, is self the only object interested in the changed files ? I guess so ? if so, which syntax to apply ? at the moment it's
    "webpackHotUpdate" + currentHash
    I was thinking of
    "webpackHotUpdate" + currentHash + "|" + currentChanged

@msuperina
Copy link

ok I have created an example here https://github.com/msuperina/webpack-dev-server/tree/master/examples/hmr-css
@jeroenransijn I had exaclty the same problem as you today and it solves it
I am happy to open a PR but I need some indications about the message contracts in the socket communication. Also, "webpackHotUpdate" + currentHash + "|" + currentChanged seems hacky, I would rather have event.data as an object but need validation as it could break code of people consuming this library.

@SpaceK33z
Copy link
Member

@msuperina awesome work, very interesting. Note that this will not be accepted into webpack-dev-server since it shouldn't do anything with CSS. This should live as a separate package.

@msuperina
Copy link

msuperina commented Jan 28, 2017

@SpaceK33z thanks, I agree the CSS part should not be in the PR, it's only a consumer of the event. I will go ahead with a PR for client/index.js and Server.js as you seem to approve.

@msuperina
Copy link

I found that one webpack-contrib/extract-text-webpack-plugin#89 which seems to e be a better place to solve this. Will probably create a PR there. @SpaceK33z can you let me know if you still want a PR here to send metadata to the client in addition to the hash please ?

@SpaceK33z
Copy link
Member

Oh that PR you mentioned is awesome. For now I think we don't need that metadata. If someone has a good use case for it later on, we can always use your code.

I'm going to close this since webpack-contrib/extract-text-webpack-plugin#89 essentially fixes the proposed use case.

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

No branches or pull requests

4 participants