I'm using Webpack 5 and cannot set dev-server to pick up the latest changes immediately. That's why when I have any changes in my project I should do yarn build first and then yarn dev every time. I've already seen the same question here Webpack-dev-server doesn't pick up latest changes, but writeToDisk: true shows an error in the console. I've attached my webpack.config.js file.
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const miniCss = require('mini-css-extract-plugin');
module.exports = {
entry: { main: './src/index.js' },
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js',
publicPath: ''
},
mode: 'development',
devServer: {
static: {
directory: path.join(__dirname, "dist")
},
compress: true,
port: 8080,
devMiddleware: {
publicPath: "//localhost:8080",
},
hot: "only",
open: true
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: '/node_modules/'
},
{
test: /\.pug$/,
loader: 'pug-loader',
options: {
pretty: true
}
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, {
loader: 'css-loader'
}]
},
{
test: /\.(s*)css$/,
use: [
miniCss.loader,
'css-loader',
'sass-loader',
]
},
{
test: /\.(png|svg|jpg|gif|woff(2)?|eot|ttf|otf)$/,
type: 'asset/resource'
},
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/pages/page/page.pug',
filename: 'index.html'
}),
new MiniCssExtractPlugin(),
new miniCss(),
]
}
Your issue is most likely with hot: "only". According to https://webpack.js.org/configuration/dev-server/ you will not see a page refresh on build failures if it's not hot: true.
Other than that I don't see anything obviously wrong with your code. I suspect that if you change it to hot: true it should refresh the page and that you have some other build failure.
Related
I have setup a simple webpack 5 config file with a dev server that seems to be working but when I add
<link href="/style.css" rel="stylesheet" />
to the index.html file I get no CSS loading. Currently in dev mode I am choosing to use 'style-loader' over the 'MiniCssExtractPlugin' to enable hot module reloading natively.
any help would be greatly appreciated. There are no errors in the webpack console output just no CSS to call from the HTML file.
Webpack file:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = (env, options) => {
const mode = options.mode;
return {
context: __dirname,
entry: {
script: './src/index.bundle.ts',
style: './src/index.bundle.less',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, './src/dist')
},
devServer: {
watchFiles: ['src/**/*.less'],
static: {
directory: path.join(__dirname, 'src'),
watch: true,
},
compress: true,
port: 9000,
hot: true,
},
devtool: 'source-map',
resolve: {
extensions: ['.ts', '.tsx', '.js', '.css', '.less']
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
ignoreOrder: false,
}),
],
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
target: 'browserslist'
}
}
},
{
test: /\.ts(x)?$/,
loader: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
mode === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
},
{
loader: 'postcss-loader', // Run postcss actions
options: {
postcssOptions: {
config: path.resolve(__dirname, "postcss.config.js"),
}
}
},
]
},
{
test: /\.less$/,
use: [
mode === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'less-loader'
]
},
]
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin(), new CssMinimizerPlugin()]
},
target: 'web',
}
};
It appears you've misconfigured
static: {
directory: path.join(__dirname, 'src'),
Your output points to src/dist so it looks like it should be this instead
static: {
directory: path.join(__dirname, 'src/dist'),
On a side note, it's an anti-pattern to have compiled files within your source folder. Generally this structure is recommended.
[repo_root]/
dist/
src/
I've never used Webpack before and I'm working on a project that's just vanilla JS and HTML. I'm having an issue accessing the values I set in .env. Here's my config.
const path = require("path");
const dotenv = require('dotenv');
var webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = () => {
env = dotenv.config().parsed;
const envKeys = Object.keys(env).reduce((prev, next) => {
prev[`process.env.${next}`] = JSON.stringify(env[next]);
return prev;
}, {});
return {
entry: {
main: './src/index.js'
},
output: {
path: path.join(__dirname, '../build'),
filename: '[name].bundle.js'
},
mode: 'development',
devServer: {
contentBase: "./src/",
publicPath: "./src/",
compress: true,
port: 9000,
overlay: true,
disableHostCheck: true
},
devtool: 'inline-source-map',
resolve: {
alias: {
process: "process/browser"
}},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/'
}
}
]
},
{
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
//attributes: ['img:src', ':data-src'],
minimize: true
}
}
}
]
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
new Dotenv(),
new webpack.DefinePlugin(envKeys),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
}),
]
}
};
As you can see, I'm using dotEnv, definePlugin, and even the dotEnv-webpack plugin. Unfortunately, none of these solutions seem to allow me to access process.env.originURL.
originURL=https://localhost:3000
I'm not importing or requiring anything in my javascript file, index.js. I'm assuming this should work, but the console tells me that process is undefined.
console.log(process.env.originURL);
How can I access process.env.originURL in my index.js?
It should work. Maybe you could try this version:
new Dotenv({ systemvars: true })
If it still doesn't work, please show your package.json, as well as you .env file (randomize the values of course). Is .env at the root of you app?
I can't make my webpack to emit css sourcemap. I've put sourcemap: true everywhere where possible, and no effect, and all the solutions online suggest either this or some other plugin configuration, but I don't have any other plugin, it's super simple webpack.config.js
This is my webpack.config.js:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
var path = require("path");
module.exports = {
entry: "./main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
publicPath: "/",
sourceMapFilename: '[file].map'
},
devtool: 'source-map',
module: {
rules: [{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: ["es2015"],
sourceMap: true
}
}
},
{
test: /\.scss$/,
use: [{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '/',
sourceMap: true,
hmr: process.env.NODE_ENV === 'development',
},
},
{
loader: "css-loader",
options: {
sourceMap: true
}
},
{
loader: "sass-loader",
options: {
sourceMap: true
},
}
]
},
{
test: /\.(png|woff|woff2|eot|ttf|svg|jpg)$/,
use: [{
loader: 'url-loader',
options: {
name: 'images/[hash]-[name].[ext]'
}
}]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
ignoreOrder: false, // Enable to remove warnings about conflicting order
sourceMap: true
}),
],
};
I need this source map in dev mode, but only two files get emited main.css and bundle.js.
For those who also struggle with this, the below webpack.config.js is a proper configuration for having style's sourcemap in dev mode. Bundle.js needs to be included when in dev mode, and custom.min.css needs to be added to the HTML document in production mode.
Edit: Unfortunately this can be also wrong. Right now without any change to webpack or node, the sourcemap is not generating :/ sometimes it works, and sometimes it doesn't. There is some bug in webpack, or in some plugins, either way it's a serious bug, but it's webpack so I am not surprised...
Edit 2: Now I see that the problem is only in Firefox, Chrome and Opera have the correct map.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
var path = require("path");
module.exports = {
entry: "./main.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
publicPath: "/"
},
module: {
rules: [{
test: /\.js$/,
use: {
loader: "babel-loader",
options: { presets: ["es2015"] }
}
},
{
test: /\.scss$/,
use: [
{
loader: "style-loader" // creates style nodes from JS strings
},
{
loader: "css-loader" ,
options: {
sourceMap: true
}// translates CSS into CommonJS
},
{
loader: "sass-loader",
options: {
sourceMap: true
} // compiles Sass to CSS
}
]
},
{ test: /\.(png|woff|woff2|eot|ttf|svg|jpg)$/,
use: [{
loader: 'url-loader',
options: {
name: 'images/[hash]-[name].[ext]'
}
}] }
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '../css/custom.min.css'
})
]
};
Am trying to use HRM (Hot Module Replacement) in my webpack config, first I have set the --hot option within my package.jsong:
"scripts": {
"start": "webpack-dev-server --hot"
}
Also note that am using the HtmlWebpackPlugin in order to create an index.html file for me and put it in a "build" directory, here is my full webpack.config.js file:
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const WebpackManifestPlugin = require('webpack-manifest-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
// const webpack = require('webpack');
const mode = "development";
module.exports = {
mode: mode,
// watch: true,
devtool: "cheap-module-eval-source-map",
devServer: {
port: 9000,
// contentBase: path.resolve(__dirname, 'build')
},
entry: {
application: "./src/javascripts/index.js",
admin: "./src/javascripts/admin.js"
},
output: {
filename: mode === 'production' ? "[name]-[contenthash].js" : '[name].[hash].js',
path: path.resolve(__dirname, 'build'),
// publicPath: '/'
},
optimization: {
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
plugins: [
new MiniCssExtractPlugin({
filename: mode === 'production' ? "[name]-[contenthash].css" : "[name]-[hash].css",
hmr: mode === 'production' ? false : true
}),
new CleanWebpackPlugin(),
new WebpackManifestPlugin(),
new HtmlWebpackPlugin({
template: './src/template.html',
// filename: '../index.html'
})
// new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')({
overrideBrowserslist: ['last 3 versions', 'ie > 9']
})
]
}
},
],
},
{
test: /\.scss$/i,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{
loader: 'postcss-loader',
options: {
plugins: [
require('autoprefixer')({
overrideBrowserslist: ['last 3 versions', 'ie > 9']
})
]
}
},
'sass-loader'
],
},
{
test: /\.(png|jpg|gif|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
limit: 8192,
name: '[name].[hash:7].[ext]'
},
},
{
loader: 'image-webpack-loader'
}
],
}
]
}
}
As you see the entry I use mainly is ./src/javascripts/index.js where am importing a file called application.scss:
import application from "../stylesheets/application.scss"
the application.scss contains:
span{
background-color: blue;
}
So I want to hot reload the page whenever I change the background color of the span located in my html page.
when I run npm run start then I change the background color of the span the update works the first time (BUT IT DOES A FULL PAGE RELOAD) then if I try to change the background color again nothing get updated and all I see in the console is this:
[HMR] Nothing hot updated. log.js:24
[HMR] App is up to date.
Am not sure what am missing here, but someone can see what am doing wrong?
From the documentation,
it tries to update with HMR before trying to reload the whole page
So, presumably, if that fails, it will just reload the entire page, which is what you're seeing.
Have you tried including this at the end of your primary entry file?
if (module.hot) {
module.hot.accept(function (err) {
console.log('An error occurred while accepting new version');
});
}
Also, inspect the -hot.js file requests of network tab of Chrome's developer tools --- are they status 200, 404, 500? Maybe your server is crashing or not serving them correctly.
Ensure in your webpack that you also have
devServer: {
// contentBase: './dist', // this watches for html changes, apparently?
contentBase: path.resolve(__dirname, 'build') // in your specific case maybe?
hot: true,
},
I have a simple HMR setup for reloading typescript files and postcss files. And they work perfectly well and modules reload without a page refresh. But when I change my HTML files, the website doesn't reload on it's own and the HTML content is not being hot-reloaded in.
This is my webpack config file:
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: resolve(__dirname, 'src/main.ts'),
output: {
path: resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
}
]
}
]
},
devtool: 'source-map',
mode: 'development',
plugins: [
new HtmlWebpackPlugin({
template: resolve(__dirname, './src/index.html'),
}),
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(resolve(__dirname, 'dist'))
],
devServer: {
contentBase: resolve(__dirname, 'dist'),
port: 9000,
hot: true,
open: true,
progress: true,
}
}
With webpack 5,this setting is work for me:
devServer: {
hot:true,
open:true,
watchFiles: ['src/**/*']
},
webpack-dev-server github issue #3881
Problem is that html-webpack-plugin doesn't react to change and doesn't trigger the hmr.
In order to achieve that you could try something like this:
const { resolve } = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
let devServer; // set below in devserver part
function reloadHtml() {
this.plugin('compilation',
thing => thing.plugin('html-webpack-plugin-after-emit', trigger));
const cache = {};
function trigger(data, callback) {
const orig = cache[data.outputName];
const html = data.html.source();
if (orig && orig !== html)
devServer.sockWrite(devServer.sockets, 'content-changed');
cache[data.outputName] = html;
callback();
}
}
module.exports = {
entry: resolve(__dirname, 'src/main.ts'),
output: {
path: resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
}
]
}
]
},
devtool: 'source-map',
mode: 'development',
plugins: [
reloadHtml,
new HtmlWebpackPlugin({
template: resolve(__dirname, './src/index.html'),
}),
new webpack.HotModuleReplacementPlugin(),
new CleanWebpackPlugin(resolve(__dirname, 'dist'))
],
devServer: {
before(app, server) {
devServer = server;
},
contentBase: resolve(__dirname, 'dist'),
port: 9000,
hot: true,
open: true,
progress: true,
}
}
If you get :
DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead
You can change reloadHtml to this:
function reloadHtml() {
const cache = {}
const plugin = {name: 'CustomHtmlReloadPlugin'}
this.hooks.compilation.tap(plugin, compilation => {
compilation.hooks.htmlWebpackPluginAfterEmit.tap(plugin, data => {
const orig = cache[data.outputName]
const html = data.html.source()
if (orig && orig !== html) {
devServer.sockWrite(devServer.sockets, 'content-changed')
}
cache[data.outputName] = html
})
})
}
Not sure if this is the case, but if you only want to extend your current HMR config to do a browser reload when your outer html/view files changed, then you can do it with a few extra lines using chokidar.
Maybe you already have it, because webpack-dev-server is using chokidar internally, but if not found then install it first with npm or yarn:
npm install chokidar --save
yarn add -D chokidar
Then require it in your webpack config:
const chokidar = require('chokidar');
Then in your devServer config:
devServer: {
before(app, server) {
chokidar.watch([
'./src/views/**/*.html'
]).on('all', function() {
server.sockWrite(server.sockets, 'content-changed');
})
},
Also check the API for more options.
I'm using it with Webpack4. Don't know if it works with earlier versions...
Hope it helps you or others looking for this situation.