Have ideas to improve npm?Join in the discussion! »

    ts-command-line-args
    TypeScript icon, indicating that this package has built-in type declarations

    2.0.1 • Public • Published

    ts-command-line-args

    A Typescript wrapper around command-line-args with additional support for markdown usage guide generation

    npm Build Status NPM Typescript codecov

    Features

    • Converts command line arguments:
    $ myExampleCli --sourcePath=pathOne --targetPath=pathTwo

    into strongly typed objects:

    { sourcePath: "pathOne", targetPath: "pathTwo"}
    • Support for optional values, multiple values, aliases
    • Prints a useful error message and exits the process if required arguments are missing:
    $ myExampleCli
    Required parameter 'sourcePath' was not passed. Please provide a value by running 'myExampleCli --sourcePath=passedValue'
    Required parameter 'targetPath' was not passed. Please provide a value by running 'myExampleCli --targetPath=passedValue'
    To view the help guide run 'myExampleCli -h'
    
    • Supports printing usage guides to the console with a help argument:
    $ myExampleCli -h
    My Example CLI
    
      Thanks for using Our Awesome Library
    
    Options
      --sourcePath string
      --targetPath string
      -h, --help                Prints this usage guide
    
    • Supports writing the same usage guide to markdown (see Markdown Generation section below, generated by write-markdown)

    Usage

    Take a typescript interface (or type or class):

    interface ICopyFilesArguments{
       sourcePath: string;
       targetPath: string;
       copyFiles: boolean;
       resetPermissions: boolean;
       filter?: string;
       excludePaths?: string[];
    }

    and use this to enforce the correct generation of options for command-line-args:

    import { parse } from 'ts-command-line-args';
    
    // args typed as ICopyFilesArguments
    export const args = parse<ICopyFilesArguments>({
        sourcePath: String,
        targetPath: String,
        copyFiles: { type: Boolean, alias: 'c' },
        resetPermissions: Boolean,
        filter: { type: String, optional: true },
        excludePaths: { type: String, multiple: true, optional: true },
    });

    With the above setup these commands will all work

    $ node exampleConfig.js --sourcePath=source --targetPath=target
    $ node exampleConfig.js --sourcePath source --targetPath target --copyFiles
    $ node exampleConfig.js --sourcePath source --targetPath target -c
    $ node exampleConfig.js --sourcePath source --targetPath target --filter src --excludePaths=one --excludePaths=two
    $ node exampleConfig.js --sourcePath source --targetPath target --filter src --excludePaths one two

    Usage Guide

    command-line-usage support is included:

    import { parse } from 'ts-command-line-args';
    
    interface ICopyFilesArguments {
        sourcePath: string;
        targetPath: string;
        copyFiles: boolean;
        resetPermissions: boolean;
        filter?: string;
        excludePaths?: string[];
        help?: boolean;
    }
    
    export const args = parse<ICopyFilesArguments>(
        {
            sourcePath: String,
            targetPath: String,
            copyFiles: { type: Boolean, alias: 'c', description: 'Copies files rather than moves them' },
            resetPermissions: Boolean,
            filter: { type: String, optional: true },
            excludePaths: { type: String, multiple: true, optional: true },
            help: { type: Boolean, optional: true, alias: 'h', description: 'Prints this usage guide' },
        },
        {
            helpArg: 'help',
            headerContentSections: [{ header: 'My Example Config', content: 'Thanks for using Our Awesome Library' }],
            footerContentSections: [{ header: 'Footer', content: `Copyright: Big Faceless Corp. inc.` }],
        },
    );

    with the above config these commands:

    $ node exampleConfigWithHelp.js -h
    $ node exampleConfigWithHelp.js --help

    will give the following output:

    My Example Config
    
      Thanks for using Our Awesome Library
    
    Options
    
      --sourcePath string
      --targetPath string
      -c, --copyFiles           Copies files rather than moves them
      --resetPermissions
      --filter string
      --excludePaths string[]
      -h, --help                Prints this usage guide
    
    Footer
    
      Copyright: Big Faceless Corp. inc.
    

    Loading Config from File

    The arguments for an application can also be defined in a config file. The following config allows this:

    interface ICopyFilesArguments {
        sourcePath: string;
        targetPath: string;
        copyFiles: boolean;
        resetPermissions: boolean;
        filter?: string;
        excludePaths?: string[];
        configFile?: string;
        jsonPath?: string;
    }
    
    export const args = parse<ICopyFilesArguments>(
        {
            sourcePath: String,
            targetPath: String,
            copyFiles: { type: Boolean },
            resetPermissions: Boolean,
            filter: { type: String, optional: true },
            excludePaths: { type: String, multiple: true, optional: true },
            configFile: { type: String, optional: true },
            jsonPath: { type: String, optional: true },
        },
        {
            loadFromFileArg: 'configFile',
            loadFromFileJsonPathArg: 'jsonPath',
        },
    );

    With this configuration the same command line arguments can be passed:

    $ node exampleConfig.js --sourcePath source --targetPath target

    but the user of the script can use a config file instead:

    package.json (for example)

    {
        "name": "myPackage",
        "version": "0.1.1",
        "copyFileConfig": {
            "copyFileOne": {
                "sourcePath": "source",
                "targetPath": "target",
                "copyFiles": true,
                "resetPermissions": false,
                "excludePaths": ["one", "two", "three"]
            }
        }
    }
    $ node exampleConfig.js --configFile package.json --jsonPath copyFileConfig.copyFileOne

    Any params passed on the command line will ovverride those defined in the file:

    $ node exampleConfig.js --configFile package.json --jsonPath copyFileConfig.copyFileOne --resetPermissions

    Boolean values in a file can be overridden by just specfying the argName for true specifying the value:

    $ myCommand --booleanOne --booleanTwo=false --booleanThree false -b=false -o=true --booleanFour=1

    When defining settings in a json file the values are still passed through the type function defined in your config so for this setup:

    interface ISampleConfig {
        startDate: Date;
        endDate: Date;
        inclusive: boolean;
        includeStart: boolean;
        includeEnd: boolean;
    }
    
    function parseDate(value: any) {
        return new Date(Date.parse(value));
    }
    
    export const args = parse<ISampleConfig>({
        startDate: { type: parseDate },
        endDate: { type: parseDate },
        initialCount: Number,
        endCount: Number,
        inclusive: Boolean,
        includeStart: Boolean,
        includeEnd: Boolean,
    });

    This settings file would be correctly parsed:

    {
        "startDate": "01/01/2020",
        "endDate": "00010201T000000Z",
        "initialCount": 1,
        "endCount": "2",
        "inclusive": true,
        "includeStart": "false",
        "includeEnd": 1,
    }

    Markdown Generation

    A markdown version of the usage guide can be generated and inserted into an existing markdown document.
    Markers in the document describe where the content should be inserted, existing content betweeen the markers is overwritten.

    write-markdown -m README.MD -j usageGuideConstants.js

    write-markdown cli options

    Argument Alias Type Description
    markdownPath m string The file to write to. Without replacement markers the whole file content will be replaced. Path can be absolute or relative.
    replaceBelow string A marker in the file to replace text below.
    replaceAbove string A marker in the file to replace text above.
    jsFile j string[] jsFile to 'require' that has an export with the 'UsageGuideConfig' export. Multiple file can be specified.
    configImportName c string[] Export name of the 'UsageGuideConfig' object. Defaults to 'usageGuideInfo'. Multiple exports can be specified.
    verify v boolean Verify the markdown file. Does not update the file but returns a non zero exit code if the markdown file is not correct. Useful for a pre-publish script.
    configFile f string Optional config file to load config from. package.json can be used if jsonPath specified as well
    jsonPath p string Used in conjunction with 'configFile'. The path within the config file to load the config from. For example: 'configs.writeMarkdown'
    verifyMessage string Optional message that is printed when markdown verification fails. Use '{fileName}' to refer to the file being processed.
    removeDoubleBlankLines boolean When replacing content removes any more than a single blank line
    skipFooter boolean Does not add the 'Markdown Generated by...' footer to the end of the markdown
    help h boolean Show this usage guide.

    Default Replacement Markers

    replaceBelow defaults to:

    '[//]: ####ts-command-line-args_write-markdown_replaceBelow'  
    

    replaceAbove defaults to:

    '[//]: ####ts-command-line-args_write-markdown_replaceAbove'  
    

    String Formatting

    The only chalk modifiers supported when converting to markdown are bold and italic.
    For example:

    {bold bold text} {italic italic text} {italic.bold bold italic text}  
    

    will be converted to:

    **boldText** *italic text* ***bold italic text***  
    

    Additional Modifiers

    Two additional style modifiers have been added that are supported when writing markdown. They are removed when printing to the console.

    {highlight someText}  
    

    surrounds the text in backticks:
    someText
    and

    {code.typescript function(message: string)\\{console.log(message);\\}}  
    

    Surrounds the text in triple back ticks (with an optional language specifer, in this case typescript):

    function(message: string){console.log(message);}  

    (the Markdown Generation section above was generated using write-markdown )

    Javascript Exports

    To generate markdown we must export the argument definitions and options used by command-line-usage so that we can require the javascript file and import the definitions when running write-markdown. We cannot export these values from a javascript file that has any side effects (such as copying files in the case of a copy-files node executable) as the same side effects would be executed when we are just trying to generate markdown.

    For example, if we had a copy-files application you would organise you code like this:

    copy-file.constants.ts:

    export interface ICopyFilesArguments {
        sourcePath: string;
        targetPath: string;
        help?: boolean;
    }
    
    const argumentConfig: ArgumentConfig<ICopyFilesArguments> = {
        sourcePath: String,
        targetPath: String,
        help: { type: Boolean, optional: true, alias: 'h', description: 'Prints this usage guide' },
    };
    
    const parseOptions: ParseOptions<ICopyFilesArguments> = {
            helpArg: 'help',
            headerContentSections: [{ header: 'copy-files', content: 'Copies files from sourcePath to targetPath' }],
        }
    
    export const usageGuideInfo: UsageGuideConfig<ICopyFilesArguments> = {
        arguments: argumentConfig,
        parseOptions,
    };

    copy-file.ts:

    // The file that actually does the work and is executed by node to copy files
    import { usageGuideInfo } from "./copy-file-constants"
    
    const args: ICopyFilesArguments = parse(usageGuideInfo.arguments, usageGuideInfo.parseOptions);
    
    //  Perform file copy operations

    The usage guide would be displayed on the command line with the following command:

    $ copy-files -h

    and markdown would be generated (after typescript had been transpiled into javascript) with:

    $ write-markdown -m markdownFile.md -j copy-file.constants.js

    Documentation

    This library is a wrapper around command-line-args so any docs or options for that library should apply to this library as well.

    Parse

    function parse<T>(config: ArgumentConfig<T>, options: ParseOptions = {}, exitProcess = true): T

    parse will return an object containing all of your command line options. For example with this config:

    import {parse} from 'ts-command-line-args';
    
    export const args = parse<ICopyFilesArguments>({
        sourcePath: String,
        targetPath: { type: String, alias: 't' },
        copyFiles: { type: Boolean, alias: 'c' },
        resetPermissions: Boolean,
        filter: { type: String, optional: true },
        excludePaths: { type: String, multiple: true, optional: true },
    });

    and this command:

    $ node exampleConfig.js --sourcePath mySource --targetPath myTarget

    the following object will be returned:

    {
        "sourcePath":"mySource",
        "targetPath":"myTarget",
        "copyFiles":false,
        "resetPermissions":false
    }

    (booleans are defaulted to false unless they are marked as optional)

    If any required options are omitted (in this case just sourcePath and targetPath) then an error message will be logged and the process will exit with no further code will be executed after the call to parse:

    $ node exampleConfigWithHelp.js
    Required parameter 'sourcePath' was not passed. Please provide a value by passing '--sourcePath=passedValue' in command line arguments
    Required parameter 'targetPath' was not passed. Please provide a value by passing '--targetPath=passedValue' or '-t passedValue' in command line arguments

    If you do not want the process to exit that this can be disabled by passing false as the last argument:

    import {parse} from 'ts-command-line-args';
    
    export const args = parse<ICopyFilesArguments>({
        sourcePath: String,
        targetPath: { type: String, alias: 't' },
        copyFiles: { type: Boolean, alias: 'c' },
        resetPermissions: Boolean,
        filter: { type: String, optional: true },
        excludePaths: { type: String, multiple: true, optional: true },
    },
    {}, // empty options object
    false
    );

    In this case errors will still be logged to the console but the process will not exit and code execution will continue after the call to parse.

    Option Definitions

    Option definitions must be passed to parse. For the most part option definitions are the same as OptionDefinition from command-line-args except that name is not required. Name is not required as we pass an object:

    {
        propertyName: {}
    }

    rather than an array of options:

    [
        { name: "propertyName" }
    ]

    Simple Arguments

    For a simple, single, required argument you only need to define the type:

    parse<IMyInterface>({
        stringArg: String, 
        numberArg: Number,
        });

    the type can be any function with the signature (value?: string) => T | undefined. The javascript provided functions String, Number and Boolean all do this for simple data types but a function could be written to convert a passed in string value to a Date for example:

    function parseDate(value?: string) {
        return value ? new Date(Date.parse(value)) : undefined;
    }
    
    parse<IMyInterface>({ myDate: parseDate });

    A similar function could be written for any complex type.

    Further Configuration

    For anything other than a single, required, simple argument a configuration object must be defined for each argument. This call:

    parse<IMyInterface>({
        stringArg: String, 
        numberArg: Number
        });

    and this:

    parse<IMyInterface>({
        stringArg: { type: String }, 
        numberArg: { type: Number },
        });

    are identical.

    Optional Arguments

    If an argument is optional it must be defined as such to avoid console errors being logged and the process exiting when parse is called.

    This interface:

    {
        requiredArg: string,
        optionalArg?: string,
    }

    defines optionalArg as optional. This must be reflected in the config passed to parse:

    parse<IMyInterface>({
        requiredArg: String, 
        requiredArg: { type: String, optional: true },
        });

    Typescript compilation will fail in the above case without optional: true being added.

    Multiple Values

    If multiple values can be passed then the argument should be defined as an array:

    {
        users: string[],
    }

    and it must defined as multiple in the config:

    parse<IMyInterface>({
        users: {requiredArg: Number, multiple: true},
        });

    Typescript compilation will fail in the above case without multiple: true being added.

    Multiple values can be passed on the command line as follows:

    $ node myApp.js users=Jeoff users=Frank users=Dave
    $ node myApp.js users Jeoff users Frank users Dave
    $ node myApp.js users=Jeoff Frank Dave
    $ node myApp.js users Jeoff Frank Dave

    For further Option Definition documentation refer to these docs.

    Options

    Most of the available options are the same as the options defined by command-line-args: https://github.com/75lb/command-line-args/blob/master/doc/API.md.

    A few additional options have been added:

    logger - used for logging errors or help guide to the console. Defaults to console.log and console.error.

    helpArg - used to defined the argument used to generate the usage guide. This is expected to be boolean but the comparison is not strict so any argument type / value that is resolved as truthy will work.

    headerContentSections / footerContentSections - optional help sections that will appear before / after the Options section that is generated from the option config. In most cases you should probably include one header section that explains what the application does.

    Markdown Generated by ts-command-line-args

    Install

    npm i ts-command-line-args

    DownloadsWeekly Downloads

    3,693

    Version

    2.0.1

    License

    ISC

    Unpacked Size

    604 kB

    Total Files

    79

    Last publish

    Collaborators

    • avatar