All Projects → privatenumber → type-flag

privatenumber / type-flag

Licence: MIT license
⛳️ Typed command-line arguments parser for Node.js

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to type-flag

rationalist
parse argument options in ruby
Stars: ✭ 42 (-60%)
Mutual labels:  argv, minimist
cleye
👁‍🗨 cleye — The intuitive & typed CLI development tool for Node.js
Stars: ✭ 235 (+123.81%)
Mutual labels:  argv, typed
mango
mango is a man-page generator for the Go flag, pflag, cobra, coral, and kong packages
Stars: ✭ 191 (+81.9%)
Mutual labels:  flag
country-emoji
Converts between country names, ISO 3166-1 codes and flag emojis.
Stars: ✭ 81 (-22.86%)
Mutual labels:  flag
yargs-interactive
Interactive support for yargs
Stars: ✭ 40 (-61.9%)
Mutual labels:  yargs
nginx cookie flag module
Module for Nginx which allows to set the flags "HttpOnly", "secure" and "SameSite" for cookies.
Stars: ✭ 101 (-3.81%)
Mutual labels:  flag
go-getoptions
Fully featured Go (golang) command line option parser with built-in auto-completion support.
Stars: ✭ 41 (-60.95%)
Mutual labels:  flag
country-flag-emoji-json
Country flag emojis in JSON format.
Stars: ✭ 92 (-12.38%)
Mutual labels:  flag
typed-prelude
Reliable, standards-oriented software for browsers & Node.
Stars: ✭ 48 (-54.29%)
Mutual labels:  typed
fliegdoc
A documentation generator for Typescript-based libraries with good support for monorepos
Stars: ✭ 24 (-77.14%)
Mutual labels:  yargs
yaclt
Yet Another Change Log Tool
Stars: ✭ 24 (-77.14%)
Mutual labels:  yargs
angular-cli-envvars
Example project for my article "Angular CLI and OS Environment Variables"
Stars: ✭ 56 (-46.67%)
Mutual labels:  yargs
goconf
Configuration loader in Go
Stars: ✭ 23 (-78.1%)
Mutual labels:  flag
argen
Generate argument parsing logic in C from a simple config
Stars: ✭ 14 (-86.67%)
Mutual labels:  argv
Swift Argument Parser
Straightforward, type-safe argument parsing for Swift
Stars: ✭ 2,430 (+2214.29%)
Mutual labels:  flag
django-flag-app
A pluggable django application that adds the ability for users to flag(or report) your models.
Stars: ✭ 13 (-87.62%)
Mutual labels:  flag
minimist2
TypeScript/JavaScript ES6 rewrite of popular Minimist argument parser
Stars: ✭ 20 (-80.95%)
Mutual labels:  yargs
flag
专为命令行爱好者设计,方便写出类似于git或者coreutils中的命令接口(ls, mv, cat),新用户建议使用https://github.com/guonaihong/clop
Stars: ✭ 48 (-54.29%)
Mutual labels:  flag
Country
Country gives a list of countries with all their flags.
Stars: ✭ 26 (-75.24%)
Mutual labels:  flag
Caporal.js
A full-featured framework for building command line applications (cli) with node.js
Stars: ✭ 3,279 (+3022.86%)
Mutual labels:  argv

type-flag

Typed command-line arguments parser. Only 1.4 kB.

Try it out online

Looking for something more robust? 👀

Try Cleye—a CLI development tool powered by type-flag.

In addition to flag parsing, it supports argument parsing and has a beautiful --help documentation generator.

Support this project by ⭐️ starring and sharing it. Follow me to see what other cool projects I'm working on! ❤️

🚀 Install

npm i type-flag

🚦 Quick start

type-flag offers a simple API to parse command-line arguments.

Let's say you want to create a script with the following usage:

$ my-script --name John --age 20

typeFlag

Here's how easy it is with type-flag:

import { typeFlag } from 'type-flag'

const parsed = typeFlag({
    name: String,
    age: {
        type: Number,
        alias: 'a'
    }
})

console.log(parsed.flags.name) // 'John'
console.log(parsed.flags.age) // 20

You can also get unknown flags and arguments from the parsed object:

// object of unknown flags passed in
console.log(parsed.unknownFlags)

// arguments
console.log(parsed._)

getFlag

Want something even simpler?

type-flag also exports a getFlag function that returns a single flag value.

import { getFlag } from 'type-flag'

const name = getFlag('--name', String)
const age = getFlag('-a,--age', Number)

