• Stars
    star
    361
  • Rank 117,957 (Top 3 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 3 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

👁‍🗨 cleye — The intuitive & typed CLI development tool for Node.js

cleye

The intuitive command-line interface (CLI) development tool.

Features

  • Minimal API surface
  • Powerful flag parsing
  • Strongly typed parameters and flags
  • Command support
  • Help documentation generation (customizable too!)

Try it out online

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

Install

npm i cleye

About

Cleye makes it very easy to develop command-line scripts in Node.js. It handles argv parsing to give you strongly typed parameters + flags and generates --help documentation based on the provided information.

Here's an example script that simply logs: Good morning/evening <name>!:

greet.js:

import { cli } from 'cleye'

// Parse argv
const argv = cli({
    name: 'greet.js',

    // Define parameters
    parameters: [
        '<first name>', // First name is required
        '[last name]' // Last name is optional
    ],

    // Define flags/options
    flags: {

        // Parses `--time` as a string
        time: {
            type: String,
            description: 'Time of day to greet (morning or evening)',
            default: 'morning'
        }
    }
})

const name = [argv._.firstName, argv._.lastName].filter(Boolean).join(' ')

if (argv.flags.time === 'morning') {
    console.log(`Good morning ${name}!`)
} else {
    console.log(`Good evening ${name}!`)
}

🛠 In development, type hints are provided on parsed flags and parameters:



Type hints for Cleye's output are very verbose and readable

📖 Generated help documentation can be viewed with the --help flag:

$ node greet.js --help

greet.js

Usage:
  greet.js [flags...] <first name> [last name]

Flags:
  -h, --help                 Show help
      --time <string>        Time of day to greet (morning or evening) (default: "morning")

Run the script to see it in action:

$ node greet.js John Doe --time evening

Good evening John Doe!

Examples

Want to dive right into some code? Check out some of these examples:

Usage

Arguments

Arguments are values passed into the script that are not associated with any flags/options.

For example, in the following command, the first argument is file-a.txt and the second is file-b.txt:

$ my-script file-a.txt file-b.txt

Arguments can be accessed from the _ array-property of the returned object.

Example:

const argv = cli({ /* ... */ })

// $ my-script file-a.txt file-b.txt

argv._ // => ["file-a.txt", "file-b.txt"] (string[])

Parameters

Parameters (aka positional arguments) are the names that map against argument values. Think of parameters as variable names and arguments as values associated with the variables.

Parameters can be defined in the parameters array-property to make specific arguments accessible by name. This is useful for writing more readable code, enforcing validation, and generating help documentation.

Parameters are defined in the following formats:

  • Required parameters are indicated by angle brackets (eg. <parameter name>).
  • Optional parameters are indicated by square brackets (eg. [parameter name]).
  • Spread parameters are indicated by ... suffix (eg. <parameter name...> or [parameter name...]).

Note, required parameters cannot come after optional parameters, and spread parameters must be last.

Parameters can be accessed in camelCase on the _ property of the returned object.

Example:

const argv = cli({
    parameters: [
        '<required parameter>',
        '[optional parameter]',
        '[optional spread...]'
    ]
})

// $ my-script a b c d

argv._.requiredParameter // => "a" (string)
argv._.optionalParameter // => "b" (string | undefined)
argv._.optionalSpread // => ["c", "d"] (string[])

End-of-flags

End-of-flags (--) (aka end-of-options) allows users to pass in a subset of arguments. This is useful for passing in arguments that should be parsed separately from the rest of the arguments or passing in arguments that look like flags.

An example of this is npm run:

$ npm run <script> -- <script arguments>

The -- indicates that all arguments afterwards should be passed into the script rather than npm.

All end-of-flag arguments will be accessible from argv._['--'].

Additionally, you can specify -- in the parameters array to parse end-of-flags arguments.

Example:

const argv = cli({
    name: 'npm-run',
    parameters: [
        '<script>',
        '--',
        '[arguments...]'
    ]
})

// $ npm-run echo -- hello world

argv._.script // => "echo" (string)
argv._.arguments // => ["hello", "world] (string[])

Flags

Flags (aka Options) are key-value pairs passed into the script in the format --flag-name <value>.

For example, in the following command, --file-a has value data.json and --file-b has value file.txt:

$ my-script --file-a data.json --file-b=file.txt

Parsing features

Cleye's flag parsing is powered by type-flag and comes with many features:

  • Array & Custom types
  • Flag delimiters: --flag value, --flag=value, --flag:value, and --flag.value
  • Combined aliases: -abcd 2-a -b -c -d 2
  • End of flags: Pass in -- to end flag parsing
  • Unknown flags: Unexpected flags stored in unknownFlags

Read the type-flag docs to learn more.

Defining flags

Flags can be specified in the flag object-property, where the key is the flag name, and the value is a flag type function or an object that describes the flag.

The flag name is recommended to be in camelCase as it will be interpreted to parse kebab-case equivalents.

The flag type function can be any function that accepts a string and returns the parsed value. Default JavaScript constructors should cover most use-cases: String, Number, Boolean, etc.

The flag description object can be used to store additional information about the flag, such as alias, default, and description. To accept multiple values for a flag, wrap the type function in an array.

All of the provided information will be used to generate better help documentation.

Example:

const argv = cli({
    flags: {
        someBoolean: Boolean,

        someString: {
            type: String,
            description: 'Some string flag',
            default: 'n/a'
        },

        someNumber: {
            // Wrap the type function in an array to allow multiple values
            type: [Number],
            alias: 'n',
            description: 'Array of numbers. (eg. -n 1 -n 2 -n 3)'
        }
    }
})

// $ my-script --some-boolean --some-string hello --some-number 1 -n 2

argv.flags.someBoolean // => true (boolean | undefined)
argv.flags.someString // => "hello" (string)
argv.flags.someNumber // => [1, 2] (number[])

Custom flag types & validation

Custom flag types can be created to validate flags and narrow types. Simply create a new function that accepts a string and returns the parsed value.

Here's an example with a custom Size type that narrows the flag type to "small" | "medium" | "large":

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

type Sizes = typeof possibleSizes[number] // => "small" | "medium" | "large"

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

    return size
}

