I have following webpack configuration file.
const path = require('path');
const webpack = require('webpack');
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'scripts.min.js'
},
resolve: {
root: [
path.resolve('./src'),
path.resolve('./node_modules')
]
},
entry: './src/app.js',
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel'
}
]
},
devServer: {
// compress: true,
inline: true,
stats: 'errors-only'
},
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({ 'mangle': false, sourcemap: false })
]
};
The problem is that when I run webpack-dev-server command, it does run the server but when I make changes to any js file they are not compiled on the fly. So I had to stop the server and run webpack and then run webpack-dev-server command to make things work.
How can I make webpack-dev-server work so that when watches for all js, css, scss files and compile those fly?
If you are working with an IDE, you have to disable "safe write".
Note that many editors support “safe write” feature and have it enabled by default, which makes dev server unable to watch files correctly. “Safe write” means changes are not written directly to original file but to temporary one instead, which is renamed and replaces original file when save operation is completed successfully. This behaviour causes file watcher to lose the track because the original file is removed. In order to prevent this issue, you have to disable “safe write” feature in your editor.
http://webpack.github.io/docs/webpack-dev-server.html#hot-mode
Related
I am trying to get more familiar with using webpack. I have converted everything to be able to load modules, and plugins. Everything loads fine when I run "npm run build-prod", when I use liveServer, HTML and everything loads properly. However when I run "npm run build-dev", it auto pops up my index.html file and starts reloading the page nonstop, and doesn't apply any CSS. The reloading issue makes me think I am using npm run build-dev wrong.
I am pretty new to webpacks, but to my understanding, the point of running "npm run build-dev", it will compile everything using my loaders and plugins without minifying everything, so I can edit and make changes in real time. Or is my understanding completely wrong.
const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
entry: "./src/client/index.js",
mode: "development",
devtool: "source-map",
stats: "verbose",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: "/.js$/",
exclude: /node_modules/,
loader: "babel-loader",
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/client/views/index.html",
filename: "./index.html",
}),
new CleanWebpackPlugin({
// Simulate the removal of files
dry: true,
// Write Logs to Console
verbose: true,
// Automatically remove all unused webpack assets on rebuild
cleanStaleWebpackAssets: true,
protectWebpackAssets: false,
}),
],
};
Here is my full repo, but without the .env variables holding my API keys.
Just a friendly advice: It feels like you started doing something complex without getting the basics right.
Why don't you try some of the examples first?
The webpack website is quite rich of those: https://webpack.js.org/guides/getting-started/
Never figured out what the issue was, but when I ran it on my Mac instead of Windows, I couldn't reproduce the issue. I tried on both Chrome and FireFox without any plug-in.
I have the following webpack.config.js
const path = require("path");
const webpack = require("webpack");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
entry: "./src/index.js",
mode: "development",
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
},
resolve: { extensions: ["*", ".js"] },
output: {
path: path.resolve(__dirname, "public/dist/"),
publicPath: "/dist/",
filename: "bundle.js"
},
devServer: {
contentBase: __dirname,
port: 3000,
publicPath: "http://localhost:3000/dist/",
proxy: {
'/api' : {
target: 'http://localhost:8000',
secure: false
}
},
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebPackPlugin({
hash: true,
template: "./src/index.html",
filename: "index.html"
})
],
optimization: {
minimize: true,
minimizer: [new TerserPlugin()]
}
};
When I run webpack-dev-server, everything compiles fine, but when I navigate to localhost:3000/public/dist/index.html, I get a "file not found" error.
However, when I run webpack --mode development, the index.html file is correctly written to public/dist and I'm able to open it on my web browser.
I'm not sure why this file is correctly written when webpack is run normally, but not when the dev server is ran. I know the webpack dev server serves files from memory so I don't expect it to show up on my filesystem but I at least expect to be able to open it from the browser.
Thanks!
Webpack dev server doesn't actually build the files to your project directory. From their docs:
webpack-dev-server doesn't write any output files after compiling. Instead, it keeps bundle files in memory and serves them as if they were real files mounted at the server's root path.
So running webpack-dev-server, you shouldn't expect to see any built files (like your resultant index.html) in your dist directory. It will be serving the project from memory directly to your localhost:3000, or whatever pathname you choose. However, running webpack actually builds the files to your destination directory, and you'll find them there. Hope that clears it up for you.
I am migrating from gulp to webpack setup. I want webpack process my js and css assets and pack them into bundles. I have set up 2 webpack config files: one for js and one for css.
The total sizes of css and js assets in my project are similar: rougly 70 files (400kb minified) in each section.
My question is related to poor webpack performance when processing css assets compared to js.
To compare:
JS build (first run): 15-30 seconds
JS build (with cache): 2 seconds
CSS build (first run): 15 seconds
CSS build (with cache): 10 seconds
Obviously the CSS builder doesn't use cache as efficiently as the CSS part. To be honest I don't think it uses caching at all (node_modules/.cache doesn't have anything related) and the only reason for the 2nd run to be faster is filesystem warmup.
The problem in watch mode is even bigger. I did a test where I run webpack in watch mode and modify one small file (just a few lines), which has to be included in a bigger bundle:
JS update: 150ms
CSS update: 1200-2000ms
CSS builder doesn't perform very well here. Also, the larger the bundles, the longer it takes to update them, even though a change is done in a small file which should be compiled instantly.
Furthermore, it seems that increasing the number of entry points also negatively affects update times. More entries = slower updates, even though the update only affects one tiny file.
I've tried playing with cache-loader and placing it before and after extract plugin. Honestly it doesn't help much. When cache loader is placed in front of extract plugin, no css is being emitted in watch mode (only the js files). Placing cache loader after the extractor improves production build a bit, but slows down watch mode updates.
My current guess is that sass loader doesn't use caching and that on every module update, the entire bundle has to be compiled from scratch and go through sass > postcss > css > extract pipe all over again. I wonder if delegating import management to postcss-import and running sass after postcss would do a better job. I have tried to write a sass-compatible import resolver for postcss-import. It seems to work a bit faster in a small test environment, but using it in our real project causes webpack to crash :( Didn't have enough time to find out why that happens yet.
How can I improve CSS build times of my current setup?
My JS and CSS configs are below.
JS:
const path = require('path');
const merge = require('webpack-merge');
const EntryPlugin = require('webpack-watched-glob-entries-plugin');
const dir = require('./node/dirconfig');
// use NODE_ENV var to switch between production and development
const devel = (process.env.NODE_ENV == 'development');
// base config for both prod and devel modes
let config =
{
name: 'scripts',
// webpack preset
// this should take care of production optimizations automatically
mode: devel ? 'development' : 'production',
// use all js files inside bundle dir as entries
entry: EntryPlugin.getEntries(
dir.assets + '/js/bundle/*.js'
),
output: {
path: dir.static + '/js',
filename: "[name].js"
},
externals: {
jquery: 'jQuery'
},
resolve: {
modules: [dir.assets + '/js', 'node_modules']
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
},
]
},
plugins: [
new EntryPlugin(),
],
};
// additional config for development mode
const development =
{
// add eslint loader
module: {
rules: [
{
enforce: "pre", // make sure this rule gets executed first
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'eslint-loader',
options: {
cache: true,
},
},
},
]
}
};
module.exports = merge(config, devel ? development : {});
CSS:
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const EntryPlugin = require('webpack-watched-glob-entries-plugin');
const dir = require('./node/dirconfig');
// use NODE_ENV var to switch between production and development
const devel = (process.env.NODE_ENV == 'development');
// base config for both prod and devel modes
let config =
{
name: 'styles',
// webpack preset
mode: devel ? 'development' : 'production',
// use all .scss files which don't start with _ as entries
entry: EntryPlugin.getEntries(
dir.assets + '/sass/**/!(_*).scss'
),
output: {
path: dir.static + '/css',
filename: "[name].js"
},
// enable sourcemaps in devel mode
devtool: devel ? 'inline-source-map' : false,
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
// 'cache-loader',
{
loader: 'css-loader',
options: {
sourceMap: devel,
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: devel,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: devel,
}
},
]
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css", // relative to path setting in the output section
}),
new EntryPlugin()
],
};
module.exports = config;
Add thread-loader to js handling loaders.
Replace css-loader with fast-css-loader and sass-loader with fast-sass-loader.
Place cache-loader as the first loader of js files and after extract plugin in the css loaders.
I'm using ASP.NET Core 2.0. If anyone wants to see detailed code or run it themselves, the code can be found here: https://github.com/jakelauer/BaseballTheater/tree/master/BaseballTheaterCore
My basic problem is that I'm expecting each generated js file in my project to have a sourcemap back to the original .ts or .tsx file. That is not working except for my entry file (./ClientApp/boot.tsx).
Here is my webpack.config.js:
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
const bundleOutputDir = './wwwroot/dist';
module.exports = (env) => {
const isDevBuild = !(env && env.prod);
return [{
stats: { modules: false },
entry: { 'main': './ClientApp/boot.tsx' },
resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
output: {
path: path.join(__dirname, bundleOutputDir),
filename: '[name].js',
publicPath: 'dist/'
},
module: {
rules: [
{ test: /\.tsx?$/, include: /ClientApp/, use: 'awesome-typescript-loader?silent=true' },
{ test: /\.css$/, use: isDevBuild ? ['style-loader', 'css-loader'] : ExtractTextPlugin.extract({ use: 'css-loader?minimize' }) },
{
test: /\.scss/,
use: ["style-loader", "css-loader", "sass-loader"]
},
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
]
},
plugins: [
new CheckerPlugin(),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./wwwroot/dist/vendor-manifest.json')
})
].concat(isDevBuild ? [
// Plugins that apply in development builds only
new webpack.SourceMapDevToolPlugin({
filename: '[file].map', // Remove this line if you prefer inline source maps
moduleFilenameTemplate: path.relative(bundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
})
] : [
// Plugins that apply in production builds only
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin('site.css')
])
}];
};
Based on my interpretation of this file and limited understanding of webpack, this should work. Each of my files does generated a .js.map file, and it appears to be referenced in the generated .js file. However, none of them actually load except the one for boot.tsx when debugging in Chrome.
An example of one of the js files in Chrome:
And that file does have the correct files to load:
When I open main.js.map in /wwwroot/dist/ and Ctrl+F for ts inside there, I only find boot.tsx and none of the other .ts or .tsx files I would expect to find.
I am no webpack expert, so I'm not sure what else to do!
From the comments, we've come to the solution by:
upgrading to the newest webpack (v4 in this case) and
installing the source-map loader via npm install --save-dev source-map-loader and
setting devtool: 'source-map' in the webpack.config.js.
The source-map option tells webpack to emit a full separate source map file. This is from the webpack docs:
source-map - A full SourceMap is emitted as a separate file. It adds a reference comment to the bundle so development tools know where to find it.
I was reading this webpack tutorial:
https://webpack.github.io/docs/usage.html
It says it bundles the src files and node_modules. If I want to add another .js file there, how can I do this? This is a thirdpartyjs file that is not part of the source and not part of the node_modules files. This is my current webpack.config.js:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/only-dev-server',
'./app/app.js'
],
output: {
path: path.resolve(__dirname, "dist"),
publicPath: "/dist/",
filename: "dist.js",
sourceMapFilename: "dist.map"
},
devtool: 'source-map',
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('development')
}
}),
],
module: {
loaders: [{
loader: 'babel',
exclude: /node_modules/
}]
},
devServer: {
inline: true
},
node: {
fs: "empty"
},
watch: false
}
The start point for code is the entry field in config. In your config entry point is the list of files. Webpack gets all, resolve their dependencies and output in one file.
You have two options for adding third party script:
add the file path to entry list before app.js
require this file from app.js
In response to Dmitry's answer:
add the file path to entry list before app.js
This has the effect that you will get a bundled .js file for each entry point, which you might not want.
require this file from app.js
You might not have access to app.js if it is written dynamically, or for whatever reason you might not want to edit app.js.
Another option:
You can use webpack-inject-plugin to inject any JS code as string into the resulting .js bundle created by webpack. This way you can read the File you want to inject as a string (e.g. fs.readFile in nodejs) and inject it with the plugin.
Another solution but without using any extra plugins:
//Webpack.config.js
entry: {
main: './src/index',
/**
/* object is passed to load script at global scope and exec immediately
/* but if you don't need then simply do:
/* myCustomScriptEntry: './src/myCustomScript'
*/
myCustomScriptEntry: {
import: './src/myCustomScript',
library: {
name: 'myCustomScriptEntry',
type: 'var',
},
},
},
new HtmlWebpackPlugin({
template: './public/index.html',
excludeChunks: ['myCustomScriptEntry'], //exclude it from being autoreferenced in script tag
favicon: './public/favicon.svg',
title: 'Alida',
}),
and
//index.html
<script type="text/javascript" src="<%= compilation.namedChunks.get('myCustomScriptEntry').files[0] %>"></script>