fluidtrends/slana

View on GitHub
src/parse.ts

Summary

Maintainability
A
0 mins
Test Coverage
import out from "chalk"
import yargs from "yargs"
import path from "path"
import fs from "fs-extra"

import {
    Option,
    Command,
    Inventory,
    Example
} from '.'

var exampleCounter = 1

export function parseCommandOptionsAndExamples(command: any, cli: any) {    
    var options: any = {}
    var groupName =  out.bold.green(command.name);
    
    if (command.more) {
        groupName =  groupName + "\n\n" + " ".repeat(3) + out.grey(command.more);
    }

    groupName =  groupName + "\n";

    if (command.options) {
        command.options.forEach((option: Option) => {
            if (option.name) {
                options[option.name] = {
                    name: option.name,
                    default: option.default,
                    describe: out.grey(option.description),
                    group: groupName,
                    type: option.type
                } as Option
                cli.option(option.name, options[option.name]); 
            }
        });
    }

    if (command.examples && cli.argv._.length > 0 && cli.argv._[0] === 'help') {
        if (command.options) {
            cli.option("-".repeat(12), {
                describe: "",
                group: groupName
            }); 
        }

        var counter = 1
        command.examples.forEach((example: Example) => {
            if (example.command) {
                cli.option("➔ " + out.yellow("Example " + exampleCounter + "." + counter + ":") + "\n", {
                    describe:  out.green(example.command) + "\n ↳ "  + out.grey(example.description || "") ,
                    group: groupName
                });
                counter = counter + 1;
            }
        });

        exampleCounter = exampleCounter + 1;
    }

    return options;
}

export function extractCommand(cli?: any, inventory?: Inventory) {
    // Actually do the hard work and parse the command line
    cli = cli.argv;

    if (cli._.length === 0) {
        // Looks like we did not specify a command so let"s
        // just bail out with the usage message
        yargs.showHelp();
        return;
    }

    // We have successfully parsed the command line, so now let"s construct a command;
    // first keep track of the command type and we assume no options to start with
    var command = {
        name: cli._[0],
        options: {}
    } as any;

    if (command.name === 'help') {
        yargs.showHelp();
        return;
    }

    // We want to make sure that the command we've just parsed is one we support,
    // so to do that we"re going to look through our inventory and see if we can find it
    var unknown = true;
    inventory?.commands.forEach((cmd) => {
        var cmdName = cmd.name.split(' ')[0];
        (cmdName === command.name) ? unknown = false : null
    });

    if (unknown) {
        // If this command is one we don"t support, let"s just stop now with a nice
        // error message, but with the usage as well
        yargs.showHelp();
        throw new Error(`Sorry, the ${out.red.bold(command.name)} command is not supported.`);
    }

    // We've now got ourselves a parsed command that we support so let's prepare it
    // for execution
    inventory?.commands.forEach((cmd) => {
        var cmdName = cmd.name.split(' ')[0];
        cmdName === command.name && cmd.options &&

            // so first, let"s fill the command with all its expected options
            cmd.options.forEach((option: Option) => command.options[option.name] = cli[option.name])
    });

    // This command should now be ready for further manipulation
    return command;
}

export function parseCommand(command: Command, cli: any, dir: string) {
    if (!command.executor) {
        // We need a command executor, otherwise, there"s nothing to execute
        throw new Error(`Make sure the ${out.green(command.name)} command has a valid command executor`);
    }

    // We will parse the command into this temporary structure and we start off
    // with the values from the inventory
    var cmd = {
        name: command.name,
        description: command.description,
        options: {}
    } as any

    try {
        // Attempt to resolve the executor
        cmd.executor = require(path.join(dir, command.executor)).default;

        if (typeof cmd.executor != "function") {
            // We need an executor function, not something else (ie. an object)
            throw new Error();
        }
    } catch (e) {
        // Looks like the executor could not be resolved
        throw new Error(`Make sure the ${out.green(command.name)} command has a valid command executor`);
    }

    // Looks like this command has options, so let"s parse them
    Object.assign(cmd.options, parseCommandOptionsAndExamples(command, cli));

    // We"ve got ourselves a parsed command, ready for processing
    return cmd;
}