WebPack 4 SplitChunksPlugin for NodeJS - node.js

I'm producing bundles for a NodeJS 8.x (AWS Lambda) and would like to selectively minify chunks (business code to remain non-minified and third party libraries to be minified to save on bundle size).
I've configured Webpack as below but it main bundle of mine doesn't work because it is not built to be able to require the other files (one file for each npm dependency).
How can I configure WebPack to produce chunks which can require each other in NodeJS?
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: (module) => {
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
return `npm.${packageName}`;
},
},
},
},

Instead of minifying the output chunks selectively, I opted to not use the SplitChunksPlugin at all and instead minify in a custom loader as part of the compilation.
This makes it possible to cherry pick exactly which input files (Webpack calls them assets) you want to minify and which ones you don't.
npm install terser --save-dev
tools/es-minify-loader.js
const Terser = require('terser');
module.exports = function esMinifyLoader(source) {
const output = Terser.minify(source, { compress: true, mangle: true });
return output.code;
};
webpack.config.js
...
module: {
rules: [
{
exclude: [
/dont-minify-this-file\.js/,
/some-library-or-folder-you-dont-want-minified/i,
],
test: [
/\.js$/
],
use: [
{
loader: path.resolve(__dirname, 'tools/es-minify-loader.js'),
}
]
}
]
},
...

Related

Webpack Failed to compile: loader issues with optional chaining

Created a react application with create-react-app along with typescript as the template.
Trying to use optional chaining:
const inputRef = useRef<HTMLInputElement | null>(null);
const handleClick = () => {
if(inputRef) {
inputRef?.current?.click();
}
};
On load my application gives the error:
Here is my webpack.config.js file:
'use strict';
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
module.exports = function(webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
// In development, we always serve from the root. This makes config easier.
const publicPath = isEnvProduction
? paths.servedPath
: isEnvDevelopment && '/';
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
const shouldUseRelativeAssetPaths = publicPath === './';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
const publicUrl = isEnvProduction
? publicPath.slice(0, -1)
: isEnvDevelopment && '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
),
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
],
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: {
sourceMap: isEnvProduction && shouldUseSourceMap,
},
});
}
return loaders;
};
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
entry: [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
// When you save a file, the client will either apply hot updates (in case
// of CSS changes), or refresh the page (in case of JS changes). When you
// make a syntax error, this client will display a syntax error overlay.
// Note: instead of the default WebpackDevServer client, we use a custom one
// to bring better experience for Create React App users. You can replace
// the line below with these two lines if you prefer the stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'),
// Finally, this is your app's code:
paths.appIndexJs,
// We include the app code last so that if there is a runtime error during
// initialization, it doesn't blow up the WebpackDevServer client, and
// changing JS code would still trigger a refresh.
].filter(Boolean),
output: {
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
// Add /* filename */ comments to generated require()s in the output.
pathinfo: isEnvDevelopment,
// There will be one main bundle, and one file per asynchronous chunk.
// In development, it does not produce real files.
filename: isEnvProduction
? 'static/js/[name].[chunkhash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
// There are also additional JS chunk files if you use code splitting.
chunkFilename: isEnvProduction
? 'static/js/[name].[chunkhash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
// We use "/" in development.
publicPath: publicPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
},
optimization: {
minimize: isEnvProduction,
minimizer: [
// This is only used in production mode
new TerserPlugin({
terserOptions: {
parse: {
// we want terser to parse ecma 8 code. However, we don't want it
// to apply any minfication steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
// Disabled because of an issue with Terser breaking valid code:
// https://github.com/facebook/create-react-app/issues/5250
// Pending futher investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
// Enable file caching
cache: true,
sourceMap: shouldUseSourceMap,
}),
// This is only used in production mode
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
// `inline: false` forces the sourcemap to be output into a
// separate file
inline: false,
// `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap
annotation: true,
}
: false,
},
}),
],
// Automatically split vendor and commons
// https://twitter.com/wSokra/status/969633336732905474
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: {
chunks: 'all',
name: false,
},
// Keep the runtime chunk separated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
runtimeChunk: true,
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
},
plugins: [
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
// guards against forgotten dependencies and such.
PnpWebpackPlugin,
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
test: /\.(js|mjs|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data URLs to avoid requests.
// A missing `test` is equivalent to a match.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'#svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
cacheCompression: isEnvProduction,
compact: isEnvProduction,
},
},
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
cacheCompression: isEnvProduction,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use MiniCSSExtractPlugin to extract that CSS
// to a file, but in development "style" loader enables hot editing
// of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader'
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
isEnvProduction &&
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// This gives some necessary context to module not found errors, such as
// the requesting resource.
new ModuleNotFoundPlugin(paths.appPath),
new webpack.DefinePlugin(env.stringified),
// This is necessary to emit hot updates (currently CSS only):
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
isEnvDevelopment && new CaseSensitivePathsPlugin(),
isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvProduction &&
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
isEnvProduction &&
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [/\.map$/, /asset-manifest\.json$/],
importWorkboxFrom: 'cdn',
navigateFallback: publicUrl + '/index.html',
navigateFallbackBlacklist: [
// Exclude URLs starting with /_, as they're likely an API call
new RegExp('^/_'),
// Exclude URLs containing a dot, as they're likely a resource in
// public/ and not a SPA route
new RegExp('/[^/]+\\.[^/]+$'),
],
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'preserve',
},
reportFiles: [
'**',
'!**/*.json',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!**/src/setupProxy.*',
'!**/src/setupTests.*',
],
watch: paths.appSrc,
silent: true,
formatter: typescriptFormatter,
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
performance: false,
};
};
You haven't stated what version of create-react-app you're using, and the latest version should support optional chaining out of the box, but you can try adding support for optional chaining directly by including the associated babel plugin #babel/plugin-proposal-optional-chaining.
First npm i #babel/plugin-proposal-optional-chaining --save-dev.
Then update your webpack config module.rules for babel-loader.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'#svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
// Try adding this plugin
require.resolve('#babel/plugin-proposal-optional-chaining')
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
cacheCompression: isEnvProduction,
compact: isEnvProduction,
},
}

