Webpack Core Mechanism - Complete Summary
Part 1: Single File Scenario
Q: What happens when webpack processes a single JS file?
A: Webpack creates a minimal runtime:
// 1. IIFE wrapper (creates isolated scope)
(() => {
// 2. __webpack_require__ object (with helper methods only)
var __webpack_require__ = {};
// 3. Helper methods for ES6 exports
__webpack_require__.r = (exports) => { /* mark as ES6 module */ }
__webpack_require__.d = (exports, definition) => { /* define exports */ }
__webpack_require__.o = (obj, prop) => { /* hasOwnProperty check */ }
// 4. Your code runs directly (not wrapped in __webpack_modules__)
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
// ... your original code here
})();
Key points:
- ✅ No
__webpack_modules__object (not needed - only one file) - ✅
__webpack_require__exists but ONLY for its helper methods (.r, .d, .o) - ✅ It's NOT actually "requiring" anything - just handling exports
- ✅ Your code runs directly in the IIFE
Part 2: Multiple Files Scenario
Q: What happens when webpack processes multiple dependent files?
A: Webpack creates a full module system:
(() => {
// 1. Module storage - key-value map
var __webpack_modules__ = {
"./src/logger.js": ((module, __webpack_exports__, __webpack_require__) => {
// logger.js code wrapped in function
}),
"./src/utils.js": ((module, __webpack_exports__, __webpack_require__) => {
// utils.js code wrapped in function
})
};
// 2. Module cache
var __webpack_module_cache__ = {};
// 3. Module loader function
function __webpack_require__(moduleId) {
// Check cache → Create module → Execute → Return exports
}
// 4. Helper methods
__webpack_require__.r = ...
__webpack_require__.d = ...
__webpack_require__.o = ...
// 5. Entry code runs
__webpack_require__("./src/index.js");
})();
Key points:
- ✅
__webpack_modules__stores ALL modules as functions (factory pattern) - ✅ Each module is wrapped:
(module, exports, require) => { code } - ✅
__webpack_require__(moduleId)loads modules on-demand - ✅ Cache prevents duplicate execution
Part 3: Understanding the Factory Function Parameters
Q: What are the 3 parameters in the module wrapper?
((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
// your module code
})
A:
| Parameter | Purpose | When Used |
|---|---|---|
__unused_webpack_module | The module object itself | Used in CommonJS (module.exports = ...) |
__webpack_exports__ | Where exports are stored | Used by __webpack_require__.d() |
__webpack_require__ | Loader function to import other modules | Called when your code has import |
Why "unused" for the first parameter?
- In ES6 modules, you use
export, which writes to__webpack_exports__ - In CommonJS, you use
module.exports = ..., which needs themoduleobject - Your code uses ES6, so the first parameter is unused
Part 4: Execution Flow Example
Q: What happens when index.js calls __webpack_require__("./logger.js")?
A: Step-by-step execution:
Step 1: index.js starts running
↓
Step 2: Encounters __webpack_require__("./logger.js")
↓
Step 3: __webpack_require__ function executes:
- Check cache: __webpack_module_cache__["./logger.js"] → undefined
- Create module object: { exports: {} }
- Store in cache
↓
Step 4: Execute the factory function:
__webpack_modules__["./logger.js"](module, module.exports, __webpack_require__)
↓
Step 5: logger.js code runs:
- Encounters __webpack_require__("./utils.js")
- Repeats Step 3-4 for utils.js
↓
Step 6: utils.js finishes, returns exports to logger.js
↓
Step 7: logger.js finishes, returns exports to index.js
↓
Step 8: index.js continues execution with logger's exports
Key insight: It's a recursive loading process with caching!
Part 5: Transform Rules Summary
| Your Code | Webpack Output |
|---|---|
import { log } from './logger.js' | var _logger__MODULE_0__ = __webpack_require__("./logger.js") |
export const version = '1.0.0' | __webpack_require__.d(__webpack_exports__, { version: () => version }) |
log('hello') | (0, _logger__MODULE_0__.log)('hello') |
Connection to Your Web3 Work
When you write in your Aladdin project:
import { usePrivy } from '@privy-io/react-auth'
import { useQuery } from '@tanstack/react-query'
Webpack does this:
- Finds all dependencies (Privy, TanStack Query, their dependencies...)
- Assigns each a moduleId (path)
- Wraps each in a factory function
- Stores in
__webpack_modules__ - Your bundle can load them on-demand via
__webpack_require__
This is why your bundle.js is several MB - it contains the entire dependency tree, flattened and wrapped!
Your understanding is excellent! 🎯 You've grasped the core mechanism. Ready to explore code splitting, tree-shaking, or production mode next?