Skip to content

Commit 8851d8b

Browse files
David Heinemeier Hanssongauravtiwari
authored andcommitted
Add HMR to the dev server (#641)
* Add HMR * Fix development.js changes * Update sass loader and remove redundant config from vue loader * Remove redundant variables * Update condition for extracting CSS * Update readme and changelog * Fix typo * Make linter happy * Add a comment for HMR mode Lowercase * Add a note to turn off HMR * Fix wording
1 parent cef39c8 commit 8851d8b

File tree

9 files changed

+73
-62
lines changed

9 files changed

+73
-62
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
- Move dev-server config options under defaults so it's transparently available in all environments
2929

30+
- Add new `HMR` option for hot-module-replacement
31+
3032

3133
### Removed
3234

README.md

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,12 @@ Similary you can also control and configure `webpack-dev-server` settings from `
330330
# config/webpacker.yml
331331
development:
332332
dev_server:
333-
host: 0.0.0.0
333+
host: localhost
334334
port: 8080
335-
https: false
336335
```
337336

337+
If you have `hmr` turned to true, then the `stylesheet_pack_tag` generates no output, as you will want to configure your styles to be inlined in your JavaScript for hot reloading. During production and testing, the `stylesheet_pack_tag` will create the appropriate HTML tags.
338+
338339
#### Resolved Paths
339340

340341
If you are adding webpacker to an existing app that has most of the assets inside
@@ -398,26 +399,23 @@ plugins:
398399
Webpacker out-of-the-box provides CDN support using your Rails app `config.action_controller.asset_host` setting. If you already have [CDN](http://guides.rubyonrails.org/asset_pipeline.html#cdns) added in your rails app
399400
you don't need to do anything extra for webpacker, it just works.
400401

401-
### HTTPS in development
402-
403-
If you're using the `webpack-dev-server` in development, you can serve views over HTTPS
404-
by setting the `https` option for `webpack-dev-server` to `true` in `config/webpacker.yml`,
405-
then start the dev server as usual with `./bin/webpack-dev-server`.
406-
407-
Please note that the `webpack-dev-server` will use a self-signed certificate,
408-
so your web browser will display a warning upon accessing the page.
409-
410-
411402
### Hot module replacement
412403

413-
Webpacker out-of-the-box doesn't ship with HMR just yet. You will need to
414-
install additional plugins for Webpack if you want to add HMR support.
404+
Webpacker out-of-the-box supports HMR with `webpack-dev-server` and
405+
you can toggle it by setting `dev_server/hmr` option inside webpacker.yml.
415406

416-
You can checkout these links on this subject:
407+
Checkout this guide for more information:
417408

418409
- https://webpack.js.org/configuration/dev-server/#devserver-hot
410+
411+
To support HMR with React you would need to add `react-hot-loader`. Checkout this guide for
412+
more information:
413+
419414
- https://webpack.js.org/guides/hmr-react/
420415

416+
**Note:** Don't forget to disable `HMR` if you are not running `webpack-dev-server`
417+
otherwise you will get not found error for stylesheets.
418+
421419

422420
## Linking Styles, Images and Fonts
423421

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
11
const ExtractTextPlugin = require('extract-text-webpack-plugin')
22
const path = require('path')
3-
const { env } = require('../configuration.js')
3+
const { env, settings } = require('../configuration.js')
44

55
const postcssConfigPath = path.resolve(process.cwd(), '.postcssrc.yml')
6+
const isProduction = env.NODE_ENV === 'production'
7+
const extractCSS = !settings.dev_server.hmr
68

7-
module.exports = {
9+
const extractOptions = {
10+
fallback: 'style-loader',
11+
use: [
12+
{ loader: 'css-loader', options: { minimize: isProduction } },
13+
{ loader: 'postcss-loader', options: { sourceMap: true, config: { path: postcssConfigPath } } },
14+
'resolve-url-loader',
15+
{ loader: 'sass-loader', options: { sourceMap: true, indentedSyntax: true } }
16+
]
17+
}
18+
19+
// For production extract styles to a separate bundle
20+
const extractCSSLoader = {
21+
test: /\.(scss|sass|css)$/i,
22+
use: ExtractTextPlugin.extract(extractOptions)
23+
}
24+
25+
// For hot-reloading use regular loaders
26+
const inlineCSSLoader = {
827
test: /\.(scss|sass|css)$/i,
9-
use: ExtractTextPlugin.extract({
10-
fallback: 'style-loader',
11-
use: [
12-
{ loader: 'css-loader', options: { minimize: env.NODE_ENV === 'production' } },
13-
{ loader: 'postcss-loader', options: { sourceMap: true, config: { path: postcssConfigPath } } },
14-
'resolve-url-loader',
15-
{ loader: 'sass-loader', options: { sourceMap: true } }
16-
]
17-
})
28+
use: ['style-loader'].concat(extractOptions.use)
1829
}
30+
31+
module.exports = isProduction || extractCSS ? extractCSSLoader : inlineCSSLoader
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
const { join } = require('path')
2+
const { settings } = require('../configuration.js')
3+
14
module.exports = {
25
test: /\.(js|jsx)?(\.erb)?$/,
36
exclude: /node_modules/,
4-
loader: 'babel-loader'
7+
loader: 'babel-loader',
8+
options: {
9+
cacheDirectory: join(settings.cache_path, 'babel-loader')
10+
}
511
}
Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,12 @@
1-
const ExtractTextPlugin = require('extract-text-webpack-plugin')
2-
const { env } = require('../configuration.js')
1+
const { env, settings } = require('../configuration.js')
32

4-
// Change it to false if you prefer Vue styles to be inlined by javascript in runtime
5-
const extractStyles = false
6-
7-
const cssLoader = [
8-
{ loader: 'css-loader', options: { minimize: env.NODE_ENV === 'production' } },
9-
{ loader: 'postcss-loader', options: { sourceMap: true } },
10-
'resolve-url-loader'
11-
]
12-
const sassLoader = cssLoader.concat([
13-
{ loader: 'sass-loader', options: { sourceMap: true, indentedSyntax: true } }
14-
])
15-
const scssLoader = cssLoader.concat([
16-
{ loader: 'sass-loader', options: { sourceMap: true } }
17-
])
18-
19-
function vueStyleLoader(loader) {
20-
if (extractStyles) {
21-
return ExtractTextPlugin.extract({
22-
fallback: 'vue-style-loader',
23-
use: loader
24-
})
25-
}
26-
return ['vue-style-loader'].concat(loader)
27-
}
3+
const isProduction = env.NODE_ENV === 'production'
4+
const extractCSS = !settings.dev_server.hmr
285

296
module.exports = {
307
test: /\.vue$/,
318
loader: 'vue-loader',
329
options: {
33-
loaders: {
34-
js: 'babel-loader',
35-
file: 'file-loader',
36-
css: vueStyleLoader(cssLoader),
37-
scss: vueStyleLoader(scssLoader),
38-
sass: vueStyleLoader(sassLoader)
39-
}
10+
extractCSS: isProduction || extractCSS
4011
}
4112
}

lib/install/config/webpack/development.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Note: You must restart bin/webpack-dev-server for changes to take effect
22

3+
const webpack = require('webpack')
34
const merge = require('webpack-merge')
45
const sharedConfig = require('./shared.js')
56
const { settings, output } = require('./configuration.js')
@@ -15,6 +16,7 @@ module.exports = merge(sharedConfig, {
1516
clientLogLevel: 'none',
1617
host: settings.dev_server.host,
1718
port: settings.dev_server.port,
19+
hot: settings.dev_server.hmr,
1820
contentBase: output.path,
1921
publicPath: output.publicPath,
2022
compress: true,
@@ -26,5 +28,10 @@ module.exports = merge(sharedConfig, {
2628
stats: {
2729
errorDetails: true
2830
}
29-
}
31+
},
32+
33+
plugins: settings.dev_server.hmr ? [
34+
new webpack.HotModuleReplacementPlugin(),
35+
new webpack.NamedModulesPlugin()
36+
] : []
3037
})

lib/install/config/webpacker.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ development:
3333
dev_server:
3434
host: localhost
3535
port: 3035
36+
hmr: false
3637

3738
test:
3839
<<: *default

lib/webpacker/dev_server.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ def running?
1212
false
1313
end
1414

15+
def hot_module_replacing?
16+
fetch(:hmr)
17+
end
18+
1519
def host
1620
fetch(:host)
1721
end

lib/webpacker/helper.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,26 @@ def javascript_pack_tag(*names, **options)
3232
# in config/webpack/shared.js. By default, this list is auto-generated to match everything in
3333
# app/javascript/packs/*.js. In production mode, the digested reference is automatically looked up.
3434
#
35+
# Note: If the development server is running and hot module replacement is active, this will return nothing.
36+
# In that setup you need to configure your styles to be inlined in your JavaScript for hot reloading.
37+
#
3538
# Examples:
3639
#
3740
# # In development mode:
3841
# <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # =>
3942
# <link rel="stylesheet" media="screen" href="/packs/calendar.css" data-turbolinks-track="reload" />
4043
#
44+
# # In development mode with hot module replacement:
45+
# <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # =>
46+
# nil
47+
#
4148
# # In production mode:
4249
# <%= stylesheet_pack_tag 'calendar', 'data-turbolinks-track': 'reload' %> # =>
4350
# <link rel="stylesheet" media="screen" href="/packs/calendar-1016838bab065ae1e122.css" data-turbolinks-track="reload" />
4451
def stylesheet_pack_tag(*names, **options)
45-
stylesheet_link_tag(*sources_from_pack_manifest(names, type: :stylesheet), **options)
52+
unless Webpacker.dev_server.running? && Webpacker.dev_server.hot_module_replacing?
53+
stylesheet_link_tag(*sources_from_pack_manifest(names, type: :stylesheet), **options)
54+
end
4655
end
4756

4857
private

0 commit comments

Comments
 (0)