webpack 5 maxing out on RAM when bundling for karma tests

I recently updated webpack to version 5, and now when compiling a bundle for karma tests my system is maxing out of RAM and I get the JavaScript heap out of memory error. This happens both on my local machine that has 8gb RAM, and also on a gitlab runner hosted on digital ocean which has 4gb RAM.
Locally, if I kill all other apps, and set the memory allocation options node --max-old-space-size=4096 testscript.js I can sometimes get past the bundling, so I think it currently requires around 4gb+ RAM just to bundle, and this worries me. On the gitlab runner, linux terminates the docker container and I get the "killed" error
Before updating to weback 5 and therefore also before the memory issue my webpack configuration looked a bit differently, because webpack 5 doesn't include node libs by default, I have had to add some polyfills.
These are new in the configuration
crypto: false,
fs: false,
http: false,
path: require.resolve('path-browserify'),
stream: require.resolve('stream-browserify')
and these:
new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
new webpack.ProvidePlugin({ process: 'process/browser' }),
This is what the final webpack configuration for the karma bundling looks like:
const path = require('path');
const webpack = require('webpack');
const { excludeNodeModulesExcept } = require('../src/webpack/config');
const moduleListToTranspileForIE11 = ['debug', 'loader-utils', 'query-string', 'split-on-first', 'strict-uri-encode'];
const devModuleListToTranspileForIE11 = ['fetch-mock', 'proxy-polyfill', 'sinon', 'tr46', 'webidl-conversions', 'whatwg-url'];
module.exports = {
context: path.resolve(__dirname, '..'),
devtool: 'source-map',
entry: [
'./test/karma-webpack.main.js'
],
externals: {
'react/addons': true,
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'css-loader', 'postcss-loader']
},
{
test: /\.js$/,
exclude: excludeNodeModulesExcept([].concat(devModuleListToTranspileForIE11, moduleListToTranspileForIE11)),
use: {
loader: 'babel-loader',
options: {
plugins: [
['#babel/plugin-transform-for-of', { assumeArray: true }]
],
presets: [
['#babel/preset-env', {}]
],
cacheDirectory: true // Note: for faster rebuilds
}
}
},
{
test: /\.svg$/,
loader: 'svg-react-loader'
}
]
},
mode: process.env.NODE_ENV || 'development',
resolveLoader: {
modules: [
'node_modules',
path.resolve(__dirname, '../src/webpack/loader')
]
},
output: {
path: path.resolve(__dirname, '../output/test'),
publicPath: '/',
filename: 'karma-webpack.js'
},
resolve: {
fallback: {
crypto: false,
fs: false,
http: false,
path: require.resolve('path-browserify'),
stream: require.resolve('stream-browserify')
}
},
performance: {
hints: false
},
plugins: [
new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }),
new webpack.ProvidePlugin({ process: 'process/browser' }),
new webpack.IgnorePlugin(/style\/demoHack\/\w+\.css/),
new webpack.IgnorePlugin(/app\.css/),
new webpack.IgnorePlugin(/config\/output\/build\/clientConfig\.json/),
new webpack.IgnorePlugin(/output\/local\/\w+\.json/)
]
};
The entry point ./test/karma-webpack.main.js looks like this:
require('./mocha.main');
var appReq = require.context('../src', true, /\.js$/);
appReq.keys().filter(function (key) {
return ['./react.main.js', './src/helper/polyfill.js'].indexOf(key) === -1 && !key.startsWith('./tool') && !key.startsWith('./src/test');
}).map(appReq);
// Only start mocha in browser.
if (!window.__karma__) {
window.mocha.run();
}
and is based on: https://github.com/FormidableLabs/full-stack-testing/blob/master/test/client/main.js
This seems to be including all mocha (file.test.js) files in the bundling, which makes it very slow, as those files also includes a lot of devDependencies. However, this was not a problem prior to the update. I have tried filtering out all files that are .test.js - then the bundling is fast, and I don't see any memory issues, however, karma is not running any tests then, the test suite is simply empty.
I am looking for advice with anything that could look suspicious about this configuration, and why it cannot bundle with less than 4gb memory available.

