Welcome to Chapter 2! In the previous chapter, Command Definition, we created a menu entry for our heapdump command. We told the system what the command is, but we didn't actually load the heavy code required to run it.
In this chapter, we will learn how to load that code efficiently using a concept called Lazy Module Loading.
Imagine a massive library with thousands of books.
Our heapdump tool is like a very heavy encyclopedia. It requires complex logic to snapshot the computer's memory.
If a user just wants to check the version of our CLI (myapp --version), we shouldn't waste time or computer memory loading the heavy heapdump code. We should only load it when the user explicitly types myapp heapdump.
In JavaScript and TypeScript, there are two ways to bring code from one file to another.
This is what you usually see at the top of a file. It runs immediately when the application starts.
// The "Eager" way
import { runHeapDump } from './heapdump.js'
// The code above runs INSTANTLY when the app starts.
// This slows down startup time.
This is a function that imports the code only when it is called.
// The "Lazy" way
const loadMyCode = () => import('./heapdump.js')
// The file is NOT read yet.
// It is only read when we call loadMyCode().
For our project, we exclusively use Dynamic Imports for commands.
When you ask the librarian to fetch a book from the archive, it isn't instantaneous. You have to wait a few seconds.
In code, this "waiting" is represented by a Promise.
When we call import(), it returns a Promise that eventually resolves to the code module.
Let's revisit the index.ts file we looked at in Chapter 1. We solve the performance problem by defining a load function that uses a dynamic import.
// From index.ts
const heapDump = {
// ... other properties ...
load: () => import('./heapdump.js'),
}
What is happening here?
() => ....import('./heapdump.js').load property.
By doing this, the file ./heapdump.js is ignored during startup. It sits quietly on the disk until the application decides it is time to execute the command.
How does the application actually use this? Let's look at the flow when a user actually types the command.
The application acts as a "Router." It matches the user's text input to a command definition, and then triggers the load.
While we don't need to write the Router logic ourselves (it's part of the framework), understanding it helps us write better commands.
Here is a simplified version of what the Router does when it receives the command:
// Simplified Router Logic
async function handleCommand(commandDef: Command) {
console.log("User selected: " + commandDef.name)
// 1. Trigger the lazy load
const module = await commandDef.load()
// 2. The module is now loaded!
console.log("Code loaded from file.")
}
Explanation:
async/await: Because loading a file takes time (remember the Promise?), the Router awaits the result.const module: This variable now holds the contents of ./heapdump.js.
The file we are loading (heapdump.js or heapdump.ts) acts as the "Service Layer." We will discuss exactly what goes inside this file in the next chapter. For now, just know that Lazy Module Loading is the bridge that connects the Definition to the Execution.
In this chapter, we learned about Lazy Module Loading.
() => import(...) syntax to create a loading function.Now that we have successfully loaded the module, what do we do with it? The system needs a standard way to run the code we just fetched.
Next Chapter: Command Execution Handler
Generated by Code IQ