Excluding node_modules from Webpack 4 bundle for web app - javascript

What I want to do:
Exclude my node_modules from being bundled by Webpack 4 (not just the babel-loader).
My current production bundle size:
Version: webpack 4.42.0
Time: 34941ms
Built at: 09/23/2020 2:05:13 PM
Asset Size Chunks Chunk Names
./public/bundle.js 4.87 MiB 0 [emitted] [big] bundle
Entrypoint bundle [big] = ./public/bundle.js
My current development bundle size:
Version: webpack 4.42.0
Time: 10780ms
Built at: 09/23/2020 1:50:15 PM
Asset Size Chunks Chunk Names
./public/bundle.js 10.4 MiB bundle [emitted] bundle
./public/bundle.js.map 11.2 MiB bundle [emitted] [dev] bundle
Entrypoint bundle = ./public/bundle.js ./public/bundle.js.map
To my knowledge, 4.87 MiB and 10.4 MiB are MASSIVE for a webpack bundle.
My current production webpack config:
'use strict'
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
const TerserPlugin = require('terser-webpack-plugin')
module.exports = smp.wrap({
mode: 'production',
entry: {
'bundle': './client/index.js',
},
output: {
path: __dirname,
filename: './public/[name].js'
},
optimization: {
minimizer: [new TerserPlugin({
include: /\.js$/,
})],
minimize: true
},
context: __dirname,
devtool: 'source-map',
module: {
rules: [
{
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: ['react', 'env']
}
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
}
})
What I have tried:
Using webpack-node-externals
target: 'node', // I've also tried target: 'web' as well.
externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
This successfully excludes node_modules, BUT causes a breaking error in my app:
Uncaught ReferenceError: require is not defined
at Object.react (external "react":1)
at __webpack_require__ (bootstrap:19)
at Object../client/index.js (index.js:1)
at __webpack_require__ (bootstrap:19)
at bootstrap:83
at bootstrap:83
I've read into this and it seems that this webpack-node-externals package should not be used for web applications. Removing the externals prop, and toggling target between 'node' and 'web' does not exclude my node_modules successfully.
Am I doomed here? Do I simply have a huge application and should not expect to get the bundle size down any further from 4.87 MiB for production and 10.4 MiB for development? It seems extremely large based on everything I've read.

Related

Why won't webpack ignore my node_modules?

I have tried configuring it multiple ways and reduced the config file to a bare minimal but it always compiles my node_modules folder.
I have tried using a regular expression as seen below, as well as inserting the direct path.
Webpack requires direct paths which I am using instead of the path module for clarity.
Webpack config file
// style-loader and css-loader are both required to handle .css files
const css = {
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
};
// babel-loader handles .js and .jsx files
const jsx = {
test: /\.jsx?/,
exclude: [
/node_modules/
],
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react']
}
}
};
// this webpack configuration file supports these two file types
const file_types = {
rules: [
css,
jsx
]
};
const input = '/Users/c/top/frame/index.jsx';
const output = {
filename: 'bundle.js',
path: '/Users/c/top/frame-server/dist'
};
const stats = { warnings: true };
// combine above objects and export
const config_obj = {
stats: stats,
entry: input,
output: output,
module: file_types
};
module.exports = (env) => {
return config_obj;
};
Yet still I see them compiled:
asset bundle.js 209 KiB [compared for emit] [minimized] (name: main) 1 related asset
orphan modules 787 KiB [orphan] 460 modules
runtime modules 663 bytes 3 modules
cacheable modules 375 KiB
modules by path ./node_modules/ 155 KiB 22 modules
modules by path ./A1People/ 4.31 KiB 6 modules
modules by path ./F1PageUser/ 4.9 KiB 5 modules
modules by path ./F1Apex/ 2.87 KiB 4 modules
modules by path ./A1Article/ 3.43 KiB 4 modules
modules by path ./F1M1/ 2.9 KiB 4 modules
modules by path ./C0Images/ 2.02 KiB 3 modules
modules by path ./C2Menu/ 2.04 KiB 3 modules
modules by path ./C3BarAdmin/ 1.56 KiB
./node_modules/css-loader/dist/cjs.js!./C3BarAdmin/C3BarAdmin.css 504 bytes [built] [code generated]
+ 2 modules
+ 8 modules
webpack 5.69.1 compiled with 2 warnings in 6635 ms
I am following the documentation by webpack found here.
and version as follows:
"webpack": "^5.69.1",
"webpack-cli": "^4.9.2"
I am getting a strange warning but I don't know if it is relevant:
WARNING in ./node_modules/React/cjs/react.production.min.js
There are multiple modules with names that only differ in casing.
This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:
* /Users/c/top/frame/node_modules/React/cjs/react.production.min.js
Used by 1 module(s), i. e.
/Users/c/top/frame/node_modules/React/index.js
* /Users/c/top/frame/node_modules/react/cjs/react.production.min.js
Used by 1 module(s), i. e.
/Users/c/top/frame/node_modules/react/index.js
# ./node_modules/React/index.js 4:2-59
# ./F1PageUser/ULogin.jsx 2:0-26 5:22-41 7:18-37 9:18-37 11:31-50
# ./F1PageUser/User.jsx 4:0-34 14:47-53
# ./F1Page/F1Page.jsx 8:0-44 21:57-63
# ./F1/F1.jsx 5:0-42 18:184-190
# ./index.jsx 8:0-29 12:36-38

