Named exports export circ dep

This example assumes you have followed along with the basics and the default export example.

The full source code is very similar to the previous example, but now we have:

Note: This example uses ES5, but the same is true for ES6

entry.js
moduleA.js
moduleC.js
moduleB.js

_10
import { namedA, namedWithImportedValue } from './moduleA';
_10
_10
console.log(({ namedA, namedWithImportedValue }));

How does the circular dependency look like?

We have an import chain where moduleA.js eventually depends on itself:


_10
entry -> moduleA -> moduleC -> moduleB -> moduleA

And moduleA.js defines named exports

How does Webpack handle this circular dependency with named exports?

Follow on for a detailed explanation, but in short:

In detail

Full file

Requiring moduleA.js the first time (1)

Similar to the previous example, Webpack first looks in the cache and does not find a cache entry.

Next, the preliminary exports are set: module.exports = {}

And the module function is executed.

What is new is that within the module function, is a bit of code which sets getters for the named exports on the module cache.

Requiring moduleA.js the first time (2)

The first thing to note about these getters are that these are set directly on the module cache.

__webpack_exports__ is a reference to __webpack_module_cache__["./src/regular named export/moduleA.js"].exports

If we were to set a breakpoint before line 12, the module cache would be:

[Dropdown] module cache with breakpoint before line 12

_10
var __webpack_module_cache__ = {
_10
"./src/regular named export/moduleA.js": {
_10
exports: {
_10
namedA: function () { return namedA; },
_10
namedWithImportedValue: function () { return namedWithImportedValue; }
_10
}
_10
}
_10
}

Requiring moduleA.js the first time (3)

The second important thing to note is that these getters return variables which will be instantiated later

Let's fast forward a bit and see why this matters >>

Requiring moduleA.js the second time (1)

We have recursively called __webpack_require__ a couple of times:

  1. entry -> __webpack_require__('./src/regular/moduleA.js')
  2. moduleA -> __webpack_require__('./src/regular/moduleC.js')
  3. moduleC -> __webpack_require__('./src/regular/moduleB.js')

And we are now about to execute the moduleB.js code

Requiring moduleA.js the second time (2)

There now is a cache hit when requiring moduleA. The cache returns the export object containing the getters.

However when we try to access the getter on line 21, we get an error:

main.js:9 Uncaught ReferenceError: Cannot access 'namedWithImportedValue' before initialization
dist/main.js

_83
(function () {
_83
"use strict";
_83
var __webpack_modules__ = ({
_83
"./src/regular named export/moduleA.js":
_83
(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
_83
__webpack_require__.r(__webpack_exports__);
_83
__webpack_require__.d(__webpack_exports__, {
_83
"namedA": function () { return namedA; },
_83
"namedWithImportedValue": function () { return namedWithImportedValue; }
_83
});
_83
_83
var _moduleC__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/regular named export/moduleC.js");
_83
const namedA = 'a';
_83
const namedWithImportedValue = _moduleC__WEBPACK_IMPORTED_MODULE_0__["default"] + 'b';
_83
}),
_83
_83
"./src/regular named export/moduleB.js":
_83
(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
_83
__webpack_require__.r(__webpack_exports__);
_83
var _moduleA__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/regular named export/moduleA.js");
_83
const bar = _moduleA__WEBPACK_IMPORTED_MODULE_0__.namedWithImportedValue + 'bar';
_83
__webpack_exports__["default"] = (bar);
_83
}),
_83
_83
"./src/regular named export/moduleC.js":
_83
(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
_83
__webpack_require__.r(__webpack_exports__);
_83
var _moduleB__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/regular named export/moduleB.js");
_83
const baz = _moduleB__WEBPACK_IMPORTED_MODULE_0__["default"] + 'baz';
_83
__webpack_exports__["default"] = (baz);
_83
})
_83
});
_83
_83
var __webpack_module_cache__ = {};
_83
_83
function __webpack_require__(moduleId) {
_83
var cachedModule = __webpack_module_cache__[moduleId];
_83
if (cachedModule !== undefined) {
_83
return cachedModule.exports;
_83
}
_83
_83
var module = __webpack_module_cache__[moduleId] = {
_83
exports: {}
_83
};
_83
_83
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
_83
_83
return module.exports;
_83
}
_83
_83
!function () {
_83
__webpack_require__.d = function (exports, definition) {
_83
for (var key in definition) {
_83
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
_83
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
_83
}
_83
}
_83
};
_83
}();
_83
_83
!function () {
_83
__webpack_require__.o = function (obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
_83
}();
_83
_83
!function () {
_83
__webpack_require__.r = function (exports) {
_83
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
_83
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
_83
}
_83
Object.defineProperty(exports, '__esModule', { value: true });
_83
};
_83
}();
_83
_83
var __webpack_exports__ = {};
_83
_83
!function () {
_83
__webpack_require__.r(__webpack_exports__);
_83
var _moduleA__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/regular named export/moduleA.js");
_83
console.log(({ namedA: _moduleA__WEBPACK_IMPORTED_MODULE_0__.namedA, namedWithImportedValue: _moduleA__WEBPACK_IMPORTED_MODULE_0__.namedWithImportedValue }));
_83
}();
_83
_83
})()
_83
;

Oops! None of the calls to __webpack_require__() finish, and the named circular dependency might have just broken your app!

Take aways

  1. Webpack transpiles named exports into getters
  2. When the module which is depending on itself uses named exports, it will throw an error

Next example

Let's see how es6 behaves vs es5. Click here for the default exports (es6) example