Welcome back! In Chapter 2: Environment Feature Flags, we learned how to use environment variables to safely hide or show our command.
Now that we know if the command should exist, we need to decide how to bring its code into memory. This brings us to a critical performance concept called Lazy Module Loading.
The Central Use Case: Imagine you are a carpenter. You have a massive shed filled with 500 different toolsβsaws, drills, hammers, sanders, and heavy machinery.
When you leave your house in the morning to fix a small loose screw, do you put all 500 tools into your backpack?
Of course not! That would be incredibly heavy and slow. instead:
Lazy Module Loading applies this exact logic to software.
If our CLI tool has 50 commands, we don't want to load the computer code for all 50 commands every time the user types help. We want to load the code for the doctor command only when the user actually types doctor.
To understand the code, we need to understand the difference between two ways of importing code in JavaScript.
This is what you usually see at the top of a file. It loads the code immediately, as soon as the application starts.
// This loads the code INSTANTLY, whether we use it or not.
import { heavyLogic } from './heavyFile.js'
This is a function. It doesn't load anything until you actually run the function.
// This loads NOTHING right now.
const loadMyTool = () => import('./heavyFile.js')
// The code is only loaded when we run this:
loadMyTool()
In our doctor project, we use this concept in our command definition. Let's look at index.ts again.
// src/commands/doctor/index.ts
const doctor: Command = {
name: 'doctor',
// ... identity and flags ...
// THE KEY PART:
load: () => import('./doctor.js'),
}
Explanation:
load.() => ... part).import()../doctor.js. This is where the actual heavy logic lives.
Because we wrapped the import in a function, the file ./doctor.js is not read when the application starts. It sits quietly on the hard drive until called upon.
What happens when a user runs the application? Let's trace the steps to see how the system saves memory.
index.ts (the definition). It sees the load function but does not run it. The memory usage is tiny.doctor in the terminal.load() function.doctor.js, and brings it into memory.So, where does the heavy code actually live? It lives in a separate file entirely.
We split our command into two files:
index.ts: The lightweight definition (The Menu Item).doctor.js (or .tsx): The heavy implementation (The Meal).1. The Definition (index.ts) This file is small. It imports nothing heavy.
// index.ts
// Quick to load, minimal dependencies
const doctor = {
name: 'doctor',
load: () => import('./doctor.js'), // Points to the heavy file
}
export default doctor
2. The Implementation (doctor.js) This file might contain thousands of lines of code, huge libraries, and complex logic.
// doctor.js
// This file is NOT loaded until the user asks for it.
// Heavy libraries are imported here
import { heavyDatabaseTool } from 'massive-library'
export default function runDoctor() {
console.log("Running diagnosis...")
// ... lots of complex logic
}
By separating these two, we ensure that if a user only wants to use a different command (like login), they never pay the performance cost of loading the doctor libraries.
In this chapter, we learned that Lazy Module Loading is a performance pattern. It allows us to keep our application startup time fast ("snappy") by only loading the heavy code for a command when it is explicitly requested.
We achieved this by using Dynamic Imports (import()) inside our load function, rather than standard static imports at the top of the file.
Now that we have successfully loaded our heavy code, we need a way to display information to the user. In the next chapter, we will learn about the display engine used by the doctor command.
Next Chapter: Local JSX Command Handler
Generated by Code IQ