Now that we have basic understand of Webpack's module resolution system, it's time to understand how a circular dependency works.
We use the same four files as on the previous page: entry.js, moduleA.js, moduleB.js and moduleC.js. These are compiled into dist/main.js.
Note: This example is only valid for target 'es5'. We will discuss 'es6' in a later page.
Before continuing, try to figure out what will be console logged by entry.js.
We have an import chain where moduleA.js eventually depends on itself:
_10entry -> moduleA -> moduleC -> moduleB -> moduleA
And moduleA.js defines a default export, no named exports
Follow on for a detailed explanation, but in short:
module.exports = {}.undefined values for default exports.moduleA.jsThe entry point is being executed first. The first thing it encounters is a dependency on moduleA.js.
moduleA.js (1)There is a cache miss for moduleA.js. Webpack will start the process of looking the module from scratch.
Before any module code is being executed Webpack already sets a PRELIMINARY value for exports for that module in the module cache
This is key in understanding why we do not end up in an infinite loop.
moduleA.js (2)Imagine we set a breakpoint on line 37, where we are about to call the module function and execute code from moduleA.js
The values of variables is quite interesting!
Without executing a line of code of moduleA.js we have an entry in the module cache!!!
_10moduleId = "moduleA.js";_10cachedModule = undefined;_10__webpack_module_cache__: {_10  "moduleA.js": {_10    exports: {}_10  }_10}
moduleA.js functionNow we carry on with actually executing the moduleA.js code.
The first thing we encounter on line 7 is an import for moduleC.js, which will trigger another call to __webpack_require__.
Similarly, the first line of moduleC will trigger yet another call to __webpack_require__ to resolve moduleB.
Let's fast foward a bit >>
moduleB.js functionSo we are now 3 layers of recursion deep:
__webpack_require__('./src/regular/moduleA.js')__webpack_require__('./src/regular/moduleC.js')__webpack_require__('./src/regular/moduleB.js')Let's add a breakpoint on line 14 and inspect the moduleCache there:
_11__webpack_module_cache__: {_11  "moduleA.js": {_11    exports: {}_11  },_11  "moduleC.js": {_11    exports: {}_11  },_11  "moduleB.js": {_11    exports: {}_11  }_11}
This means that when we call __webpack_require__("./src/regular/moduleA.js") now, we will get a cached result. Even though the value in the cache is not properly set.
This means that the value of 'const bar' on line 15 will become undefined + 'bar' and we can also set the default export to "undefinedbar"
Let's fast forward again >>
moduleA function executionWe've resolved moduleC.js and moduleB.js and can now continue with the rest of moduleA.js
Let's add a breakpoint on line 8 and inspect the moduleCache there:
_15__webpack_module_cache__: {_15  "moduleA.js": {_15    exports: {}_15  },_15  "moduleC.js": {_15    exports: {_15      default: "undefinedbarbaz"_15    }_15  },_15  "moduleB.js": {_15    exports: {_15      default: "undefinedbar"_15    }_15  }_15}
This means that the default export of moduleA will become "undefinedbarbaz" + 'bar'
Lastly, after all imports are required, it means that the module cache will become:
_17  __webpack_module_cache__: {_17    "moduleA.js": {_17      exports: {_17        default: "undefinedbarbazbar"_17      }_17    },_17    "moduleC.js": {_17      exports: {_17        default: "undefinedbarbaz"_17      }_17    },_17    "moduleB.js": {_17      exports: {_17        default: "undefinedbar"_17      }_17    }_17  }
And the entrypoint will console.log 'undefinedbarbazbar'.
module.exports = {} for each module.undefinedClick here to visit the next example with named exports