Isolated Context
For the core functionality of parsing input and writing to the terminal, command line applications require few external dependencies. They need to be able to write to stdout for console output and stderr for errors. For Stricli, these requirements are encapsulated in the CommandContext type.
It is a simple object that stores a process property that has stdout/stderr writable streams. This context is required when running the app, and is how Stricli prints help text and error messages to the console.
For Node or Node-compatible environments, this is as simple as passing { process } or globalThis to run.
Terminal Output
The provided context is bound to this on the command's implementation function. The global process will still be available, but this indirection allows for injecting alternate implementations without hot-swapping globals. This allows applications to fully test the implementation functions by controlling all of their inputs and dependencies.
Check out the testing section for more information on how to test your commands.
Practically, using the context is not required for implementing a command within a Stricli application. If you don't get benefit from this indirection you can choose to ignore this context completely and use process directly or even log with console.log or console.error.
Application Context
This object serves double duty as the context for the command and the application itself. There are some additional options that control the application's behavior. The first is process.exit() which allows Stricli to set the exit code of the application once the command finishes (or throws an error). The other is locale which is used by the localization logic to determine which language the text should be in.
Custom Data
However, the context type can be customized which opens up some more options via dependency injection. You can define a custom context to store arbitrary data, which will then get passed through to your command.
/// types.ts
interface User {
readonly id: number;
readonly name: string;
}
interface CustomContext extends CommandContext {
readonly user?: User;
}
/// impl.ts
export default function(this: CustomContext) {
if (this.user) {
this.process.stdout.write(`Logged in as ${this.user.name}`);
} else {
this.process.stdout.write(`Not logged in`);
}
}
/// run.ts
const user = ... // load user
await run(app, process.argv.slice(2), { process, user });
In this example, imagine that you store user information in the user's local environment. You can fetch that information and store it in the context for use in any/all of your commands.