General design
The ZPM codebase shares a similar high-level design with the Berry codebase. Installs work as follows:
-
We start with a queue containing a set of root descriptors. Typically that will be the descriptors for the workspaces in the project.
-
For each descriptor, we pass it down to the
resolve_descriptorfunction, which will select the proper resolver function for the given range. This resolver function will then return us a resolution object. -
Each resolution we receive triggers two events:
-
First, we enqueue new descriptors into our queue for each dependency listed in the resolution.
-
Second, we forward the resolution’s locator to the
fetch_locatorfunction. It will select the proper fetch function for the given reference, which will then pull the package data (either as a cache reference, or local path reference).
-
-
Once we have finished resolving all descriptors and fetching all locators, we enter the link phase by calling
link_project. What happens there changes depending on the configured linker, but in the end they yield two new pieces of information:-
A list of locations on disk for each package in the dependency graph. For the Yarn PnP and pnpm linkers we’ll have exactly one location per locator, whereas the
node_moduleslinker may return multiple locations per locator to satisfy the hoisting. -
A list of “build requests”, ie instructions on how to build the packages that have been laid out on disk. These requests can depend on other requests.
-
-
The core will then take all the build requests and process them, parallelizing the builds as much as possible while still respecting the dependencies between them.