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

Live CSS reloading #28

Open
rstacruz opened this issue Dec 21, 2016 · 22 comments
Open

Live CSS reloading #28

rstacruz opened this issue Dec 21, 2016 · 22 comments

Comments

@rstacruz
Copy link

rstacruz commented Dec 21, 2016

I'm using extract-text-webpack-plugin to write .css files and webpack-livereload-plugin reloads the entire page when a CSS file changes.

tiny-lr has support for liveCSS to reload the CSS in place.

Any clues on how I can take advantage of that?

@0x80
Copy link

0x80 commented Dec 28, 2016

For me the whole page seems to be reloaded no matter if I use inline css or the extract-text plugin. Have you managed to get live reload working properly without extracted css?

@rstacruz
Copy link
Author

For me the whole page seems to be reloaded no matter if I use inline css or the extract-text plugin.

Yes, that's what I'm getting. What I'm expecting to happen, though, is that the CSS reloads in-page without needing a full page reload.

Have you managed to get live reload working properly without extracted css?

Yep, but I haven't tried with CSS! JS works fine though

@statianzo
Copy link
Owner

@rstacruz I was unaware of tiny-lr's liveCSS support. Looks useful for projects using the extract-text plugin.

My guess as to what's causing a full refresh is that livereload-plugin is notifying about all files rather than just those that have changed. include should only be of files with a new hash.

LiveReloadPlugin.prototype.done = function done(stats) {
  var hash = stats.compilation.hash;
  var childHashes = (stats.compilation.children || []).map(child => child.hash);
  var files = Object.keys(stats.compilation.assets);
  var include = files.filter(function(file) {
    return !file.match(this.ignore);
  }, this);

  if (this.isRunning && (hash !== this.lastHash || !arraysEqual(childHashes, this.lastChildHashes)) && include.length > 0) {
    this.lastHash = hash;
    this.lastChildHashes = childHashes;
    setTimeout(function onTimeout() {
      this.server.notifyClients(include);
    }.bind(this));
  }
};

@defmech
Copy link

defmech commented Jan 18, 2017

I was having the same issue as you @rstacruz. What I ended up doing was moving the livereload to a npm script. I've also just discovered you can chain tasks in npm scripts. Example.
"scripts": { "build": "webpack --progress --watch", "reload": "livereload dist/ --port 35729", "dev": "npm run -s reload & npm run -s build" },

So npm run dev will start webpack and the livereload.

Apologies as this isn't directly related to the this project. Hopefully might be useful.

@kayue
Copy link

kayue commented Jan 24, 2017

@defmech May I know how do you compile you SCSS? I am trying your method but the entire page is refreshed even I just changed some SCSS. Also the compile time is long because it attempt to recompile all the JSs.

@defmech
Copy link

defmech commented Jan 24, 2017

@kayue Webpack by default packs it into the javascript. I'm extracting the CSS using the ExtractTextPlugin. Here's a snippet of my webpack.config.js.

module: {
    loaders: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      query: {
        presets: [
          ["es2015", {
            "modules": false
          }]
        ]
      }
    }, {
      test: /\.scss$/,
      loader: ExtractTextPlugin.extract('css-loader!postcss-loader!sass-loader')
    }]
  },
  plugins: [
    // css optimize
    new OptimizeCssAssetsPlugin(),
    // sass
    new ExtractTextPlugin({
      filename: '../css/main.css',
      allChunks: true
    }),
    // uglify js
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      },
      output: {
        comments: false
      },
      sourceMap: true
    }),
    // env plugin
    new webpack.DefinePlugin({
      'proccess.env': {
        NODE_ENV: JSON.stringify(nodeEnv)
      }
    })
  ],

@kayue
Copy link

kayue commented Jan 25, 2017

@defmech I have very similar setup and using ExtractTextPlugin too, but how do you prevent entire page being refreshed?

I have an import statement in my JS (import './bootstrap.scss';), maybe this makes Webpack think I am changing some JS? But if I don't import CSS in my JavaScript, then Webpack won't monitor CSS file for changes at all...

Thank you for your time.

