webpack v5 SourceMapDevToolPlugin "exclude" option not working - javascript

After upgarding to webpack 5, vendor.js file couldn't be excluded from being source-mapped via SourceMapDevToolPlugin.
// webpack.config.ts - removed other config for brevity
import { Configuration } from 'webpack-dev-server';
export default (env) => {
const config: Configuration = {};
config.mode = 'production';
config.entry = './entry.app.js';
config.output = {
path: path.join(__dirname, '/public'),
pathinfo: true,
filename: '[name].[fullhash].js',
chunkFilename: '[name].[fullhash].js',
};
config.devtool = 'source-map';
config.bail = true;
config.plugins = [
new webpack.SourceMapDevToolPlugin({
filename: '[file].map',
exclude: ['vendor.js'],
}),
];
config.optimization = {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: false,
sourceMap: false,
}),
new CssMinimizerPlugin(),
],
moduleIds: 'deterministic',
splitChunks: {
chunks: 'all',
maxInitialRequests: 100,
minSize: 0,
cacheGroups: {
vendor: {
name: 'vendor',
test: /([/\\]node_modules[/\\]|[/\\]dev[/\\]vendor[/\\])/,
chunks: 'all',
},
},
},
};
return config;
}
// entry.app.js - removed some lines for brevity
import './horrible-asset-loader';
import './setup-for-angular';
import { runApp } from './assets/js/app';
runApp();
// horrible-asset-loader.js
// contains a lot of require statements of npm packages saved into our repository under a vendor folder. crazy i know but I don't know why this was done.
require('ng-bs-daterangepicker/dist/ng-bs-daterangepicker.min.js'); // throwing an error when building because webpack is trying to create a source map for it
// Temporary solution to bundle multiple javascript files into one. This will be replaced by ES6 import.
SourceMapDevToolPlugin exclude config i've tried so far:
// from https://webpack.js.org/plugins/source-map-dev-tool-plugin/#exclude-vendor-maps
exclude: ['vendor.js']
//from https://github.com/webpack/webpack/issues/2431
exclude: /vendor.*.*/
exclude: 'vendor'
// just me desperately trying every possible config
exclude: ['vendor']
exclude: /vendor\.[0-9a-zA-Z]\.js/
exclude: 'vendor.js'
exclude: ['vendor.[chunkhash].js']
exclude: ['vendor.[fullhash].js']
The github issue link mentioned about an issue with UglifyJsPlugin but we aren't using that so I ruled it out.
Although if i set config.devtool to false, the SourceDevToolPlugin config works.
Is there something wrong with my config?
UPDATE: I think i got it now. Looks like i just really have to set devtool to false based on this example: https://webpack.js.org/plugins/source-map-dev-tool-plugin/#basic-use-case
I just thought devtool should only be set to false for development mode because of this note:
If you want to use a custom configuration for this plugin in development mode, make sure to disable the default one. I.e. set devtool: false.
Am I right?
UPDATE 1: Yup! looks like I'm right. I should've read the other comments on the github issue: https://github.com/webpack/webpack/issues/2431#issuecomment-245547872
Sorry for wasting anyone's time.

This was a really silly mistake. I misunderstood the docs for the plugin:
https://webpack.js.org/plugins/source-map-dev-tool-plugin/#basic-use-case
Setting devtool to false fixed the issue.

Related

webpack/terser: How can I exclude a package from minification, but still include that package in the packed result?

I've found a number of solutions for excluding particular modules from minification, but all of the solutions I've seen so far not only skip minification of those packages, they cause those packages to be completely omitted from webpack's output, requiring you to provide the omitted code by some other means.
What I want to do is continue to package all needed code into one single output file, but simply have sections of that output non-minified. Is that possible?
The reason that I want to do this in this particular case is that the mysql package is failing after minification. For the moment I've disabled all minification, but I'd rather not solve this problem that way.
const webpack = require('webpack');
const LicensePlugin = require('webpack-license-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const path = require('path');
const mode = process.env.NODE_ENV || 'production';
// noinspection JSUnresolvedFunction
module.exports = {
mode,
entry: './app/app.ts',
target: 'node',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.js'
},
node: {
__dirname: false,
__filename: false,
global: true
},
resolve: {
extensions: ['.ts', '.js'],
mainFields: ['fesm2015', 'module', 'main']
},
module: {
rules: [
{
test: /\.ts$/,
use: [
'ts-loader',
]
}
]
},
optimization: {
// TODO: Minification breaks mysql. How to exclude mysql from minification, but include in output?
minimize: false, // mode === 'production',
minimizer: [new TerserPlugin({
exclude: /node_modules\/mysql/, // <-- This doesn't work
terserOptions: {
output: { max_line_len: 511 },
}
})],
},
devtool: 'source-map',
ignoreWarnings: [{ message: /require function is used in a way|the request of a dependency is an expression/ }],
plugins: [
new webpack.BannerPlugin({ banner: '#!/usr/bin/env node', raw: true }),
new LicensePlugin({
outputFilename: '3rdpartylicenses.txt',
excludedPackageTest: name => /^(asynclist|emitter)/.test(name)
})
]
};
Update: The particular issue with mysql and minification seems to be mangling of variable and/or function names. If I set the mangle option to false, I can minify all of the code, including mysql, successfully. I have yet to figure out which specific names can't be mangled without causing problems.

