Related
When using webpack-dev-server, when I make changes to the javascript file the changes are immediately reflected on the webpage.
However, when I make a change to the HTML file, the website does not reflect them. In order to see the new html page, I must first run webpack --mode production, and then, if I re-run webpack-dev-server --hot --mode development, I can see the new HTML changes.
This is quite annoying and I'd like my html changes to be hot-reloaded just like the javascript code. Is there a trick I'm missing? I've spent all day googling and playing with options. The only thing that has helped is to add
devServer:
...
devMiddleware: {
writeToDisk: true
},
to my webpack.config.js as per this answer. However, this has the following problem: My output dist/ folder gets clogged with .js files with checksum names every time a hot reload happens, which is also really annoying.
My project tree:
The full webpack.config.js:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: './src/index.ts',
watch: true,
module: {
rules: [
{
test: /\.ts?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/'
},
plugins: [
new HtmlWebpackPlugin({
title: "My app",
template: "./src/index.html",
/* This output directory is relative to the **OUTPUT** 'publicPath'!
So, if you were to write "./dist/index.html" below, it would output it in "./dist/dist/index.html"!
(You can verify this by running `npx webpack --watch` and looking what files it touches/generates.)
*/
filename: "index.html",
inject: "body"
})
],
devServer: {
// devMiddleware: { writeToDisk: true },
static: {
directory: path.join(__dirname, "dist"),
watch: true
},
compress: true,
webSocketServer: 'ws',
host: '0.0.0.0',
port: 10000,
/* Ensure I can access the server directly, without incurring the 'invalid host header' error */
allowedHosts: "all",
/* So that the webpack-dev-server reloads when I change the index.html file, even though it isn't explicitly listed anywhere. */
watchFiles: ['src/**/*'],
open: true,
hot: true,
},
};
Nevermind, I have found the issue:
Unfortunately Stackoverflow doesn't support line numbers, but if you look in the webpack.config.js code in my original question, you will find the following code:
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/'
},
That publicPath parameter appears to be what was causing the problem. Removing it made the hot-reload 'magic' autodetect that I've changed the HTML file, and re-deploy it.
today I've noticed a strange bug (or I am to dumb?) with my webpack-dev-server.
I've got a Spring Boot App with thymleaf templates. Some pages may only load one others may have more than one js-file:
// main.js
import "../style.scss";
single.html:
<body>
<script th:src="#{/myapp/js/main.js}"></script>
</body>
multiple.html
<body>
<script th:src="#{/myapp/js/main.js}"></script>
<script th:src="#{/myapp/js/other.js}"></script>
</body>
I've splitted my config into a dev, production and common part:
webpack.common.js:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: {
main: path.resolve(__dirname + "/src/main/js/main.js"),
other: path.resolve(__dirname + "/src/main/js/other.js"),
},
output: {
path: path.resolve(__dirname, "./src/main/resources/static/myapp"),
filename: "js/[name].js",
},
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader",
"sass-loader",
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [
// prettier-ignore
["#babel/preset-env", {
corejs: "3.6.4",
// debug: true,
useBuiltIns: "usage"
}
],
],
},
},
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "css/[name].css",
chunkFilename: "css/[name].css",
}),
],
};
webpack.dev.js
const common = require("./webpack.common");
const { merge } = require("webpack-merge");
module.exports = merge(common, {
mode: "development",
devtool: "inline-source-map",
devServer: {
proxy: {
"/": "http://localhost:8081",
},
port: 8083,
devMiddleware: {
publicPath: "/myapp",
},
},
});
The strange behaviour: If I'm editing files for the page which only a single script has been loaded (single.html), changes are applied immediately. For example changing the background color in the css file is displayed without pagereload. If I'm editing a page where multiple scripts (entry points) are used this is not working anymore. My dev-console logs the following:
[HMR] Update failed: Loading hot update chunk global failed.
(missing: http://localhost:8083/myapp/main.618757b0411fc5552e94.hot-update.js)
The first entry point / chunk (main.js) cannot be loaded, caused by the hash? I need to manually refresh the whole page, to apply changes. I've already searched for solutions and tried to apply this tip
optimization: {
runtimeChunk: {
name: 'single',
},
}
However my dev console does not log any HMR output anymore and nothing happens. It seems like HMR has stopped working in my browser. Webpack is running and bundling it correctly!
Any ideas? Thanks so far and apologizes for this wall of text.
The optimization.runtimeChunk option should be true or 'single' and not an object with name:
optimization: {
runtimeChunk: 'single',
},
As the documentation explains this is an alias for:
optimization: {
runtimeChunk: {
name: 'runtime',
},
},
Also, you'll want to include the runtime.js file on the page, however you need to do that for your set up. It should only be served in the dev environment (so for example, if NODE_ENV is "development").
In my case I have production, server-side script files alongside webpack-dev-server, and needed this option enabled so it would correctly serve runtime.js from the manifest.json:
devServer: {
devMiddleware: {
writeToDisk: true,
},
}
I have a simple project with a few html files in root folder and a few javascript modules in scripts directory.
I want to start using webpack now because I want minification and to start using environments. Everything was working before I started using webpack.
I read documentation for Webpack 5 but could not find anything that solves my problem. So I came up with this webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
entry: {
lib: "./scripts/lib.js"
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
devServer: {
contentBase: "./dist",
inline: true, //Enable watch and live reload
host: "localhost",
port: 8080,
stats: "errors-only",
https: false
},
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "index.html",
hash: true
})
],
module: {
rules: [
{
test: /\.html$/i,
loader: 'html-loader',
},
],
},
};
My index.html imports and uses module functions like this:
<script type="module">
import * as lib from "./scripts/lib.js";
window.lib = lib;
</script>
<body onload="lib.myFn()">
</body>
But after running this webpack (using webpack serve) I am getting "is not a function" or "module not found" errors.
How can I compile and run my existing project with webpack without making any changes in my directory structure and fix this error so I can start using functions in lib.js in my index.html? Thanks.
EDIT: It's now resolved. Got in to work this morning and thought "have you tried turning it on and off again?". So I did. Removed node_modules, reinstalled all packages - it worked. FML.
I'm upgrading to Webpack 4 and can't seem to get the watch to work.
When I run the watch script everything runs as expected the first time, but errors out during a file update.
The scripts I try:
"dev": "cross-env ENV=dev webpack --config config/bundling/webpack.config.js --mode=development",
"watch": "cross-env WATCH=true yarn run dev --watch"
(redundancies in the cross-env variables will be fixed later)
The errors I get are the following:
"WARNING in configuration
The 'mode' option has not been set, webpack will fallback to
'production' for this value. Set 'mode' option to 'development' or
'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior.
Learn more: https://webpack.js.org/concepts/mode/"
"ERROR in multi (webpack)-dev-server/client?http://localhost:8080 ./src
Module not found: Error: Can't resolve './src' in [MY PATH HERE]
# multi (webpack)-dev-server/client?http://localhost:8080 ./src main[1]"
It seems like it doesn't read my webpack.config.js or the mode variable the on watch? Also, it succeeds in building the bundle, leading me to thing this might be an issue solely with the built-in webpack-dev-server.
I've tried everything I can think of, changing the scripts, changing the syntax of the mode flag, setting mode in webpack.config.js, tried relative paths, tried absolute paths, tried different versions of webpack and webpack-dev-server, moved my config file to the project root, sacrificed a small CPU to the Gods of code - nothing works.
I've been at this for days without any progress. Any help would be appreciated.
Versions:
"webpack": "^4.27.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10"
Config:
require('dotenv').config()
const CopyWebpackPlugin = require('copy-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const BrowserSyncPlugin = require('browser-sync-webpack-plugin')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
const moduleRules = require('./module.rules')
const config = require('./editable.config')
module.exports = function() {
const isDev = !!(process.env.ENV === 'dev')
const isProd = !!(process.env.ENV === 'prod')
const doServe = !!(process.env.SERVE === 'true')
const doWatch = !!(process.env.WATCH === 'true')
const webpackConfig = {
// Set mode
mode: isProd ? 'production' : 'development',
// Entry points.
entry: config.entrypoints,
// JS output name and destination.
output: {
path: config.paths.public,
filename: config.outputs.javascript.filename
},
// External dependencies.
externals: config.externals,
// Custom resolutions.
resolve: config.resolve,
// Rules for handling filetypes.
module: {
rules: [
moduleRules.javascript,
moduleRules.sass,
moduleRules.fonts,
moduleRules.images,
]
},
// Plugins running in every build.
plugins: [
new FriendlyErrorsWebpackPlugin(),
new MiniCssExtractPlugin(config.outputs.css),
new CleanWebpackPlugin(config.paths.public, { root: config.paths.root }),
new CopyWebpackPlugin([{
context: config.paths.images,
from: {
glob: `${config.paths.images}/**/*`,
flatten: false,
dot: false
},
to: config.outputs.image.filename,
}]),
new CopyWebpackPlugin([{
context: config.paths.fonts,
from: {
glob: `${config.paths.fonts}/**/*`,
flatten: false,
dot: false
},
to: config.outputs.font.filename,
}]),
],
devtool: isDev ? config.settings.sourceMaps : false,
watch: doWatch
}
// Set BrowserSync settings if serving
if (doServe) {
// setting our default settings...
const browserSyncSettings = {
host: 'localhost',
port: 3000,
proxy: process.env.HOME,
files: [
{
match: ['../../**/*.php'],
fn: function (event, file) {
if (event === 'change') {
this.reload()
}
}
}
]
}
// ...and overwriting them with user settings
Object.assign(browserSyncSettings, config.settings.browserSync)
webpackConfig.plugins.push(new BrowserSyncPlugin(browserSyncSettings))
}
return webpackConfig;
}
Config, part 2
const path = require('path')
module.exports = {
paths: {
root: path.resolve(__dirname, '../../'),
public: path.resolve(__dirname, '../../public'),
src: path.resolve(__dirname, '../../src'),
javascript: path.resolve(__dirname, '../../src/js'),
sass: path.resolve(__dirname, '../../src/sass'),
fonts: path.resolve(__dirname, '../../src/fonts'),
images: path.resolve(__dirname, '../../src/images'),
relative: '../../',
external: /node_modules/
},
entrypoints: {
main: ['./src/js/app.js', './src/sass/style.scss']
},
outputs: {
javascript: { filename: 'js/[name].js' },
css: { filename: 'css/[name].css' },
font: { filename: 'fonts/[path][name].[ext]' },
image: { filename: 'images/[path][name].[ext]' }
},
externals: {
},
resolve: {
},
settings: {
sourceMaps: 'cheap-module-source-map',
autoprefixer: {
browsers: ['last 3 versions', '> 1%', 'ie >= 10'],
},
browserSync: {
host: 'localhost',
port: 3000
}
}
}
Bonus question: is it possible to watch without having Webpack 4 start up a new devServer?
Thanks! <3
So I've setup webpack and webpack-dev-server, but webpack-dev-server does not auto-reload. If i modify a file and save it there is no change in the browser until I manually refresh.
Here is my webpack config and my script file that runs webpack-dev-server. Does anyone see anything that could be preventing auto-reload from working?
I put these together by reading through multiple tutorials, the docs, and by reading through the react-create-app generated files.
config/webpack.config.dev.js
'use strict';
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const autoprefixer = require('autoprefixer');
const webpack = require('webpack');
const extractSass = new ExtractTextPlugin('style.css');
module.exports = {
entry : './src/index.jsx',
eslint: {configFile: './src/.eslintrc.json'},
module: {
loaders: [
{
exclude: /node_modules/,
include: ['src'],
loader: 'babel',
test : /(\.js|\.jsx)$/
},
{
exclude: /node_modules/,
include: ['src']
loader : extractSass.extract([ 'css', 'postcss', 'sass' ]),
test : /\.scss$/
}
],
preLoaders: [
{
exclude: /node_modules/,
loader : 'eslint',
query : {presets: [ 'react', 'latest' ]},
test : /(\.js|\.jsx)$/
}
]
},
output: {
filename : 'bundle.js',
path : 'dist',
publicPath: '/'
},
plugins: [
extractSass,
new HtmlWebpackPlugin({
inject : true,
template: paths.appHtml
}),
new webpack.HotModuleReplacementPlugin({multistep: true})
],
postcss: () => [
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9'
]
})
]
};
scripts/dev.js
run with $ yarn run dev
'use strict';
const WebpackDevServer = require('webpack-dev-server');
const config = require('../config/webpack.config.dev.js');
const webpack = require('webpack');
const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
clientLogLevel : 'warn',
compress : true,
contentBase : 'public',
filename : config.output.filename,
historyApiFallback: true,
hot : true,
inline : true,
lazy : false,
noInfo : true,
publicPath : '/',
quiet : true,
stats : 'errors-only',
watchOptions : {
aggregateTimeout: 300,
poll : 1000
}
});
server.listen(8080, 'localhost', () => {
console.log('Listening on port 8080');
});
According to the webpack dev server documentation you should add this entry point to the webpack configuration to support automatic refresh.
config.entry.unshift("webpack-dev-server/client?http://localhost:8080/");
jontem pointed out in his answer that my config was missing a webpack-dev-server client.
Here's the steps I took to apply his solution and also setup HMR.
config/webpack.config.dev.js
module.config = {
// ...
entry: [
// converted entry to an array
// to allow me to unshift the client later
path.resolve(__dirname, '../src/index.jsx')
],
// ...
module: {
loaders: {
// ...
{
// Use style loader instead of ExtractTextPlugin
// To allow for style injection / hot reloading css
exclude: /node_modules/,
loaders: [ 'style', 'css', 'postcss', 'sass' ],
test : /\.scss$/
},
// ...
}
}
}
scripts/dev.js
'use strict';
const WebpackDevServer = require('webpack-dev-server');
const config = require('../config/webpack.config.dev.js');
const webpack = require('webpack');
// unshift `webpack-dev-server` client
// and hot dev-server
config.entry.unshift('webpack-dev-server/client?/', 'webpack/hot/dev-server');
const compiler = webpack(config);
// ...
I had the same issue and the following configuration enabled static and the in-memory bundle auto-reloading. The key is to enable devServer.watchContentBase.
config/webpack.config.dev.js
...
module.exports = {
...
devServer: {
contentBase: ...,
publicPath: ...,
watchContentBase: true
},
...
}
package.json
{
...
"scripts": {
"develop": "webpack-dev-server --open --mode development --config config/webpack.config.dev.js",
...
}
...
}
Please add the following in your webpack config and try.
devServer: {
hot: true,
inline: true,
host: "localhost",
port: 8082,
watchOptions: {
poll: true
}
}
note: I was using webpack version ^3.11.0
I had a similar problem, fixed it by adding
watchOptions: {
poll: true
}
When I first installed the webpack starter, everything worked flawlessly, after a week of changes to webpack.config.js, it stopped working. I tinkered with various recommendations, the one that worked was watchOptions: {poll:true }
FYI I am webpack 4 with "webpack": "4.29.6", "webpack-cli": "^3.3.0", "webpack-dev-server": "3.3.1"
devServer: {
port: 3000,
contentBase: './',
watchOptions: {
poll: true
}
}
Also
if you use extract-loader, auto reload will not work
Hot Module Replacement feature
devServer: {
// ,,,
contentBase: '/your/path'
watchContentBase: true
hot: true,
},
prevents refresh your static files in web page, unless you press F5 button in browser
This third party webpack dev server documentation has the answer that I needed:
https://wohugb.gitbooks.io/webpack/content/dev_tools/webpack-dev-server.html
The relevant section reproduced below:
There is no inline: true flag in the webpack-dev-server configuration, because the webpack-dev-server module has no access to the webpack configuration. Instead the user have to add the webpack-dev-server client entry point to the webpack configuration.
To do this just add webpack-dev-server/client?http://: to (all) entry point(s). I. e. with the above configuration:
var config = require("./webpack.config.js");
config.entry.app.unshift("webpack-dev-server/client?http://localhost:8080");
var compiler = webpack(config);
var server = new webpackDevServer(compiler, {...});
server.listen(8080);
I also had the same issue and after adding this code line my problem solved. Now auto reloading worked
devServer : {
contentBase : './',
watchOptions : {
poll: true
}
}
For anyone experiencing this with Webpack v5, you need to set target to web in your config, like so:
module.exports = {
entry: "...",
target: "web",
.....
}