@defmech
Copy link

defmech commented Jan 25, 2017

@kayue I've uploaded my build system https://github.com/defmech/WebPack-2-Build-System so you see how I've set up my environment. Hopefully that helps.

@pmowrer
Copy link

pmowrer commented May 12, 2017

Looks like the current implementation, referenced in @statianzo's #28 (comment), reports all chunks to LiveReload on each compilation, no matter what has changed.

Replacing this logic with the a changedFiles array from the plugin example docs, I easily was able to make live CSS reloading work. I'll put together a PR next week unless someone jumps on it.

@statianzo
Copy link
Owner

changedFiles looks like the right source. Thanks for taking on the PR!

@pmowrer
Copy link

pmowrer commented May 14, 2017

Sure thing! I'll remove the hash logic as well unless there's a good reason to keep it?

(Referencing:)
(hash !== this.lastHash || !arraysEqual(childHashes, this.lastChildHash))

@statianzo
Copy link
Owner

The logic was there so a reload would only get triggered when there was a change. If your solution keeps that behavior, then feel free to rip out the hash comparison code.

@pmowrer
Copy link

pmowrer commented May 15, 2017

I'm afraid I'll have to punt on this for now. Looks like live reloading of <style> tags isn't supported, which makes this a pointless endeavor for us.

@statianzo
Copy link
Owner

Ahh, yes. It'll only work for <link> or @import based on the href attribute. CSS in JS and raw <style> tags would cause a full refresh.
https://github.com/livereload/livereload-js/blob/03b09762e930f6c6c67ee270c208d1c11de0247f/src/reloader.coffee#L149-L179

@tbranyen
Copy link

tbranyen commented Jun 2, 2017

Posted a PR fix here: #33 it works perfectly with this configuration:

    }, {
      test: /\.css$/,
      use: ExtractTextPlugin.extract({
        fallback: 'style-loader',
        use: {
          loader: 'css-loader',
          options: 'url=false&sourceMap',
        }
      })
    }]

@sveint
Copy link

sveint commented Aug 9, 2018

Couldn't get @statianzo snippet to work (it always included all files). To simplify everything I just check the md5 sum from disk instead. Can probably be improved to not recalculate hashes, but didn't want to spend too long on it and this works fine for my setup.

const LivereloadWebpackPlugin = require('webpack-livereload-plugin')
const md5File = require('md5-file')

LivereloadWebpackPlugin.prototype.done = function done(stats) {
    this.fileHashes = this.fileHashes || {}

    const fileHashes = {}
    for (let file of Object.keys(stats.compilation.assets)) {
        fileHashes[file] = md5File.sync(stats.compilation.assets[file].existsAt)
    }

    const toInclude = Object.keys(fileHashes).filter((file) => {
        if (this.ignore && file.match(this.ignore)) {
            return false
        }
        return !(file in this.fileHashes) || this.fileHashes[file] !== fileHashes[file]
    })

    if (this.isRunning && toInclude.length) {
        this.fileHashes = fileHashes
        console.log('Live Reload: Reloading ' + toInclude.join(', '))
        setTimeout(
            function onTimeout() {
                this.server.notifyClients(toInclude)
            }.bind(this)
        )
    }
}

@claudio-borges
Copy link

Thanks @sveint ! This worked out perfect for me.

@veke
Copy link

veke commented Apr 20, 2021

There is still issues with the CSS reloading when using mini-css-extract-plugin. I can see that styles changes without full reload but after short time page does full reload. @sveint snippet does not work with plugin version 3 anymore because the plugin code is changed.
If using the useSourceHash option to true I get error