Why isn't webpack tree-shaking swiperjs modules?

So, swiperjs, from docs:
"By default Swiper exports only core version without additional modules (like Navigation, Pagination, etc.). So you need to import and configure them too:"
// core version + navigation, pagination modules:
import Swiper, { Navigation, Pagination } from 'swiper';
Well, I did. This is my index.js test file:
import Swiper, { Navigation, Pagination } from 'swiper';
console.log(Swiper);
And this is my webpack config:
const path = require('path');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
//mode: 'development',
mode: 'production',
watch: true,
entry: {
index: './src/index.js',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
optimization: {
minimize: true,
usedExports: true,
}
plugins: [
new BundleAnalyzerPlugin()
],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: { presets: ['#babel/preset-env'] }
}
} // babel
] // rules
} // module
};
Well, the BundleAnalyzer graph shows that ALL the swiper modules are bundled. Why? How can I avoid that?
I already disabled sideEffect from package.json and activated providedExports in optimizations.
The source code of swiper.esm.js is copied here for reference: https://jsfiddle.net/fw4zj8qk/
I've found out the issue: with swiper (at least), sideEffects option must be used at loader level and not at package level. Or maybe you can use at package level, but explicitly declaring the files. Didn't try though.
Instead, it did worked by setting sideEffects like this:
module: {
rules: [
// [...] here normally there are babel settings and the like
{ // here doing the swiper loader and declaring no sideEffects
test: /swiper\.esm\.js/,
sideEffects: false
}
] // rules
} // module
Now swiper gets tree shaked.

Webpack 5 - Uncaught ReferenceError: process is not defined

