From 04c987d28851eb7de65b411336285552d9ff0675 Mon Sep 17 00:00:00 2001 From: Jason Yu Date: Sun, 15 Mar 2026 08:04:08 +0000 Subject: [PATCH 1/2] upgrade deps, add tests, improve CI and docs - Upgrade all dependencies to latest versions - Update biome.json with stricter linting rules (noFloatingPromises, noUnusedImports) - Fix floating promise warnings by adding void to async IIFEs - Fix confirm --help exiting with code 1 when no message provided - Fix select/checkbox not detecting missing choices when no -c flag given - Add integration tests for all packages using node:test - Add example script tests with local npx stub - Add lint and test steps to CD workflow before publish - Remove NPM_TOKEN from CD (switched to trusted publishing) - Add pnpm catalogMode: strict and cleanupUnusedCatalogs settings - Rewrite README with clearer structure, option tables, and full example Co-Authored-By: Claude Opus 4.6 --- .github/workflows/cd.yml | 7 +- README.md | 135 ++++++----- biome.json | 41 +++- package.json | 5 +- packages/checkbox/index.js | 4 +- packages/checkbox/index.test.js | 38 +++ packages/checkbox/package.json | 2 +- packages/confirm/index.js | 12 +- packages/confirm/index.test.js | 37 +++ packages/confirm/package.json | 2 +- packages/editor/package.json | 2 +- packages/editor/prompt.js | 2 +- packages/input/index.js | 2 +- packages/input/index.test.js | 25 ++ packages/input/package.json | 2 +- packages/number/index.js | 2 +- packages/number/index.test.js | 25 ++ packages/number/package.json | 2 +- packages/password/index.js | 2 +- packages/password/index.test.js | 25 ++ packages/password/package.json | 2 +- packages/select/index.js | 4 +- packages/select/index.test.js | 41 ++++ packages/select/package.json | 2 +- packages/test-helper/index.js | 64 +++++ packages/test-helper/npx-stub.sh | 24 ++ packages/test-helper/package.json | 7 + pnpm-lock.yaml | 387 +++++++++++++----------------- pnpm-workspace.yaml | 18 +- 29 files changed, 604 insertions(+), 317 deletions(-) create mode 100644 packages/checkbox/index.test.js create mode 100644 packages/confirm/index.test.js create mode 100644 packages/input/index.test.js create mode 100644 packages/number/index.test.js create mode 100644 packages/password/index.test.js create mode 100644 packages/select/index.test.js create mode 100644 packages/test-helper/index.js create mode 100755 packages/test-helper/npx-stub.sh create mode 100644 packages/test-helper/package.json diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 94eff52..2134fed 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -31,8 +31,13 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Lint + run: npx biome check + + - name: Test + run: pnpm test + - name: Publish to npm run: pnpm -r publish --access public env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_CONFIG_PROVENANCE: true diff --git a/README.md b/README.md index 9afd98d..bf628ed 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,59 @@ # @inquirer-cli -The `@inquirer-cli` suite provides standalone command-line interface (CLI) tools for various prompt types, leveraging the robust functionality of [Inquirer.js](https://github.com/SBoudrias/Inquirer.js). These tools enable developers to incorporate interactive prompts directly into shell scripts or command pipelines without the need to write custom JavaScript code. +Interactive command-line prompts for shell scripts, powered by [Inquirer.js](https://github.com/SBoudrias/Inquirer.js). -## Installation +The `@inquirer-cli` suite lets you add interactive prompts (text input, confirmations, selections, and more) to any shell script - no JavaScript required. -No installation required. Simply do to start the prompt: +## Quick Start + +No installation needed. Run any prompt directly with `npx`: ```bash -$ npx -y @inquirer-cli/ [...args] +name=$(npx -y @inquirer-cli/input "What is your name?") +echo "Hello, $name!" ``` -> **Note**: The `-y` flag is used with `npx` to skip the installation prompt. This is necessary because the CLI's output is consumed by `$()` in bash, which would otherwise cause the script to freeze. +> **Why `-y`?** The `-y` flag auto-confirms the `npx` install prompt. Without it, the confirmation prompt would interfere when capturing output with `$()`. -Replace `` with the desired prompt listed in the next section. +## How It Works -## Prompts +Each prompt renders its UI to **stderr** and writes the user's answer to **stdout**. This means you can capture the answer with `$()` while the prompt remains visible in the terminal. -Each prompt type is available as a separate package under the `@inquirer-cli` scope: -Each package corresponds to an Inquirer.js prompt type and is designed to be used independently. +## Prompts ### [`@inquirer-cli/input`](./packages/input/README.md) -> Uses [@inquirer/input](https://github.com/SBoudrias/Inquirer.js/tree/main/packages/input). - -Prompts the user for text input. - -Example: +Prompts for free-text input. ```bash name=$(npx -y @inquirer-cli/input -r "What is your name?") echo "Hello, $name!" ``` -### [`@inquirer-cli/number`](./packages/number/README.md) +| Option | Description | +|---|---| +| `` | The prompt message (required) | +| `-r, --required` | Reject empty input | +| `-h, --help` | Show help | -> Uses [@inquirer/number](https://github.com/SBoudrias/Inquirer.js/tree/main/packages/number). - -Requests a numeric input from the user. +### [`@inquirer-cli/number`](./packages/number/README.md) -Ask the user for their age: +Prompts for numeric input. ```bash age=$(npx -y @inquirer-cli/number -r "Enter your age") echo "You are $age years old." ``` -### [`@inquirer-cli/confirm`](./packages/confirm/README.md) - -> Uses [@inquirer/confirm](https://github.com/SBoudrias/Inquirer.js/tree/main/packages/confirm). +| Option | Description | +|---|---| +| `` | The prompt message (required) | +| `-r, --required` | Reject empty input | +| `-h, --help` | Show help | -Presents a yes/no confirmation to the user. +### [`@inquirer-cli/confirm`](./packages/confirm/README.md) -Confirm an action with the user: +Prompts for a yes/no confirmation. Outputs `true` or `false`. ```bash if $(npx -y @inquirer-cli/confirm "Do you want to continue?"); then @@ -61,79 +63,96 @@ else fi ``` -### [`@inquirer-cli/select`](./packages/select/README.md) - -> Uses [@inquirer/select](https://github.com/SBoudrias/Inquirer.js/tree/main/packages/select). +| Option | Description | +|---|---| +| `` | The prompt message (required) | +| `-y, --yes` | Default to "yes" instead of "no" | +| `-h, --help` | Show help | -Offers a list of options for the user to select one. +### [`@inquirer-cli/select`](./packages/select/README.md) -Let the user choose a fruit: +Prompts the user to pick one option from a list. ```bash -fruit=$(npx "@inquirer-cli/select" -c "Apple" -c "Banana" -c "Cherry" "Pick a fruit") +fruit=$(npx -y @inquirer-cli/select -c Apple -c Banana -c Cherry "Pick a fruit") echo "You selected: $fruit" ``` -### [`@inquirer-cli/checkbox`](./packages/checkbox/README.md) - -> Uses [@inquirer/checkbox](https://github.com/SBoudrias/Inquirer.js/tree/main/packages/checkbox). +| Option | Description | +|---|---| +| `` | The prompt message (required) | +| `-c, --choice ` | Add a choice (use multiple times, at least one required) | +| `-r, --required` | Reject empty selection | +| `-h, --help` | Show help | -Allows the user to select multiple options from a list. +### [`@inquirer-cli/checkbox`](./packages/checkbox/README.md) -Allow the user to select multiple options: +Prompts the user to select multiple options from a list. Outputs selected values as a space-separated string. ```bash -choices=$(npx -y @inquirer-cli/checkbox -r "Select your favorite colors" -c "Red" -c "Blue" -c "Green") +colors=$(npx -y @inquirer-cli/checkbox -r "Select your favorite colors" -c Red -c Blue -c Green) echo "You selected:" -for choice in $choices; do - echo "- $choice" +for color in $colors; do + echo "- $color" done ``` -### [`@inquirer-cli/password`](./packages/password/README.md) - -> Uses [@inquirer/password](https://github.com/SBoudrias/Inquirer.js/tree/main/packages/password). +| Option | Description | +|---|---| +| `` | The prompt message (required) | +| `-c, --choice ` | Add a choice (use multiple times, at least one required) | +| `-r, --required` | Require at least one selection | +| `-h, --help` | Show help | -Prompts the user for sensitive information with input masking. +### [`@inquirer-cli/password`](./packages/password/README.md) -Prompt the user for a password: +Prompts for sensitive input with masking. ```bash -password=$(npx -y @inquirer-cli/password -r "Enter your password") -echo "Password received. $password" +secret=$(npx -y @inquirer-cli/password -r "Enter your password") ``` -### ~~[`@inquirer-cli/editor`](./packages/editor/README.md)~~ (🚧 NOT SUPPORTED YET) +| Option | Description | +|---|---| +| `` | The prompt message (required) | +| `-r, --required` | Reject empty input | +| `-h, --help` | Show help | -I wished `editor` would work like the following but sadly I couldn't make it work. Any help on this would be appreciated! +### ~~[`@inquirer-cli/editor`](./packages/editor/README.md)~~ (not supported yet) -> Uses [@inquirer/editor](https://github.com/SBoudrias/Inquirer.js/tree/main/packages/editor). +Opens the user's default editor for multi-line input. This package is currently not functional due to technical difficulties. Contributions welcome! ```bash notes=$(npx -y @inquirer-cli/editor "Write your notes") echo "Your notes: $notes" ``` -## Options +## Full Example + +```bash +#!/bin/sh -Each CLI prompt accepts various options to customize its behavior. Common options include: +name=$(npx -y @inquirer-cli/input -r "What is your name?") +age=$(npx -y @inquirer-cli/number -r "How old are you?") +lang=$(npx -y @inquirer-cli/select -c JavaScript -c Python -c Go "Favorite language?") -- `--choices` (or `-c`): - A space-separated list of choices (applicable to `select` and `checkbox` prompts). +if $(npx -y @inquirer-cli/confirm "Save profile?"); then + echo "Saved: $name, age $age, likes $lang" +fi +``` -- `--required` (or `-r`): - When set, empty responses will be reprompted (not applicable to `confirm`). +## Requirements -For a full list of options and detailed usage, refer to the documentation of the respective `@inquirer-cli` package. +- Node.js >= 16 ## Author -[@ycmjason](https://github.com/ycmjason)) +[@ycmjason](https://github.com/ycmjason) ## License -Licensed under the MIT License. +MIT --- -*The `@inquirer-cli` project is an independent initiative and is not affiliated with or endorsed by Inquirer.js.* +*`@inquirer-cli` is an independent project and is not affiliated with or endorsed by Inquirer.js.* diff --git a/biome.json b/biome.json index 4134296..f416f73 100644 --- a/biome.json +++ b/biome.json @@ -1,12 +1,30 @@ { "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, "linter": { "enabled": true, "rules": { - "recommended": true + "recommended": true, + "suspicious": { + "noExplicitAny": "off" + }, + "correctness": { + "noUnusedImports": { + "level": "error", + "fix": "safe" + } + }, + "nursery": { + "noFloatingPromises": "error" + } } }, "assist": { + "enabled": true, "actions": { "source": { "organizeImports": "on" @@ -19,11 +37,6 @@ "indentWidth": 2, "lineWidth": 100 }, - "json": { - "formatter": { - "enabled": true - } - }, "javascript": { "formatter": { "trailingCommas": "all", @@ -31,5 +44,21 @@ "arrowParentheses": "asNeeded", "quoteStyle": "single" } + }, + "files": { + "includes": [ + "**/*", + "!**/.cache/*", + "!**/.claude/*", + "!**/dist/*", + "!**/node_modules/*", + "!**/output/*", + "!**/*.gen.*" + ] + }, + "css": { + "parser": { + "tailwindDirectives": true + } } } diff --git a/package.json b/package.json index 9ff8fac..2b39dd9 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "shell", "automation" ], + "type": "module", "license": "MIT", "author": { "name": "YCM Jason", @@ -24,6 +25,7 @@ "url": "https://github.com/fishballapp/inquirer-cli/issues" }, "scripts": { + "test": "node --test 'packages/*/index.test.js'", "check:fix": "biome check --fix", "version:bump": "pnpm -r exec pnpm version --no-git-tag-version $(npx -y @inquirer-cli/select -c patch -c minor -c major 'Select Version') && pnpm check:fix" }, @@ -31,7 +33,8 @@ "minimist": "catalog:" }, "devDependencies": { - "@biomejs/biome": "^2.2.4" + "@biomejs/biome": "^2.4.7", + "@inquirer-cli/test-helper": "workspace:*" }, "packageManager": "pnpm@10.16.0+sha512.8066e7b034217b700a9a4dbb3a005061d641ba130a89915213a10b3ca4919c19c037bec8066afdc559b89635fdb806b16ea673f2468fbb28aabfa13c53e3f769" } diff --git a/packages/checkbox/index.js b/packages/checkbox/index.js index 8fd3261..035570e 100755 --- a/packages/checkbox/index.js +++ b/packages/checkbox/index.js @@ -9,7 +9,7 @@ const args = minimist(process.argv.slice(2), { alias: { choice: 'c', required: 'r' }, }); const message = args._[0]; -const choices = [args.choice].flat(); +const choices = [args.choice].flat().filter(c => c !== undefined); const required = args.required; function showHelp() { @@ -42,7 +42,7 @@ if (choices.length === 0) { process.exit(1); } -(async () => { +void (async () => { const answer = await checkbox({ message, choices, required }, { output: process.stderr }); console.log(answer.join(' ')); // Log as a space-separated string })(); diff --git a/packages/checkbox/index.test.js b/packages/checkbox/index.test.js new file mode 100644 index 0000000..c197a47 --- /dev/null +++ b/packages/checkbox/index.test.js @@ -0,0 +1,38 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { run, runScript } from '@inquirer-cli/test-helper'; + +const cmd = ['packages/checkbox/index.js']; + +describe('checkbox', () => { + it('selects first choice with space then submits', async () => { + const { stdout, code } = await run([...cmd, 'Pick', '-c', 'Red', '-c', 'Blue'], ' \r'); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, 'Red'); + }); + + it('selects multiple choices', async () => { + const { stdout, code } = await run([...cmd, 'Pick', '-c', 'Red', '-c', 'Blue'], stdin => { + stdin.write(' '); + stdin.write('\x1b[B'); + setTimeout(() => { + stdin.write(' '); + stdin.write('\r'); + }, 100); + }); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, 'Red Blue'); + }); + + it('shows help', async () => { + const { stdout, code } = await run([...cmd, '--help'], ''); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Usage:')); + }); + + it('example script works', async () => { + const { stdout, code } = await runScript('packages/checkbox/example/example.sh', ' \r'); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Choices selected: Red')); + }); +}); diff --git a/packages/checkbox/package.json b/packages/checkbox/package.json index 84f928b..e10accb 100644 --- a/packages/checkbox/package.json +++ b/packages/checkbox/package.json @@ -1,6 +1,6 @@ { "name": "@inquirer-cli/checkbox", - "version": "1.0.8", + "version": "1.0.9", "description": "CLI tool for selecting multiple options using Inquirer.js", "main": "index.js", "bin": "index.js", diff --git a/packages/confirm/index.js b/packages/confirm/index.js index 351508c..a3fd2d6 100755 --- a/packages/confirm/index.js +++ b/packages/confirm/index.js @@ -24,18 +24,18 @@ Example: npx @inquirer-cli/confirm -y "Do you want to continue?"`); } -if (!message) { - console.error('Error: is required.'); +if (args.help || args.h) { showHelp(); - process.exit(1); + process.exit(0); } -if (args.help || args.h) { +if (!message) { + console.error('Error: is required.'); showHelp(); - process.exit(0); + process.exit(1); } -(async () => { +void (async () => { const answer = await confirm( { message, diff --git a/packages/confirm/index.test.js b/packages/confirm/index.test.js new file mode 100644 index 0000000..c07ec27 --- /dev/null +++ b/packages/confirm/index.test.js @@ -0,0 +1,37 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { run, runScript } from '@inquirer-cli/test-helper'; + +const cmd = ['packages/confirm/index.js']; + +describe('confirm', () => { + it('confirms with y', async () => { + const { stdout, code } = await run([...cmd, 'Continue?'], 'y\r'); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, 'true'); + }); + + it('denies with n', async () => { + const { stdout, code } = await run([...cmd, 'Continue?'], 'n\r'); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, 'false'); + }); + + it('shows help', async () => { + const { stdout, code } = await run([...cmd, '--help'], ''); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Usage:')); + }); + + it('example script works (yes)', async () => { + const { stdout, code } = await runScript('packages/confirm/example/example.sh', 'y\r'); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Continuing...')); + }); + + it('example script works (no)', async () => { + const { stdout, code } = await runScript('packages/confirm/example/example.sh', 'n\r'); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Aborted.')); + }); +}); diff --git a/packages/confirm/package.json b/packages/confirm/package.json index a46ac10..38b8587 100644 --- a/packages/confirm/package.json +++ b/packages/confirm/package.json @@ -1,6 +1,6 @@ { "name": "@inquirer-cli/confirm", - "version": "1.0.8", + "version": "1.0.9", "description": "CLI tool for yes/no confirmation using Inquirer.js", "main": "index.js", "bin": "index.js", diff --git a/packages/editor/package.json b/packages/editor/package.json index 1b6add0..5e1cfab 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,7 +1,7 @@ { "name": "@inquirer-cli/editor", "private": true, - "version": "1.0.8", + "version": "1.0.9", "description": "CLI tool for multi-line input using the default editor via Inquirer.js", "main": "index.js", "bin": "index.js", diff --git a/packages/editor/prompt.js b/packages/editor/prompt.js index 81528cc..758f670 100644 --- a/packages/editor/prompt.js +++ b/packages/editor/prompt.js @@ -1,6 +1,6 @@ import editor from '@inquirer/editor'; -(async () => { +void (async () => { const answer = await editor({ message: process.env.INQUIRER_MESSAGE }); console.error(`"${answer}"`); })(); diff --git a/packages/input/index.js b/packages/input/index.js index c77fe44..12fc499 100755 --- a/packages/input/index.js +++ b/packages/input/index.js @@ -34,7 +34,7 @@ if (!message) { process.exit(1); } -(async () => { +void (async () => { const answer = await input({ message, required }, { output: process.stderr }); console.log(answer); })(); diff --git a/packages/input/index.test.js b/packages/input/index.test.js new file mode 100644 index 0000000..367a5cb --- /dev/null +++ b/packages/input/index.test.js @@ -0,0 +1,25 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { run, runScript } from '@inquirer-cli/test-helper'; + +const cmd = ['packages/input/index.js']; + +describe('input', () => { + it('accepts text input', async () => { + const { stdout, code } = await run([...cmd, 'Name?'], 'Alice\r'); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, 'Alice'); + }); + + it('shows help', async () => { + const { stdout, code } = await run([...cmd, '--help'], ''); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Usage:')); + }); + + it('example script works', async () => { + const { stdout, code } = await runScript('packages/input/example/example.sh', 'Alice\r'); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Name received: Alice')); + }); +}); diff --git a/packages/input/package.json b/packages/input/package.json index 36e714c..46b80be 100644 --- a/packages/input/package.json +++ b/packages/input/package.json @@ -1,6 +1,6 @@ { "name": "@inquirer-cli/input", - "version": "1.0.8", + "version": "1.0.9", "description": "CLI tool for text input using Inquirer.js", "main": "index.js", "bin": "index.js", diff --git a/packages/number/index.js b/packages/number/index.js index 0834e8e..33cb5e3 100755 --- a/packages/number/index.js +++ b/packages/number/index.js @@ -34,7 +34,7 @@ if (!message) { process.exit(1); } -(async () => { +void (async () => { const answer = await number({ message, required }, { output: process.stderr }); console.log(answer); })(); diff --git a/packages/number/index.test.js b/packages/number/index.test.js new file mode 100644 index 0000000..9bea3e2 --- /dev/null +++ b/packages/number/index.test.js @@ -0,0 +1,25 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { run, runScript } from '@inquirer-cli/test-helper'; + +const cmd = ['packages/number/index.js']; + +describe('number', () => { + it('accepts numeric input', async () => { + const { stdout, code } = await run([...cmd, 'Age?'], '42\r'); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, '42'); + }); + + it('shows help', async () => { + const { stdout, code } = await run([...cmd, '--help'], ''); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Usage:')); + }); + + it('example script works', async () => { + const { stdout, code } = await runScript('packages/number/example/example.sh', '25\r'); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Age received: 25')); + }); +}); diff --git a/packages/number/package.json b/packages/number/package.json index 2349642..428a68b 100644 --- a/packages/number/package.json +++ b/packages/number/package.json @@ -1,6 +1,6 @@ { "name": "@inquirer-cli/number", - "version": "1.0.8", + "version": "1.0.9", "description": "CLI tool for numeric input using Inquirer.js", "main": "index.js", "bin": "index.js", diff --git a/packages/password/index.js b/packages/password/index.js index 787e9e1..a73786c 100755 --- a/packages/password/index.js +++ b/packages/password/index.js @@ -34,7 +34,7 @@ if (!message) { process.exit(1); } -(async () => { +void (async () => { const answer = await password({ message, required }, { output: process.stderr }); console.log(answer); })(); diff --git a/packages/password/index.test.js b/packages/password/index.test.js new file mode 100644 index 0000000..67885b7 --- /dev/null +++ b/packages/password/index.test.js @@ -0,0 +1,25 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { run, runScript } from '@inquirer-cli/test-helper'; + +const cmd = ['packages/password/index.js']; + +describe('password', () => { + it('accepts password input', async () => { + const { stdout, code } = await run([...cmd, 'Secret?'], 'hunter2\r'); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, 'hunter2'); + }); + + it('shows help', async () => { + const { stdout, code } = await run([...cmd, '--help'], ''); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Usage:')); + }); + + it('example script works', async () => { + const { stdout, code } = await runScript('packages/password/example/example.sh', 'secret123\r'); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Password received. secret123')); + }); +}); diff --git a/packages/password/package.json b/packages/password/package.json index 3d5282a..587ee86 100644 --- a/packages/password/package.json +++ b/packages/password/package.json @@ -1,6 +1,6 @@ { "name": "@inquirer-cli/password", - "version": "1.0.8", + "version": "1.0.9", "description": "CLI tool for masked password input using Inquirer.js", "main": "index.js", "bin": "index.js", diff --git a/packages/select/index.js b/packages/select/index.js index 0aff612..bc7eaac 100755 --- a/packages/select/index.js +++ b/packages/select/index.js @@ -12,7 +12,7 @@ const args = minimist(process.argv.slice(2), { }, }); const message = args._[0]; -const choices = [args.choice].flat(); +const choices = [args.choice].flat().filter(c => c !== undefined); const required = args.required; function showHelp() { @@ -45,7 +45,7 @@ if (choices.length === 0) { process.exit(1); } -(async () => { +void (async () => { const answer = await select({ message, choices, required }, { output: process.stderr }); console.log(answer); })(); diff --git a/packages/select/index.test.js b/packages/select/index.test.js new file mode 100644 index 0000000..bd3b2e3 --- /dev/null +++ b/packages/select/index.test.js @@ -0,0 +1,41 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { run, runScript } from '@inquirer-cli/test-helper'; + +const cmd = ['packages/select/index.js']; + +describe('select', () => { + it('selects the first choice by default', async () => { + const { stdout, code } = await run([...cmd, 'Pick', '-c', 'Apple', '-c', 'Banana'], '\r'); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, 'Apple'); + }); + + it('selects second choice with arrow down', async () => { + const { stdout, code } = await run([...cmd, 'Pick', '-c', 'Apple', '-c', 'Banana'], '\x1b[B\r'); + assert.strictEqual(code, 0); + assert.strictEqual(stdout, 'Banana'); + }); + + it('shows help', async () => { + const { stdout, code } = await run([...cmd, '--help'], ''); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('Usage:')); + }); + + it('errors without message', async () => { + const { code } = await run(cmd, ''); + assert.strictEqual(code, 1); + }); + + it('errors without choices', async () => { + const { code } = await run([...cmd, 'Pick'], ''); + assert.strictEqual(code, 1); + }); + + it('example script works', async () => { + const { stdout, code } = await runScript('packages/select/example/example.sh', '\r'); + assert.strictEqual(code, 0); + assert.ok(stdout.includes('You selected: Apple')); + }); +}); diff --git a/packages/select/package.json b/packages/select/package.json index 077bf23..edd8bb9 100644 --- a/packages/select/package.json +++ b/packages/select/package.json @@ -1,6 +1,6 @@ { "name": "@inquirer-cli/select", - "version": "1.0.8", + "version": "1.0.9", "description": "CLI tool for selecting an option using Inquirer.js", "main": "index.js", "bin": { diff --git a/packages/test-helper/index.js b/packages/test-helper/index.js new file mode 100644 index 0000000..23a801d --- /dev/null +++ b/packages/test-helper/index.js @@ -0,0 +1,64 @@ +import { spawn } from 'node:child_process'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); +const NPX_STUB = join(dirname(fileURLToPath(import.meta.url)), 'npx-stub.sh'); + +function spawnWithInput(child, input, timeout) { + return new Promise((resolve, reject) => { + let stdout = ''; + let stderr = ''; + let inputSent = false; + + child.stdout.on('data', d => { + stdout += d; + }); + + child.stderr.on('data', d => { + stderr += d; + if (!inputSent && stderr.includes('?')) { + inputSent = true; + setTimeout(() => { + if (typeof input === 'string') { + child.stdin.write(input); + } else { + input(child.stdin); + } + }, 100); + } + }); + + const timer = setTimeout(() => { + child.kill(); + reject(new Error(`Timed out.\nstdout: ${stdout}\nstderr: ${stderr}`)); + }, timeout); + + child.on('close', code => { + clearTimeout(timer); + resolve({ stdout: stdout.trim(), stderr, code }); + }); + + child.on('error', reject); + }); +} + +export function run(args, input, { timeout = 5000 } = {}) { + const child = spawn('node', args, { + stdio: ['pipe', 'pipe', 'pipe'], + }); + return spawnWithInput(child, input, timeout); +} + +export function runScript(scriptPath, input, { timeout = 5000 } = {}) { + const child = spawn('sh', [scriptPath], { + stdio: ['pipe', 'pipe', 'pipe'], + cwd: ROOT, + env: { + ...process.env, + PATH: `${dirname(NPX_STUB)}:${process.env.PATH}`, + INQUIRER_CLI_ROOT: ROOT, + }, + }); + return spawnWithInput(child, input, timeout); +} diff --git a/packages/test-helper/npx-stub.sh b/packages/test-helper/npx-stub.sh new file mode 100755 index 0000000..78a6a50 --- /dev/null +++ b/packages/test-helper/npx-stub.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# Stub npx that resolves @inquirer-cli/* to local packages. +# Strips the -y flag and maps package names to local index.js files. + +args="" +pkg="" +for arg in "$@"; do + case "$arg" in + -y) ;; + @inquirer-cli/*) + pkg=$(echo "$arg" | sed 's|@inquirer-cli/||' | tr -d '"') + ;; + *) + args="$args \"$arg\"" + ;; + esac +done + +if [ -n "$pkg" ]; then + eval exec node "$INQUIRER_CLI_ROOT/packages/$pkg/index.js" $args +else + # Fall through to real npx for non-inquirer-cli packages + eval exec "$(which -a npx | grep -v "$0" | head -1)" "$@" +fi diff --git a/packages/test-helper/package.json b/packages/test-helper/package.json new file mode 100644 index 0000000..9022c61 --- /dev/null +++ b/packages/test-helper/package.json @@ -0,0 +1,7 @@ +{ + "name": "@inquirer-cli/test-helper", + "private": true, + "version": "0.0.1", + "type": "module", + "main": "index.js" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f55349..4dee3d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,26 +7,26 @@ settings: catalogs: default: '@inquirer/checkbox': - specifier: ^4.2.2 - version: 4.2.2 + specifier: ^5.1.0 + version: 5.1.0 '@inquirer/confirm': - specifier: ^5.1.16 - version: 5.1.16 + specifier: ^6.0.8 + version: 6.0.8 '@inquirer/editor': - specifier: ^4.2.18 - version: 4.2.18 + specifier: ^5.0.8 + version: 5.0.8 '@inquirer/input': - specifier: ^4.2.2 - version: 4.2.2 + specifier: ^5.0.8 + version: 5.0.8 '@inquirer/number': - specifier: ^3.0.18 - version: 3.0.18 + specifier: ^4.0.8 + version: 4.0.8 '@inquirer/password': - specifier: ^4.0.18 - version: 4.0.18 + specifier: ^5.0.8 + version: 5.0.8 '@inquirer/select': - specifier: ^4.3.2 - version: 4.3.2 + specifier: ^5.1.0 + version: 5.1.0 minimist: specifier: ^1.2.8 version: 1.2.8 @@ -40,14 +40,17 @@ importers: version: 1.2.8 devDependencies: '@biomejs/biome': - specifier: ^2.2.4 - version: 2.2.4 + specifier: ^2.4.7 + version: 2.4.7 + '@inquirer-cli/test-helper': + specifier: workspace:* + version: link:packages/test-helper packages/checkbox: dependencies: '@inquirer/checkbox': specifier: 'catalog:' - version: 4.2.2 + version: 5.1.0 minimist: specifier: 'catalog:' version: 1.2.8 @@ -56,7 +59,7 @@ importers: dependencies: '@inquirer/confirm': specifier: 'catalog:' - version: 5.1.16 + version: 6.0.8 minimist: specifier: 'catalog:' version: 1.2.8 @@ -65,7 +68,7 @@ importers: dependencies: '@inquirer/editor': specifier: 'catalog:' - version: 4.2.18 + version: 5.0.8 minimist: specifier: 'catalog:' version: 1.2.8 @@ -74,7 +77,7 @@ importers: dependencies: '@inquirer/input': specifier: 'catalog:' - version: 4.2.2 + version: 5.0.8 minimist: specifier: 'catalog:' version: 1.2.8 @@ -83,7 +86,7 @@ importers: dependencies: '@inquirer/number': specifier: 'catalog:' - version: 3.0.18 + version: 4.0.8 minimist: specifier: 'catalog:' version: 1.2.8 @@ -92,7 +95,7 @@ importers: dependencies: '@inquirer/password': specifier: 'catalog:' - version: 4.0.18 + version: 5.0.8 minimist: specifier: 'catalog:' version: 1.2.8 @@ -101,203 +104,192 @@ importers: dependencies: '@inquirer/select': specifier: 'catalog:' - version: 4.3.2 + version: 5.1.0 minimist: specifier: 'catalog:' version: 1.2.8 + packages/test-helper: {} + packages: - '@biomejs/biome@2.2.4': - resolution: {integrity: sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==} + '@biomejs/biome@2.4.7': + resolution: {integrity: sha512-vXrgcmNGZ4lpdwZSpMf1hWw1aWS6B+SyeSYKTLrNsiUsAdSRN0J4d/7mF3ogJFbIwFFSOL3wT92Zzxia/d5/ng==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.2.4': - resolution: {integrity: sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==} + '@biomejs/cli-darwin-arm64@2.4.7': + resolution: {integrity: sha512-Oo0cF5mHzmvDmTXw8XSjhCia8K6YrZnk7aCS54+/HxyMdZMruMO3nfpDsrlar/EQWe41r1qrwKiCa2QDYHDzWA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.2.4': - resolution: {integrity: sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==} + '@biomejs/cli-darwin-x64@2.4.7': + resolution: {integrity: sha512-I+cOG3sd/7HdFtvDSnF9QQPrWguUH7zrkIMMykM3PtfWU9soTcS2yRb9Myq6MHmzbeCT08D1UmY+BaiMl5CcoQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.2.4': - resolution: {integrity: sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==} + '@biomejs/cli-linux-arm64-musl@2.4.7': + resolution: {integrity: sha512-I2NvM9KPb09jWml93O2/5WMfNR7Lee5Latag1JThDRMURVhPX74p9UDnyTw3Ae6cE1DgXfw7sqQgX7rkvpc0vw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@2.2.4': - resolution: {integrity: sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==} + '@biomejs/cli-linux-arm64@2.4.7': + resolution: {integrity: sha512-om6FugwmibzfP/6ALj5WRDVSND4H2G9X0nkI1HZpp2ySf9lW2j0X68oQSaHEnls6666oy4KDsc5RFjT4m0kV0w==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@2.2.4': - resolution: {integrity: sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==} + '@biomejs/cli-linux-x64-musl@2.4.7': + resolution: {integrity: sha512-00kx4YrBMU8374zd2wHuRV5wseh0rom5HqRND+vDldJPrWwQw+mzd/d8byI9hPx926CG+vWzq6AeiT7Yi5y59g==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@2.2.4': - resolution: {integrity: sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==} + '@biomejs/cli-linux-x64@2.4.7': + resolution: {integrity: sha512-bV8/uo2Tj+gumnk4sUdkerWyCPRabaZdv88IpbmDWARQQoA/Q0YaqPz1a+LSEDIL7OfrnPi9Hq1Llz4ZIGyIQQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@2.2.4': - resolution: {integrity: sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==} + '@biomejs/cli-win32-arm64@2.4.7': + resolution: {integrity: sha512-hOUHBMlFCvDhu3WCq6vaBoG0dp0LkWxSEnEEsxxXvOa9TfT6ZBnbh72A/xBM7CBYB7WgwqboetzFEVDnMxelyw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.2.4': - resolution: {integrity: sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==} + '@biomejs/cli-win32-x64@2.4.7': + resolution: {integrity: sha512-qEpGjSkPC3qX4ycbMUthXvi9CkRq7kZpkqMY1OyhmYlYLnANnooDQ7hDerM8+0NJ+DZKVnsIc07h30XOpt7LtQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] - '@inquirer/checkbox@4.2.2': - resolution: {integrity: sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==} - engines: {node: '>=18'} + '@inquirer/ansi@2.0.3': + resolution: {integrity: sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/checkbox@5.1.0': + resolution: {integrity: sha512-/HjF1LN0a1h4/OFsbGKHNDtWICFU/dqXCdym719HFTyJo9IG7Otr+ziGWc9S0iQuohRZllh+WprSgd5UW5Fw0g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/confirm@5.1.16': - resolution: {integrity: sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==} - engines: {node: '>=18'} + '@inquirer/confirm@6.0.8': + resolution: {integrity: sha512-Di6dgmiZ9xCSUxWUReWTqDtbhXCuG2MQm2xmgSAIruzQzBqNf49b8E07/vbCYY506kDe8BiwJbegXweG8M1klw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/core@10.2.0': - resolution: {integrity: sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==} - engines: {node: '>=18'} + '@inquirer/core@11.1.5': + resolution: {integrity: sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/editor@4.2.18': - resolution: {integrity: sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==} - engines: {node: '>=18'} + '@inquirer/editor@5.0.8': + resolution: {integrity: sha512-sLcpbb9B3XqUEGrj1N66KwhDhEckzZ4nI/W6SvLXyBX8Wic3LDLENlWRvkOGpCPoserabe+MxQkpiMoI8irvyA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/external-editor@1.0.1': - resolution: {integrity: sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==} - engines: {node: '>=18'} + '@inquirer/external-editor@2.0.3': + resolution: {integrity: sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/figures@1.0.13': - resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} - engines: {node: '>=18'} + '@inquirer/figures@2.0.3': + resolution: {integrity: sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} - '@inquirer/input@4.2.2': - resolution: {integrity: sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==} - engines: {node: '>=18'} + '@inquirer/input@5.0.8': + resolution: {integrity: sha512-p0IJslw0AmedLEkOU+yrEX3Aj2RTpQq7ZOf8nc1DIhjzaxRWrrgeuE5Kyh39fVRgtcACaMXx/9WNo8+GjgBOfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/number@3.0.18': - resolution: {integrity: sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==} - engines: {node: '>=18'} + '@inquirer/number@4.0.8': + resolution: {integrity: sha512-uGLiQah9A0F9UIvJBX52m0CnqtLaym0WpT9V4YZrjZ+YRDKZdwwoEPz06N6w8ChE2lrnsdyhY9sL+Y690Kh9gQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/password@4.0.18': - resolution: {integrity: sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==} - engines: {node: '>=18'} + '@inquirer/password@5.0.8': + resolution: {integrity: sha512-zt1sF4lYLdvPqvmvHdmjOzuUUjuCQ897pdUCO8RbXMUDKXJTTyOQgtn23le+jwcb+MpHl3VAFvzIdxRAf6aPlA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/select@4.3.2': - resolution: {integrity: sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==} - engines: {node: '>=18'} + '@inquirer/select@5.1.0': + resolution: {integrity: sha512-OyYbKnchS1u+zRe14LpYrN8S0wH1vD0p2yKISvSsJdH2TpI87fh4eZdWnpdbrGauCRWDph3NwxRmM4Pcm/hx1Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/type@3.0.8': - resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==} - engines: {node: '>=18'} + '@inquirer/type@4.0.3': + resolution: {integrity: sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - chardet@2.1.0: - resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} - engines: {node: ^18.17.0 || >=20.5.0} + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -306,178 +298,127 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - yoctocolors-cjs@2.1.3: - resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} - engines: {node: '>=18'} - snapshots: - '@biomejs/biome@2.2.4': + '@biomejs/biome@2.4.7': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.2.4 - '@biomejs/cli-darwin-x64': 2.2.4 - '@biomejs/cli-linux-arm64': 2.2.4 - '@biomejs/cli-linux-arm64-musl': 2.2.4 - '@biomejs/cli-linux-x64': 2.2.4 - '@biomejs/cli-linux-x64-musl': 2.2.4 - '@biomejs/cli-win32-arm64': 2.2.4 - '@biomejs/cli-win32-x64': 2.2.4 - - '@biomejs/cli-darwin-arm64@2.2.4': + '@biomejs/cli-darwin-arm64': 2.4.7 + '@biomejs/cli-darwin-x64': 2.4.7 + '@biomejs/cli-linux-arm64': 2.4.7 + '@biomejs/cli-linux-arm64-musl': 2.4.7 + '@biomejs/cli-linux-x64': 2.4.7 + '@biomejs/cli-linux-x64-musl': 2.4.7 + '@biomejs/cli-win32-arm64': 2.4.7 + '@biomejs/cli-win32-x64': 2.4.7 + + '@biomejs/cli-darwin-arm64@2.4.7': optional: true - '@biomejs/cli-darwin-x64@2.2.4': + '@biomejs/cli-darwin-x64@2.4.7': optional: true - '@biomejs/cli-linux-arm64-musl@2.2.4': + '@biomejs/cli-linux-arm64-musl@2.4.7': optional: true - '@biomejs/cli-linux-arm64@2.2.4': + '@biomejs/cli-linux-arm64@2.4.7': optional: true - '@biomejs/cli-linux-x64-musl@2.2.4': + '@biomejs/cli-linux-x64-musl@2.4.7': optional: true - '@biomejs/cli-linux-x64@2.2.4': + '@biomejs/cli-linux-x64@2.4.7': optional: true - '@biomejs/cli-win32-arm64@2.2.4': + '@biomejs/cli-win32-arm64@2.4.7': optional: true - '@biomejs/cli-win32-x64@2.2.4': + '@biomejs/cli-win32-x64@2.4.7': optional: true - '@inquirer/checkbox@4.2.2': + '@inquirer/ansi@2.0.3': {} + + '@inquirer/checkbox@5.1.0': dependencies: - '@inquirer/core': 10.2.0 - '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8 - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.3 + '@inquirer/core': 11.1.5 + '@inquirer/figures': 2.0.3 + '@inquirer/type': 4.0.3 - '@inquirer/confirm@5.1.16': + '@inquirer/confirm@6.0.8': dependencies: - '@inquirer/core': 10.2.0 - '@inquirer/type': 3.0.8 + '@inquirer/core': 11.1.5 + '@inquirer/type': 4.0.3 - '@inquirer/core@10.2.0': + '@inquirer/core@11.1.5': dependencies: - '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8 - ansi-escapes: 4.3.2 + '@inquirer/ansi': 2.0.3 + '@inquirer/figures': 2.0.3 + '@inquirer/type': 4.0.3 cli-width: 4.1.0 - mute-stream: 2.0.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 - '@inquirer/editor@4.2.18': + '@inquirer/editor@5.0.8': dependencies: - '@inquirer/core': 10.2.0 - '@inquirer/external-editor': 1.0.1 - '@inquirer/type': 3.0.8 + '@inquirer/core': 11.1.5 + '@inquirer/external-editor': 2.0.3 + '@inquirer/type': 4.0.3 - '@inquirer/external-editor@1.0.1': + '@inquirer/external-editor@2.0.3': dependencies: - chardet: 2.1.0 - iconv-lite: 0.6.3 + chardet: 2.1.1 + iconv-lite: 0.7.2 - '@inquirer/figures@1.0.13': {} + '@inquirer/figures@2.0.3': {} - '@inquirer/input@4.2.2': + '@inquirer/input@5.0.8': dependencies: - '@inquirer/core': 10.2.0 - '@inquirer/type': 3.0.8 + '@inquirer/core': 11.1.5 + '@inquirer/type': 4.0.3 - '@inquirer/number@3.0.18': + '@inquirer/number@4.0.8': dependencies: - '@inquirer/core': 10.2.0 - '@inquirer/type': 3.0.8 + '@inquirer/core': 11.1.5 + '@inquirer/type': 4.0.3 - '@inquirer/password@4.0.18': + '@inquirer/password@5.0.8': dependencies: - '@inquirer/core': 10.2.0 - '@inquirer/type': 3.0.8 - ansi-escapes: 4.3.2 + '@inquirer/ansi': 2.0.3 + '@inquirer/core': 11.1.5 + '@inquirer/type': 4.0.3 - '@inquirer/select@4.3.2': + '@inquirer/select@5.1.0': dependencies: - '@inquirer/core': 10.2.0 - '@inquirer/figures': 1.0.13 - '@inquirer/type': 3.0.8 - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.3 + '@inquirer/core': 11.1.5 + '@inquirer/figures': 2.0.3 + '@inquirer/type': 4.0.3 - '@inquirer/type@3.0.8': {} + '@inquirer/type@4.0.3': {} - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@5.0.1: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - chardet@2.1.0: {} + chardet@2.1.1: {} cli-width@4.1.0: {} - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 + fast-string-truncated-width@3.0.3: {} - color-name@1.1.4: {} + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 - emoji-regex@8.0.0: {} + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 - iconv-lite@0.6.3: + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 - is-fullwidth-code-point@3.0.0: {} - minimist@1.2.8: {} - mute-stream@2.0.0: {} + mute-stream@3.0.0: {} safer-buffer@2.1.2: {} signal-exit@4.1.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - type-fest@0.21.3: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - yoctocolors-cjs@2.1.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index eff2812..52aa0c5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,14 +1,18 @@ packages: - packages/* +settings: + catalogMode: strict + cleanupUnusedCatalogs: true + catalog: - '@inquirer/checkbox': ^4.2.2 - '@inquirer/confirm': ^5.1.16 - '@inquirer/editor': ^4.2.18 - '@inquirer/input': ^4.2.2 - '@inquirer/number': ^3.0.18 - '@inquirer/password': ^4.0.18 - '@inquirer/select': ^4.3.2 + '@inquirer/checkbox': ^5.1.0 + '@inquirer/confirm': ^6.0.8 + '@inquirer/editor': ^5.0.8 + '@inquirer/input': ^5.0.8 + '@inquirer/number': ^4.0.8 + '@inquirer/password': ^5.0.8 + '@inquirer/select': ^5.1.0 minimist: ^1.2.8 onlyBuiltDependencies: From 96da5aa8ecf07efe5e76991b81351c3f2ff5d013 Mon Sep 17 00:00:00 2001 From: Jason Yu Date: Sun, 15 Mar 2026 08:10:03 +0000 Subject: [PATCH 2/2] add CI workflow for pull requests Co-Authored-By: Claude Opus 4.6 --- .github/workflows/ci.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4939cc8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: CI + +on: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint + run: npx biome check + + - name: Test + run: pnpm test