Welcome back! In the previous chapter, Local JSX Execution Interface, we built the "Stage Director" (the call function) for our skills command. We saw that our component needed a list of commands to display, and we passed them like this:
commands={context.options.commands}
But we never explained where context came from or how the commands got inside it.
In this chapter, we will explore Context Dependency Injection. This is the mechanism that delivers data to your command safely and cleanly.
Imagine you are a contractor hired to fix a specific machine at a large construction site.
In skills.tsx, the context parameter is that red toolbox.
We could just make a global list called ALL_COMMANDS and import it. But what if we want to test the skills command with a fake list? What if the list changes based on user permissions?
By injecting the data via context, our command becomes:
Our skills command has one job: List all other available skills.
However, the skills.tsx file doesn't know about the other commands (like weather or help). That information lives in the Command Registry (which we discussed in Command Registration Pattern).
We need to get that list from the Registry into skills.tsx without connecting them directly.
First, we use TypeScript to define what our "toolbox" looks like. This ensures we don't try to grab a tool that isn't there.
// From ../../commands.js (simplified)
export type LocalJSXCommandContext = {
options: {
// The list of all registered commands
commands: Command[];
// Other settings (like verbose mode, etc)
[key: string]: any;
};
}
Explanation: This definition tells us that context will always have an options property, and inside that, there will be a list of commands.
Now, let's look at our call function in skills.tsx again. Notice the second parameter.
// skills.tsx
export async function call(
onDone: LocalJSXCommandOnDone,
context: LocalJSXCommandContext // <--- Here is the toolbox!
) {
// ...
}
Explanation:
context object for us.Now that we have the box, we simply open it and use the data.
// Inside the call function
const commandList = context.options.commands;
// Pass it to the visual component
return <SkillsMenu commands={commandList} onExit={onDone} />;
Explanation: We access context.options.commands. We don't need to import the Registry. We don't need to fetch anything from a database. The data is just there, ready to use.
How does the data get into the box? The main application acts as the "Site Manager." Before it tells your command to run, it gathers everything you might need.
skills. The System knows this command needs a list of other commands.{ options: { commands: [...] } }.module.call(...) and hands over that object.Here is a simplified view of the code running inside the main application (not in your command file) that performs this magic.
// Simplified System Runner (Pseudo-code)
async function runCommand(commandName: string) {
// 1. Gather dependencies
const allCommands = commandRegistry.getCommands();
// 2. Build the toolbox (Context)
const context = {
options: {
commands: allCommands,
verbose: true // Example of other options
}
};
// 3. Load the module (Lazy Loading from Chapter 2)
const module = await loadCommand(commandName);
// 4. Inject dependencies!
// We pass 'context' into the function here.
await module.call(onDoneCallback, context);
}
Explanation:
context isn't magic. It's just a variable created by the runner and passed to your function.In this chapter, we learned about Context Dependency Injection.
context parameter.call function.Now we have the Code (Chapter 2), the UI (Chapter 3), and the Data (Chapter 4).
But what happens when the user actually interacts with our menu? If they select a command, how do we shut down the current skills command and start the new one? We need to manage the life and death of our command.
We will explore this in the final chapter.
Next Chapter: Lifecycle Flow Control
Generated by Code IQ