const argv = cli({
    flags: {
        size: {
            type: Size,
            description: 'Size of the pizza (small, medium, large)'
        }
    }
})

// $ my-script --size large

argv.flags.size // => "large" ("small" | "medium" | "large")

Default flags

By default, Cleye will try to handle the --help, -h and --version flags.

Help flag

Handling --help, -h is enabled by default.

To disable it, set help to false. The help documentation can still be manually displayed by calling .showHelp(helpOptions) on the returned object.

Version flag

To enable handling --version, specify the version property.

cli({
    version: '1.2.3'
})
$ my-script --version
1.2.3

The version is also shown in the help documentation. To opt out of handling --version while still showing the version in --help, pass the version into help.version.

Commands

Commands allow organizing multiple "scripts" into a single script. An example of this is the npm install command, which is essentially an "install" script inside the "npm" script, adjacent to other commands like npm run.

Defining commands

A command can be created by importing the command function and initializing it with a name. The rest of the options are the same as the cli function.

Pass the created command into cli option's commands array-property to register it:

npm.js

import { cli, command } from 'cleye'

const argv = cli({
    name: 'npm',

    version: '1.2.3',

    commands: [
        command({
            // Command name
            name: 'install',

            parameters: ['<package name>'],

            flags: {
                noSave: Boolean,
                saveDev: Boolean
            }
        })
    ]
})

// $ npm install lodash

argv.command // => "install" (string)
argv._.packageName // => "lodash" (string)

Depending on the command given, the resulting type can be narrowed:

Command callback

When a CLI app has many commands, it's recommended to organize each command in its own file. With this structure, parsed output handling for each command is better placed where they are respectively defined rather than the single cli output point. This can be done by passing a callback function into the command function (callbacks are supported in the cli function too).

Example:

install-command.js (install command using callback)

import { command } from 'cleye'

export const installCommand = command({
    // Command name
    name: 'install',

    parameters: ['<package name>'],

    flags: {
        noSave: Boolean,
        saveDev: Boolean
    }
}, (argv) => {
    // $ npm install lodash

    argv._.packageName // => "lodash" (string)
})

npm.js (CLI entry file)

import { installCommand } from './install-command.js'

cli({
    name: 'npm',

    commands: [
        installCommand
    ]
})

Help documentation

Cleye uses all information provided to generate rich help documentation. The more information you give, the better the docs!

Help customization

The help document can be customized by passing a render(nodes, renderers) => string function to help.render.

