6.3 Module graph with imports

Overview

This marks the primary departure from the hello world program discussed in the preceding chapter. Unlike the earlier hello world program that lacked any imports, resulting in a graph containing only one node for the main module, the hello v2 example incorporates imports, inevitably causing the graph to expand. Let's delve into the process of graph construction and its subsequent expansion.
The process of building the graph unfolds through the following sequential steps:
  1. 1.
    Inclusion of the Main Module: As in the previous scenario, the main module assumes its position within the graph.
  2. 2.
    Fetching the Main Module: This involves retrieving the main module.
  3. 3.
    Fetching Associated Modules: All modules tied to the main module are fetched.
  4. 4.
    Integration into the Graph: Each fetched module is integrated into the evolving graph.
  5. 5.
    Enlistment in Pending Modules: Alongside integration, these fetched modules are enlisted in the list of pending modules.
  6. 6.
    Iterating through Pending Modules: A loop iterates over the pending modules, enabling further processing.
  7. 7.
    Visiting Pending Modules: Each pending module is visited in turn.
  8. 8.
    Parsing the Module: The module in question undergoes a parsing process.
  9. 9.
    Traversal of Dependencies: The module's dependencies are traversed.
  10. 10.
    Fetching Dependencies: These dependencies are fetched from their sources.
In this revamped hello v2 example, just as in the previous version, the main module is promptly integrated. However, in contrast, the inclusion of dependencies is deferred until a later stage. This strategic alteration leads to the gradual growth of the graph as each dependency is sequentially added.

Build the module graph

In the process of creating a hello v2 application in Deno, the construction of the module graph occurs through several distinct steps. The initial phase involves the recursive retrieval, inclusion, and exploration of all the necessary dependencies. This sequence of actions continues until there are no remaining dependencies left to consider.
Add module:
Visit module:
Let's walk through the process and understand how the graph is constructed for the hello V2 application:

Step 1

The initial action involves invoking the 'fill' function using the main module.
Module specifier is file:///Users/mayankc/Work/source/denoExamples/helloLogV2.ts.

Step 2

The FileFetcher function is invoked using the primary module specifier.

Step 3

A Rust future is generated to retrieve the specifier in Deno. This Rust future will be marked as complete once the fetching process finishes. Once generated, the Rust future is included in the list of pending tasks.
pending list = [ file:///Users/mayankc/Work/source/denoExamples/helloLogV2.ts ]

Step 4

The 'fill' function is placed within the loop where it patiently awaits the resolution of the upcoming future in the pending list. This singular future happens to be the one associated with fetching the main module. It's important to note that, at this juncture, the graph is composed of just a single node, representing the main module.

Step 5

The Rust future of the main module is resolved in Deno. This means that Deno finishes handling the operations related to the main module. The main module specifier, which is like the address of the main module, is fetched from its source and then stored in a cache for future use.
Following this, the "visit" function is triggered for the main module that's now stored in the cache. This function is designed to handle actions related to the cached module. It's important to understand that when the "visit" function is called, it receives the cached module as an input.
visit = file:///Users/mayankc/Work/source/denoExamples/helloLogV2.ts

Step 6

When Deno processes a cached module, it carefully examines its contents. In the context of the hello world v2 program, the main module comes with several dependencies. These dependencies are like pieces of a puzzle that need to be gathered before the full picture can be assembled.
Deno takes these dependencies and prepares them for retrieval. It sets up a plan for fetching them from their respective sources.
pending list = [ https://deno.land/x/machine_id/mod.ts, npm:nanoid ]
At this particular juncture, the module graph consists of three nodes:

Step 8

The upcoming Rust future will be resolved as soon as the module is fetched. Inside the fill function, the loop handles the next item that's ready to be processed from the pending list. Moving forward, the npm:nanoid module is the next one in line to be visited and explored.
visit = npm:nanoid

Step 9

When Deno processes a cached module, it carefully examines its contents. Within this parsed module, there exist certain dependencies. Deno takes these dependencies and allocates them to a fetch future, which is then included in the list of tasks awaiting execution. In the case of an NPM module, it's worth noting that the dependencies that need to be retrieved will be stored conveniently within the designated "node_modules" folder.
pending list = [ file:///Users/mayankc/Work/source/denoExamples/node_modules/.deno/[email protected]/node_modules/nanoid/url-alphabet/index.js ]
At this point, the module graph has four nodes:

Step 10

Let's go a bit faster now.
The loop within the "fill" function is responsible for handling the upcoming item in the list of tasks to be done. In this case, the next item waiting to be processed is located at a specific web address: "file:///Users/mayankc/Work/source/denoExamples/node_modules/.deno/[email protected]/node_modules/nanoid/url-alphabet/index.js".
visit = file:///Users/mayankc/Work/source/denoExamples/node_modules/.deno/[email protected]/node_modules/nanoid/url-alphabet/index.js

Step 11

The cached module undergoes parsing, during which no additional dependencies are detected. As a result, no further nodes are introduced into the module graph. This ensures that the module graph retains its original count of 4 nodes.

Step 14

pending list = []
The pending list is now empty, signifying that all tasks have been completed. The loop within the "fill" function has ceased its operation, leading to a break. Consequently, the module graph construction has concluded. As a result, we are presented with the ultimate form of the graph, which can be visualized in the following manner:
--
Except for dynamic imports, Deno processes all static imports first. It fetches these imports, interprets their contents, and then loads them into the V8 engine before the program or application commences its execution. The handling of dynamic imports is deferred until a later stage in the process.