*** Edit - Ignore if you want answer only ***
Seeing as this question is still receiving views and upvotes I feel responsible to share some knowledge after going through the webpack rabbithole and coming out the other end.
If you:
are building a greenfield/early-stage modern javascript project
are considering migrating from create-react-app
don't have much experience with bundling
do not need advanced features like module federation or server side rendering (which doesn't need webpack anymore)
Consider using the next generaton bundlers such as vite/parcel (easy setup), esbuild/rollup (more setup required)
Webpack was/is a fantastic contribution to the frontend world and I'm glad I learned all its intricacies, however, the new bundlers are much faster during development and easier to mantain. It's great when it works but for those inexperienced with it; despite fantastic docs the learning curve can make it a horrible pain to debug.
To clarify, I'm not a maintainer on any of these projects - just a dev who enjoys good tooling. In today's landscape, webpack is comparable to using a sledgehammer to crack a nut.
*** End of Edit ***
Webpack newbie here, I was told by webpack cli that I needed to provide an alias for crypto as webpack no longer includes default node libraries. Now I'm getting this error, other answers haven't helped so much. crypto-browserify is trying to access process.browser. Can anyone shed more light? I was told by cli to install stream-browserify too so i did.
React v17, Babel 7.12.9, webpack 5.6.0
webpack.common.js
const paths = require('./paths');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const dotenv = require('dotenv-webpack');
module.exports = {
entry: [paths.src + '/index.js'],
output: {
path: paths.build,
filename: '[name].bundle.js',
publicPath: '/',
},
plugins: [
new dotenv(),
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: paths.public,
to: 'assets',
globOptions: {
ignore: ['*.DS_Store'],
},
},
],
}),
new HtmlWebpackPlugin({
title: 'Webpack Boilerplate',
// favicon: paths.src + '/images/favicon.png',
template: paths.src + '/template.html',
filename: 'index.html',
}),
],
resolve: {
fallback: {
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
},
},
module: {
rules: [
// javascript
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
// images
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
// Fonts and SVGs
{
test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
type: 'asset/inline',
},
// CSS, PostCSS, and Sass
{
test: /\.(scss|css)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
esModule: true,
sourceMap: true,
importLoaders: 1,
modules: {
auto: true,
namedExport: true,
},
},
},
{ loader: 'postcss-loader', options: { sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
],
},
],
},
};
webpack.dev.js
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
mode: 'development',
// Control how source maps are generated
devtool: 'inline-source-map',
// Spin up a server for quick development
devServer: {
historyApiFallback: true,
contentBase: paths.build,
open: true,
compress: true,
hot: true,
port: 8080,
},
plugins: [
// Only update what has changed on hot reload
new webpack.HotModuleReplacementPlugin(),
],
});
In webpack 5 automatic node.js polyfills are removed. In the migration docs it is mention that
Try to use frontend-compatible modules whenever possible.
It's possible to manually add a polyfill for a node.js core module.
An error message will give a hint on how to achieve that.
Package authors: Use the browser field in package.json to make a
package frontend-compatible. Provide alternative
implementations/dependencies for the browser.
See this issue.
Now you can refer this PR and check the libs that were removed and install them.
Next add alias for the lib in your webpack config.
For ex.
resolve: {
alias: {
process: "process/browser"
}
}
Update:
This can also be done using ProvidePlugin
package.json
"devDependencies": {
...
"process": "0.11.10",
}
webpack.config.js
module.exports = {
...
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
],
}
npm i process was all I needed.
Hope the correction I proposed will be accepted and released soon
I have this problem for HtmlWebpackPlugin, I added 'templateParameters' parameter to HtmlWebpackPlugin and it was fixed for me:
new HtmlWebpackPlugin({
baseUrl: '/',
template: 'app/index.html',
templateParameters(compilation, assets, options) {
return {
compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options,
},
process,
}
},
chunksSortMode: 'auto',
minify: {
collapseWhitespace: false,
},
cache: true,
}),
1. npm i dotenv-webpack
2. //Define dotenv in your webpack config
const Dotenv = require('dotenv-webpack');
plugins: [
new Dotenv({
path: './.env', // Path to .env file (this is the default)
safe: true, // load .env.example (defaults to "false" which does not use dotenv-safe)
})
],

Splitting out react components as widgets

I'm trying to create JS widgets from my existing react app. So currently I have an app that looks something like this
-src
- config
- components
- containers
- lib
- assets
- widgets
- widgetOne
- widgetTwo
- components
- widget.js
- index.js
- index.js
- index.html
So, I want directories in the widgets directories to be self contained apps that I can break out into a separate js file and a client can just add the js script into their page in a script tag.
I've come close but still facing a few issues. Also, I wanted to see if someone had experience doing this following a better pattern.
Right now I'm using webpack to do this splitting. I'm just defining /src/widgets/widgetOne/index.js as an entry point and perfectly creates a separate file.
Here is my webpack:
const appConstants = function() {
switch (process.env.NODE_ENV) {
case 'local':
const localConfig = require('./config/local');
return localConfig.config();
case 'development':
const devConfig = require('./config/development');
return devConfig.config();
case 'production':
default:
const prodConfig = require('./config/production');
return prodConfig.config();
}
};
const HtmlWebPackPlugin = require("html-webpack-plugin");
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const htmlWebpackPlugin = new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html",
hash: true
});
let webpackConfig = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
exclude: [ /assets/, /node_modules/ ],
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true,
importLoaders: 1,
localIdentName: "[name]_[local]_[hash:base64]",
sourceMap: true,
minimize: true
}
}
]
},
{
test: /\.(pdf|jpg|png|gif|svg|ico)$/,
exclude: [/node_modules/],
use: [
{
loader: 'file-loader'
},
]
},
{
test: /\.(woff|woff2|eot|ttf|svg)$/,
exclude: [/node_modules/],
use: {
loader: 'url-loader?limit100000'
}
}
]
},
entry: {
main: [ "#babel/polyfill", "./src/index.js"],
widgetOne: ["./src/widgets/widgetOne/index.js"]
},
output: {
publicPath: appConstants().BASENAME ? JSON.parse(appConstants().BASENAME) : '/'
},
optimization: {
splitChunks: {
chunks: 'all'
}
},
plugins: [
htmlWebpackPlugin,
new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /en/),
new BundleAnalyzerPlugin({
analyzerMode: 'disabled',
generateStatsFile: true,
statsOptions: { source: false }
}),
new webpack.DefinePlugin({
'process.env': appConstants()
}),
new webpack.EnvironmentPlugin(['NODE_ENV'])
],
devServer: {
historyApiFallback: true,
port: 9090
}
};
module.exports = webpackConfig;
The problem I have now is while I get the widgetOne.js:
1) I end up with a vendor~widgetOne.js file that I also need to include to make the widgetOne app to work.
2) The widgetOne.js also gets added to my index.html file for my main app which I do not want.
Is there a way to configure webpack properly to make this work?
So, this is the solution I came up with that seems to work. I still don't know if this is the best approach but it' the only one I was able to get to work for me.
I decided to build the widgets as a different environment process and and modify the webpack configs based on that environment.
So the package.json I add this line under scritps:
"build-widgets": "cross-env NODE_ENV=plugins webpack --mode development",
And I added this section to the end of my webpack.config.js file:
// Override webpack configs when building plugins
if ( process.env.NODE_ENV === 'plugins') {
webpackConfig.entry = {
widgetOne: [ "#babel/polyfill", "./src/plugins/widgetOne/index.js"]
}
webpackConfig.output = {
publicPath: appConstants().DS_BASENAME ? JSON.parse(appConstants().DS_BASENAME) : '/',
path: __dirname + '/dist/widgets',
library: 'MyApp',
libraryTarget: 'umd',
umdNamedDefine: true
}
}
Alternatively, I could have just added a second webpack.config.js exclusively to deal with my widgets build. In my case I didn't feel the need for it just yet but it's something to be considered for sure, just for the sake of keeping configs separate.

