clack-stepper
v0.1.1
Published
A card-based stepper container for @clack/prompts with back-navigation, async steps, and conditional flow.
Maintainers
Readme
clack-stepper
A card-based stepper container for @clack/prompts. Like group(), but with back-navigation, async steps, and conditional flow.
Each step renders in a fixed terminal region with a breadcrumb header. Going back (esc/ctrl+c) clears the current card and re-renders the previous step in place.
Install
npm install clack-stepperUsage
import * as p from "@clack/prompts";
import { stepper } from "clack-stepper";
p.intro("Project setup");
const result = await stepper(
{
name: {
label: "Name",
run: () => p.text({ message: "Project name?" }),
},
language: {
label: "Language",
run: ({ results }) =>
p.select({
message: `Language for ${results.name}?`,
options: [
{ value: "js", label: "JavaScript" },
{ value: "py", label: "Python" },
],
}),
},
framework: {
label: "Framework",
when: ({ results }) => results.language === "js",
run: async ({ results }) => {
const opts = await fetchFrameworks(results.language);
return p.select({ message: "Framework?", options: opts });
},
},
},
{
onCancel: () => {
p.cancel("Cancelled.");
process.exit(0);
},
},
);
p.outro("Done!");API
stepper(steps, options?)
Returns a promise that resolves to an object keyed by step name, containing each step's result.
Step definition
Each key in the steps object defines a step:
| Property | Type | Description |
|----------|------|-------------|
| label | string | Display text for the breadcrumb header. |
| run | (ctx) => Promise<T> | The prompt to run. Typically a @clack/prompts call. |
| when | (ctx) => boolean | Optional. Step is skipped when this returns false. Re-evaluated on each visit. |
The ctx argument contains:
results-- accumulated answers from completed steps (partial, typed).io-- I/O streams for forwarding to prompts (see Testing below).
Options
| Property | Type | Description |
|----------|------|-------------|
| onCancel | ({ results }) => void | Called when the user escapes past the first step. |
| output | Writable | Custom output stream for the stepper's chrome. Defaults to process.stdout. |
| promptIO | { input?, output? } | I/O streams forwarded to step callbacks via ctx.io. |
How it works
escorctrl+con any step goes back to the previous step.escon the first step triggersonCancel.- Steps with
whenare re-evaluated each time the stepper reaches them, so changing an earlier answer can add or remove later steps. - Any
asyncwork (fetching data, spinners) can happen insiderunbefore calling a prompt.
Testing
The stepper's logic can be tested by mocking the run functions directly:
import { stepper } from "clack-stepper";
const result = await stepper(
{
name: { label: "Name", run: () => Promise.resolve("my-app") },
lang: { label: "Lang", run: () => Promise.resolve("js") },
},
{ output: mockOutput },
);Return a Symbol from run to simulate cancel/back-navigation.
For integration tests with real @clack/prompts, pass mock streams via promptIO and spread ctx.io into prompt options:
const result = await stepper(
{
name: {
label: "Name",
run: ({ io }) => p.text({ message: "Name?", ...io }),
},
},
{ output: mockOutput, promptIO: { input: mockInput, output: mockPromptOutput } },
);