๐Ÿ“ commands/keybindings/ ยท 05_lazy_module_loading.md

Chapter 5: Lazy Module Loading

๐Ÿ“„ commands/keybindings/05_lazy_module_loading.md

Chapter 5: Lazy Module Loading

In the previous chapter, External Editor Delegation, we learned how to pause our CLI to let the user edit files in their favorite text editor.

We have built a robust command. However, imagine if our CLI tool grew from having just 1 command to having 100 commands. If we loaded the code for all 100 commands every time the user typed a single letter, our application would be incredibly slow to start.

In this final chapter, we will explore Lazy Module Loading. This is a performance pattern that keeps your application fast, no matter how big it grows.

The Librarian Analogy

Imagine a library with thousands of books.

  1. The Eager Librarian (Bad): When you walk in, the librarian tries to carry every single book in their arms, just in case you ask for one. They move slowly, stumble, and are exhausted before you even speak.
  2. The Lazy Librarian (Good): The librarian sits at a desk with a small box of Index Cards.

In our code:

The Use Case

We want the CLI to start instantly.


Concept: Dynamic Imports

In JavaScript/TypeScript, there are two ways to import code.

1. Static Import (The "Carry Everything" approach)

This happens at the very top of a file. The computer loads this file immediately when the program starts.

// โŒ Don't do this for heavy commands
import { call } from './keybindings.js' 

2. Dynamic Import (The "Fetch Later" approach)

This is a function that you call later. It tells the computer: "Please go get this file now."

// โœ… Do this inside a function
const loadCommand = () => import('./keybindings.js')

Implementation: The load Property

Let's look at our menu file, index.ts. This is the only place we need to change to enable Lazy Loading.

We use a specific property called load.

// --- File: index.ts ---
const keybindings = {
  name: 'keybindings',
  description: 'Open configuration file',
  
  // The Lazy Load Magic:
  load: () => import('./keybindings.js'), 
  
} satisfies Command

Explanation:

Why is this faster?

When the CLI starts up:

  1. It reads index.ts.
  2. It sees the load function, but it does not run it.
  3. It skips over the heavy keybindings.js file entirely.
  4. The CLI is ready to accept input in milliseconds.

Under the Hood: The Sequence

Let's visualize the difference between the startup phase and the execution phase.

sequenceDiagram participant User participant CLI as CLI Main participant Index as index.ts (Card) participant Logic as keybindings.ts (Book) Note over User, Logic: Phase 1: Startup (Fast) User->>CLI: Starts Application CLI->>Index: Read Metadata Index-->>CLI: Returns Name & Description Note over CLI: CLI is ready! (Logic file is NOT touched) Note over User, Logic: Phase 2: Execution User->>CLI: Run "keybindings" CLI->>Index: Execute load() function Index->>Logic: Dynamic Import... Logic-->>CLI: Module Loaded into Memory CLI->>Logic: Run call() function

Deep Dive: How the Framework uses it

You might be wondering: How does the main CLI know how to use this?

While we don't write the framework code in this tutorial, here is a simplified example of what the CLI framework does behind the scenes when a user types a command.

// --- Pseudo-code for the Main CLI Runner ---

async function runCommand(commandName: string) {
  // 1. Find the command definition (The Index Card)
  const commandDef = allCommands.find(c => c.name === commandName)

  // 2. NOW we call the load function (Walk to the shelf)
  console.log("Loading module...")
  const module = await commandDef.load()

  // 3. Run the logic (Read the book)
  await module.call() 
}

Explanation:


Conclusion & Series Wrap-Up

Congratulations! You have completed the Keybindings Command Tutorial.

Throughout these five chapters, you have built a professional-grade CLI command using best practices:

  1. Command Module Structure: You organized code by separating Metadata (index.ts) from Logic (keybindings.ts).
  2. Feature Gating: You learned to control access using isEnabled checks, hiding unfinished features from users.
  3. Safe Resource Initialization: You used the wx flag to safely create files without overwriting existing user data.
  4. External Editor Delegation: You learned to use spawn to hand off control to tools like Vim or VS Code.
  5. Lazy Module Loading: You optimized performance by using Dynamic Imports to load code only when needed.

By combining these five patterns, you have created a tool that is safe, fast, and respectful of the user's environment. You are now ready to build complex CLI tools that can scale to hundreds of commands without slowing down.

Happy coding!


Generated by Code IQ