npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@alwaysai/alwayscli

v0.3.3

Published

A library for building command-line interfaces

Downloads

919

Readme

alwaysCLI Build Status

A framework for building command-line interfaces (CLIs) in Node.js. This package includes runtime JavaScript files suitable for Node.js >=8 as well as the corresponding TypeScript type declarations.

Usage

npm install @alwaysai/alwayscli

In an alwaysCLI program, commands are organized into a tree. Each "leaf" represents an action whereas "branches" collect and organize leaves. For example, in the command alwaysai user login, alwaysai is the "root" command (a branch), user is a branch of commands related to user authentication and login is the action leaf. Your CLI need not have branches. Here is a simple CLI whose root is a leaf:

// readme.ts
import {
  CliLeaf,
  CliFlagInput,
  CliNumberArrayInput,
  runCliAndExit,
} from '@alwaysai/alwayscli';

export const root = CliLeaf({
  name: 'multiply',
  description: 'Multiply numbers and print the result',
  positionalInput: CliNumberArrayInput({ required: true }),
  namedInputs: {
    squared: CliFlagInput({
      description: 'Square the result before printing it',
    }),
  },
  action(args, { squared }) {
    const multiplied = args.reduce((a, b) => a * b, 1);
    if (squared) {
      return multiplied * multiplied;
    }
    return multiplied;
  },
});

if (require.main === module) {
  runCliAndExit(root);
}

Here's how that behaves as a CLI.

$ ts-node readme.ts
Usage: multiply <num0> [...] [<options>]

   Multiply numbers and print the result

Options:

   [--squared] : Square the result before printing it

Error: "<num0> [...]": Value is required

With arguments:

$ ts-node readme.ts 1 2 3
6
$ ts-node readme.ts 1 2 3 --squared
36

The if (require.main === module) conditional is Node.js for "if this file is the entry point", which is true when you do ts-node readme.ts, but not when you do import { root } from './readme.ts' for unit tests for example.

More generally the usage of an alwaysCLI CLI is:

<program> <branch> <leaf> <positional-args> --name <named-args> -- <escaped-args>

To invoke an action the user provides (in order):

  • zero or more branch names
  • a leaf name
  • zero or more positional args
  • zero or more "options" (inputs of the form --foo bar)

API

Input<T, U>

TODO

CliLeaf({name, description?, args?, options?, action, hidden?, version?})

A factory for creating "action" commands. Returns the newly-created leaf.

name

If this "leaf" is a subcommand, name is the string that the user will pass as the "subcommand" argument to invoke this action. If this "leaf" is the root command, name should be the CLI's name.

description

(Optional) A string that will be included in Usage: if present.

positionalInput

(Optional) An Input for

namedInputs

(Optional) An object of named Inputs, for example:

const options = {
  path: createStringInput({
    description: 'An absolute or relative path',
  }),
}

The args and options properties define how the command-line arguments get parsed and transformed before being passed into the action function.

action(args, options)

The function that defines your command logic. action can return a value synchronously like in the "multiply" example above, or it can be an async function that returns a Promise. If action returns/resolves a value, that value is console.logged before the CLI exits. If action throws/rejects, the exception is console.logged before the CLI exits. That means that if you don't want the user to see a stack trace, your action should throw a string instead of an Error object. The type of the args argument received by action is derived by the args property of the leaf. Similarly, the options argument type is derived from leaf.options.

hidden

(Optional) boolean

version

(Optional) string. If provided, this string will be printed when the user does cli --version or cli -v. If this value is not provided, alwaysCLI will attempt to find a version string in your package.json file.

CliBranch({name, description, subcommands, hidden?})

A factory function similar to CliLeaf. Returns the newly-created Branch object.

name

If this "branch" is not the root command, name is the string that the user will pass as the "subcommand" argument to invoke actions in this part of the command tree. If this "branch" command is the root command, name should be the CLI's name.

description

(Optional) A string that will be included in Usage: if present.

subcommands

An array of Branch and/or Leaf objects.

hidden

(Optional) boolean

createCli(root)

Returns a function of the form (...args: string[]) => Promise<any> that can be invoked as e.g. cli('foo', 'bar') for unit tests or as cli(process.argv.slice(2)) in an executable CLI script.

root

A Leaf or Branch

ArgvInterface

cli is a function that takes command-line arguments (strings) as input and returns a Promise representing the execution of the arguments. We export cli so that we can unit test it like so.

More information

This library has a couple dozen unit tests with >90% coverage. If you want to see more examples of how things works, check out the .test.ts files in the src directory. Also check out src/examples. If you encounter any bugs or have any questions or feature requests, please don't hesitate to file an issue or submit a pull request on this project's repository on GitHub.

Release procedure

To release the package to npmjs.org follow the steps:

  • publish new version: npm run publish:<major|minor|patch>
  • check the github pipeline running, if successful a new version will be created and published to npmjs.org
  • to get the auto-generated commit and tags, simply pull: git pull

Pipeline is lintng, unit testing and building package on:

  • [x] Ubuntu latest / Node.js: 16.x, 18.x, 20.x, 22.x
  • [x] MacOS latest / Node.js: 16.x, 18.x, 20.x, 22.x
  • [x] Windows latest / Node.js: 16.x, 18.x, 20.x, 22.x