Skip to content

A nodeJs browser library for emulating a terminal, with command and argument parsing

License

Notifications You must be signed in to change notification settings

paolo-projects/terminal-emulator

Repository files navigation

Terminal Emulator

A nodeJs browser library for emulating a terminal, with command and argument parsing, and definition of schemes with callbacks.

Guide

To build your terminal-like interface for web or nodejs based applications, you start from the TerminalEmulator class.

You create an instance of it and set it up with the necessary parameters.

Another important player is the OutputStream class. You have to create your own subclass implementing two methods:

  • abstract write(message: string): void;
  • abstract clear(): void;

The implementation of these methods depends upon whether you're using an HTML div element, a plain string, a console for output, etc.

After you istantiated the class you provide command schemes to it.

A command scheme can be added through the .command(...) method on the emulator class and by providing a combination of command name, parameter types and a callback, you define a scheme.

After you defined your schemes with your callbacks, you then call the .parse(string) method on the emulator, and it will tokenize the command line, parse the tokens and assign them to the appropriate category (command name, positional argument, flag argument, value argument). It will then check the schemes and when a match is found it will call the callback defined into the scheme, which is defined like so:

type CommandSchemeMatchCallback = (
    command: string,
    args: ArgumentList,
    outputStream: OutputStream
) => Promise<boolean>;

The callback returns a promise to support async operations. This is needed when a command executes an HTTP request, or file I/O, to correctly signal the end of the operation to the caller. For instance, it is needed when you want to disable the terminal input while the command is executing.

An example

This is an example OutputStream subclass that prints to the debug console.

class ConsoleOutputStream extends OutputStream {
    constructor() {
        super(''); // No end of line character as console.log prints a newline by default
    }
    write(message: string): void {
        console.log(message);
    }
    clear(): void {
        console.clear();
    }
}

You then istantiate the TerminalEmulator class and set it up with the OutputStream istance.

const stream = new ConsoleOutputStream();
const emulator = new TerminalEmulator(stream, new DefaultLocalization());

The localization parameter object consists of the strings to support localization of the error messages shown to the user.

Now you can start adding schemes to the emulator instance.

const callback = async (
    command: string,
    args: ArgumentList,
    outputStream: OutputStream
) => {
    const name = args.getArgument('name', ValueArgument<string>)?.value;
    outputStream.writeLine(`Hello, ${name}`);

    return true;
};

emulator.command('hello')
    .withValueArgument('name')
    .callback(callback);

You then parse your terminal input.

const cmdLine = 'hello --name John';
emulator.parse(cmdLine);

Output:

> hello --name John
  Hello, John

Argument types

The supported argument types are:

  • Positional arguments

    Positional arguments don't require a flag indicating the parameter name, you just place them next to the command like this:

    command arg1 arg2 arg3 ...

    .command('hello').withPositionalArgument();
  • Flag arguments

    Flag arguments are like a switch. When added to the command, the application knows that something has to be done. Example:

    command --arg

    .command('hello').withFlagArgument('arg');
  • Value arguments

    Value arguments are key/value type arguments. They're essentially a flag argument followed by a positional argument. Equivalent to options passed to the application, they specify how something has to be done, like in this example:

    bake --food bread --temperature high

    .command('hello').withValueArgument('food');

Help message

An help message is displayed every time a command is invoked with the --help flag argument. If more schemes sharing the same command are added, make sure you add the help message, through the .withHelpMessage(message) function on the builder class, to only one of the entries. Otherwise only the first entry with the help message will be displayed.

The help text will also be displayed when the command is invoked with wrong arguments.

About

A nodeJs browser library for emulating a terminal, with command and argument parsing

Resources

License

Stars

Watchers

Forks

Packages