[webpack-cli] Error: Content and Map of this Source is not available (only size() is supported

Webpack 5
Livereload plugin 3.0.1

@veke
Copy link

veke commented Apr 26, 2021

Well, I ended up to modify the existing @sveint "hack" to work with version 3 of this plugin. In case someone else is needing this.

const md5File = require('md5-file');
const LivereloadWebpackPlugin = require('webpack-livereload-plugin');
const liveReload = new LivereloadWebpackPlugin({ your configs });
const outputPath = path.resolve(__dirname, 'your_output_path'); // probably can be resolved also from the hook

liveReload._afterEmit = (compilation) => {
    const self = liveReload;
    const fileHashes = {};
    self.fileHashes = self.fileHashes || {};

    compilation.getAssets().forEach(asset => {
        fileHashes[asset.name] = md5File.sync(`${outputPath}/${asset.name}`);
    });

    const include = Object.keys(fileHashes).filter(file => {
        if (self.options.ignore && file.match(self.options.ignore)) {
            return false;
        }
        return !(file in self.fileHashes) || self.fileHashes[file] !== fileHashes[file];
    });

    if (self._isRunning() && include.length) {
        self.fileHashes = fileHashes;
        self.logger.info('Reloading ' + include.join(', '));
        setTimeout(() => {
            self.server.notifyClients(include);
        }, self.options.delay);
    }
}
...
module.exports = {
  output: {
    path: outputPath
  },
  plugins: [liveReload]
};

web-mi added a commit that referenced this issue Apr 28, 2021
…terEmit only has SizeOnlySource

- Feature: New option `useSourceSize` to compare source sizes
web-mi added a commit that referenced this issue Apr 28, 2021
…terEmit only has SizeOnlySource

Feature: New option `useSourceSize` to compare source sizes
web-mi added a commit that referenced this issue Apr 28, 2021
…terEmit only has SizeOnlySource

Feature: New option `useSourceSize` to compare source sizes
web-mi added a commit that referenced this issue Apr 28, 2021
…terEmit only has SizeOnlySource

Feature: New option `useSourceSize` to compare source sizes
web-mi added a commit that referenced this issue Apr 28, 2021
…terEmit only has SizeOnlySource

Feature: New option `useSourceSize` to compare source sizes
web-mi added a commit that referenced this issue Apr 28, 2021
…terEmit only has SizeOnlySource

Feature: New option `useSourceSize` to compare source sizes
statianzo pushed a commit that referenced this issue Apr 29, 2021
…terEmit only has SizeOnlySource (#67)

Feature: New option `useSourceSize` to compare source sizes
@veke
Copy link

veke commented May 3, 2021

Hi, I tried the bugfix branch (useSourceSize = true). Here is my test results:
background-color is changed => no full page reload and the color is changed. OK
new property added => no full page reload, changes are applied. OK
edit property integer value, for example add height: 200px, then change it to 100px and back to for example 500px => no full page reload, no visible changes at all. Not OK.
(useSourceHash did not have impact, at least it is not giving error anymore)

@web-mi
Copy link
Collaborator

web-mi commented May 4, 2021

Hi Veke,
as i mentioned in the readme useSourceSize will not reload if you change only the signs but you didn't remove or add any character because filesize stays the same by changing "100px" to "200px" or "500px".

And i know why useSourceHash did not have any impact.
Maybe you enabled useSourceHash AND useSourceSize.
If you have enabled both useSourceSize will filter out the files because the size haven't changed and they will not handelt by useSourceHash anymore.

Could you please try only enabling useSourceHash and disable or remove useSourceSize from config and get back to me if that works. Then i will add a notice to the readme that useSourceHash and useSourceSize won't work together.

@veke
Copy link

veke commented May 4, 2021

"as i mentioned in the readme useSourceSize will not reload if you change only the signs but you didn't remove or add any character because filesize stays the same by changing "100px" to "200px" or "500px".

ah, ok. Yes I understand. My point only was to point out that when using this new option, full page reload is fixed but these kind of CSS changes are not applied at all so the "hack" #28 (comment) mentioned earlier in this issue is still the only working solution.

And i know why useSourceHash did not have any impact.
Maybe you enabled useSourceHash AND useSourceSize.
If you have enabled both useSourceSize will filter out the files because the size haven't changed and they will not handelt by useSourceHash anymore.

Ok, my intention was to report that this branch did fix the error I got earlier when using the useSourceHash (with or without the useSourceSize) and it did not have impact to the CSS full page reload issue.

Thanks for the contribution

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