Improve webpack image/css build time?

I have recently taken over a frontend project, and noticed that the initial start time for the dev script (with hot module reloading etc) takes extremely long, ~2 minutes.
After setting verbose to true, I realised almost all of this time is spent recompiling CSS and images which haven't even changed.
The output of the webpack build is a long list that looks like this:
---------
Webpack stdout for /Users/myuser/Documents/repos/myApp-frontend/client/modules/App/background.jpg
---------
Hash: 0f500227a855ef9eb67c
Version: webpack 2.1.0-beta.8
Time: 68ms
Asset Size Chunks Chunk Names
.webpack.res.1504199219496_875923.js 132 kB 0 [emitted] main
+ 1 hidden modules
---------
Webpack stdout for /Users/myuser/Documents/repos/myApp-frontend/client/modules/App/AppAuthorized.css
---------
Hash: 83f1c479b77c7539baeb
Version: webpack 2.1.0-beta.8
Time: 549ms
Asset Size Chunks Chunk Names
.webpack.res.1504199221679_732531.js 23.2 kB 0 [emitted] main
+ 5 hidden modules
The images seem to process quickly, but each of these 2kb css files is taking half a second to compile, and there are dozens of them.
Here's what my webpack config looks like:
var cssnext = require('postcss-cssnext');
var postcssFocus = require('postcss-focus');
var postcssReporter = require('postcss-reporter');
var precss = require('precss');
var syntax = require('postcss-scss');
module.exports = {
output: {
publicPath: '/',
libraryTarget: 'commonjs2',
},
resolve: {
extensions: ['', '.js', '.jsx'],
modules: [
'client',
'node_modules',
],
},
module: {
loaders: [
{
test: /\.css$/,
exclude: /node_modules/,
loader: 'cache-loader!style-loader!css-loader?localIdentName=[name]__[local]__[hash:base64:5]&modules&importLoaders=1&sourceMap!postcss-loader',
},
{
test: /\.jpe?g$|\.gif$|\.png$|\.svg$/i,
exclude: /node_modules/,
loader: 'cache-loader!url-loader?limit=9999999999999',
},
{
test: /\.ico$/,
loader: 'file-loader?name=[name].[ext]'
},
],
},
postcss: () => ({
plugins: [
precss(),
postcssFocus(),
cssnext({
browsers: ['last 2 versions', 'IE > 10'],
}),
postcssReporter({
clearMessages: true,
}),
],
syntax: syntax,
}),
};
Any advice to speed the CSS compilation up, or cache assets somehow?
To improve the performance of Webpack build / hot-reloading, I usually check the following list and make sure I am doing the right thing:
For dev mode env, set the correct 'source-map'. Normally, I go with 'eval' which seems to be the fastest. You can find the benchmark here: https://webpack.js.org/configuration/devtool/?_sm_au_=iHV0NZ57NZfZ44WQ
For dev mode env, remove all the optimisation plugins as they take extra time and only use them in production.
For dev mode env, check the version of loaders in package.json, especially for css-loader, style-loader and sass-loader because some latest version may slow down the build. Check https://github.com/webpack-contrib/css-loader/issues/124 for more details.
Upgrading to Babel 6 may give 10%-20% performance boost.
Use webpack-dev-server for dev mode env.
Always run 'npm outdated' to check any modules are too old.
Make sure the computer is not running too slow. Try to restart.

Webpack: ENOENT: no such file or directory, open 'head.ejs'