The nodes parameter contains an array of nodes that will be used to render the document. The renderers parameter is an object of functions used to render the document. Each node has properties type and data, where type corresponds to a property in renderers and data is passed into the render function.

Default renderers can be found in /src/render-help/renderers.ts.

Here's an example that adds an extra sentence at the end and also updates the flags table to use the = operator (--flag <value>--flag=<value>):

cli({
    // ...,

    help: {
        render(nodes, renderers) {
            /* Modify nodes... */

            // Add some text at end of document
            nodes.push('\nCheckout Cleye: https://github.com/privatenumber/cleye')

            /* Extend renderers... */

            // Make all flag examples use `=` as the separator
            renderers.flagOperator = () => '='

            /* Render nodes and return help */
            return renderers.render(nodes)
        }
    }
})

Responsive tables

Cleye's "Flags" table in the help document is responsive and wraps cell text content based on the column & terminal width. It also has breakpoints to display more vertically-optimized tables for narrower viewports.

This feature is powered by terminal-columns and can be configured via the renderers.table renderer.

Normal width Narrow width

API

cli(options, callback?, argvs?)

Return type:

type ParsedArgv = {
    // Parsed arguments
    _: string[] & Parameters

    // Parsed flags
    flags: {
        [flagName: string]: InferredType
    }

    // Unexpected flags
    unknownFlags: {
        [flagName: string]: (string | boolean)[]
    }

    // Method to print version
    showVersion: () => void

    // Method to print help
    showHelp: (options: HelpOptions) => void
}

Function to parse argvs by declaring parameters and flags.

options

Options object to configure cli.

name

Type: string

Name of the script used in the help documentation.

version

Type: string

Version of the script used in the help documentation.

Passing this in enables auto-handling --version. To provide a version for the documentation without auto-handling --version, pass it into help.version.

parameters

Type: string[]

Parameter names to map onto arguments. Also used for validation and help documentation.

Parameters must be defined in the following formats:

Format Description
<parameter name> Required parameter
[parameter name] Optional parameter
<parameter name...> Required spread parameter (1 or more)
[parameter name...] Optional spread parameter (0 or more)

Required parameters must be defined before optional parameters, and spread parameters must be defined at the end.

flags

Type: An object that maps the flag name (in camelCase) to a flag type function or an object describing the flag:

Property Type Description
type Function Flag value parsing function.
alias string Single character alias for the flag.
default any Default value for the flag.
description string Description of the flag shown in --help.
placeholder string Placeholder for the flag value shown in --help.
help

Type: false or an object with the following properties.

Property Type Description
version string Version shown in --help.
description string Description shown in --help.
usage string | string[] Usage code examples shown in --help.
examples string | string[] Example code snippets shown in --help.
render (nodes, renderers) => string Function to customize the help document.

Handling --help, -h is enabled by default. To disable it, pass in false.

commands

Type: Command[]

Array of commands to register.

ignoreArgv

Type:

type IgnoreArgvCallback = (
    type: 'known-flag' | 'unknown-flag' | 'argument',
    flagOrArgv: string,
    value: string | undefined,
) => boolean | void

A callback to ignore argv tokens from being parsed.

callback(parsed)

Type:

Optional callback function that is called when the script is invoked without a command.

argvs

Type: string[]

Default: process.argv.slice(2)

The raw parameters array to parse.

command(options, callback?)

options

Property Type Description
name string Required name used to invoke the command.
alias string | string[] Aliases used to invoke the command.
parameters string[] Parameters for the command. Same as parameters.
flags Flags Flags for the command. Same as flags.
help false | HelpOptions Help options for the command. Same as help.

callback(parsed)

Type:

Optional callback function that is called when the command is invoked.

More Repositories

1

tasuku

✅ タスク — The minimal task runner for Node.js
TypeScript
1,687
star
2

minification-benchmarks

🏃‍♂️🏃‍♀️🏃 JS minification benchmarks: babel-minify, esbuild, terser, uglify-js, swc, google closure compiler, tdewolff/minify
TypeScript
830
star
3

snap-tweet

Snap a screenshot of a tweet 📸
TypeScript
312
star
4

vue-2-3

↔️ Interop Vue 2 components in Vue 3 apps and vice versa
JavaScript
277
star
5

pkgroll