include a config file in webpack bundle

In my webpack project I want to bundle my project and include an example usage file with it. However when I bundle my project in this form
module.exports = {
entry: {
main: './src/main.js',
about: './src/pages/config.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
library: 'report' //makes this a global variable we can call from basic javascript page,
devtool: 'source-map',
devServer: {
port: 3001,
clientLogLevel: 'none',
stats: 'errors-only'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] //npm install --save-dev style-loader css-loader
}
]
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
new HtmlWebpackPlugin({
title: 'ESS Map Learning',
}),
],
};
},
The outputted file in the dist is converted or loaded into bundled code and is unreadable as an example. I am trying to find a way to load this file so it is not changed and sits in the bundle in its original format
If you want to include an example usage file along with your bundle, you can try the following two ways:
1. use CopyWebpackPlugin
With this plugin, you can copy specific files from your source project into your output folder.
First you need to install the plugin:
npm install --save-dev copy-webpack-plugin
Then in your webpack configuration file:
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin([
{
from: "path/to/some-example.js",
to: "" // leave blank if you want to keep the folder hierachy as in the source code, otherwise specify an output path
},
]),
]
}
More about the plugin:
Webpack Plugins: CopyWebpackPlugin
2. use the "Asset Modules" configuration
module.exports = {
module: {
rules: [
{
test: /example\.js/, // regex to match your source example file path
type: 'asset/resource', // files with this type will be copied to your output folder
generator: {
filename: 'examples/[name][ext]', // you can give a desired name for your output file, or webpack will defaultly name it with hash value
}
}
]
},
// rest of your webpack configuration
// ...
}
Caveats: I think webpack will never process your example file if it is not a dependency of any of your entry points (which is very likely the case). In this sense I guess using the CopyWebpackPlugin is more reliable.
More about asset modules:
Webpack Guides: Asset Management
Webpack Guides: Asset Modules

How to include only specific module in webpack without transpiling its dependencies?