console.log(name) // 'John'
console.log(age) // 20

These are quick demos but type-flag can do so much more:

  • Accept multiple flag values
  • Flag operators (e.g. =) for explicitly passing in a value
  • Parse unknown flags
  • Parse alias groups (e.g. -abc)

Keep reading to learn more!

🧑‍💻 Usage

Defining flags

Pass in an object where the key is the flag name and the value is the flag type—a parser function that takes in a string and parses it to that type. Default JavaScript constructors should be able to cover most use-cases: String, Number, Boolean, etc.

The value can also be an object with the type property as the flag type.

typeFlag({
    // Short-hand
    stringFlag: String,
    numberFlag: Number,
    booleanFlag: Boolean,

    // Object syntax:
    stringFlag: {
        type: String
    }
})

Array type

To accept multiple values of a flag, wrap the type with an array:

const parsed = typeFlag({
    myFlag: [String]
})

// $ node ./cli --my-flag A --my-flag B
parsed.flags.myFlag // => ['A', 'B']

Aliases

Flags are often given single-character aliases for shorthand usage (eg. --help to -h). To give a flag an alias, use the object syntax and set the alias property to a single-character name.

typeFlag({
    myFlag: {
        type: String,
        alias: 'm'
    }
})

// $ node ./cli -m hello
parsed.flags.myFlag // => 'hello'

Default values

Flags that are not passed in will default to being undefined. To set a different default value, use the object syntax and pass in a value as the default property. When a default is provided, the return type will reflect that instead of undefined.

When using mutable values (eg. objects/arrays) as a default, pass in a function that creates it to avoid mutation-related bugs.

const parsed = typeFlag({
    someNumber: {
        type: Number,
        default: 1
    },

    manyNumbers: {
        type: [Number],

        // Use a function to return an object or array
        default: () => [1, 2, 3]
    }
})

To get undefined in the parsed flag type, make sure strict or strictNullChecks is enabled.

kebab-case flags mapped to camelCase

When passing in the flags, they can be in kebab-case and will automatically map to the camelCase equivalent.

const parsed = typeFlag({
    someString: [String]
})

// $ node ./cli --someString hello --some-string world
parsed.flags.someString // => ['hello', 'world']

Unknown flags

When unrecognized flags are passed in, they are interpreted as a boolean, or a string if explicitly passed in. Unknown flags are not converted to camelCase to allow for accurate error handling.

const parsed = typeFlag({})

// $ node ./cli --some-flag --some-flag=1234
parsed.unknownFlags // => { 'some-flag': [true, '1234'] }

Arguments

Arguments are values passed in that are not associated with any flags. All arguments are stored in the _ property.

Everything after -- (end-of-flags) is treated as an argument (including flags) and will be stored in the _['--'] property.

const parsed = typeFlag({
    myFlag: [String]
})

// $ node ./cli --my-flag value arg1 -- --my-flag world
parsed.flags.myFlag // => ['value']
parsed._ // => ['arg1', '--my-flag', 'world']
parsed._['--'] // => ['--my-flag', 'world']

Flag value delimiters

The characters =, : and . are reserved for delimiting the value from the flag.

$ node ./cli --flag=value --flag:value --flag.value

This allows for usage like --flag:key=value or --flag.property=value to be possible.

Mutated argv array

When type-flag iterates over the argv array, it removes the tokens it parses out via mutation.

By default, type-flag works on a new copy of process.argv.slice(2) so this doesn't have any side-effects. But if you want to leverage this behavior to extract certain flags and arguments, you can pass in your own copy of process.argv.slice(2).

This may be useful for filtering out certain flags before passing down the argv to a child process.

Ignoring unknown flags

Sometimes it may be undesirable to parse unknown flags. In these cases, you can ignore them so they're left unparsed in the argv array.

const argv = process.argv.slice(2)
const parsed = typeFlag(
    {},
    argv,
    {
        ignore: type => type === 'unknown-flag'
    }
)

// $ node ./cli --unknown=hello
parsed._ // => []
argv // => ['--unknown=hello']

Ignoring everything after the first argument

Similarly to how Node.js only reads flags passed in before the first argument, type-flag can be configured to ignore everything after the first argument.

const argv = process.argv.slice(2)

let stopParsing = false
const parsed = typeFlag(
    {
        myFlag: [Boolean]
    },
    argv,
    {
        ignore(type) {
            if (stopParsing) {
                return true
            }
            const isArgument = type === 'argument'
            if (isArgument) {
                stopParsing = isArgument
                return stopParsing
            }
        }
    }
)

