5.5 Program State

The second step in running the code is creating a program state. Program state is referenced many times in the Deno code. Program state is a collection of some important attributes and functionalities.
let program_state = ProgramState::new(flags.clone())?;

Attributes

The program state is an important part of Deno. It is created right after creating the module specifier for the main module. The new function creates a new program state. A program state consist of the following attributes:
  • Flags
    • These are flags that were parsed from the command line arguments
  • Deno directory path
    • The path to the directory used by Deno for all it's work
  • File fetcher
    • This is another big component present in CLI
    • File fetcher also contains HTTP cache, file cache, dependents cache, certificate file, etc.
// PROGRAM STATE
pub fn new(flags: flags::Flags) -> Result<Arc<Self>, AnyError> {
let custom_root = env::var("DENO_DIR").map(String::into).ok();
let dir = deno_dir::DenoDir::new(custom_root)?;
let deps_cache_location = dir.root.join("deps");
let http_cache = http_cache::HttpCache::new(&deps_cache_location);
let ca_file = flags.ca_file.clone().or_else(|| env::var("DENO_CERT").ok());
let cache_usage = if flags.cached_only {
CacheSetting::Only
} else if !flags.cache_blocklist.is_empty() {
CacheSetting::ReloadSome(flags.cache_blocklist.clone())
} else if flags.reload {
CacheSetting::ReloadAll
} else {
CacheSetting::Use
};
let file_fetcher = FileFetcher::new(
http_cache,
cache_usage,
!flags.no_remote,
ca_file.as_deref(),
)?;
let lockfile = if let Some(filename) = &flags.lock {
let lockfile = Lockfile::new(filename.clone(), flags.lock_write)?;
Some(Arc::new(Mutex::new(lockfile)))
} else {
None
};
let maybe_import_map: Option<ImportMap> =
match flags.import_map_path.as_ref() {
None => None,
Some(file_path) => {
if !flags.unstable {
exit_unstable("--import-map")
}
Some(ImportMap::load(file_path)?)
}
};
let maybe_inspect_host = flags.inspect.or(flags.inspect_brk);
let maybe_inspector_server = match maybe_inspect_host {
Some(host) => Some(Arc::new(InspectorServer::new(
host,
http_util::get_user_agent(),
))),
None => None,
};
let program_state = ProgramState {
dir,
flags,
file_fetcher,
modules: Default::default(),
lockfile,
maybe_import_map,
maybe_inspector_server,
};
Ok(Arc::new(program_state))
}

Internal directories

It'd be good to see the internal directory paths used by Deno for its work.
Here are the root directory paths on MacOS:
root dir: /Users/mayankc/Library/Caches/deno
deps dir: /Users/mayankc/Library/Caches/deno/deps
gen dir: /Users/mayankc/Library/Caches/deno/gen
Here are the directory paths for HTTP and HTTPS cache. These are present inside the deps directory.
http cache: /Users/mayankc/Library/Caches/deno/deps/http
https cache: /Users/mayankc/Library/Caches/deno/deps/https
There is no cache for local files as accessing local files is significantly cheaper than accessing remote files.

FileFetcher

FileFetcher offers functionality to fetch a file and store it in the cache. File fetcher is used for both local and remote files. Even local files are fetched which simply means they are read.
File fetcher consists of the following:
  • FileCache
  • HttpCache (for files that are fetched through http)
  • Cache setting
    • use cached
    • reload all
    • etc.
  • Http Client
    • This is a wrapper over async reqwest::Client
  • FileFetcher supports three URI schemes
    • http
    • https
    • file
We'll go over the file fetcher in detail when we discuss module loading.
There are some more attributes present in the program state, but those aren't relevant for the hello world program. These other parts are: lockfile, maybe_import_map, maybe_inspect_host.

Functionality

In addition to the attributes, the program state also offers two very important functionalities:
  • Prepare Module
    • Fetch, load, and compile a module
  • Fetch compiled module
    • Fetch an already compiled module

Prepare Module

Prepare module prepares a module that needs to be loaded in JS runtime. Preparation of a module consists of fetching, loading, and compiling a module and all its dependencies. It goes through all the imports recursively and loads them. At the end of preparation, a module and all its dependencies are compiled and loaded into v8.
Here are some of the steps performed during module preparation:
  • Initialize module graph
    • All the ES modules are maintained in a module graph
  • Main module
    • Add the main module to the graph
    • Recursively go through all the dependencies and build the graph
    • Fetch and load all the dependencies
  • Compile
    • Compile the module graph
That's all for now. We'll go through this in detail when we go over module loading.

Fetch compiled module

This fetches a compiled module given its specifier. It uses the file fetcher to fetch the compiled source for a specifier. Again we'll go through this in detail when we go over the module loading.