I would like my workflow to update styles without refreshing the browser. I see in the console that HMR is enabled. However, updating a scss file causes the browser to fully refresh. This is specially inconvenient in development when working on a view that loads data from an API.
I'm using webpack 4.42.0, webpack-dev-server 3.11.0. Full package.json:
{
"name": "example",
"version": "1.0.0",
"description": "example",
"main": "index.js",
"scripts": {
"webpack": "webpack",
"start": "webpack-dev-server --mode development",
"build": "webpack --mode production",
"serve": "node server/index.js",
"test": "jest --watch",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"babel": {
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-proposal-class-properties"
]
},
"browserslist": [
"last 4 version",
"ie 10",
"ie 11",
"Safari >= 8",
"iOS >= 8",
"Android >= 4.4"
],
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.9.0",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/polyfill": "^7.8.7",
"@babel/preset-env": "^7.9.0",
"@babel/preset-react": "^7.9.1",
"@storybook/addon-actions": "^5.3.17",
"@storybook/addon-links": "^5.3.17",
"@storybook/addons": "^5.3.17",
"@storybook/react": "^5.3.17",
"autoprefixer": "^9.7.4",
"babel-loader": "^8.1.0",
"css-loader": "^3.4.2",
"dotenv-webpack": "^1.8.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.3.0",
"jest": "^25.3.0",
"jest-enzyme": "^7.1.2",
"node-sass": "^4.14.1",
"postcss-loader": "^3.0.0",
"sass-loader": "^8.0.2",
"storybook-react-router": "^1.0.8",
"style-loader": "^1.1.3",
"svg-spritemap-webpack-plugin": "^3.5.4",
"url-loader": "^4.1.0",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.28",
"@fortawesome/pro-light-svg-icons": "^5.13.0",
"@fortawesome/pro-regular-svg-icons": "^5.13.0",
"@fortawesome/pro-solid-svg-icons": "^5.13.0",
"@fortawesome/react-fontawesome": "^0.1.9",
"axios": "^0.19.2",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"firebase": "^7.15.0",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2"
}
}
webpack.config.js:
const path = require('path');
const SVGSpritemapPlugin = require('svg-spritemap-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const env = process.env.NODE_ENV;
module.exports = {
entry: './app/src/js/index.js',
output: {
path: path.resolve( __dirname, 'app/dist'),
filename: 'js/main.js',
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
},
devtool: 'source-map',
devServer: {
publicPath: '/',
contentBase: './app/src', // './app/dist/js',
watchContentBase: true,
historyApiFallback: true,
open: true,
hotOnly: true,
inline: true,
proxy: {
'/api': 'http://localhost:4001',
},
},
module: {
rules: [
{ test: /\.jsx?$/, exclude: /node_modules/, use: 'babel-loader' },
{
test: /\.s(a|c)ss$/,
exclude: /node_modules/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
},
{
test: /\.(png|jpg|jpeg|gif)$/,
loader: 'url-loader'
}
],
},
plugins: [
new Dotenv(),
new SVGSpritemapPlugin('./app/src/svg/*.svg', {
output: {
filename: 'svg/icons.svg',
},
sprite: {
prefix: false,
generate: {
title: false,
},
},
}),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
}),
]
};
Project structure:
- root
- - /app
- - - /dist
- - - /src
- - - - /js
- - - - - /components
- - - - - index.js
- - - - /sass
- - - - - /components
- - - - - /globals
- - - - - style.scss
- - /server
index.js
imports style.scss
In case someone stumbles into this, my problem was a missing publicPath
property on the output
object; by setting it to match the publicPath
in devServer
, style updates now update without refreshing:
output: {
path: path.resolve( __dirname, 'app/dist'),
filename: 'js/main.js',
+ publicPath: '/app/dist/'
},
...
devServer: {
publicPath: '/app/dist/',
...
}
For js changes, I also added if (module && module.hot) module.hot.accept();
to my entry file.