Technical Documentation: bombshell-dev/clack
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (`README.md`)
stylish interactive prompts for JavaScript CLIs[`@clack/prompts`](packages/prompts#readme): opinionated, ready-to-use prompt components
[`@clack/core`](packages/core#readme): headless, unstyled prompt primitives
[Read the docs](https://bomb.sh/docs/clack/basics/getting-started/)
Overview (`.changeset/README.md`)
Changesets
Hello and welcome! This folder has been automatically generated by @changesets/cli, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it in our repository
We have a quick list of common questions to get you started engaging with this project in
our documentation
You will be responsible for writing a changeset for your Astro PR. This is a Markdown file that states:
- which repository has changed
- the kind of change according to Astro's semantic versioning
- a message describing the change that will be publicly displayed on the repo's CHANGELOG
---
"astro": patch
---
Fixes unexpected `undefined` value when calling an action from the client without a return valueYou can generate a changeset using the pnpm changeset command, which will prompt you for the necessary information and create a randomly-named file in the .changeset folder. You can then edit the generated file to add more detail, or write your changeset from scratch.
Format
Begin your changeset message with a present tense verb that completes a sentence in the style of: "This PR . . ." or "This contribution ..."
- Adds
- Removes
- Fixes
- Updates
- Refactors
- Improves
- Deprecates
Finish the introductory sentence with a message focusing on what has changed about the codebase (not what your feature itself does) that is meaningful to a user of Astro. It is usually more helpful to describe the change as someone building an Astro site will experience it, instead of describing how you fixed it or what the code in the PR does:
// What the code now does
Logs helpful errors if content is invalid
// The nature of the change to the Astro code base
Adds logging for content collections configuration errors.You may then include additional paragraphs if necessary to describe the change in more detail. This may not be needed for a small bug fix, but is often helpful if the reader must make a change to their own code, or needs to understand how the change might affect them.
The level of detail of a changeset depends on the type of change (e.g. patch vs minor, breaking or not, only affects integration authors etc.) You may also use <h4> - <h6> Markdown headings to break your longer content into sections.
:::tip[Don't hide the good stuff in the changeset!]
CHANGELOGS are often read only once, when someone is updating to the latest version of a package. Documentation is a constant reference that will be revisited and consulted frequently.
Make sure that the helpful explanations and examples for your fellow developers in both your changeset and your PR description are captured in the actual feature documentation, too!
:::
Patch updates
These updates are often fixes, refactors or other small improvements. They are typically not user-facing, and do not require someone to update their own project code.
Verbs like "fixes" and "refactors" are helpful to let readers know this is an internal or implementation change that they do not need to worry about. At the same time, these messages are helpful to someone who is interested in keeping up with small changes to the codebase.
Often one line is enough to describe your change meaningfully to an Astro user:
---
"astro": patch
---
Fixes a bug where the toolbar audit would incorrectly flag images as above the fold---
"astro": patch
---
Refactors internal handling of styles and scripts for content collections to improve build performance---
"astro": patch
---
Updates the `HTMLAttributes` type exported from `astro` to allow data attributesThese do not need to be full sentences and do not need end punctuation unless you write multiple sentences.
:::tip[Help your reader figure out if this is change important to them]
Even though these are small changes described by very short sentences, they still need to communicate a lot!
Check that you have clearly stated not just what has changed but also who needs to know. If your reader can identify that a change is important to them, they can always seek out further information if they need to know more.
:::
Tips and Examples
For a great changeset message:
Include the specific API changed (using inline code highlighting as appropriate) when your change might not be easy to identify so that your reader can easily tell whether it's something they are using and need to care about:
Improves automatic fallbacks generation
vs
โ Improves automatic
fallbacksgeneration for the experimental Fonts API
When the specific API change is not user-facing (e.g. a type not publicly exposed) and/or your reader will not recognize it by name, describe the use case or end result that will be meaningful to the reader instead:
Adds
| (string & {})for better autocomplete ofApp.SessionDatavs
โ Improves autocompletion for session keys
New features
Begin your changeset with "Adds" to alert readers that there is something new and mention the names of any new items (options, functions) that have been added directly in the first sentence:
---
"astro": minor
---
Adds a new `flamethrow` view transitions animationAdditionally, describe what people are now able to do because of these additions that they could not before.
The changeset is an opportunity to call people's attention to new things they might wish to try in their Astro project, and may include a code example showing basic usage of the new feature:
---
"astro": minor
---
Adds a new, optional property `timeout` for the `client:idle` directive
This value allows you to specify a maximum time to wait, in milliseconds, before hydrating a UI framework component, even if the page is not yet done with its initial load.
This means you can delay hydration for lower-priority UI elements with more control to ensure your element is interactive within a specified time frame.
```
```You can also use <h4> - <h6> Markdown headings to break your longer content into sections.
Breaking changes
Changesets focus on what has changed, and must include any breaking changes, including changes to default behavior. Most users will have several default settings configured (often by not setting any value themselves), so changes to defaults can have a significant impact on someone's project!
Verbs like "removes", "changes", and "deprecates" call attention to something that might require attention. Unlike a new feature someone can choose not to use, changing default or expected behavior cannot be ignored.
You can also use <h4> - <h6> Markdown headings to break your content into sections, for example, to add headings like, "What should I do?" or "Migrating from the previous version."
---
"astro": major
---
Removes support for returning simple objects from endpoints. You must now return a `Response` instead.Changeset messages for breaking changes must also provide clear guidance for updating. Diff code samples are encouraged when appropriate:
---
"astro": major
---
Removes support for Shiki custom language's `path` property. The language JSON file must now be imported and passed to the option instead.
```diff
// astro.config.js
+ import customLang from './custom.tmLanguage.json'
export default defineConfig({
markdown: {
shikiConfig: {
langs: [
- { path: './custom.tmLanguage.json' },
+ customLang,
],
},
},
})
```If your contribution changes a default value, then it is helpful to describe how to revert back to the previous behavior. Readers may also appreciate being informed that they can remove configuration that is no longer needed.
---
"astro": major
---
Changes the default value of `security.checkOrigin` to true, which now enables Cross-Site Request Forgery (CSRF) protection by default for pages rendered on demand.
If you had previously configured `security.checkOrigin: true`, you no longer need this set in your Astro config. This is now the default and it is safe to remove.
To revert to the previous default behavior and opt out of automatically checking that the โoriginโ header matches the URL sent by each request, you must now explicitly set `security.checkOrigin: false`:
```diff
export default defineConfig({
+ security: {
+ checkOrigin: false
+ }
})
```Using Markdown section headings
For longer descriptions that you want to split into sections, always start at the <h4> level, using #### where you would normally use ## in a regular Markdown post. This ensures readability when your message is incorporated into the final CHANGELOG:
---
"astro": minor
---
Adds a new Sessions API to store user state between requests for on-demand rendered pages.
#### Configuring session storage
#### Using sessions
##### In `.astro` pages
##### In API endpoints
#### Upgrading from the experimental APIOverview (`packages/core/README.md`)
`@clack/core`
Clack contains low-level primitives for implementing your own command-line applications.
Currently, TextPrompt, SelectPrompt, and ConfirmPrompt are exposed as well as the base Prompt class.
Each Prompt accepts a render function.
import { TextPrompt, isCancel } from '@clack/core';
const p = new TextPrompt({
render() {
return `What's your name?\n${this.userInputWithCursor}`;
},
});
const name = await p.prompt();
if (isCancel(name)) {
process.exit(0);
}Overview (`packages/prompts/README.md`)
`@clack/prompts`
Effortlessly build beautiful command-line apps ๐ช Try the demo
@clack/prompts is an opinionated, pre-styled wrapper around @clack/core.
- ๐ค 80% smaller than other options
- ๐ Beautiful, minimal UI
- โ Simple API
- ๐งฑ Comes with
text,password,confirm,date,select,autocomplete,selectKey,multiselect,path, andspinnercomponents
Basics
Setup
The intro and outro functions will print a message to begin or end a prompt session, respectively.
import { intro, outro } from '@clack/prompts';
intro(`create-my-app`);
// Do stuff
outro(`You're all set!`);Cancellation
The isCancel function is a guard that detects when a user cancels a question with CTRL + C. You should handle this situation for each prompt, optionally providing a nice cancellation message with the cancel utility.
import { isCancel, cancel, text } from '@clack/prompts';
const value = await text({
message: 'What is the meaning of life?',
});
if (isCancel(value)) {
cancel('Operation cancelled.');
process.exit(0);
}Components
Text
The text component accepts a single line of text.
import { text } from '@clack/prompts';
const meaning = await text({
message: 'What is the meaning of life?',
placeholder: 'Not sure',
initialValue: '42',
validate(value) {
if (value.length === 0) return `Value is required!`;
},
});Password
The password prompt behaves like the text prompt, but masks the input as the user types.
import { password } from '@clack/prompts';
const secret = await password({
message: 'Set a password.',
mask: '*',
validate(value) {
if (!value || value.length < 8) return 'Password must be at least 8 characters.';
},
});Confirm
The confirm prompt accepts a yes or no choice, returning a boolean value corresponding to the user's selection.
import { confirm } from '@clack/prompts';
const shouldContinue = await confirm({
message: 'Do you want to continue?',
});Date
The date prompt provides an interactive date picker, allowing users to navigate between year, month, and day segments and increment/decrement values using keyboard controls.
import { date } from '@clack/prompts';
const birthday = await date({
message: 'Pick your birthday',
minDate: new Date('1900-01-01'),
initialValue: new Date(),
maxDate: new Date(),
});Select
The select component allows a user to choose one value from a list of options. The result is the value prop of a given option.
import { select } from '@clack/prompts';
const projectType = await select({
message: 'Pick a project type.',
options: [
{ value: 'ts', label: 'TypeScript' },
{ value: 'js', label: 'JavaScript', disabled: true },
{ value: 'coffee', label: 'CoffeeScript', hint: 'oh no' },
],
});Autocomplete
The autocomplete prompt combines text input with a searchable list of options. It's perfect for when you have a large list of options and want to help users find what they're looking for quickly.
import { autocomplete } from '@clack/prompts';
const framework = await autocomplete({
message: 'Pick a framework.',
placeholder: 'Type to search...',
options: [
{ value: 'next', label: 'Next.js' },
{ value: 'nuxt', label: 'Nuxt' },
{ value: 'sveltekit', label: 'SvelteKit' },
{ value: 'remix', label: 'Remix' },
],
});Autocomplete Multi-Select
The autocompleteMultiselect prompt combines the search functionality of autocomplete with the ability to select multiple options.
import { autocomplete } from '@clack/prompts';
const framework = await autocomplete({
message: 'Search for a framework',
options: [
{ value: 'next', label: 'Next.js', hint: 'React framework' },
{ value: 'astro', label: 'Astro', hint: 'Content-focused' },
{ value: 'svelte', label: 'SvelteKit', hint: 'Compile-time framework' },
{ value: 'remix', label: 'Remix', hint: 'Full stack framework' },
{ value: 'nuxt', label: 'Nuxt', hint: 'Vue framework' },
],
placeholder: 'Type to search...',
maxItems: 5,
});Select Key
The selectKey component lets a user choose an option by pressing its single-character string value key directly.
import { selectKey } from '@clack/prompts';
const action = await selectKey({
message: 'Pick an action.',
options: [
{ value: 'd', label: 'Deploy' },
{ value: 't', label: 'Run tests' },
{ value: 'q', label: 'Quit' },
],
});Multi-Select
The multiselect component allows a user to choose many values from a list of options. The result is an array with all selected value props.
import { multiselect } from '@clack/prompts';
const additionalTools = await multiselect({
message: 'Select additional tools.',
options: [
{ value: 'eslint', label: 'ESLint', hint: 'recommended' },
{ value: 'prettier', label: 'Prettier', disabled: true },
{ value: 'gh-action', label: 'GitHub Action' },
],
required: false,
});It is also possible to select multiple items arranged into hierarchy by using groupMultiselect:
import { groupMultiselect } from '@clack/prompts';
const basket = await groupMultiselect({
message: 'Select your favorite fruits and vegetables:',
options: {
fruits: [
{ value: 'apple', label: 'apple' },
{ value: 'banana', label: 'banana' },
{ value: 'cherry', label: 'cherry' },
],
vegetables: [
{ value: 'carrot', label: 'carrot' },
{ value: 'spinach', label: 'spinach' },
{ value: 'potato', label: 'potato' },
]
}
});Multi-Line Text
The multi-line prompt accepts multiple lines of text input. By default, pressing Enter twice submits the input.
import { multiline } from '@clack/prompts';
const bio = await multiline({
message: 'Tell us about yourself.',
placeholder: 'Start typing...',
validate(value) {
if (value.length === 0) return `value is required`;
},
});Set showSubmit to display an explicit submit button instead of double Enter submission:
const bio = await multiline({
message: 'Tell us about yourself.',
showSubmit: true,
});Path
The path prompt extends autocomplete to provide file and directory suggestions.
import { path } from '@clack/prompts';
const targetDir = await path({
message: 'Select an existing directory.',
directory: true,
});Spinner
The spinner component surfaces a pending action, such as a long-running download or dependency installation.
import { spinner } from '@clack/prompts';
const s = spinner();
s.start('Installing via npm');
// Do installation here
s.stop('Installed via npm');Progress
The progress component extends the spinner component to add a progress bar to visualize the progression of an action.
import { progress } from '@clack/prompts';
const p = progress({ max: 10 });
p.start('Downloading archive');
// Do download here
p.advance(3, 'Downloading (30%)');
// ...
p.advance(5, 'Downloading (80%)');
// ...
p.stop('Archive downloaded');Utilities
Grouping
Grouping prompts together is a great way to keep your code organized. This accepts a JSON object with a name that can be used to reference the group later. The second argument is an optional but has a onCancel callback that will be called if the user cancels one of the prompts in the group.
import * as p from '@clack/prompts';
const group = await p.group(
{
name: () => p.text({ message: 'What is your name?' }),
age: () => p.text({ message: 'What is your age?' }),
color: ({ results }) =>
p.multiselect({
message: `What is your favorite color ${results.name}?`,
options: [
{ value: 'red', label: 'Red' },
{ value: 'green', label: 'Green' },
{ value: 'blue', label: 'Blue' },
],
}),
},
{
// On Cancel callback that wraps the group
// So if the user cancels one of the prompts in the group this function will be called
onCancel: ({ results }) => {
p.cancel('Operation cancelled.');
process.exit(0);
},
}
);
console.log(group.name, group.age, group.color);Tasks
Execute multiple tasks in spinners.
import { tasks } from '@clack/prompts';
await tasks([
{
title: 'Installing via npm',
task: async (message) => {
// Do installation here
return 'Installed via npm';
},
},
]);Logs
import { log } from '@clack/prompts';
log.info('Info!');
log.success('Success!');
log.step('Step!');
log.warn('Warn!');
log.error('Error!');
log.message('Hello, World', { symbol: color.cyan('~') });Stream
When interacting with dynamic LLMs or other streaming message providers, use the stream APIs to log messages from an iterable, even an async one.
import { stream } from '@clack/prompts';
stream.info((function *() { yield 'Info!'; })());
stream.success((function *() { yield 'Success!'; })());
stream.step((function *() { yield 'Step!'; })());
stream.warn((function *() { yield 'Warn!'; })());
stream.error((function *() { yield 'Error!'; })());
stream.message((function *() { yield 'Hello'; yield ", World" })(), { symbol: color.cyan('~') });Task Log
When executing a sub-process or a similar sub-task, taskLog can be used to render the output continuously and clear it at the end if it was successful.
import { taskLog } from '@clack/prompts';
const log = taskLog({
title: 'Running npm install'
});
for await (const line of npmInstall()) {
log.message(line);
}
if (success) {
log.success('Done!');
} else {
log.error('Failed!');
}--- METRICS ---
- Files Extracted: 4
- Estimated Token Budget: ~5242 tokens
- Canonical Reference: https://codewiki.google/github.com/bombshell-dev/clack