The [name][1] is provisional.
Implements an isolated content-addressable package loading system.
The process starts with the importer. When you want to use a module,
it first has to be imported into the store along with all the modules
it requires. We analyze the module to identify its dependencies by
using containment to replace `require` with a shim which records the
dependency modules. In the future we could do this by parsing the
source instead since not all requires happen at the top level; the
current approach means that importing only works on well-behaved modules.
Once we determine a module's dependency tree, we walk it down to the
leaves and for each leaf module we checksum it. The checksum is based
on several factors: the contents of the file, the checksum of letrec
itself, and the versions of Fennel and Lua used. The leaf module is
copied into the store using a key based on the checksum. Once the
leaves are imported, we walk back down the tree, but for non-leaf
modules we incorporate the checksums of their dependencies into their
checksums. We also store metadata for each module indicating how the
module names for each dependency map to checksums.
After a module and its dependencies have been imported, it can be
loaded. We add a package searcher which knows how to look in the store
for modules. It loads it in a context which has `require` replaced
with a letrec-aware equivalent. This version of `require` looks at the
metadata tree that was generated during import, and whenever a module
needs to load one of its dependencies, it ensures that it always gets
the right one that was locked in at import time.
Right now we just use a local filesystem directory is the store, but
it's easy to imagine replacing it with a networked store such as IPFS,
git, DAT, etc.
* Import: to take a normal module and import it into the store
* Store: the storage location for imported modules
* Module: a unit of code returning a value referenced by a single name
* Searcher: a function which searches for a module and provides it to require
* Resolve: the process of making a module content-addressable
* Containment: the process of loading a module in an isolated way to analyze it
[1]: https://erlang.org/pipermail/erlang-questions/2011-May/058768.html