📦 🍣 Next-gen package bundler for TypeScript & ESM
TypeScript
249
star
6

ts-runtime-comparison

Comparison of Node.js TypeScript runtimes
TypeScript
219
star
7

vue-frag

🤲 Create Fragments (multiple root-elements) in Vue 2
TypeScript
194
star
8

link

🔗 A better `npm link`
TypeScript
117
star
9

type-flag

⛳️ Typed command-line arguments parser for Node.js
TypeScript
114
star
10

get-tsconfig

tsconfig.json paser & paths matcher
TypeScript
91
star
11

github-cdn

🛰 Github CDN Server
JavaScript
47
star
12

ci

Run npm ci using the appropriate Node package manager (npm, yarn, pnpm)
TypeScript
45
star
13

dbgr

Lightweight debugger for Node.js
TypeScript
44
star
14

clean-pkg-json

Script to remove unnecessary properties from package.json on prepublish hook
TypeScript
41
star
15

vue-frag-plugin

Webpack/Rollup/Vite plugin to add multiple root-node support to Vue 2 SFCs
TypeScript
41
star
16

instant-mocha

☕️ Build tests with Webpack and run with Mocha in one command
TypeScript
41
star
17

resolve-pkg-maps

Resolve package.json `exports` & `imports` maps
TypeScript
39
star
18

git-publish

☁️ Publish your npm package to a GitHub repository branch
TypeScript
38
star
19

compare-bun-node

Comparison of Bun's API against Node.js's
JavaScript
38
star
20

VisualQuery

Light & minimal front-end query builder for jQuery
JavaScript
38
star
21

webpack-localize-assets-plugin

🌐 Localize your Webpack bundle with multiple locales
TypeScript
38
star
22

webpack-json-access-optimizer

Webpack plugin to tree-shake and minify JSON modules
TypeScript
37
star
23

terminal-columns

Render readable & responsive tables in the terminal
TypeScript
32
star
24

deps

📦🔍 Analyze which package.json dependencies are in-use with V8 Coverage 🔥
TypeScript
31
star
25

vue-pseudo-window

🖼 Declaratively interface window/document/body in your Vue template
JavaScript
31
star
26

manten

💮 満点 - Lightweight testing library for Node.js
TypeScript
29
star
27

svg-browser-export

Export SVG to PNG, JPEG, or WEBP in the browser
TypeScript
23
star
28

comment-mark

Interpolate strings with HTML comment markers!
TypeScript
23
star
29

npm-multi-publish

Publish an npm package to multiple registries
JavaScript
23
star
30

reactive-json-file

💎 Reactively sync JSON mutations to disk
TypeScript
23
star
31

playwright-start

Start a long-running Playwright browser server via CLI
TypeScript
22
star
32

ink-task-list

Task runner components for Ink
TypeScript
22
star
33

chainset

Set object values using property chaining syntax
TypeScript
21
star
34

build-this-branch

🤖 Script to automate creating built branches
TypeScript
21
star
35

vue-vnode-syringe

🧬 Add attributes and event-listeners to <slot> content 💉
JavaScript
20
star
36

vue-dom-hints

Vue.js devtool for identifying Vue components and their SFC paths in the DOM
JavaScript
20
star
37

vue-grep

Grep your Vue.js codebase with CSS selectors
TypeScript
19
star
38

git-squash-branch

Script to squash the commits in the current Git branch
TypeScript
17
star
39

fs-fixture

Easily create test fixtures at a temporary file-system path
TypeScript
16
star
40

vue-split-view

Create a resizable split-view to partition the UI
Vue
16
star
41

alias-imports

Create Node.js aliases via imports map
TypeScript
16
star
42

vue-import-loader

🌐 Webpack loader to automatically detect & import used components
JavaScript
13
star
43

fs-require

Create a require() function from any file-system. Great for in-memory fs testing!
TypeScript
13
star
44

pkg-entry-points

Get all entry-points for an npm package. Supports the `exports` field in `package.json`
TypeScript
13
star
45

vue-subslot

💍 Pick out specific elements from the component <slot>
JavaScript
12
star
46

gh-emojis

Use GitHub emojis from their API as an npm package
JavaScript
12
star
47

litemark

🖋 GitHub Flavored Markdown Editor
Vue
12
star
48

hirok.io

👤 My personal website & blog
Vue
11
star
49

vue-proxi

