My notes after reading:
Eventually modules systems were designed for JS:
- CommonJS (CJS)
- ES Modules (ESM)
Imports are basically a reference view into another module. Changes made in the parent module will reflect in the variables imported by child modules. The dependence is, as always, represented as a tree but ESM allows cyclic dependencies as well.
ES module system runs in three phases asynchronously
- Construction: all loaded modules are parsed and converted to module records. These module records contains the information of
- Instantiation: all modules are instantiated and allocated working memory. The instance contains the instruction and space for variables and data structures.
- Evaluation: The code is executed to determine the initial state.
These steps are part of ECMA spec but they always execute under a loader and in case of browsers that loader is HTML spec. which loads the module files and then calls parse, instantiate and evaluate.
In short all we need is to provide the entry point of our application code and the loader will parse and load all the required modules and create the dependency tree.
Multiple modules can depend on the same module. So they share the module instance. There can only be one instance per module.
Loaded and parsed modules are maintained in a module map. given a module location get the status (loading or loaded) if loaded get the module record.
Imports are reference to exported items memory location. Changes reflect everywhere. Once the dependency graph is ready the instantiation process traverses it in a depth-first manner and allocates the memory. This way all imports have a reference for the exported items.
Evaluation step executes the top level code (the global code) first and only once in the application lifetime. Cyclic dependencies are handled using timeout and tricks.
Browsers differentiate between module and non-module JS import using