Webpack Entry Points

I've been going through the Webpack guides. One section that caught me by surprise was Output Management, specifically regarding multiple entry points. In there, the following source code is defined:

src/print.js

export default function printMe() {
  console.log('I get called from print.js!');
}

src/index.js

import _ from 'lodash';
import printMe from './print.js';

function component() {
  const element = document.createElement('div');
  const btn = document.createElement('button');

  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  btn.innerHTML = 'Click me and check the console!';
  btn.onclick = printMe;

  element.appendChild(btn);

  return element;
}

document.body.appendChild(component());

webpack.config.js

import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export default {
  entry: {
    index: './src/index.js',
    print: './src/print.js',
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
};

Now, given the following HTML file:

dist/index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Output Management</title>
    <script src="./print.bundle.js"></script>
  </head>
  <body>
    <script src="./index.bundle.js"></script>
  </body>
</html>

When executing webpack, we get the following output artifacts:

dist/
├── index.bundle.js
├── print.bundle.js
└── index.html

Out of curiosity, I inspected print.bundle.js, which to my surprise, consisted of an immediately invoked function expression (IIFE) without any side effects:

(()=>{"use strict"})();

This confused me; I was expecting to see a semblance of the printMe function.

After conversing with ChatGPT 5.2, I quickly realized that my mental model of an entry point was completely wrong. Here is the message from GPT that made everything click:

An entry like print: "./src/print.js" produces a file (print.bundle.js) that is treated as its own program: it runs when included by <script src="./print.bundle.js"></script>. It is not emitted as “a module that other bundles can import from” in the ES-module sense.

Now, thinking of print.js as a program, it's apparent that running such a program (with merely a function declaration) would have zero side effects, just like print.bundle.js.

In other words, running print.js and print.bundle.js results in the same side effect(s): none. Hence, webpack's optimizations outputted a minimal webpack wrapper: an IIFE that does nothing.

Adding Side Effects to print.js

Next, I tinkered with print.js to give it a side effect:

export default function printMe() {
  console.log('I get called from print.js!');
}

printMe();

Running webpack again, we're given the following print.bundle.js artifact:

(()=>{"use strict";console.log("I get called from print.js!")})();

As was before, this version still doesn't contain the printMe function. However, it is still equivalent to the original print.js we just modified; they both log "I get called from print.js!".

Then, where did the printMe function actually go?

At this point, it's important to remember what webpack is: a module bundler. It bundles the modules (files) that a particular module (e.g. src/index.js) needs in order to run. Going back to our program analogy, this means index.bundle.js must consist of all of the code it needs to run as its own program.

With that being said, let's inspect src/index.js once more. There, we can see it holds a reference to the printMe function from the src/print.js module:

btn.onclick = printMe;

Therefore, index.bundle.js must include the printMe function itself, else it would not be able to run. If we inspect index.bundle.js, we can see it in its minified form:

function e(){console.log("I get called from print.js!")}

Caveats

The observations I've made here are based off the webpack.config.js configuration in the Output Management guide. In reality, webpack can be configured in such a way that many of the behaviors I've observed here can be altered.

I also made a pull request into the documentation site to help clarify this for future readers like myself.