💠 Tiny proxy component for Vue.js
JavaScript
11
star
50

bfs

Find the path of a value in a complex JavaScript object graph/tree.
JavaScript
11
star
51

prerelease-checks

Run essential pre-release checks before releasing an npm package
TypeScript
11
star
52

git-detect-case-change

🤖 Script to detect file name case changes in a Git repository
TypeScript
10
star
53

cli-simple-table

Simple CLI table for simple people
TypeScript
9
star
54

webpack-test-utils

Utility functions to test Webpack loaders/plugins
TypeScript
8
star
55

vue-svg-icon-set

Efficiently expose a SVG icon set in Vue
JavaScript
8
star
56

webpack-playground

Demonstration of different Webpack configurations
JavaScript
8
star
57

eslint-config

TypeScript
7
star
58

vue-v

Tiny component to render Vue.js vNodes in the template.
JavaScript
7
star
59

i-peers

npx package to install local peer-dependencies
JavaScript
7
star
60

fs.promises.exists

🪐 The missing fs.promises.exists(). Also supports case-sensitive/insensitive file paths.
TypeScript
7
star
61

vue-just-ssr

🔥 Instantly add a Vue SSR dev-env to your Webpack build
JavaScript
7
star
62

issue-reproductions

A repository to collect and organize my issue reproductions
JavaScript
6
star
63

webpack-analyze-duplication-plugin

Detect duplicated modules in your Webpack build
JavaScript
6
star
64

vue-feather-icon-set

Optimized Feather icon set for Vue using SVG references
JavaScript
6
star
65

is-fs-case-sensitive

Check whether the file-system is case-sensitive
TypeScript
6
star
66

npm-registry-sync

A daemon to sync packages across npm registries
TypeScript
6
star
67

babel-plugin-debug-object-location

Babel plugin to help you determine where an object or array was instantiated
TypeScript
5
star
68

generate-batched-pr-manifest

Generate a manifest of all the PRs included in a batched PR
TypeScript
5
star
69

entry-file-plugin

Create an ESM entry-file in your Webpack build to consolidate entry-point exports
TypeScript
5
star
70

privatenumber

5
star
71

webpack-dependency-size

👩‍🔬 Webpack plugin to get an overview of bundled dependencies and their size
JavaScript
5
star
72

vue-demo-collapse

Vue component that shows a demo and a "Show Code" button to expand source code
Vue
4
star
73

markdown-it-add-attrs

markdown-it plugin to add attributes to all rendered elements
JavaScript
4
star
74

browser-reload-plugin

Automatically reload the browser page on every Webpack watch build
JavaScript
4
star
75

rollup-playground

Demonstration of different Rollup configurations
JavaScript
3
star
76

rollup-plugin-htmlvue

Rollup plugin for transforming HTML/XML to Vue SFC
TypeScript
3
star
77

htmlvue-loader

Webpack loader for compiling HTML to Vue
JavaScript
3
star
78

vue-ast-utils

Utils for working with Vue 3 AST nodes
TypeScript
3
star
79

systemjs-unpkg

SystemJS extra to auto-resolve bare specifiers to UNPKG
JavaScript
3
star
80

web-diff

Visually diff web pages
JavaScript
2
star
81

rollup-plugin-aggregate-exports

Emit an entry file to aggregate exports across multiple files.
JavaScript
2
star
82

svg-trace-loader

Webpack loader to trace and flatten SVGs into one path
JavaScript
2
star
83

repkg

🚚 On-demand AMD npm package bundling service
JavaScript
2
star
84

postcss-custom-properties-transformer

PostCSS plugin to apply transformations to CSS custom properties (eg. mangling)
JavaScript
2
star
85

webpack-distsize

Track Webpack output size via version control
JavaScript
2
star
86

motion-orientation-api

Motion & Orientation API explorer
Vue
2
star
87

vue-server-renderer

JavaScript
2
star
88

postcss-import-alias

Use aliases in your PostCSS import statements
JavaScript
2
star
89

pm2-no-daemon-bug

JavaScript
1
star
90

resume

1
star
91

fs-router-patch

Add routing for shimming fs access
JavaScript
1
star
92

gulp-watchman

JavaScript
1
star
93

.github

1
star
94

sharp-vs-resvgjs

JavaScript
1
star
95

mempack

Run a Webpack build in-memory
JavaScript
1
star