2.4 Deno components
Last updated
Last updated
In the previous section, we discussed Deno's overall structure at a high level, covering both core and third-party components. Now, let's focus on Deno's core components, excluding third-party elements for the time being.
When we refer to Deno components in this section, we mean the essential parts of Deno itself, excluding third-party libraries. Deno's primary function is straightforward: it executes TypeScript and JavaScript programs.
Deno handles various tasks, including:
Providing the main program structure
Managing sub-commands
Orchestrating the event loop
Offering a debugger inspector
Acting as a linter
Fetching files
Resolving modules
Providing a standard library
Presenting core APIs
Deno takes care of everything outside Tokio and v8's responsibilities, particularly v8. While v8 runs JavaScript efficiently within a sandboxed environment, it relies on Deno for tasks like file management, networking, input-output operations, timers, and more. This collaboration between Deno and v8 creates a comprehensive runtime environment for executing programs.
We previously overviewed Deno's components at a high level. Now, we'll explore them in detail, referencing the same diagram:
As discussed earlier, the CLI component plays a vital role in Deno. However, it's important to note that V8, with its extensive codebase of around a million lines, is the largest component, surpassing CLI in size. The CLI serves two key purposes: orchestration and service provision, making it central to the Deno ecosystem.
As an orchestrator, the CLI coordinates the activities of essential services like ext, runtime, graph, cache, file fetching, core functionalities, ops, tokio, and the V8 engine. By harmonizing their interactions, the CLI ensures smooth operation and efficient collaboration among Deno's components.
Additionally, the CLI provides essential services to other Deno components. Its dual role as orchestrator and service provider underscores its importance in the Deno framework, enabling right communication, smooth execution, and robust performance among Deno's components.
The CLI also provides the Deno executable, containing the primary (or main) program that implements various subcommands and user-oriented APIs. While the CLI doesn't cover all APIs, it encompasses a significant portion. Essentially, the CLI serves as the gateway to Deno's functionality, providing tools for executing Deno-powered tasks and interacting with its capabilities.
The following list outlines the functionalities provided by the CLI.
The main program in Deno is the entry point for all operations. When the "deno" command is invoked, it triggers the execution process. Note that this marks the beginning of Deno's operations, but not the immediate start of the program's functionality.
Before the program can run, several essential tasks need to be completed. We will explore these tasks in detail in chapters 4, 5, and 6. By understanding these chapters, we will gain a comprehensive understanding of the processes that occur before the program's actual runtime. This understanding will provide valuable insights into the fundamental aspects of Deno's functioning.
Deno offers a range of development utilities through its sub-commands, despite appearing as a single executable. The 'run' and 'test' sub-commands are the most prominent and frequently used by developers. 'Run' executes JavaScript or TypeScript programs, while 'test' initiates unit tests to ensure code integrity.
Deno provides various sub-commands for different development aspects, including:
run: Executes JavaScript or TypeScript programs
bench: Conducts benchmarking
bundle: Merges modules and dependencies into a single file
cache: Caches dependencies for performance enhancement
check: Performs type-checking on dependencies
compile: Compiles scripts into self-contained executables (UNSTABLE)
completions: Generates shell completion scripts
coverage: Produces coverage reports for code analysis
doc: Displays module documentation
eval: Executes scripts for on-the-fly evaluation
fmt: Automatically formats source files for consistent coding styles
init: Initializes new projects
info: Provides cache or source file-related information
install: Installs scripts as executables
uninstall: Removes installed scripts
lsp: Starts the language server for enhanced code editing
lint: Performs source code linting for quality enhancement
repl: Sets up a Read-Eval-Print Loop environment for interactive scripting
task: Executes tasks defined in configuration files
test: Launches tests for code validation
types: Outputs runtime TypeScript declarations
upgrade: Upgrades the Deno executable to specified versions
vendor: Imports remote modules into local directories
help: Provides guidance and information on subcommands
The file fetcher plays a crucial role in the Deno ecosystem. Unlike platforms with dedicated package managers, Deno takes a flexible approach. It enables modules to be accessed through HTTP, local sources, or NPM repositories. The file fetcher's main responsibility is to retrieve module files, regardless of their location.
Deno relies heavily on flags to enable its sandboxing feature. These flags are essential for enforcing permissions that maintain Deno's robust sandboxing system. The flags are created using command-line arguments.
Sandboxing is a critical component of Deno's security framework, designed to limit and control the actions a Deno script can perform. The flags act as gatekeepers, allowing or blocking specific operations and interactions within the script's runtime environment.
The module loader in Deno plays a vital role in handling ES modules. It provides various functionalities to resolve, load, and prepare modules for use. This includes:
Fetching modules from their sources
Compiling them as needed
Caching them for optimized performance
The module loader manages the entire process, ensuring efficient and effective handling of ES modules.
The file watcher monitors the main source code and its dependencies, triggering a Deno process restart when it detects changes. This ensures the Deno runtime stays current with the latest modifications, facilitating a smoother & quicker development experience.
When working on a Deno project, the file watcher observes your code changes and alerts the system if it detects modifications in the main code or dependent files. This alert triggers an automatic restart of the Deno process, ensuring it runs with the most up-to-date code.
The runtime is the core of Deno, comprising its essential operations and features. It combines Deno's core capabilities written in JavaScript with foundational operations coded in Rust, enabling efficient and reliable performance. The runtime framework utilizes "workers" - specialized components that handle specific tasks in parallel, enhancing Deno's multitasking capabilities. Workers play a vital role in optimizing Deno's functionality, whether it's executing scripts, managing I/O operations, or orchestrating tasks.
Given its importance, the runtime is housed within the main Deno repository.
The runtime consists of multiple interconnected services, categorized as:
Workers: Separate execution contexts for concurrent code execution, enhancing performance and efficiency.
Permissions: Manages code execution access, ensuring security and control by only granting explicit permissions.
Metrics: Collects and provides performance and behavior insights, aiding developers in optimization.
Ops: Facilitates interaction with the underlying operating system, enabling system-level operations.
JavaScript Runtime: The core where JavaScript code is executed, enabling program execution and functionality realization.
Deno's runtime combines its services to provide a secure, efficient, and developer-friendly environment for executing JavaScript and TypeScript code. This combination ensures Deno works smoothly, supporting modern development practices and various applications.
The runtime serves as the interface for accessing the Deno API in both TypeScript and JavaScript environments. It includes all core features necessary for executing programs within Deno. While Deno's CLI and core services are primarily implemented in Rust, the runtime implements these services in both Rust and JavaScript.
Deno's runtime code is written entirely in JavaScript, without using ECMAScript modules. The runtime provides a comprehensive Deno API for user programs and implements low-level Rust operations, playing a crucial role in the Deno ecosystem.
Workers play a vital role in Deno's runtime environment, similar to Node.js. Deno executes the main program in the primary thread, but can also create additional web worker threads if needed. Each worker, including the main worker, has its own runtime and event loop.
There are two distinct types of workers:
Main Worker: As implied by its name, the main worker is responsible for running the primary program. It operates within the primary thread itself, without requiring a separate thread for execution. This main worker is automatically established when the program runs.
Web Worker: The web worker is an additional type of worker that can be initiated by an application. Usually, these additional workers are employed to handle tasks that demand substantial CPU resources, potentially causing the main event loop to be blocked. For tasks that revolve around intensive input/output operations, a separate worker isn't necessary since Deno effectively manages asynchronous I/O tasks within the primary thread. These web workers follow the conventional standard of web workers found in web browsers.
In summary, workers are pivotal in the functioning of Deno's runtime environment. They provide a means to offload resource-intensive operations from the main thread, allowing the program to continue running smoothly. While the main worker handles the main program execution, web workers are an advantageous tool to tackle heavy computational tasks without hampering the overall responsiveness of the application. This enables developers to build robust and scalable applications without compromising performance.
Permissions play a vital role in creating a secure sandbox environment in Deno. These permissions cover a wide range of actions, including reading and writing files, accessing the network, managing the environment, spawning processes, utilizing plugins, and more. The importance of permissions is particularly significant in Deno and distinguishes it from other platforms like Node.js.
Deno's approach to permissions is a notable differentiator. It allows users to grant or deny specific abilities to their scripts, enhancing security. This approach contrasts with traditional systems, where scripts often have unrestricted access to resources, potentially leading to vulnerabilities.
Off lately, Node.js has also recognized the importance of permissions. Over time, it has been incorporating a permission-like system in its releases, acknowledging the security benefits it brings. This shift demonstrates the growing recognition of the importance of permissions in ensuring secure sandbox environments.
The JS Runtime in Deno is a collection of code that operates within the user's JavaScript environment. This crucial component forms the foundation of Deno's capabilities, all written in pure JavaScript. The following functionalities are provided by the JS runtime:
Build Information: This encompasses details like the target platform, architecture, operating system, vendor, and environment. Such information assists in optimizing Deno's behavior for different setups.
Colors: Deno's runtime allows you to work with colorization, enhancing the visual experience of output in the terminal.
Errors (non-ECMAScript): While not a part of the official ECMAScript specification, Deno's runtime introduces its own error handling mechanisms to improve code robustness.
Version Information: Accessing information about the current Deno version helps users stay updated and aware of the features available.
Console Utilities: The runtime provides tools for efficient interaction with the console, aiding in debugging and logging.
Dispatch: This involves sending operations to Rust, the underlying programming language Deno is built with, and processing the responses efficiently.
Timers: The runtime supports various timer functionalities such as timeout and interval, enabling scheduling and coordination in applications.
Workers: Deno's runtime comprehends the intricacies of the web worker lifecycle and facilitates communication between parent and child workers.
I/O Operations: Input and output operations like copying, reading, and writing files are handled smoothly.
Buffer Management: Efficient handling and manipulation of buffers, used for various data operations.
Websockets: Support for websockets enables real-time, bidirectional communication between clients and servers.
File Operations: Deno's runtime manages standard I/O streams like stdout and stdin, as well as operations like creating, opening, seeking, and manipulating files.
File System Operations: The runtime encompasses a plethora of file system tasks like changing directories, modifying permissions and ownership, copying files, reading directories, and more.
Metrics: Facilities for gathering various metrics to monitor and optimize Deno's performance.
Networking (Net): Deno's runtime facilitates network-related tasks like connecting, listening, and working with datagrams.
Operating System Information: Accessing essential information about the operating system, including release, memory, CPU, and load averages.
TypeScript Compiler: A TypeScript compiler is available within the user space, enabling seamless TypeScript development.
File Watcher: Deno's runtime includes a file watcher, which can monitor changes in files and trigger appropriate actions.
Permissions Management: This involves querying, revoking, and requesting permissions required for specific actions.
Process Control: The runtime handles processes, including running and killing them.
TLS (Transport Layer Security): Facilities for starting, listening, and connecting with secure TLS connections.
User Prompts: Interaction with users is facilitated through prompts like alerts, confirmations, and input queries.
The JS Runtime API offers a wide range of functionalities, enabling developers to build robust and feature-rich applications. Notably, the JS runtime automates various complex operations, such as managing interactions with Deno, thereby simplifying the development process and providing a seamless user experience. This automation eliminates the need for developers to have in-depth knowledge of low-level operations, making it easier to build applications. The JS Runtime API plays a crucial role in abstracting away underlying complexities, allowing developers to focus on building applications without worrying about intricate details.
Operations (OPs) refer to functionalities that extend beyond the ECMAScript specification. The V8 engine operates within a confined sandbox, strictly adhering to ECMAScript standards. However, the runtime environment manages additional services like networking, file input/output, timers, and more.
Exploring the world of OPs is a significant topic that warrants a dedicated section. In the next segment, we will thoroughly examine OPs, illuminating their intricacies and importance within Deno's functionality. This upcoming section will provide a detailed understanding of OPs, their role in Deno, and their impact on the runtime environment.
"Ext" refers to external APIs, a vital component of Deno's functionality. These external APIs consist of modules, each with a specific purpose. Two key components make up these modules:
JS APIs accessible from user space: These are JavaScript APIs designed to be called directly from your code. They provide a bridge between your application's logic and Deno's capabilities. You can use these JS APIs to interact with various functionalities offered by Deno, simplifying the process of utilizing Deno's features in your applications.
OPs supporting the JS APIs: Operations (OPs) support the JS APIs, executing the operations requested through the JS APIs. They are the behind-the-scenes workers that carry out tasks triggered by your code.
In summary, Deno's "Ext" modules combine accessible JS APIs and underlying OPs, providing a seamless and effective way to interact with external functionalities while maintaining a clear division of responsibilities for better code organization and execution. This combination enables efficient and organized interaction with external capabilities, making it easier to build robust applications.
The "ext" component in Deno comprises a range of modules, each serving a distinct purpose. These modules include "broadcast_channel", "cache", "console", "crypto", "fetch", "ffi", "fs", "http", "io", "kv", "napi", "net", "node", "tls", "url", "web", "webidl", "websocket", and "webstorage". Each module corresponds to a specific set of web APIs, providing various capabilities for that particular web API.
A notable feature of the "ext" component is its extensibility, allowing developers to add new modules without modifying the existing codebase. This flexibility enables developers to enhance Deno's functionality by introducing custom modules tailored to their specific needs.
In essence, the "ext" component serves as a comprehensive toolset, providing convenient access to various web-related functionalities. As technology advances and new web APIs emerge, the extensibility of Deno's "ext" component ensures adaptability and accommodation, fostering a platform for continuous growth and innovation. This modular approach makes sure that new web APIs do not touch the core Deno code.
The Deno core component is a fundamental element within the Deno framework, playing a crucial role in its operation. Notably, this essential component is located outside of Deno's primary repository, in its own dedicated repository. The code that powers this core functionality is housed in this separate repository, highlighting the importance of the Deno core component as a standalone entity.
The core component is the central hub of the Deno runtime, analogous to the heart of a living organism. This foundational framework provides essential functionalities that enable the smooth operation of Deno's runtime code. These functionalities include managing JavaScript APIs, executing JavaScript code segments, orchestrating an event loop for asynchronous operations, interacting with the V8 engine for efficient code execution, facilitating various operations (known as ops), and more.
Notably, the core component is the first module loaded when Deno initializes its runtime environment. As the base of the Deno ecosystem, it sets the stage for the entire runtime's functionality. Additionally, the core component is responsible for loading external modules, which extend Deno's capabilities beyond its core functionalities. These modules enhance Deno's versatility, introducing new tools and features that cater to diverse programming needs.
The following list highlights key functionalities provided by Deno's core framework:
Interfacing with V8: Enabling interaction with the V8 engine for efficient code execution
Data exchange: Facilitating data exchange between V8, Rust, and JavaScript
Executing operations: Executing both synchronous and asynchronous operations from JavaScript in Rust and returning the results
Console logging: Providing console logging capabilities for debugging and monitoring purposes
Maintaining OPs metrics: Tracking and managing metrics for operations (OPs)
Event loop: Orchestrating an event loop for smooth asynchronous operations
Low-level read and write APIs: Offering low-level APIs for reading and writing data
The graph component, as its name suggests, provides essential services to the Deno CLI for building a module graph. This graph is a map of how various modules depend on each other.
The graph component is a Rust crate containing the fundamental code for constructing the module graph, aligning with the Deno CLI's module resolution logic. Additionally, it offers a web assembly interface to the generated code, allowing the constructed logic to be accessed and utilized from JavaScript or TypeScript environments.
This feature expands the module graph's functionality beyond the Deno CLI, enabling its use in diverse contexts. The graph component starts with the main file or main module, loading all dependencies recursively using a simple graph data structure. This structure keeps track of visited modules, avoiding repeated reloading and recompilation.
As we work through a simple program with dependencies, we will gain a deeper understanding of the graph component's inner workings. This exploration will give us a thorough understanding of how it works and why it's important.
The NPM component, as its name suggests, is responsible for managing packages obtained from NPM. This involves finding and gathering the necessary packages, ensuring they are properly installed, and maintaining them over time. The NPM component in Deno serves as a guide, navigating the vast NPM repository, understanding package metadata, and more. However, to keep this book simple, we will not explore the details of NPM packages when discussing Deno's inner workings.
The cache component's code is located in two places: within Deno's CLI code and in a separate crate for external use. Initially developed for Deno CLI, its utility extended to other contexts, allowing consistent cache access. Like Deno CLI, other components like deno_graph, deno_doc, dnt, and emit can interact with and populate the cache using the same methods.
The cache system stores downloaded packages and modules on your computer's disk, acting as a storage space. The main storage location is the DENO_DIR/cache folder.
Here's how it works:
Deno checks the cache for a needed module.
If found, Deno uses the cached version instead of redownloading.
If not found, Deno downloads the module and stores a copy in the cache for future use.
In simple terms, the cache system is a convenient storage area for JavaScript modules, saving them for quick access and avoiding repeated downloads. We'll explore the cache concept further as we work through a program with dependencies.
--
We have now discussed the components that are native to Deno, not relying on third-party sources. Next, we will examine the components that come from external sources. However, before we do that, we will take a closer look at the Operations (OPs) to gain a better understanding of their role in Deno's architecture.