How to correctly use Webpack's CommonsChunkPlugin with multiple entry points?

I'm having a problem with maintaining stable module IDs for my explicit vendor chunk. Here's my setup (modeled after SurviveJS):
const gitrev = require('git-rev-sync');
const path = require('path');
const webpack = require('webpack');
const ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
const packageJson = require('./package.json');
const gulpPaths = require('./gulp/lib/gulp_paths');
module.exports = {
context: __dirname,
entry: {
vendor: Object.keys(packageJson.dependencies),
web: path.join(
__dirname,
gulpPaths.SCRIPT_SOURCE_DIR,
gulpPaths.SCRIPT_BUNDLE_SOURCE_FILE
),
// server: path.join(
// __dirname,
// gulpPaths.SCRIPT_SOURCE_DIR,
// gulpPaths.SCRIPT_SERVER_SIDE_SOURCE_FILE
// ),
},
output: {
path: path.join(__dirname, gulpPaths.SCRIPT_OUTPUT_DIR),
filename: '[name]_bundle.js',
},
plugins: [
new webpack.DefinePlugin({
__DEV__: false,
__TEST__: false,
__COMMIT__: JSON.stringify(gitrev.long()),
'process.env': {
NODE_ENV: JSON.stringify('production'),
},
}),
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor', 'manifest'],
minChunks: Infinity,
}),
new webpack.optimize.UglifyJsPlugin(),
],
resolve: {
extensions: ['', '.js', '.jsx'],
},
module: {
loaders: [
{
test: /\.jsx?$/,
loaders: ['babel'],
exclude: /node_modules/,
},
{ // Turn off AMD module loading on eventemitter2
test: /eventemitter2/,
loader: 'imports?define=>false',
}
]
},
};
It works pretty well; I can ride back and forth through my repository history, and the vendor chunk changes only if the vendor libraries actually change in node_modules. However, once I uncomment the additional entry point, everything changes: the vendor chunk changes without any changes to the vendor libraries. If I manually whitelist only a couple of libraries, it helps, but doesn't remove the problem entirely. It looks like it also conflicts the same way with DedupePlugin and OccurenceOrderPlugin.
I also tried to use the approaches outlined in the Webpack documentation (recordsPath and ChunkManifestPlugin, without luck).
I wouldn't split hair over this, since the server entry point is anyway a failed experiment in server-side rendering and should probably be removed; however, soon I'm going to have multiple entry points anyway, for more fine-tuning of the page loading time, and it's probably going to get funny.
As advised by Juho Vepsäläinen, the best option for me was using the NamedModulesPlugin. For the record, the HashModuleIds plugin is also worth looking at, but it's only available in Webpack 2.

Categories

Resources