What causes the discrepancy in size between development mode bundles created with lodash and lodash-es?

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'production',
    entry: {
        app: './src/index.js'
    },
    plugins: [
        new HtmlWebpackPlugin({ title: 'production' })
    ],
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist'),
        clean: true
    }
}

index.js

import { join } from 'lodash';

const component = () => {
    const element = document.createElement('div');
    element.innerHTML = join(['Hello', 'webpack'], ' ~ ');

    return element;
};

document.body.appendChild(component());

 I am currently using the above configuration to bundle index.js in production mode.

 When I utilize lodash, the size of the app.bundle.js is 70kb. Switching to lodash-es reduces the size to 219b. This decrease is likely due to the Tree-Shaking feature supported by lodash-es.

 However, when transitioning to development mode, the size of app.bundle.js with lodash increases to 554kb, while it drops to 5kb when using lodash-es.

 I understand that Tree-Shaking is automatically enabled in production mode but not in development mode. My curiosity lies in what exactly causes the difference in size between lodash and lodash-es in development mode?

Answer №1

The reason behind this behavior is due to the fact that the lodash-es package has been designated as side-effect-free, as mentioned in the Webpack documentation about tree shaking.

With the release of webpack 4, a new feature allows developers to use the "sideEffects" property in the package.json file to indicate which files are considered "pure" and therefore safe to remove if not used.

You can locate the sideEffects setting in the package.json file of the lodash-es package.

{
  "name": "lodash-es",
  "version": "4.17.21",
  "description": "Lodash exported as ES modules.",
  "keywords": "es6, modules, stdlib, util",
  "homepage": "https://lodash.com/custom-builds",
  "bugs": "https://github.com/lodash/lodash-cli/issues",
  "repository": "lodash/lodash",
  "license": "MIT",
  "type": "module",
  "jsnext:main": "lodash.js",
  "main": "lodash.js",
  "module": "lodash.js",
  "sideEffects": false
}

All code within these files is devoid of side effects, hence the false value for the sideEffects property informs webpack that it can safely eliminate unused exports.

This explains why even when using the mode setting as 'development', importing only specific functions like { join } from 'lodash-es' results in removal of other unused exports by webpack.

On the other hand, for the standard lodash package which uses commonjs instead of ESM, webpack cannot perform tree shaking efficiently.

For example:

Create a directory named lib and configure the sideEffects field as false in its package.json.

Directory structure:

$ tree -L 2 -I node_modules
.
├── lib
│   ├── a.js
│   ├── b.js
│   ├── index.js
│   └── package.json
├── package-lock.json
├── package.json
├── src
│   └── index.js
└── webpack.config.js

lib/a.js:

const a = () => 'a';

export default a;

lib/b.js:

const b = () => 'b';

export default b;

lib/index.js:

export { default as a } from './a';
export { default as b } from './b';

lib/package.json:

{
    "name": "lib",
    "main": "index.js",
    "sideEffects": false
}

src/index.js:

import { a } from '../lib';
console.log(a());

webpack.config.js:

module.exports = {
    mode: 'development',
    output: {
        clean: true,
    },
};

Build logs:

> webpack

asset main.js 4.11 KiB [emitted] (name: main)
runtime modules 670 bytes 3 modules
orphan modules 112 bytes [orphan] 2 modules
cacheable modules 86 bytes
  ./src/index.js 46 bytes [built] [code generated]
  ./lib/a.js 40 bytes [built] [code generated]
webpack 5.88.2 compiled successfully in 78 ms

Output

dist/main.js:

/******/    "use strict";
/******/    var __webpack_modules__ = ({

/***/ "./lib/a.js":
/*!******************!*\
  !*** ./lib/a.js ***!
  \******************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nconst a = () => 'a';\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (a);\n\n\n//# sourceURL=webpack:///./lib/a.js?");

/***/ }),

/***/ "./src/index.js":
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _lib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../lib */ \"./lib/a.js\");\n\nconsole.log((0,_lib__WEBPACK_IMPORTED_MODULE_0__[\"default\"])());\n\n\n//# sourceURL=webpack:///./src/index.js?");

/***/ })

If we exclude the sideEffects entry from package.json, observe the resulting output:

dist/main.js:

/******/ (() => { // webpackBootstrap
/******/    "use strict";
/******/    var __webpack_modules__ = ({

/***/ "./lib/a.js":
/*!******************!*\
  !*** ./lib/a.js ***!
  \******************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nconst a = () => 'a';\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (a);\n\n\n//# sourceURL=webpack:///./lib/a.js?");

/***/ }),

/***/ "./lib/b.js":
/*!******************!*\
  !*** ./lib/b.js ***!
  \******************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nconst b = () => 'b';\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (b);\n\n\n//# sourceURL=webpack:///./lib/b.js?");

/***/ }),

/***/ "./lib/index.js":
/*!**********************!*\
  !*** ./lib/index.js ***!
  \**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   a: () => (/* reexport safe */ _a__WEBPACK_IMPORTED_MODULE_0__[\"default\"]),\n/* harmony export */   b: () => (/* reexport safe */ _b__WEBPACK_IMPORTED_MODULE_1__[\"default\"])\n/* harmony export */ });\n/* harmony import */ var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */ \"./lib/a.js\");\n/* harmony import */ var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./b */ \"./lib/b.js\");\n\n\n\n\n//# sourceURL=webpack:///./lib/index.js?");

/***/ }),

/***/ "./src/index.js":
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _lib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../lib */ \"./lib/index.js\");\n\nconsole.log((0,_lib__WEBPACK_IMPORTED_MODULE_0__.a)());\n\n\n//# sourceURL=webpack:///./src/index.js?");