I have the following webpack.config.js file:
'use strict';
let path = require('path');
let webpack = require('webpack');
let HtmlWebpackPlugin = require('html-webpack-plugin');
let ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: [
'./index.js'
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
},
devtool: 'inline-source-map',
watch: true,
module: {
rules: [
{
test: /\.jsx?$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: [ 'style-loader', 'css-loader?modules', ],
publicPath: '/dist'
})
},
{
test: /\.ejs$/,
use: 'ejs-compiled-loader'
}
],
},
plugins: [
new ExtractTextPlugin({
filename: 'styles.css'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new HtmlWebpackPlugin({
minify: {
collapseWhitespace: true
},
hash: true,
template: 'ejs-render-loader!./views/index.ejs'
}),
]
};
When trying to load an ejs file that contains <% somefile %> the file cannot be found.. this is the error I receive:
Child html-webpack-plugin for "index.html":
Asset Size Chunks Chunk Names
index.html 26.2 kB 0
chunk {0} index.html 554 bytes [entry]
[./node_modules/ejs-render-loader/index.js!./views/index.ejs] ./~/ejs-render-loader!./views/index.ejs 554 bytes {0} [built] [failed] [1 error]
ERROR in ./~/ejs-render-loader!./views/index.ejs
Module build failed: Error: ENOENT: no such file or directory, open 'head.ejs'
at Object.fs.openSync (fs.js:584:18)
at fs.readFileSync (fs.js:491:33)
at Object.exports.parse (/var/www/rio-olympics-3/monitor/node_modules/ejs-compiled-loader/node_modules/ejs/lib/ejs.js:168:19)
at Object.exports.compile (/var/www/rio-olympics-3/monitor/node_modules/ejs-compiled-loader/node_modules/ejs/lib/ejs.js:245:15)
at Object.module.exports (/var/www/rio-olympics-3/monitor/node_modules/ejs-compiled-loader/index.js:7:22)
webpack: Failed to compile.
I have tried many file path formats, and none of them worked, here is my ejs file with my head.ejs in the same folder:
<!DOCTYPE html>
<html lang="en">
<% include head %>
<body>
<div id="navbar-app"></div>
<p> Welcome, more coming soon! </p>
</body>
<!-- insert component scripts below here -->
<script src="dist/js/NavBarMain.js"></script>
</html>
You have two different loaders configured for the index.ejs, which results in a clash.
A general rule for all .ejs:
{
test: /\.ejs$/,
use: 'ejs-compiled-loader'
}
An inline rule for the template in html-webpack-plugin:
template: 'ejs-render-loader!./views/index.ejs'
If you only want to use the inline loader you can add a leading !, so webpack won't apply other loaders:
template: '!ejs-render-loader!./views/index.ejs'
The above fixes your problem, but it is a little questionable why you have two different ejs loaders. To be honest I'm not fully aware of the differences in the loaders since both point to the same GitHub repository, although it seems to me like ejs-render-loader is just an earlier version. They actually handle the includes differently, that's why you get the error.
From the example in Usage of ejs-compiled-loader:
// Child Templates
// path is relative to where webpack is being run
<%- include templates/child -%>
That means if you want to use the ejs-compiled-loader for all .ejs you need to change the include to:
<% include views/head %>
And you can get rid of the inline loader in the html-webpack-plugin:
template: './views/index.ejs'
It's up to you whether you want to change this, I would just try to avoid loader conflicts and I don't think ejs-render-loader will be updated anymore.
In fact there is a v2.2.0 of ejs-compiled-loader which went back to the other include behaviour, relative to the current file, which is definitively more intuitive. You can install it with:
npm install --save-dev ejs-compiled-loader#2.2.0

webpack-dev-server --inline recompiles and reloads page but no changes shown

I'm having some issues getting Webpack's live reloading working. I believe I have everything setup correctly. The problem is when saving files the browser refreshes but no changes are displayed.
I run the webpack development server with the following command:
webpack-dev-server --inline
And I start my PHP development server with this command:
php -S localhost:8000 -t public/
In my public/index.php file include the webpack-dev-server.js file:
<script src="http://localhost:8080/webpack-dev-server.js"></script>
When I make a changes to entry.js and hit save I can see the files recompiling and the browser refreshes. Except no changes are made.
I get the following output at the terminal when saving entry.js:
Time: 44ms
Asset Size Chunks Chunk Names
app.js 240 kB 0 [emitted] main
app.css 42.4 kB 0 [emitted] main
chunk {0} app.js, app.css (main) 222 kB [rendered]
[75] ./src/js/entry.js 108 bytes {0} [built]
+ 77 hidden modules
webpack: bundle is now VALID.
webpack: bundle is now INVALID.
Hash: 3937d6fc90a1794a8daa
Version: webpack 1.13.3
I tried to add a simple alert("Testing"); to entry.js but got no popup. If I check the webpack-dev-server.js file for any line that contains the word Testing it finds non.
Here is the contents of webpack.config.js:
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const path = require('path');
module.exports = {
context: __dirname,
entry: path.join(__dirname, '/src/js/entry.js'),
output: {
path: path.join(__dirname, '/public'),
filename: "app.js"
},
module: {
loaders: [
{
test: /\.css$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader')
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['es2015']
}
},
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" }
]
},
postcss: [
require('postcss-import'),
require('postcss-nested'),
require('autoprefixer'),
require('precss'),
require('postcss-custom-media'),
require('lost')
],
plugins: [
new ExtractTextPlugin('app.css')
]
};
Is there anything I'm doing wrong?
Edit: Also tried the following:
php -S localhost:8000 -t public starts the webserver. webpack-dev-server --content-base=public --inline --hot --watch" starts the webpack-dev-server. I load localhost:8000/index.php and monitor the console inspector. I can see that hot module replacement is enabled. If I modify and save entry.js and include a simple alert('Hello'); I can see in the web console that it's recompiling. The page refreshes, yet nothing happens. No changes to be seen.

