Welcome back! In the previous chapter, Command Registry Definition, we created the "Menu" for our restaurant. We told the application that a command named hooks exists, but we didn't actually bake the lasagna yet.
Now, we are going to learn how to deliver that order to the kitchen. This concept is called Dynamic Command Loading.
Imagine you own a car repair workshop. You fix all kinds of cars: Toyotas, Fords, Ferraris.
The Problem: If you kept every spare part for every car model in your main lobby, you wouldn't be able to walk! It would be cluttered, heavy, and slow to navigate.
The Solution: You keep the lobby empty and clean. You have a direct phone line to a huge warehouse.
In programming, this is called "Lazy Loading". We don't load the heavy code for the hooks command until the user actually types hooks in their terminal. This keeps our CLI tool starting up lightning fast.
In our index.ts from the previous chapter, we wrote a specific line of code that acts as our "phone line" to the warehouse.
Input: index.ts (Review)
const hooks = {
name: 'hooks',
// ... other metadata
// This is the dynamic loader!
load: () => import('./hooks.js'),
} satisfies Command
Explanation:
import(...): This is a special JavaScript function. Unlike the standard import X from Y at the top of a file (which happens immediately), import() acts like a function call.() => ...: We wrap it in an arrow function so it doesn't run yet. It waits until we call it.Now, let's create the file that sits in the warehouse. This is the file that gets loaded when the phone rings.
We will create a file named hooks.tsx (or .js). This contains the actual code that runs.
Input: hooks.tsx
import React from 'react';
import { Text } from 'ink';
// This is the heavy component we avoided loading earlier
export default function HooksCommand() {
return (
<Text>
Displaying all hook configurations...
</Text>
);
}
Explanation:
export default: This is crucial. When the dynamic importer grabs this file, it looks for the default export to know what to run.hooks?How does the main application go from seeing the menu to serving the dish? Let's walk through the process step-by-step.
my-tool hooks in the terminal.hooks.load() function we defined.hooks.js, and compiles it.Here is a diagram of the flow:
Let's look at a simplified version of the code inside the Main Application that handles this magic.
Input: cli-runner.ts (Simplified)
// Assume we found the 'command' object from the registry
async function runCommand(command: Command) {
console.log("Loading module...");
// 1. CALL THE WAREHOUSE
// We await because reading the file takes a few milliseconds
const module = await command.load();
// 2. USE THE PART
// We access the 'default' export we defined earlier
const LogicComponent = module.default;
// 3. START THE ENGINE
// render() handles the UI (See Chapter 3)
render(LogicComponent);
}
Explanation:
async / await: Dynamic loading is asynchronous (it takes time). We must await the result of command.load().module.default: When you import a file dynamically, you get an object containing all exports. Since we used export default in our hooks.tsx, we access it here.This dynamic loading pattern is the bridge between two other major concepts in our system:
In this chapter, we implemented a "Just-In-Time" delivery system for our code.
() => import(...) to delay loading.hooks.tsx).awaits the file loading before execution.
Now that we have successfully loaded our file, we are looking at a block of code that looks like HTML (<Text>...</Text>). How does a command line tool understand that?
In the next chapter, we will explore the technology that allows us to write UI components for the terminal.
Generated by Code IQ