/***/ })

In this scenario, webpack retains the lib/b.js and lib/index.js files in the bundled output dist/main.js.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

cycle through several handlebars entities

Hey friends, I could really use your help right now. I'm attempting to iterate through these objects using handlebars. RowDataPacket { idUser: 1, username: 'xxxxxx', password: 'xxxxx', fullname: 'Julian Rincon'}, RowDat ...

Unable to connect executable "node": unable to locate library "libcrypto.so.3"

When using Termux (my_distro): $ pkg show openssl Package: openssl Version: 3.0.1-1 Maintainer: @termux Installed-Size: 6648 kB Depends: ca-certificates, zlib Conflicts: libcurl (<< 7.61.0-1) Breaks: openssl-tool (<< 1.1.1b-1), openssl-dev Repl ...

error message about missing relative modules in vuejs webpack

I am working on a full-stack application using express and the default vue-webpack-boilerplate. The structure of my project is as follows: ├── client │   ├── build │   │   └── ... │   ├── config │   │   └ ...

Error encountered during Atom execution - The command '/usr/bin/env: 'node' was not found in the directory

Just starting out with coding on Atom and I'm stuck dealing with the same error message every time I try to run my Javascript code. bash: line 1: node: command not found /usr/bin/env: ‘node’: No such file or directory I've searched for solu ...

Leveraging node.js and express for incorporating custom stylesheets and scripts

I recently designed a webpage with the following elements at the beginning: <link href="./css/font-awesome.min.css" rel="stylesheet"> <link href="http://fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600" rel="stylesheet ...

Using Sequelize to send data from the client-side to the server-side

I am currently developing a project for a fictional library database and website interface. I am facing an issue where only 2 out of the 4 new loan form inputs are being passed to the req.body. Even though all items have a name attribute, it seems like onl ...

Troubleshooting Azure typescript function: Entry point for function cannot be determined

project structure: <root-directory> ├── README.md ├── dist ├── bin ├── dependencies ├── host.json ├── local.settings.json ├── node_modules ├── package-lock.json ├── package.json ├── sealwork ...

During the npm start process, a fatal error occurred: MarkCompactCollector encountered an issue with promoting young objects, leading to a failed allocation due to the JavaScript heap

Currently, I am in the process of deploying my project to an AWS LightSail instance. Here is a breakdown of what I have done so far: I cloned the repository Ran npm install Executed npm start. However, I encountered an Allocation failed Error during thi ...

Using Node.js to render when a task has been completed

I am currently developing a Node.js Application using Express.js. One of the challenges I face is rendering data from another site on a page using Cheerio.js. While this in itself is not an issue, I struggle with determining how to render the data once the ...

Exploring PassportJS: The power of using multiple local strategies all at once

Currently, I am faced with the challenge of implementing two LOCAL strategies using Passportjs simultaneously. In this specific scenario, there is a user and a room, each requiring their own name and password for authentication. To handle this, I can set u ...

nw command connected with nanoc

Upon using OSX and installing NWjs, I encountered an error when attempting to run a site with nw ./: WARNING: The watch command is deprecated. Consider using guard-nanoc instead (visit https://github.com/nanoc/guard-nanoc). Error: Current working di ...

Troubleshooting React's failure to start with node.js running behind an Nginx Reverse Proxy

This is my first attempt at setting up a Node.js server in production (other than one Meteor server), and I've run into a problem that I can't figure out on my own. I have built an application using React. Here is the server code: // Imports va ...

Toggle between different socket.io servers for seamless connectivity

I'm looking for help with a situation where I need a socket.io client to connect to server A, disconnect, and then connect to server B. Any ideas on how I can achieve this? Thanks in advance! UPDATE: Attached below is the code snippet that's gi ...

Leveraging AJAX Post Data in NodeJS using Express

I'm currently working on retrieving the values I send for an ajax post within my node application. After referring to a helpful post on Stack Overflow, here is what I have implemented so far: Within Node.js: var express = require('express&apos ...

Unable to resolve the circular dependency issue with mongoose schemas

I am dealing with 3 models: Book, Author, and 'Category'. An Author can have multiple books, a Category can have multiple books, and a Book cannot be created without a valid Author or Category. const schema = new mongoose.Schema( { title: ...

keeping variables hidden from the user until a certain action is taken

In my project, I am working with three main code classes: 1) node.js (utilizing express framework) 2) index.html (serving as the home page when users visit the site) 3) sck.js which performs a specific function (details to follow on the first and second fi ...

Encountering issues with Ubuntu 16.04: Finding it difficult to resolve problems due to conflicting packages that are

I've attempted to uninstall and reinstall node.js multiple times in order to install npm. I gave Aptitude a shot to resolve dependencies but with no success, and considered using Synaptic Package Manager, although I'm unsure of the next steps. Th ...

What type of data does a router function return?

What is the recommended return type to exit a router function explicitly in order to avoid excessive else blocks and deep indentations? app.get("/xx", function(req, res) { if (c1) { res.render("c1"); // What should be returned here? } ...

Incompatible with the node/npm version range you are using (node 14 up to node 18)

Currently, I am in the process of updating my React 17 application to utilize a more recent version of Node and NPM. The application is currently operating on 14.17.2, but I need it to transition to 18.14.2 so that I can containerize it using a Docker cont ...

While Mongoose.create is successfully inserting data into the database, it is also capturing any errors that may

I'm currently in the process of developing a RESTful API for products to handle CRUD operations. Utilizing MongoDB as my database and combining it with mongoose and express, I aim to efficiently access the data. However, upon attempting to create a ne ...