// $ node ./cli --my-flag ./file.js --my-flag
parsed.flags.myFlag // => [true]
argv // => ['./file.js', '--my-flag']

👨🏻‍🏫 Examples

Custom flag type

Basic types can be set using built-in functions in JavaScript, but sometimes you want to a new type, narrow the type, or add validation.

To create a new type, simply declare a function that accepts a string argument and returns the parsed value with the expected type.

In this example, the size flag is enforced to be either small, medium or large.

const possibleSizes = ['small', 'medium', 'large'] as const

type Sizes = typeof possibleSizes[number]

function Size(size: Sizes) {
    if (!possibleSizes.includes(size)) {
        throw new Error(`Invalid size: "${size}"`)
    }

    return size
}

const parsed = typeFlag({
    size: Size
})

parsed resolves to the following type:

type Parsed = {
    flags: {
        size: 'small' | 'medium' | 'large' | undefined
    }
    // ...
}

Optional value flag

To create a string flag that acts as a boolean when nothing is passed in, create a custom type that returns both types.

function OptionalString(value: string) {
    if (!value) {
        return true
    }

    return value
}

const parsed = typeFlag({
    string: OptionalString
})

// $ node ./cli --string
parsed.flags.string // => true

// $ node ./cli --string hello
parsed.flags.string // => 'hello'

Accepting flag values with = in it

In use-cases where flag values contain =, you can use : instead. This allows flags like --define:K=V.

const parsed = typeFlag({
    define: String
})

// $ node ./cli --define:key=value
parsed.flags.define // => 'key=value'

Dot-nested flags

type Environment = {
    TOKEN?: string
    CI?: boolean
}

function EnvironmentObject(value: string): Environment {
    const [propertyName, propertyValue] = value.split('=')
    return {
        [propertyName]: propertyValue || true
    }
}

const parsed = typeFlag({
    env: [EnvironmentObject]
})

const env = parsed.flags.env.reduce(
    (agg, next) => Object.assign(agg, next),
    {}
)

// $ node ./cli --env.TOKEN=123 --env.CI
env // => { TOKEN: 123, CI: true }

Inverting a boolean

To invert a boolean flag, false must be passed in with the = operator (or any other value delimiters).

const parsed = typeFlag({
    booleanFlag: Boolean
})

// $ node ./cli --boolean-flag=false
parsed.flags.booleanFlag // => false

Without explicitly specfying the flag value via =, the false will be parsed as a separate argument.

// $ node ./cli --boolean-flag false
parsed.flags.booleanFlag // => true
parsed._ // => ['false']

Counting flags

To create an API where passing in a flag multiple times increases a count (a pretty common one is -vvv), you can use an array-boolean type and count the size of the array:

const parsed = typeFlag({
    verbose: {
        type: [Boolean],
        alias: 'v'
    }
})

// $ node ./cli -vvv
parsed.flags.verbose.length // => 3

⚙️ API

typeFlag(flagSchema, argv, options)

Returns an object with the shape:

type Parsed = {
    flags: {
        [flagName: string]: InferredType
    }
    unknownFlags: {
        [flagName: string]: (string | boolean)[]
    }
    _: string[]
}

flagSchema

Type:

type TypeFunction = (argvValue: any) => any

type FlagSchema = {
    [flagName: string]: TypeFunction | [TypeFunction] | {
        type: TypeFunction | [TypeFunction]
        alias?: string
        default?: any
    }
}

An object containing flag schema definitions. Where the key is the flag name, and the value is either the type function or an object containing the type function and/or alias.

argv

Type: string[]

Default: process.argv.slice(2)

The argv array to parse. The array is mutated to remove the parsed flags.

options

Type:

type Options = {
    // Callback to skip parsing on certain argv tokens
    ignore?: (
        type: 'known-flag' | 'unknown-flag' | 'argument',
        flagOrArgv: string,
        value: string | undefined,
    ) => boolean | void
}

getFlag(flagNames, flagType, argv)

flagNames

Type: string

A comma-separated list of flag names to parse.

flagType

Type:

type TypeFunction = (argvValue: any) => any

type FlagType = TypeFunction | [TypeFunction]

A function to parse the flag value. Wrap the function in an array to retrieve all values.

argv

Type: string[]

Default: process.argv.slice(2)

The argv array to parse. The array is mutated to remove the parsed flags.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].