• Stars
    star
    2,167
  • Rank 21,278 (Top 0.5 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 5 years ago
  • Updated 3 months ago

Reviews

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

Repository Details

🎨 Next.js-like framework for CLIs made with Ink

Pastel test

Next.js-like framework for CLIs made with Ink.

Features

  • Create files in commands folder to add commands.
  • Create folders in commands to add subcommands.
  • Define options and arguments via Zod.
  • Full type-safety of options and arguments thanks to Zod.
  • Auto-generated help message for commands, options and arguments.
  • Uses battle-tested Commander package under the hood.

Install

npm install pastel ink react zod

Geting started

Use create-pastel-app to quickly scaffold a Pastel app with TypeScript, linter and tests set up.

npm create pastel-app hello-world
hello-world
Manual setup

  1. Set up a new project.
mkdir hello-world
cd hello-world
npm init --yes
  1. Install Pastel and TypeScript.
npm install pastel
npm install --save-dev typescript @sindresorhus/tsconfig
  1. Create a tsconfig.json file to set up TypeScript.
{
	"extends": "@sindresorhus/tsconfig",
	"compilerOptions": {
		"moduleResolution": "node16",
		"module": "node16",
		"outDir": "build",
		"sourceMap": true,
		"tsx": "react"
	},
	"include": ["source"]
}
  1. Create a source folder for the source code.
mkdir source
  1. Create a source/cli.ts file with the following code, which will be CLI's entrypoint:
#!/usr/bin/env node
import Pastel from 'pastel';

const app = new Pastel({
	importMeta: import.meta,
});

await app.run();
  1. Create source/commands folder for defining CLI's commands.
mkdir source/commands
  1. Create an source/commands/index.tsx file for a default command, with the following code:
import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	name: zod.string().describe('Your name'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Index({options}: Props) {
	return <Text>Hello, {options.name}!</Text>;
}
  1. Build your CLI.
npx tsc
  1. Set up an executable file.

9.1. Add bin field to package.json, which points to the compiled version of source/cli.ts file.

	"bin": "build/cli.js"

9.2. Make your CLI available globally.

npm link --global
  1. Run your CLI.
hello-world --name=Jane
Hello, Jane!
hello-world --help
Usage: hello-world [options]

Options:
  --name         Your name
  -v, --version  Show version number
  -h, --help     Show help

Table of contents

Commands

Pastel treats every file in the commands folder as a command, where filename is a command's name (excluding the extension). Files are expected to export a React component, which will be rendered when command is executed.

You can also nest files in folders to create subcommands.

Here's an example, which defines login and logout commands:

commands/
	login.tsx
	logout.tsx

login.tsx

import React from 'react';
import {Text} from 'ink';

export default function Login() {
	return <Text>Logging in</Text>;
}

logout.tsx

import React from 'react';
import {Text} from 'ink';

export default function Logout() {
	return <Text>Logging out</Text>;
}

Given that your executable is named my-cli, you can execute these commands like so:

$ my-cli login
$ my-cli logout

Index commands

Files named index.tsx are index commands. They will be executed by default, when no other command isn't specified.

commands/
	index.tsx
	login.tsx
	logout.tsx

Running my-cli without a command name will execute index.tsx command.

$ my-cli

Index command is useful when you're building a single-purpose CLI, which has only one command. For example, np or fast-cli.

Default commands

Default commands are similar to index commands, because they too will be executed when an explicit command isn't specified. The difference is default commands still have a name, just like any other command, and they'll show up in the help message.

Default commands are useful for creating shortcuts to commands that are used most often.

Let's say there are 3 commands available: deploy, login and logout.

commands/
	deploy.tsx
	login.tsx
	logout.tsx

Each of them can be executed by typing their name.

$ my-cli deploy
$ my-cli login
$ my-cli logout

Chances are, deploy command is going to be used a lot more frequently than login and logout, so it makes sense to make deploy a default command in this CLI.

Export a variable named isDefault from the command file and set it to true to mark that command as a default one.

import React from 'react';
import {Text} from 'ink';

+ export const isDefault = true;

export default function Deploy() {
	return <Text>Deploying...</Text>;
}

Now, running my-cli or my-cli deploy will execute a deploy command.

$ my-cli

Vercel's CLI is a real-world example of this approach, where both vercel and vercel deploy trigger a new deploy of your project.

Subcommands

As your CLI grows and more commands are added, it makes sense to group the related commands together.

To do that, create nested folders in commands folder and put the relevant commands inside to create subcommands. Here's an example for a CLI that triggers deploys and manages domains for your project:

commands/
	deploy.tsx
	login.tsx
	logout.tsx
	domains/
		list.tsx
		add.tsx
		remove.tsx

Commands for managing domains would be executed like so:

$ my-cli domains list
$ my-cli domains add
$ my-cli domains remove

Subcommands can even be deeply nested within many folders.

Aliases

Commands can have an alias, which is usually a shorter alternative name for the same command. Power users prefer aliases instead of full names for commands they use often. For example, most users type npm i instead of npm install.

Any command in Pastel can assign an alias by exporting a variable named alias:

import React from 'react';
import {Text} from 'ink';

+ export const alias = 'i';

export default function Install() {
	return <Text>Installing something...</Text>;
}

Now the same install command can be executed by only typing i:

$ my-cli i

Options

Commands can define options to customize their default behavior or ask for some additional data to run properly. For example, a command that creates a new server might specify options for choosing a server's name, an operating system, memory size or a region where that server should be spin up.

Pastel uses Zod to define, parse and validate command options. Export a variable named options and set a Zod object schema. Pastel will parse that schema and automatically set these options up. When command is executed, option values are passed via options prop to your component.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	name: zod.string().describe('Server name'),
	os: zod.enum(['Ubuntu', 'Debian']).describe('Operating system'),
	memory: zod.number().describe('Memory size'),
	region: zod.enum(['waw', 'lhr', 'nyc']).describe('Region'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Deploy({options}: Props) {
	return (
		<Text>
			Deploying a server named "{options.name}" running {options.os} with memory
			size of {options.memory} MB in {options.region} region
		</Text>
	);
}

With options set up, here's an example deploy command:

$ my-cli deploy --name=Test --os=Ubuntu --memory=1024 --region=waw
Deploying a server named "Test" running Ubuntu with memory size of 1024 MB in waw region.

Help message is auto-generated for you as well.

$ my-cli deploy --help
Usage: my-cli deploy [options]

Options:
  --name         Server name
  --os           Operating system (choices: "Ubuntu", "Debian")
  --memory       Memory size
  --region       Region
  -v, --version  Show version number
  -h, --help     Show help

Types

Pastel only supports string, number, boolean, enum, array and set types for defining options.

String

Example that defines a --name string option:

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	name: zod.string().describe('Your name'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Name = {options.name}</Text>;
}
$ my-cli --name=Jane
Name = Jane

Number

Example that defines a --size number option:

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	age: zod.number().describe('Your age'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Age = {options.age}</Text>;
}
$ my-cli --age=28
Age = 28

Boolean

Example that defines a --compress number option:

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	compress: zod.boolean().describe('Compress output'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Compress = {String(options.compress)}</Text>;
}
$ my-cli --compress
Compress = true

Boolean options are special, because they can't be required and default to false, even if Zod schema doesn't use a default(false) function.

When boolean option defaults to true, it's treated as a negated option, which adds a no- prefix to its name.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	compress: zod.boolean().default(true).describe("Don't compress output"),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Compress = {String(options.compress)}</Text>;
}
$ my-cli --no-compress
Compress = false

Enum

Example that defines an --os enum option with a set of allowed values.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	os: zod.enum(['Ubuntu', 'Debian']).describe('Operating system'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Operating system = {options.os}</Text>;
}
$ my-cli --os=Ubuntu
Operating system = Ubuntu

$ my-cli --os=Debian
Operating system = Debian

$ my-cli --os=Windows
error: option '--os <os>' argument 'Windows' is invalid. Allowed choices are Ubuntu, Debian.

Array

Example that defines a --tag array option, which can be specified multiple times.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	tag: zod.array(zod.string()).describe('Tags'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Tags = {options.tags.join(', ')}</Text>;
}
$ my-cli --tag=App --tag=Production
Tags = App, Production

Array options can only include strings (zod.string), numbers (zod.number) or enums (zod.enum).

Set

Example that defines a --tag set option, which can be specified multiple times. It's similar to an array option, except duplicate values are removed, since the option's value is a Set instance.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	tag: zod.set(zod.string()).describe('Tags'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Tags = {[...options.tags].join(', ')}</Text>;
}
$ my-cli --tag=App --tag=Production --tag=Production
Tags = App, Production

Set options can only include strings (zod.string), numbers (zod.number) or enums (zod.enum).

Optional or required options

Pastel determines whether option is optional or required by parsing its Zod schema. Since Zod schemas are required by default, so are options in Pastel.

If an option isn't be required for a command to function properly, mark it as optional.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	os: zod.enum(['Ubuntu', 'Debian']).optional().describe('Operating system'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Operating system = {options.os ?? 'unspecified'}</Text>;
}
$ my-cli --os=Ubuntu
Operating system = Ubuntu

$ my-cli
Operating system = unspecified

Default value

Default value for an option can be set via a default function in Zod schema.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const options = zod.object({
	size: zod.number().default(1024).describe('Memory size'),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Memory = {options.size} MB</Text>;
}
$ my-cli
Memory size = 1024 MB

JSON representation of default value will be displayed in the help message.

$ my-cli --help
Usage: my-cli [options]

Options:
  --size  Memory size (default: 1024)

You can also customize it via defaultValueDescription option in option helper function.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';
import {option} from 'pastel';

export const options = zod.object({
	size: zod
		.number()
		.default(1024)
		.describe(
			option({
				description: 'Memory size',
				defaultValueDescription: '1 GB',
			}),
		),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Memory = {options.size} MB</Text>;
}
$ my-cli --help
Usage: my-cli [options]

Options:
  --size  Memory size (default: 1 GB)

Alias

Options can specify an alias, which is usually the first letter of an original option name.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';
import {option} from 'pastel';

export const options = zod.object({
	force: zod.boolean().describe(
		option({
			description: 'Force',
			alias: 'f',
		}),
	),
});

type Props = {
	options: zod.infer<typeof options>;
};

export default function Example({options}: Props) {
	return <Text>Force = {String(options.force)}</Text>;
}
$ my-cli --force
Force = true

$ my-cli -f
Force = true

Arguments

Arguments are similar to options, except they don't require a flag to specify them (e.g. --name) and they're always specified after command name and options. For example, mv requires 2 arguments, where first argument is a source path and second argument is a target path.

$ mv source.txt target.txt

A theoretical mv command in Pastel can define similar arguments like so:

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const args = zod.tuple([zod.string(), zod.string()]);

type Props = {
	args: zod.infer<typeof args>;
};

export default function Move({args}: Props) {
	return (
		<Text>
			Moving from {args[0]} to {args[1]}
		</Text>
	);
}
$ my-cli source.txt target.txt
Moving from source.txt to target.txt

This command defines two positional arguments, which means that argument's position matters for command's execution. This is why positional arguments are defined via zod.tuple in Zod, where a specific number of values is expected.

However, there are commands like rm, which can accept any number of arguments. To accomplish that in Pastel, use zod.array instead.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';

export const args = zod.array(zod.string());

type Props = {
	args: zod.infer<typeof args>;
};

export default function Remove({args}: Props) {
	return <Text>Removing {args.join(', ')}</Text>;
}
$ my-cli a.txt b.txt c.txt
Removing a.txt, b.txt, c.txt

Types

Pastel only supports string, number and enum types for defining arguments inside tuple or array.

String

Example that defines a string argument.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';
import {argument} from 'pastel';

export const args = zod.tuple([
	zod.string().describe(
		argument({
			name: 'name',
			description: 'Your name',
		}),
	),
]);

type Props = {
	args: zod.infer<typeof args>;
};

export default function Hello({args}: Props) {
	return <Text>Hello, {args[0]}</Text>;
}
$ my-cli Jane
Hello, Jane

Number

Example that defines a number argument.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';
import {argument} from 'pastel';

export const args = zod.tuple([
	zod.number().describe(
		argument({
			name: 'age',
			description: 'Age',
		}),
	),
]);

type Props = {
	args: zod.infer<typeof args>;
};

export default function Hello({args}: Props) {
	return <Text>Your age is {args[0]}</Text>;
}
$ my-cli 28
Your age is 28

Enum

Example that defines an enum argument.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';
import {argument} from 'pastel';

export const args = zod.tuple([
	zod.enum(['Ubuntu', 'Debian']).describe(
		argument({
			name: 'os',
			description: 'Operating system',
		}),
	),
]);

type Props = {
	args: zod.infer<typeof args>;
};

export default function Example({args}: Props) {
	return <Text>Operating system = {args[0]}</Text>;
}
$ my-cli Ubuntu
Operating system = Ubuntu

Default value

Default value for an argument can be via a default function in Zod schema.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';
import {argument} from 'pastel';

export const args = zod.tuple([
	zod
		.number()
		.default(1024)
		.describe(
			argument({
				name: 'number',
				description: 'Some number',
			}),
		),
]);

type Props = {
	args: zod.infer<typeof args>;
};

export default function Example({args}: Props) {
	return <Text>Some number = {args[0]}</Text>;
}
$ my-cli
Some number = 1024

JSON representation of default value will be displayed in the help message.

$ my-cli --help
Usage: my-cli [options] [number]

Arguments:
  number  Some number (default: 1024)

You can also customize it via defaultValueDescription option in option helper function.

import React from 'react';
import {Text} from 'ink';
import zod from 'zod';
import {argument} from 'pastel';

export const args = zod.tuple([
	zod
		.number()
		.default(1024)
		.describe(
			argument({
				name: 'number',
				description: 'Some number',
				defaultValueDescription: '1,204',
			}),
		),
]);

type Props = {
	args: zod.infer<typeof args>;
};

export default function Example({args}: Props) {
	return <Text>Some number = {args[0]}</Text>;
}
$ my-cli --help
Usage: my-cli [options] [number]

Arguments:
  number  Some number (default: 1,024)

Custom app

Similar to Next.js, Pastel wraps every command component with a component exported from commands/_app.tsx. If this file doesn't exist, Pastel uses a default app component, which does nothing but render your component with options and args props.

import React from 'react';
import type {AppProps} from 'pastel';

export default function App({Component, commandProps}: AppProps) {
	return <Component {...commandProps} />;
}

You can copy paste that code into commands/_app.tsx and add some logic that will be shared across all commands.

Custom program name

Pastel extracts a program name from the name field in the nearest package.json file. If it doesn't exist, a first argument in process.argv is used.

When the name of an executable doesn't match the name in package.json, it can be customized via a name option during app initialization.

import Pastel from 'pastel';

const app = new Pastel({
	name: 'custom-cli-name',
});

await app.run();

Custom description

Similar to program name, Pastel looks for a description in description field in the nearest package.json file. To customize it, use a description option when initializating Pastel.

import Pastel from 'pastel';

const app = new Pastel({
	description: 'Custom description',
});

await app.run();

Custom version

Similar to program name and description, Pastel looks for a version in version field in the nearest package.json file. If Pastel can't find it, version will be hidden in the help message and -v, --version options won't be available.

To customize it, use a version option during app initialization.

import Pastel from 'pastel';

const app = new Pastel({
	version: '1.0.0
});

await app.run()

API

Pastel(options)

Initializes a Pastel app.

options

Type: object

name

Type: string

Program name. Defaults to name in the nearest package.json or the name of the executable.

version

Type: string

Version. Defaults to version in the nearest package.json.

description

Type: string

Description. Defaults to description in the nearest package.json.

importMeta

Type: ImportMeta

Pass in import.meta. This is used to find the commands directory.

run(argv)

Parses the arguments and runs the app.

argv

Type: Array
Default: process.argv

Program arguments.

option(config)

Set additional metadata for an option. Must be used as an argument to describe function from Zod.

config

Type: object

description

Type: string

Description. If description is missing, option won't appear in the "Options" section of the help message.

defaultValueDescription

Type: string

Description of a default value.

valueDescription

Type: string

Description of a value. Replaces "value" in --flag <value> in the help message.

alias

Type: string

Alias. Usually a first letter of the full option name.

argument(config)

Set additional metadata for an argument. Must be used as an argument to describe function from Zod.

config

Type: object

name

Type: string
Default: 'arg'

Argument's name. Displayed in "Usage" part of the help message. Doesn't affect how argument is parsed.

description

Type: string

Description of an argument. If description is missing, argument won't appear in the "Arguments" section of the help message.

defaultValueDescription

Type: string

Description of a default value.

More Repositories

1

ink

🌈 React for interactive command-line apps
TypeScript
22,325
star
2

tailwind-rn

🦎 Use Tailwind CSS in React Native projects
JavaScript
3,991
star
3

trevor

🚦 Your own mini Travis CI to run tests locally
JavaScript
2,125
star
4

mongorito

🍹 MongoDB ODM for Node.js apps based on Redux
JavaScript
1,393
star
5

gifi

watch GIFs while running npm install
JavaScript
1,025
star
6

draqula

🧛 GraphQL client for minimalistic React apps
JavaScript
777
star
7

pronto

⚡ The now.sh experience for databases
JavaScript
703
star
8

cancan

🔑 Pleasant authorization library for Node.js
JavaScript
621
star
9

ink-ui

💄 Ink-redible command-line interfaces made easy
TypeScript
573
star
10

dom-chef

🍔 Build DOM elements using JSX automatically
TypeScript
488
star
11

lanterns

⛩Write Markdown and get a GraphQL API for querying them for free
JavaScript
355
star
12

google-images

Search for images using Google Images
JavaScript
300
star
13

ronin

Toolkit for killer CLI applications
JavaScript
299
star
14

reconciled

⚛️ Simple way to create a custom React renderer
JavaScript
219
star
15

import-jsx

Import and transpile JSX on the fly
JavaScript
176
star
16

excalidraw-ui

Collection of reusable UI elements for Excalidraw
157
star
17

create-ink-app

Generate a starter Ink app
JavaScript
153
star
18

ink-spinner

Spinner component for Ink
TypeScript
144
star
19

generator-micro-service

🛫 Yeoman generator to kick-start your microservice with `micro` and `ava`!
JavaScript
144
star
20

thememirror

🦚 Beautiful themes for CodeMirror
TypeScript
132
star
21

ink-text-input

Text input component for Ink
TypeScript
131
star
22

ink-select-input

Select input component for Ink
TypeScript
128
star
23

thumbbot

Create thumbnails from images, video, audio and web pages.
JavaScript
124
star
24

ink-testing-library

Utilities for testing Ink apps
TypeScript
100
star
25

npm-search

Electron app to search npm via node-modules.com
JavaScript
95
star
26

mailman

Send emails in a comfortable way via models.
JavaScript
89
star
27

crown

Roll out features gradually
JavaScript
83
star
28

sushi

Koa-like framework for CLI tools.
JavaScript
83
star
29

create-pastel-app

Generate a starter Pastel app
JavaScript
71
star
30

ohcrash-app

Front-end web app of OhCrash
JavaScript
70
star
31

switch-branch-cli

Switch git branches by their pull request title
JavaScript
68
star
32

cat-facts

Interesting cat facts
JavaScript
57
star
33

minimist-options

Clean and beautiful options for minimist
JavaScript
54
star
34

node-video-thumb

Extract thumbnails from a video. Requires ffmpeg.
JavaScript
52
star
35

ohcrash-client

💣 Node.js client to report errors to OhCrash microservice
JavaScript
49
star
36

yoga-layout-prebuilt

Prebuilt yoga-layout package
JavaScript
47
star
37

slack-voice-messages

Record voice messages inside Slack
JavaScript
44
star
38

ink-redux

Redux bindings for Ink
JavaScript
43
star
39

interactor

Organize logic into interactors
JavaScript
36
star
40

restie

JavaScript ORM that talks to RESTful interface, rather than database. For Node.js and browsers.
JavaScript
31
star
41

supertap

Generate TAP output
JavaScript
30
star
42

mocha-generators

Enable support for ES6 generators in Mocha tests
JavaScript
29
star
43

generator-ink-cli

Scaffold out a CLI based on Ink
JavaScript
25
star
44

refined-overcast

Browser extension that improves Overcast user interface
CSS
18
star
45

templato

One interface to many template engines for Node.js and browsers.
JavaScript
17
star
46

ohcrash

💣 Microservice to report errors directly to your repo's GitHub Issues
JavaScript
17
star
47

ink-password-input

Password input component for Ink
JavaScript
16
star
48

interaptor

Intercept HTTP requests for testing purposes.
JavaScript
15
star
49

memcacher

Adding tags functionality to memcached, without modifying it. For Node.js.
CoffeeScript
15
star
50

buble-register

Bublé require hook
JavaScript
14
star
51

AnsiEscapes

ANSI escape codes for manipulating the terminal
Swift
14
star
52

udp-balancer

Load balancer for UDP
JavaScript
14
star
53

route66

Rails-inspired router for Koa and Express
JavaScript
14
star
54

setup-tailwind-rn

Set up Tailwind CSS in React Native apps
JavaScript
13
star
55

awesome-print

Beautifully print your JavaScript objects
JavaScript
12
star
56

patch-console

Patch console methods to intercept output
TypeScript
12
star
57

bucket-app

Interactive readme for your projects
JavaScript
11
star
58

detect-gender

Detect gender from name using genderize.io API
JavaScript
10
star
59

leeroy-jenkins-cli

Let Leeroy Jenkins make your boring CLI life more fun
JavaScript
9
star
60

syslog-parse

Parse syslog-formatted messages
JavaScript
9
star
61

net-socket

Node.js' net.Socket that automatically reconnects, 100% same API
JavaScript
8
star
62

code-excerpt

Extract excerpts from a source code
JavaScript
8
star
63

env-name

Get environment description (node + browser)
JavaScript
8
star
64

object-to-map

Convert object to ES6 Map
JavaScript
7
star
65

tempdir

Create a temporary directory
JavaScript
7
star
66

env-info

Get environment info (node + browser)
JavaScript
7
star
67

co-bind

Function#bind for generator functions.
JavaScript
7
star
68

generator-ink-component

Scaffold out an Ink component
JavaScript
7
star
69

ghost-api

Unofficial API client for Ghost blogs
JavaScript
6
star
70

ip-hash

ip-hash balancing algorithm (based on round-robin)
JavaScript
6
star
71

object-to-array

Convert object to an array of arrays of keys and values
JavaScript
6
star
72

leeroy-jenkins

Let Leeroy Jenkins make your boring developer life more fun.
JavaScript
5
star
73

asking

Tiny utility for getting user input in CLI programs
JavaScript
5
star
74

mongorito-timestamps

Plugin to add "created" and "updated" timestamps to Mongorito models
JavaScript
5
star
75

goodness-squad

4
star
76

react-floating-label-input

Floating-label input component for React
HTML
4
star
77

search-issues

Search GitHub issues
JavaScript
4
star
78

tailwindcss-custom-variant

Tailwind plugin to add custom variants
JavaScript
4
star
79

install-script-cli

Generate bash scripts to install your CLI programs from npm
JavaScript
4
star
80

install-script

Generate install scripts for your CLIs
Shell
4
star
81

wait-for-enter

Wait until user presses Enter in Terminal
JavaScript
3
star
82

list-dir

List directory contents recursively
JavaScript
3
star
83

crown-redis-store

Redis store for Crown
JavaScript
3
star
84

git-to-github-url

Get GitHub URL for a git repository
JavaScript
3
star
85

darkmatter-sdk

Enhance Darkmatter integration with your Astro website
TypeScript
3
star
86

express-errors

Express middleware for displaying Rails-inspired error pages
JavaScript
3
star
87

convert-to-spaces

Convert tabs to spaces in a string
JavaScript
3
star
88

koa-errors

Koa middleware for displaying Rails-inspired error pages
JavaScript
2
star
89

ama

Ask Me Anything!
2
star
90

mustached

CLI tool to render Mustache templates with JSON as data input
Crystal
2
star
91

format-object

util.format with object instead of argument list
JavaScript
2
star
92

is-public-repo

Check if GitHub repository is public
JavaScript
2
star
93

astro-selfie

Astro integration to generate page screenshots to show as Open Graph images
TypeScript
2
star
94

test

Dockerfile
2
star
95

crown-memory-store

Memory store for Crown
JavaScript
2
star
96

convert-to-tabs

Convert spaces to tabs in a string
JavaScript
2
star
97

fake-exec

Fake child_process#exec output for testing
JavaScript
1
star
98

koa-log-requests

Customizable Koa middleware for logging requests in console
JavaScript
1
star
99

sushi-help

Help message middleware for Sushi
JavaScript
1
star
100

array-generators

Common array functions with support for generators (forEach, filter, map)
JavaScript
1
star