Module cycles — when two modules import each other¶
Level: 301 · for anyone whose two modules import each other
Stub — an outline, not a lesson. There is no runnable example behind this page yet, so nothing on it has been through the check that backs every other claim in this library. The questions below are what the finished page has to answer.
One line: In a cycle one module runs before the other has finished: an ES module reading the other's const at top level throws ReferenceError (the binding is still in its temporal dead zone), while CommonJS hands over a half-filled exports and reads undefined.
Keywords: circular import, ERR_REQUIRE_CYCLE_MODULE
What the finished page will answer¶
- When
a.mjsandb.mjsimport each other anda.mjsis the entry point, which body runs first? - Why does reading the other module's
constthrowReferenceError, while calling its exported function declaration works? - What does CommonJS hand over in the same cycle, and what warning does Node print about it?
- What does
require()throw when an ES module loads a CommonJS file that requires the ES module back? - Which fixes break a cycle: moving the shared value to a third module, or reading it inside a function called later?
Examples it will need¶
-
module_cycles_esm_and_cjs_sh.sh— the same two-module cycle in ESM (ReferenceError) and in CommonJS (undefined, plus Node's warning with the PID removed) -
module_cycles_hoisted_functions_sh.sh— an ESM cycle whose modules call each other's exported function declarations, which works -
module_cycles_require_cycle_sh.sh— an ES module that requires a .cjs file that requires it back, and the ERR_REQUIRE_CYCLE_MODULE code
See also¶
- Hoisting and the temporal dead zone — the temporal dead zone behind the ReferenceError
- Live bindings — why the ES module sees the value once it is set
requireand ES modules — require() of an ES module that is still loading- Built-in error types — when a ReferenceError appears, beside the other built-in errors