I'm working with a monorepo node.js project with the following structure:
rootDir
packageA
packageB
packageC
I want to produce a bundle of packageB code using webpack and babel.js. packageA has a lot of exports however only one export is used by packageA, let's call the export utils.
When I run webpack it recognizes that packageB depends on packageA so webpack dependecnies graph now includes all modules exported by packageA in its index.js although only one is really needed. So all of the modules in packageA are being transpiled by babel.js which even includes the node_modules of packages included by files in packageA. I've run into numerous issues with transpiling node_modules with babel.js. They range from babel.js not being able to deal with non-Javascript files for which loaders do not exist, webpack not finding certain modules, having mixed esm and commonjs syntax inside some files and many others*.
Other issues could only be solved by commenting out 2 problematic modules exported by packageA.
As you can see the solution is not ideal and perhaps not sustainable for long. Is there a way to have webpack transpile packageA without transpiling its node_modules? I couldn't achieve this despite my numerous regex attempts. Below are my config files:
*I managed to solve the issue by adding modules: "commonjs" in babel config however this will disable webpack tree shaking mechanism which means that I need to use a separate babelrc for my project.
webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'development',
target: 'node',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'build')
},
devtool: 'source-map',
plugins: [
new webpack.IgnorePlugin(/^(hiredis|transifex)$/)
],
module: {
rules: [
{
test: /node_modules\/datauri\/index\.js$/,
loaders: ['shebang-loader', 'babel-loader']
},
{
test: /node_modules\/needle\/lib\/needle\.js$/,
loaders: ['file-loader', 'babel-loader']
},
{
test: /\.js?$/,
use: {
loader: 'babel-loader',
options: {
rootMode: 'upward'
}
},
include: [
path.resolve(__dirname, 'src'),
"/path/to/packageA",
"/path/to/packageC",
]
}
]
}
}
babel.config.js
module.exports = {
plugins: [
[
'#babel/plugin-proposal-decorators',
{
legacy: true
}
],
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-syntax-import-meta',
'#babel/plugin-proposal-class-properties',
'#babel/plugin-proposal-json-strings',
'#babel/plugin-proposal-function-sent',
'#babel/plugin-proposal-export-namespace-from',
'#babel/plugin-proposal-numeric-separator',
'#babel/plugin-proposal-throw-expressions',
'#babel/plugin-proposal-export-default-from',
'#babel/plugin-proposal-logical-assignment-operators',
'#babel/plugin-proposal-optional-chaining',
[
'#babel/plugin-proposal-pipeline-operator',
{
proposal: 'minimal'
}
],
'#babel/plugin-proposal-nullish-coalescing-operator',
'#babel/plugin-proposal-do-expressions',
'#babel/plugin-proposal-function-bind'
],
presets: [
[
'#babel/preset-env',
{
targets: {
node: 'current'
},
corejs: 3,
useBuiltIns: 'usage',
modules: "commonjs"
}
],
'#babel/preset-typescript'
],
env: {
debug: {
sourceMaps: 'inline',
retainLines: true
}
}
}
UPDATE: I just noticed that I'm not ignoring node_modules in babel.config.js so I added:
ignore: [
/node_modules/
]
to my babel.config.js. This allowed me to stop using modules: 'commonjs' so I guess this did have some effect but I still get errors when trying to run the bundle, errors that resulted from transpiling node_modules for example this error.

Migrating RequireJS/AMD with Plugins to Webpack

I'm working on migrating a large RequireJS application to Webpack. The basic build with Webpack seems to work fine -- I've moved "paths" definitions to "alias" and I've setup loaders for my content and shims, like jQuery.
However, there's a remaining issue I'm not sure how to resolve. Basically the RequireJS app uses the "text-plugin" to include HTML templates, and Webpack is throwing "Module not found" errors for the HTML templates.
An example AMD module I want to bundle looks something like this:
AMD Module with Text Plugin
define([
'security',
'modals',
'text!../templates/contact_info.html'
], function(security, modals, contactInfoTemplate) {
return {
foo: function() { return "bar"; }
};
});
I thought I could use the raw-loader to load the template files. I aliased 'text' to be the 'raw-loader':
text: {
test: /\.html$/,
loader: "raw-loader"
},
However, I'm seeing the following error for all of my templates that are required like above:
Module not found: Error: Can't resolve 'text'
BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders. You need to specify 'text-loader' instead of 'text'.
I tried replacing 'text!...' with 'text-loader!...', and I then see this error complaining that it can't load/find the HTML module!
Module not found: Error: Can't resolve '../templates/contact_info.html'
webpack.config.js, version 3.9.1
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
let basePath = path.join(__dirname, '/');
module.exports = {
entry: {
'main': basePath + 'js/main.js',
},
context: __dirname,
output: {
path: __dirname + '/build',
filename: '[name].min.js',
libraryTarget: 'amd',
umdNamedDefine: true
},
module: {
rules: [
{
test: /(\.js)$/,
exclude: /(node_modules)/,
use: {
// babel-loader to convert ES6 code to ES5 + amdCleaning requirejs code into simple JS code, taking care of modules to load as desired
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
plugins: []
}
}
},
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
},
{ test: /\.jpg$/, use: [ "file-loader" ] },
{ test: /\.png$/, use: [ "url-loader?mimetype=image/png" ] },
{
test: /\.(html)$/,
use: {
loader: 'raw-loader',
options: {
minimize: true
}
}
}
]
},
resolve: {
modules: [
'js/**/*.js',
'node_modules',
path.resolve('./js')
],
extensions: ['.js'], // File types,
alias: {
text: {
test: /\.html$/,
loader: "raw-loader"
},
bridge: 'libs/bridge',
cache: 'libs/cache',
cards: 'libs/cards',
moment: 'libs/moment',
underscore: 'libs/underscore',
}
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
filename: 'index.html',
template: '../index.html'
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
]
};
Anyone know how to get Webpack to play nicely with the RequireJS Text plugin?
Thanks!
Maybe try installing text-loader?
In order for something like 'text!../templates/contact_info.html' to "load" properly, since it is not JS, you need to install text-loader to get webpack to understand the syntax text!.
npm install text-loader --save-dev
humm...i just installed text-loaded and it seems we also have to change text! to text-loader!

Resources