webpack-livereload-plugin
webpack-livereload-plugin copied to clipboard
Live CSS reloading
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?
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?
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
@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));
}
};
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.
@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.
@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)
}
})
],
@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.
@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.
Looks like the current implementation, referenced in @statianzo's https://github.com/statianzo/webpack-livereload-plugin/issues/28#issuecomment-269891061, 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.
changedFiles looks like the right source. Thanks for taking on the PR!
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))
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.
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.
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
Posted a PR fix here: https://github.com/statianzo/webpack-livereload-plugin/pull/33 it works perfectly with this configuration:
}, {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: {
loader: 'css-loader',
options: 'url=false&sourceMap',
}
})
}]
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)
)
}
}
Thanks @sveint ! This worked out perfect for me.
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
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]
};
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)
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.
"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" https://github.com/statianzo/webpack-livereload-plugin/issues/28#issuecomment-411733411 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