From 26a44f21674d8b2db1a72a2b6b61f173f5a4bf81 Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Tue, 12 Sep 2023 14:04:53 +0200 Subject: [PATCH 1/6] docs: add usage section --- README.md | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e5c4f56..43e2351 100644 --- a/README.md +++ b/README.md @@ -17,34 +17,39 @@ 🚧 This project is under heavy development. More features are coming soon! -## Usage +## Install Install package: ```sh -# npm npm install citty +``` -# yarn +```sh yarn add citty +``` -# pnpm +```sh pnpm install citty ``` -Import: +## Usage + +### Import Package ```js // ESM import { defineCommand, runMain } from "citty"; +``` +```js // CommonJS const { defineCommand, runMain } = require("citty"); ``` -Define main command to run: +### Main Command -```ts +```js import { defineCommand, runMain } from "citty"; const main = defineCommand({ @@ -64,7 +69,7 @@ const main = defineCommand({ description: "Use friendly greeting", }, }, - run({ args }) { + run({ args }) { // Command can be async console.log(`${args.friendly ? "Hi" : "Greetings"} ${args.name}!`); }, }); @@ -72,11 +77,151 @@ const main = defineCommand({ runMain(main); ``` +### Sub Commands + +You can define sub commands and attach them to main command to create a nested command structure. This is recursive so you can attach sub commands to sub commands, etc. + +```js +import { defineCommand, runMain } from "citty"; + +// First, you define a new command +const sub = defineCommand({ + meta: { + name: "sub", + description: "Sub command", + }, + args: { // Sub commands can have their own arguments like any other command + name: { + type: "positional", + description: "Your name", + required: true, + }, + }, + run({ args }) { // Command can be async + console.log(`Hello ${args.name}!`); + }, +}); + +// Then, you define a main command and attach sub command to it +const main = defineCommand({ + meta: { + name: "hello", + version: "1.0.0", + description: "My Awesome CLI App", + }, + commands: { + sub, // Attach sub command to main command + }, +}); + +runMain(main); +``` + +### Hooks + +`citty` supports a `setup` and `cleanup` functions that are called before and after command execution. This is useful for setting up and cleaning up resources. + +Only the `setup` and `cleanup` functions from the command called are executed. For example, if you run `hello sub`, only the `setup` and `cleanup` functions from `sub` command are executed and not the ones from `hello` command. + +```js +import { defineCommand, runMain } from "citty"; + +const main = defineCommand({ + meta: { + name: "hello", + version: "1.0.0", + description: "My Awesome CLI App", + }, + setup() { // Setup function is called before command execution or before any sub command execution + console.log("Setting up..."); + }, + cleanup() { // Cleanup function is called after command execution or after any sub command execution + console.log("Cleaning up..."); + }, + run() { + console.log("Hello World!"); + }, +}); + +runMain(main); +``` + +### Lazy Load Commands + +For large CLI apps, you may want to only load the command that is being executed. + +First, create a command in a file and export it. + +```js +import { defineCommand } from "citty"; + +export default defineCommand({ + meta: { + name: "sub", + description: "Sub command", + }, + run({ args }) { + console.log(`Hello ${args.name}!`); + }, +}); +``` + +Then, create the main command and import the sub command. + +```js +const main = defineCommand({ + meta: { + name: "hello", + version: "1.0.0", + description: "My Awesome CLI App", + }, + commands: { + sub: () => import("./sub.js").then((m) => m.default), // Lazy Import Sub Command + }, +}); +``` + +Now, when you run `hello sub`, the sub command will be loaded and executed. This avoid to load all commands at once. + +### Publish CLI App as an Executable + +You must first bundle your CLI app. To do so, you can use [`unjs/unbuild`](https://github.com/unjs/unbuild). + +Then, you must create a file named `index.mjs` in a folder named `bin` at the root of your package. This file must export the main command from the `dist` build. + +```js +#!/usr/bin/env node + +import { runMain } from '../dist/index.mjs' + +runMain() +``` + +Then, you will need to update your `package.json` file to enable the usage as a CLI: + +```json +{ + "type": "module", + "bin": "./bin/index.mjs", + // Name of the CLI will be the name of the package. You can provide an object to change the name. + // @see https://docs.npmjs.com/cli/v10/configuring-npm/package-json#bin + // "bin": { + // "my-cli": "./bin/index.mjs" + // }, + "files": [ + "bin", + "dist" + ] +} +``` + +You're ready to publish your CLI app to npm! + ## Utils ### `defineCommand` -`defineCommand` is a type helper for defining commands. +A type helper for defining commands. ### `runMain` @@ -84,7 +229,7 @@ Runs a command with usage support and graceful error handling. ### `createMain` -Create a wrapper around command that calls `runMain` when called. +Create the main command that can be executed later. Return a [`runMain`](#runmain) function. ### `runCommand` From 7e9c4b379960f0d09dcf8d53a1ac071ce4016f2d Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Tue, 12 Sep 2023 14:07:38 +0200 Subject: [PATCH 2/6] revert: docs change in utils --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43e2351..b947e31 100644 --- a/README.md +++ b/README.md @@ -229,7 +229,7 @@ Runs a command with usage support and graceful error handling. ### `createMain` -Create the main command that can be executed later. Return a [`runMain`](#runmain) function. +Create a wrapper around command that calls `runMain` when called. ### `runCommand` From fd33d68741b63293f56f9d0da7fe095316425eeb Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Tue, 12 Sep 2023 15:52:38 +0200 Subject: [PATCH 3/6] docs: update using review from jd-solanki --- README.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b947e31..1ff3c05 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,8 @@ const { defineCommand, runMain } = require("citty"); ### Main Command +Create a main command is the first step to create a CLI app. You can do it in a `index.mjs` file. + ```js import { defineCommand, runMain } from "citty"; @@ -77,6 +79,13 @@ const main = defineCommand({ runMain(main); ``` +Then, you can execute your CLI app: + +```sh +node index.mjs john +# output: Greetings john! +``` + ### Sub Commands You can define sub commands and attach them to main command to create a nested command structure. This is recursive so you can attach sub commands to sub commands, etc. @@ -117,6 +126,13 @@ const main = defineCommand({ runMain(main); ``` +Then, you can execute your CLI app: + +```sh +node index.mjs sub john +# output: Hello john! +``` + ### Hooks `citty` supports a `setup` and `cleanup` functions that are called before and after command execution. This is useful for setting up and cleaning up resources. @@ -146,6 +162,20 @@ const main = defineCommand({ runMain(main); ``` +Now, you can run your CLI app: + +```sh +node index.mjs +``` + +And you will see: + +```sh +Setting up... +Hello World! +Cleaning up... +``` + ### Lazy Load Commands For large CLI apps, you may want to only load the command that is being executed. @@ -181,7 +211,7 @@ const main = defineCommand({ }); ``` -Now, when you run `hello sub`, the sub command will be loaded and executed. This avoid to load all commands at once. +Now, when you run `node index.mjs sub`, the sub command will be loaded and executed. This avoid to load all commands at once when you start your app. ### Publish CLI App as an Executable From 96dfd7bee7c7c58fd4f2031fab40ec3d22c026ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9ban?= Date: Tue, 12 Sep 2023 15:53:43 +0200 Subject: [PATCH 4/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ff3c05..7a245da 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ Cleaning up... For large CLI apps, you may want to only load the command that is being executed. -First, create a command in a file and export it. +First, create a command in a file named `sub.mjs` and export it. ```js import { defineCommand } from "citty"; From 245382d3fe155ff6ff5efb51bd260755b8627987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9ban?= Date: Tue, 12 Sep 2023 15:53:49 +0200 Subject: [PATCH 5/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a245da..e92f21b 100644 --- a/README.md +++ b/README.md @@ -206,7 +206,7 @@ const main = defineCommand({ description: "My Awesome CLI App", }, commands: { - sub: () => import("./sub.js").then((m) => m.default), // Lazy Import Sub Command + sub: () => import("./sub.mjs").then((m) => m.default), // Lazy Import Sub Command }, }); ``` From 99a43fc23429ad4f3e27cb9e3cf0d858ab5d9d15 Mon Sep 17 00:00:00 2001 From: barbapapazes Date: Wed, 13 Sep 2023 01:37:18 +0200 Subject: [PATCH 6/6] docs: add arguments section --- README.md | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e92f21b..789efb9 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,13 @@ export default defineCommand({ name: "sub", description: "Sub command", }, + args: { + name: { + type: "positional", + description: "Your name", + required: true, + }, + }, run({ args }) { console.log(`Hello ${args.name}!`); }, @@ -247,7 +254,105 @@ Then, you will need to update your `package.json` file to enable the usage as a You're ready to publish your CLI app to npm! -## Utils +## Args Commands + +When you create a command with `defineCommand`, you can provide a `args` object to define the arguments of the command. For example: + +```js +const main = defineCommand({ + // ... + args: { + name: { + type: "positional", + description: "Your name", + required: true, + }, + friendly: { + type: "boolean", + description: "Use friendly greeting", + }, + }, + // ... +}); +``` + +There is 3 types of arguments: + +- `boolean`: A boolean flag. Can be set to `true` or `false`. Can be set using a shortcut like `--flag` (if `false` is the default or `--no-flag` if `true` is the default). +- `string`: A string flag. Can be set using a value like `--flag value`. +- `positional`: A positional argument. Can be set using a value like `value`. + +Then, you can add a description to each argument. This description will be used to generate the usage of the command. You can also use `required` to make an argument required or not. + +You can also set a default value for each argument. For example: + +```js +const main = defineCommand({ + // ... + args: { + name: { + type: "positional", + description: "Your name", + default: "Foo", + }, + friendly: { + type: "boolean", + description: "Use friendly greeting", + default: true, + }, + }, + // ... +}); +``` + +Finally, you can set aliases for each argument. For example: + +```js +const main = defineCommand({ + // ... + args: { + name: { + type: "positional", + description: "Your name", + required: true, + alias: ["n", "N"], + }, + friendly: { + type: "boolean", + description: "Use friendly greeting", + alias: ["f", "F"], + }, + }, + // ... +}); +``` + +Then, use can use these arguments in the `run` function of the command because they are passed as parameters: + +```js +const main = defineCommand({ + meta: { + name: "sub", + description: "Sub command", + }, + args: { + name: { + type: "positional", + description: "Your name", + required: true, + }, + }, + run({ args }) { + console.log(`Hello ${args.name}!`); + }, +}); +``` + +`args` is an object fully typed with the type of each argument. For example, if you have a `string` argument, the type of the argument will be `string`. If you have a `boolean` argument, the type of the argument will be `boolean`. + +## Build-in Commands + +## API ### `defineCommand`