Webpack production build does not load anything

I've been working on an app using React and Webpack for a little while. My development environment works fine, and everything loads properly using webpack-dev-server.
I decided to run a production build of the application to see what the end-product might look like size-wise and observe the general output of the webpack product build.
It turns out that running webpack -p, while it does produce output (more on that in a minute), does not load anything at all when I hit the site in a browser.. A quick check of the output tells me that none of my component code is making it into the webpack -p build.
The images and HTML copy over as they exist in my src (dev) folder, however my JS bundle output is extremely small - the file (main.js) is only 246 bytes.
Here is the output from running webpack -p
$ npm run build
> project#0.1.0 build /Users/me/Development/project
> NODE_ENV=production webpack -p --bail --progress --config webpack.config.babel.js
Hash: 9e5f6974ce21c920a375
Version: webpack 1.12.10
Time: 2003ms
Asset Size Chunks Chunk Names
index.html 1.45 kB [emitted]
images/edit.svg 524 bytes [emitted]
images/search.svg 1.19 kB [emitted]
main.js 246 bytes 0, 1 [emitted] javascript, html
+ 219 hidden modules
When I run the development version of the project, the output is markedly different... I know that the dev server dependencies are in there, and the code is not minified.. And, most importantly - everything works as expected when running the dev server.
$ npm start
> project#0.1.0 start /Users/me/Development/project
> webpack-dev-server --hot --display-modules --config webpack.config.babel.js
http://localhost:3333/
webpack result is served from /
content is served from /Users/me/Development/project/dist
Hash: 1b34ed58f9e323966ada
Version: webpack 1.12.10
Time: 2745ms
Asset Size Chunks Chunk Names
index.html 1.45 kB [emitted]
images/edit.svg 524 bytes [emitted]
images/search.svg 1.19 kB [emitted]
main.js 1.54 MB 0, 1 [emitted] html, javascript
Here's my webpack.config.babel.js file:
import webpack from 'webpack';
import path from 'path';
import ModernizrWebpackPlugin from 'modernizr-webpack-plugin';
import modernizrConfig from './modernizr.config';
const appDir = path.resolve(__dirname, './src');
const distDir = path.resolve(__dirname, './dist');
const nodeModulesDir = path.resolve(__dirname, './node_modules');
const excludeDirs = /(node_modules|bower_components)/;
module.exports = {
entry: {
javascript: appDir + '/main.js',
html: appDir + '/index.html'
},
output: {
path: distDir,
filename: 'main.js',
},
devServer: {
contentBase: distDir,
inline: true,
port: 3333
},
resolve: {
extensions: ['', '.js', '.es6'],
modulesDirectories: [
'node_modules',
'./src'
]
},
plugins: [
// new webpack.optimize.CommonsChunkPlugin('common.js'),
// new ModernizrWebpackPlugin(modernizrConfig),
],
sassLoader: {
sourceMap: false,
includePaths: [
appDir,
nodeModulesDir,
nodeModulesDir + '/breakpoint-sass/stylesheets/',
nodeModulesDir + '/susy/sass'
]
},
module: {
loaders: [
{ // js/jsx
test: /\.js?$/,
exclude: excludeDirs,
loader: 'babel',
query: {
cacheDirectory: true,
presets: [
'es2015', 'react'
]
}
},
{ // html
test: /\.html$/,
exclude: excludeDirs,
loader: 'file?name=[name].[ext]'
},
{ // images
test: /\.(gif|png|jpg|jpeg|svg)$/,
exclude: excludeDirs,
loader: 'file?name=images/[name].[ext]'
},
{ // sass
test: /\.scss$/,
exclude: excludeDirs,
loader: 'style!css!sass'
}
]
}
}
I don't think I've got a particularly complex, or uncommon setup, and I've tried changing everything from es2015/es6 to commonJS already as well, with the same result.
I'm at a loss as to what the issue could possibly be here; hoping that someone can point out some obvious error I've got, or perhaps suggest config updates/changes that could resolve this problem.
Thanks for taking the time to read everything!
As I mentioned in my comment, I've been using webpack successfully for months now, and I've never come across using an HTML file as an entry point.
Usually you'll use Webpack to package up javascript, css, and sometimes images (ie: "assets") to be used by an html file. Serving that HTML file is outside the realm of Webpack's responsibility.
I would suggest using Webpack to generate only the final javascript bundle, then using some other method (ie: express, or some other web server) to serve that file and the html that consumes it.

Categories