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 do I make Hot Module Replacement work for SASS files? #541

Closed
x-yuri opened this issue Jun 28, 2017 · 2 comments
Closed

How do I make Hot Module Replacement work for SASS files? #541

x-yuri opened this issue Jun 28, 2017 · 2 comments

Comments

@x-yuri
Copy link

x-yuri commented Jun 28, 2017

It'd be great to have at least some sort of description for making HMR work. Let me share what I did. Any criticism and suggestions are welcome.

tl;dr

diff --git a/config/webpack/development.js b/config/webpack/development.js
index d98ec5b..53812f7 100644
--- a/config/webpack/development.js
+++ b/config/webpack/development.js
@@ -1,5 +1,6 @@
 // Note: You must restart bin/webpack-watcher for changes to take effect
 
+const webpack = require('webpack')
 const merge = require('webpack-merge')
 const sharedConfig = require('./shared.js')
 
@@ -12,5 +13,9 @@ module.exports = merge(sharedConfig, {
 
   output: {
     pathinfo: true
-  }
+  },
+
+  plugins: [
+    new webpack.HotModuleReplacementPlugin()
+  ],
 })
diff --git a/config/webpack/development.server.js b/config/webpack/development.server.js
index fe840c6..e0d95fd 100644
--- a/config/webpack/development.server.js
+++ b/config/webpack/development.server.js
@@ -12,6 +12,8 @@ module.exports = merge(devConfig, {
     compress: true,
     historyApiFallback: true,
     contentBase: resolve(paths.output, paths.entry),
-    publicPath
+    publicPath,
+    hot: true,
+    headers: {'Access-Control-Allow-Origin': '*'}
   }
 })
diff --git a/config/webpack/loaders/sass.js b/config/webpack/loaders/sass.js
index cdc714f..d5d4764 100644
--- a/config/webpack/loaders/sass.js
+++ b/config/webpack/loaders/sass.js
@@ -2,8 +2,8 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin')
 
 module.exports = {
   test: /\.(scss|sass|css)$/i,
-  use: ExtractTextPlugin.extract({
+  use: ['css-hot-loader'].concat(ExtractTextPlugin.extract({
     fallback: 'style-loader',
     use: ['css-loader', 'postcss-loader', 'sass-loader'],
-  }),
+  })),
 }
diff --git a/config/webpack/shared.js b/config/webpack/shared.js
index ef5fa74..2ea6e62 100644
--- a/config/webpack/shared.js
+++ b/config/webpack/shared.js
@@ -23,7 +23,7 @@ module.exports = {
     }, {}
   ),
 
-  output: { filename: '[name].js', path: resolve(paths.output, paths.entry) },
+  output: { filename: '[name].js', path: resolve(paths.output, paths.entry), publicPath },
 
   module: {
     rules: readdirSync(loadersDir).map(file => (
diff --git a/package.json b/package.json
index dcbdec9..f46a81b 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
     "coffee-script": "^1.12.5",
     "compression-webpack-plugin": "^0.4.0",
     "cookies-js": "^1.2.3",
+    "css-hot-loader": "^1.2.0",
     "css-loader": "^0.28.0",
     "eonasdan-bootstrap-datetimepicker": "^4.17.47",
     "extract-text-webpack-plugin": "^2.1.0",

detailed explanation

According to webpack's documentation I just need to add new webpack.HotModuleReplacementPlugin() to plugins section (development.js) and run webpack-dev-server with --hot switch (development.server.js). But with that you see that HMR doesn't work properly. The whole page gets reloaded (HMR messages in console disappear).

Then you add --hot-only switch (development.server.js) for webpack-dev-server to not fall back to live reload. And see that it can't find hot-update.json file, whatever that may be:

GET http://localhost:3000/40293e2cd0bae6910c2e.hot-update.json 404 (Not Found)

That happens since it's asking for it your rails app (localhost:3000), not webpack-dev-server (localhost:8080). And the reason for that is, output.publicPath option is not set. It's set for webpack-dev-server, but not for webpack itself.

The error gets triggered here. Since $require$.p is empty string. And $require$.p is empty because it's basically output.publicPath option.

So, we add output.publicPath and the next thing you see is webpack-dev-server asking you to allow your rails app access to webpack-dev-server:

XMLHttpRequest cannot load http://localhost:8080/9b1e70204c19ca0472e5.hot-update.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.

You add it to development.server.js (headers: {'Access-Control-Allow-Origin': 'http://localhost:3000'}), and check again. This time your site doesn't see your updates:

[HMR] Checking for updates on the server...
[HMR] Nothing hot updated.

And that has to do with extract-text-webpack-pluging not meant to be used in development. So, you disable it (new ExtractTextPlugin({disable: env.NODE_ENV != 'production', filename: ...). Or this way. And now it sort of works, but you see:

GET http://localhost:8080/application.css

in console. That's because <%= stylesheet_pack_tag 'application' %> in layout still tries to find extracted css file. But we no longer extract it in development env. And thus you end up with:

<%= stylesheet_pack_tag 'application' unless Rails.env.development? %>

The one improvement you can make here is make use of css-hot-loader package. It allows not to disable extract-text-webpack-plugin in development environment.

@gauravtiwari
Copy link
Member

@x-yuri Yepp, this will be included soon. Thanks for sharing 👍 🍰

@gauravtiwari
Copy link
Member

HMR has landed on master 🎉

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

2 participants