diff --git a/packages/clack-core/LICENSE b/packages/clack-core/LICENSE deleted file mode 100644 index a95d8e47..00000000 --- a/packages/clack-core/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -MIT License - -Copyright (c) Nate Moore - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - -`ansi-regex` is adapted from https://github.com/chalk/ansi-regex - -MIT License - -Copyright (c) Sindre Sorhus (https://sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/clack-core/README.md b/packages/clack-core/README.md deleted file mode 100644 index 57ac05f2..00000000 --- a/packages/clack-core/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# `@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. - -```js -import { TextPrompt, isCancel } from '@clack/core'; - -const p = new TextPrompt({ - render() { - return `What's your name?\n${this.valueWithCursor}`; - } -}); - -const name = await p.prompt(); -if (isCancel(name)) { - process.exit(0); -} -``` diff --git a/packages/clack-core/index.ts b/packages/clack-core/index.ts deleted file mode 100644 index 37313080..00000000 --- a/packages/clack-core/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export { default as ConfirmPrompt } from './src/prompts/confirm.ts'; -export { default as GroupMultiSelectPrompt } from './src/prompts/group-multiselect.ts'; -export { default as MultiSelectPrompt } from './src/prompts/multi-select.ts'; -export { default as PasswordPrompt } from './src/prompts/password.ts'; -export { default as Prompt, isCancel } from './src/prompts/prompt.ts'; -export type { State } from './src/prompts/prompt.ts'; -export { default as SelectPrompt } from './src/prompts/select.ts'; -export { default as SelectKeyPrompt } from './src/prompts/select-key.ts'; -export { default as TextPrompt } from './src/prompts/text.ts'; -export { block } from './src/utils.ts'; diff --git a/packages/clack-core/package.json b/packages/clack-core/package.json deleted file mode 100644 index 4727de98..00000000 --- a/packages/clack-core/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@sveltejs/clack-core", - "private": true, - "version": "0.4.2", - "type": "module", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/sveltejs/cli/tree/main/packages/clack-prompts" - }, - "bugs": "https://github.com/sveltejs/cli/issues", - "scripts": { - "check": "tsc", - "format": "pnpm lint --write", - "lint": "prettier --check . --config ../../prettier.config.js --ignore-path ../../.gitignore --ignore-path .gitignore --ignore-path ../../.prettierignore" - }, - "files": [ - "dist" - ], - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./package.json": "./package.json" - }, - "devDependencies": { - "picocolors": "^1.1.1", - "sisteransi": "^1.0.5", - "wrap-ansi": "^8.1.0" - } -} diff --git a/packages/clack-core/src/prompts/confirm.ts b/packages/clack-core/src/prompts/confirm.ts deleted file mode 100644 index 50e2a8a9..00000000 --- a/packages/clack-core/src/prompts/confirm.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { cursor } from 'sisteransi'; -import Prompt, { type PromptOptions } from './prompt.ts'; - -interface ConfirmOptions extends PromptOptions { - active: string; - inactive: string; - initialValue?: boolean; -} -export default class ConfirmPrompt extends Prompt { - get cursor(): 0 | 1 { - return this.value ? 0 : 1; - } - - private get _value() { - return this.cursor === 0; - } - - constructor(opts: ConfirmOptions) { - super(opts, false); - this.value = opts.initialValue ? true : false; - - this.on('value', () => { - this.value = this._value; - }); - - this.on('confirm', (confirm) => { - this.output.write(cursor.move(0, -1)); - this.value = confirm; - this.state = 'submit'; - this.close(); - }); - - this.on('cursor', () => { - this.value = !this.value; - }); - } -} diff --git a/packages/clack-core/src/prompts/group-multiselect.ts b/packages/clack-core/src/prompts/group-multiselect.ts deleted file mode 100644 index e0601f44..00000000 --- a/packages/clack-core/src/prompts/group-multiselect.ts +++ /dev/null @@ -1,82 +0,0 @@ -import Prompt, { type PromptOptions } from './prompt.ts'; - -interface GroupMultiSelectOptions - extends PromptOptions> { - options: Record; - initialValues?: Array; - required?: boolean; - cursorAt?: T['value']; - selectableGroups?: boolean; -} -export default class GroupMultiSelectPrompt extends Prompt { - options: Array; - cursor: number = 0; - #selectableGroups: boolean; - - getGroupItems(group: string): T[] { - return this.options.filter((o) => o.group === group); - } - - isGroupSelected(group: string): boolean { - const items = this.getGroupItems(group); - return this.#selectableGroups && items.every((i) => this.value.includes(i.value)); - } - - private toggleValue() { - const item = this.options[this.cursor]!; - if (item.group === true) { - const group = item.value; - const groupedItems = this.getGroupItems(group); - if (this.isGroupSelected(group)) { - this.value = this.value.filter( - (v: string) => groupedItems.findIndex((i) => i.value === v) === -1 - ); - } else { - this.value = [...this.value, ...groupedItems.map((i) => i.value)]; - } - this.value = Array.from(new Set(this.value)); - } else { - const selected = this.value.includes(item.value); - this.value = selected - ? this.value.filter((v: T['value']) => v !== item.value) - : [...this.value, item.value]; - } - } - - constructor(opts: GroupMultiSelectOptions) { - super(opts, false); - const { options } = opts; - this.#selectableGroups = opts.selectableGroups ?? true; - this.options = Object.entries(options).flatMap(([key, option]) => [ - { value: key, group: true, label: key }, - ...option.map((opt) => ({ ...opt, group: key })) - ]) as any; - this.value = [...(opts.initialValues ?? [])]; - this.cursor = Math.max( - this.options.findIndex(({ value }) => value === opts.cursorAt), - this.#selectableGroups ? 0 : 1 - ); - - this.on('cursor', (key) => { - switch (key) { - case 'left': - case 'up': - this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1; - if (!this.#selectableGroups && this.options[this.cursor]!.group === true) { - this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1; - } - break; - case 'down': - case 'right': - this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; - if (!this.#selectableGroups && this.options[this.cursor]!.group === true) { - this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; - } - break; - case 'space': - this.toggleValue(); - break; - } - }); - } -} diff --git a/packages/clack-core/src/prompts/multi-select.ts b/packages/clack-core/src/prompts/multi-select.ts deleted file mode 100644 index d708d50e..00000000 --- a/packages/clack-core/src/prompts/multi-select.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Prompt, { type PromptOptions } from './prompt.ts'; - -interface MultiSelectOptions extends PromptOptions> { - options: T[]; - initialValues?: Array; - required?: boolean; - cursorAt?: T['value']; -} -export default class MultiSelectPrompt extends Prompt { - options: T[]; - cursor: number = 0; - - private get _value() { - return this.options[this.cursor]!.value; - } - - private toggleAll() { - const allSelected = this.value.length === this.options.length; - this.value = allSelected ? [] : this.options.map((v) => v.value); - } - - private toggleValue() { - const selected = this.value.includes(this._value); - this.value = selected - ? this.value.filter((value: T['value']) => value !== this._value) - : [...this.value, this._value]; - } - - constructor(opts: MultiSelectOptions) { - super(opts, false); - - this.options = opts.options; - this.value = [...(opts.initialValues ?? [])]; - this.cursor = Math.max( - this.options.findIndex(({ value }) => value === opts.cursorAt), - 0 - ); - this.on('key', (char) => { - if (char === 'a') { - this.toggleAll(); - } - }); - - this.on('cursor', (key) => { - switch (key) { - case 'left': - case 'up': - this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1; - break; - case 'down': - case 'right': - this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; - break; - case 'space': - this.toggleValue(); - break; - } - }); - } -} diff --git a/packages/clack-core/src/prompts/password.ts b/packages/clack-core/src/prompts/password.ts deleted file mode 100644 index fa2c55ce..00000000 --- a/packages/clack-core/src/prompts/password.ts +++ /dev/null @@ -1,33 +0,0 @@ -import color from 'picocolors'; -import Prompt, { type PromptOptions } from './prompt.ts'; - -interface PasswordOptions extends PromptOptions { - mask?: string; -} -export default class PasswordPrompt extends Prompt { - valueWithCursor = ''; - private _mask = '•'; - get cursor(): number { - return this._cursor; - } - get masked(): string { - return this.value.replaceAll(/./g, this._mask); - } - constructor({ mask, ...opts }: PasswordOptions) { - super(opts); - this._mask = mask ?? '•'; - - this.on('finalize', () => { - this.valueWithCursor = this.masked; - }); - this.on('value', () => { - if (this.cursor >= this.value.length) { - this.valueWithCursor = `${this.masked}${color.inverse(color.hidden('_'))}`; - } else { - const s1 = this.masked.slice(0, this.cursor); - const s2 = this.masked.slice(this.cursor); - this.valueWithCursor = `${s1}${color.inverse(s2[0])}${s2.slice(1)}`; - } - }); - } -} diff --git a/packages/clack-core/src/prompts/prompt.ts b/packages/clack-core/src/prompts/prompt.ts deleted file mode 100644 index 40a2a057..00000000 --- a/packages/clack-core/src/prompts/prompt.ts +++ /dev/null @@ -1,273 +0,0 @@ -import readline, { type Key, type ReadLine } from 'node:readline'; -import process, { stdin, stdout } from 'node:process'; -import { WriteStream } from 'node:tty'; -import type { Readable, Writable } from 'node:stream'; -import { cursor, erase } from 'sisteransi'; -import wrap from 'wrap-ansi'; - -function diffLines(a: string, b: string) { - if (a === b) return; - - const aLines = a.split('\n'); - const bLines = b.split('\n'); - const diff: number[] = []; - - for (let i = 0; i < Math.max(aLines.length, bLines.length); i++) { - if (aLines[i] !== bLines[i]) diff.push(i); - } - - return diff; -} - -const cancel = Symbol('clack:cancel'); -export function isCancel(value: unknown): value is symbol { - return value === cancel; -} - -function setRawMode(input: Readable, value: boolean) { - if ((input as typeof stdin).isTTY) (input as typeof stdin).setRawMode(value); -} - -const aliases = new Map([ - ['k', 'up'], - ['j', 'down'], - ['h', 'left'], - ['l', 'right'] -]); -const keys = new Set(['up', 'down', 'left', 'right', 'space', 'enter']); - -export interface PromptOptions { - render(this: Omit): string | void; - placeholder?: string; - initialValue?: any; - validate?: ((value: any) => string | void) | undefined; - input?: Readable; - output?: Writable; - debug?: boolean; -} - -export type State = 'initial' | 'active' | 'cancel' | 'submit' | 'error'; - -export default class Prompt { - protected input: Readable; - protected output: Writable; - private rl!: ReadLine; - private opts: Omit, 'render' | 'input' | 'output'>; - private _track: boolean = false; - private _render: (context: Omit) => string | void; - protected _cursor: number = 0; - - public state: State = 'initial'; - public value: any; - public error: string = ''; - - constructor( - { render, input = stdin, output = stdout, ...opts }: PromptOptions, - trackValue: boolean = true - ) { - this.opts = opts; - this.onKeypress = this.onKeypress.bind(this); - this.close = this.close.bind(this); - this.render = this.render.bind(this); - this._render = render.bind(this); - this._track = trackValue; - - this.input = input; - this.output = output; - } - - public prompt(): Promise { - const sink = new WriteStream(0); - sink._write = (chunk, encoding, done) => { - if (this._track) { - this.value = this.rl.line.replace(/\t/g, ''); - this._cursor = this.rl.cursor; - this.emit('value', this.value); - } - done(); - }; - this.input.pipe(sink); - - this.rl = readline.createInterface({ - input: this.input, - output: sink, - tabSize: 2, - prompt: '', - escapeCodeTimeout: 50 - }); - readline.emitKeypressEvents(this.input, this.rl); - this.rl.prompt(); - if (this.opts.initialValue !== undefined && this._track) { - this.rl.write(this.opts.initialValue); - } - - this.input.on('keypress', this.onKeypress); - setRawMode(this.input, true); - this.output.on('resize', this.render); - - this.render(); - - return new Promise((resolve) => { - this.once('submit', () => { - this.output.write(cursor.show); - this.output.off('resize', this.render); - setRawMode(this.input, false); - resolve(this.value); - }); - this.once('cancel', () => { - this.output.write(cursor.show); - this.output.off('resize', this.render); - setRawMode(this.input, false); - resolve(cancel); - }); - }); - } - - private subscribers = new Map any; once?: boolean }>>(); - public on(event: string, cb: (...args: any) => any): void { - const arr = this.subscribers.get(event) ?? []; - arr.push({ cb }); - this.subscribers.set(event, arr); - } - public once(event: string, cb: (...args: any) => any): void { - const arr = this.subscribers.get(event) ?? []; - arr.push({ cb, once: true }); - this.subscribers.set(event, arr); - } - public emit(event: string, ...data: any[]): void { - const cbs = this.subscribers.get(event) ?? []; - const cleanup: Array<() => void> = []; - for (const subscriber of cbs) { - subscriber.cb(...data); - if (subscriber.once) { - cleanup.push(() => cbs.splice(cbs.indexOf(subscriber), 1)); - } - } - for (const cb of cleanup) { - cb(); - } - } - private unsubscribe() { - this.subscribers.clear(); - } - - private onKeypress(char: string, key?: Key) { - if (this.state === 'error') { - this.state = 'active'; - } - if (key?.name && !this._track && aliases.has(key.name)) { - this.emit('cursor', aliases.get(key.name)); - } - if (key?.name && keys.has(key.name)) { - this.emit('cursor', key.name); - } - if (char && (char.toLowerCase() === 'y' || char.toLowerCase() === 'n')) { - this.emit('confirm', char.toLowerCase() === 'y'); - } - if (char === '\t' && this.opts.placeholder) { - if (!this.value) { - this.rl.write(this.opts.placeholder); - this.emit('value', this.opts.placeholder); - } - } - if (char) { - this.emit('key', char.toLowerCase()); - } - - if (key?.name === 'return') { - if (this.opts.validate) { - const problem = this.opts.validate(this.value); - if (problem) { - this.error = problem; - this.state = 'error'; - this.rl.write(this.value); - } - } - if (this.state !== 'error') { - this.state = 'submit'; - } - } - if (char === '\x03') { - this.state = 'cancel'; - } - if (this.state === 'submit' || this.state === 'cancel') { - this.emit('finalize'); - } - this.render(); - if (this.state === 'submit' || this.state === 'cancel') { - this.close(); - } - } - - protected close(): void { - this.input.unpipe(); - this.input.removeListener('keypress', this.onKeypress); - this.output.write('\n'); - setRawMode(this.input, false); - this.rl.close(); - this.emit(this.state, this.value); - this.unsubscribe(); - } - - private restoreCursor() { - const lines = - wrap(this._prevFrame, process.stdout.columns, { hard: true }).split('\n').length - 1; - this.output.write(cursor.move(-999, lines * -1)); - } - - private _prevFrame = ''; - private render() { - const frame = wrap(this._render(this) ?? '', process.stdout.columns, { hard: true }); - if (frame === this._prevFrame) return; - - if (this.state === 'initial') { - this.output.write(cursor.hide); - } - - const diff = diffLines(this._prevFrame, frame); - this.restoreCursor(); - if (diff) { - const diffLine = diff[0]!; - const lines = frame.split('\n'); - let newLines: string[] = []; - - // If we don't have enough vertical space to print all of the lines simultaneously, - // then we'll sticky the prompt message (first 3 lines) to the top so it's always shown. - // We'll then take the remaining space and render a snippet of the list that's relative - // to the currently selected option - if (lines.length > process.stdout.rows) { - const OFFSET = 3; - const PAGE_SIZE = process.stdout.rows - OFFSET; - - // @ts-expect-error `cursor` is a property that's implemented by prompts extending this class. - const pos: number = this.cursor; - - // page positions - const start = pos <= OFFSET ? OFFSET : pos; - const end = start + PAGE_SIZE; - - this.output.write(erase.down()); - - // stickied headers - const header = lines.slice(0, OFFSET); - const content = lines.slice(start, end); - newLines = newLines.concat(header, content); - } else { - this.output.write(cursor.move(0, diffLine)); - this.output.write(erase.down()); - - newLines = lines.slice(diffLine); - } - - this.output.write(newLines.join('\n')); - this._prevFrame = frame; - return; - } - - this.output.write(frame); - if (this.state === 'initial') { - this.state = 'active'; - } - this._prevFrame = frame; - } -} diff --git a/packages/clack-core/src/prompts/select-key.ts b/packages/clack-core/src/prompts/select-key.ts deleted file mode 100644 index b0c703e6..00000000 --- a/packages/clack-core/src/prompts/select-key.ts +++ /dev/null @@ -1,27 +0,0 @@ -import Prompt, { type PromptOptions } from './prompt.ts'; - -interface SelectKeyOptions extends PromptOptions> { - options: T[]; -} -export default class SelectKeyPrompt extends Prompt { - options: T[]; - cursor: number = 0; - - constructor(opts: SelectKeyOptions) { - super(opts, false); - - this.options = opts.options; - const keys = this.options.map(({ value: [initial] }) => initial?.toLowerCase()); - this.cursor = Math.max(keys.indexOf(opts.initialValue), 0); - - this.on('key', (key) => { - if (!keys.includes(key)) return; - const value = this.options.find(({ value: [initial] }) => initial?.toLowerCase() === key); - if (value) { - this.value = value.value; - this.state = 'submit'; - this.emit('submit'); - } - }); - } -} diff --git a/packages/clack-core/src/prompts/select.ts b/packages/clack-core/src/prompts/select.ts deleted file mode 100644 index ee5f2627..00000000 --- a/packages/clack-core/src/prompts/select.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Prompt, { type PromptOptions } from './prompt.ts'; - -interface SelectOptions extends PromptOptions> { - options: T[]; - initialValue?: T['value']; -} -export default class SelectPrompt extends Prompt { - options: T[]; - cursor: number = 0; - - private get _value() { - return this.options[this.cursor]; - } - - private changeValue() { - this.value = this._value!.value; - } - - constructor(opts: SelectOptions) { - super(opts, false); - - this.options = opts.options; - this.cursor = this.options.findIndex(({ value }) => value === opts.initialValue); - if (this.cursor === -1) this.cursor = 0; - this.changeValue(); - - this.on('cursor', (key) => { - switch (key) { - case 'left': - case 'up': - this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1; - break; - case 'down': - case 'right': - this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1; - break; - } - this.changeValue(); - }); - } -} diff --git a/packages/clack-core/src/prompts/text.ts b/packages/clack-core/src/prompts/text.ts deleted file mode 100644 index b2a89a71..00000000 --- a/packages/clack-core/src/prompts/text.ts +++ /dev/null @@ -1,33 +0,0 @@ -import color from 'picocolors'; -import Prompt, { type PromptOptions } from './prompt.ts'; - -export interface TextOptions extends PromptOptions { - placeholder?: string; - defaultValue?: string; -} - -export default class TextPrompt extends Prompt { - valueWithCursor = ''; - get cursor(): number { - return this._cursor; - } - constructor(opts: TextOptions) { - super(opts); - - this.on('finalize', () => { - if (!this.value) { - this.value = opts.defaultValue; - } - this.valueWithCursor = this.value; - }); - this.on('value', () => { - if (this.cursor >= this.value.length) { - this.valueWithCursor = `${this.value}${color.inverse(color.hidden('_'))}`; - } else { - const s1 = this.value.slice(0, this.cursor); - const s2 = this.value.slice(this.cursor); - this.valueWithCursor = `${s1}${color.inverse(s2[0])}${s2.slice(1)}`; - } - }); - } -} diff --git a/packages/clack-core/src/utils.ts b/packages/clack-core/src/utils.ts deleted file mode 100644 index b4a101e0..00000000 --- a/packages/clack-core/src/utils.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { Key } from 'node:readline'; - -import process, { stdin, stdout } from 'node:process'; -import * as readline from 'node:readline'; -import { cursor } from 'sisteransi'; - -const isWindows = process.platform.startsWith('win'); - -export type BlockOptions = { - input?: NodeJS.ReadStream | undefined; - output?: NodeJS.WriteStream | undefined; - overwrite?: boolean | undefined; - hideCursor?: boolean | undefined; -}; - -export function block({ - input = stdin, - output = stdout, - overwrite = true, - hideCursor = true -}: BlockOptions = {}) { - const rl = readline.createInterface({ - input, - output, - prompt: '', - tabSize: 1 - }); - readline.emitKeypressEvents(input, rl); - if (input.isTTY) input.setRawMode(true); - - const clear = (data: Buffer, { name }: Key) => { - const str = String(data); - if (str === '\x03') { - process.exit(0); - } - if (!overwrite) return; - const dx = name === 'return' ? 0 : -1; - const dy = name === 'return' ? -1 : 0; - - readline.moveCursor(output, dx, dy, () => { - readline.clearLine(output, 1, () => { - input.once('keypress', clear); - }); - }); - }; - if (hideCursor) process.stdout.write(cursor.hide); - input.once('keypress', clear); - - return (): void => { - input.off('keypress', clear); - if (hideCursor) process.stdout.write(cursor.show); - - // Prevent Windows specific issues: https://github.com/natemoo-re/clack/issues/176 - if (input.isTTY && !isWindows) input.setRawMode(false); - - // @ts-expect-error fix for https://github.com/nodejs/node/issues/31762#issuecomment-1441223907 - rl.terminal = false; - rl.close(); - }; -} diff --git a/packages/clack-core/tsconfig.json b/packages/clack-core/tsconfig.json deleted file mode 100644 index a1fcf6c9..00000000 --- a/packages/clack-core/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "checkJs": false, - "isolatedDeclarations": true, - "declaration": true - } -} diff --git a/packages/clack-prompts/LICENSE b/packages/clack-prompts/LICENSE deleted file mode 100644 index a95d8e47..00000000 --- a/packages/clack-prompts/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -MIT License - -Copyright (c) Nate Moore - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - -`ansi-regex` is adapted from https://github.com/chalk/ansi-regex - -MIT License - -Copyright (c) Sindre Sorhus (https://sindresorhus.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/clack-prompts/README.md b/packages/clack-prompts/README.md deleted file mode 100644 index e5837fd6..00000000 --- a/packages/clack-prompts/README.md +++ /dev/null @@ -1,174 +0,0 @@ -# `@clack/prompts` - -Effortlessly build beautiful command-line apps 🪄 [Try the demo](https://stackblitz.com/edit/clack-prompts?file=index.js) - -![clack-prompt](https://github.com/natemoo-re/clack/blob/main/.github/assets/clack-demo.gif) - ---- - -`@clack/prompts` is an opinionated, pre-styled wrapper around [`@clack/core`](https://www.npmjs.com/package/@clack/core). - -- 🤏 80% smaller than other options -- 💎 Beautiful, minimal UI -- ✅ Simple API -- 🧱 Comes with `text`, `confirm`, `select`, `multiselect`, and `spinner` components - -## Basics - -### Setup - -The `intro` and `outro` functions will print a message to begin or end a prompt session, respectively. - -```js -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. - -```js -import { isCancel, cancel, text } from '@clack/prompts'; - -const value = await text(/* TODO */); - -if (isCancel(value)) { - cancel('Operation cancelled.'); - process.exit(0); -} -``` - -## Components - -### Text - -The text component accepts a single line of text. - -```js -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!`; - } -}); -``` - -### Confirm - -The confirm component accepts a yes or no answer. The result is a boolean value of `true` or `false`. - -```js -import { confirm } from '@clack/prompts'; - -const shouldContinue = await confirm({ - message: 'Do you want to continue?' -}); -``` - -### 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. - -```js -import { select } from '@clack/prompts'; - -const projectType = await select({ - message: 'Pick a project type.', - options: [ - { value: 'ts', label: 'TypeScript' }, - { value: 'js', label: 'JavaScript' }, - { value: 'coffee', label: 'CoffeeScript', hint: 'oh no' } - ] -}); -``` - -### 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. - -```js -import { multiselect } from '@clack/prompts'; - -const additionalTools = await multiselect({ - message: 'Select additional tools.', - options: [ - { value: 'eslint', label: 'ESLint', hint: 'recommended' }, - { value: 'prettier', label: 'Prettier' }, - { value: 'gh-action', label: 'GitHub Action' } - ], - required: false -}); -``` - -### Spinner - -The spinner component surfaces a pending action, such as a long-running download or dependency installation. - -```js -import { spinner } from '@clack/prompts'; - -const s = spinner(); -s.start('Installing via npm'); -// Do installation here -s.stop('Installed via npm'); -``` - -## 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. - -```js -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. - -```js -await p.tasks([ - { - title: 'Installing via npm', - task: async (message) => { - // Do installation here - return 'Installed via npm'; - } - } -]); -``` diff --git a/packages/clack-prompts/index.ts b/packages/clack-prompts/index.ts deleted file mode 100644 index d93f8c0f..00000000 --- a/packages/clack-prompts/index.ts +++ /dev/null @@ -1,894 +0,0 @@ -import process from 'node:process'; -import { - block, - ConfirmPrompt, - GroupMultiSelectPrompt, - isCancel, - MultiSelectPrompt, - PasswordPrompt, - SelectKeyPrompt, - SelectPrompt, - type State, - TextPrompt -} from '@sveltejs/clack-core'; -import isUnicodeSupported from 'is-unicode-supported'; -import color from 'picocolors'; -import { cursor, erase } from 'sisteransi'; - -export { isCancel } from '@sveltejs/clack-core'; - -const unicode = isUnicodeSupported(); -const s = (c: string, fallback: string) => (unicode ? c : fallback); -const S_STEP_ACTIVE = s('◆', '*'); -const S_STEP_CANCEL = s('■', 'x'); -const S_STEP_ERROR = s('▲', 'x'); -const S_STEP_SUBMIT = s('◇', 'o'); - -const S_BAR_START = s('┌', 'T'); -const S_BAR = s('│', '|'); -const S_BAR_END = s('└', '—'); - -const S_RADIO_ACTIVE = s('●', '>'); -const S_RADIO_INACTIVE = s('○', ' '); -const S_CHECKBOX_ACTIVE = s('◻', '[•]'); -const S_CHECKBOX_SELECTED = s('◼', '[+]'); -const S_CHECKBOX_INACTIVE = s('◻', '[ ]'); -const S_PASSWORD_MASK = s('▪', '•'); - -const S_BAR_H = s('─', '-'); -const S_CORNER_TOP_RIGHT = s('╮', '+'); -const S_CONNECT_LEFT = s('├', '+'); -const S_CORNER_BOTTOM_RIGHT = s('╯', '+'); - -const S_INFO = s('●', '•'); -const S_SUCCESS = s('◆', '*'); -const S_WARN = s('▲', '!'); -const S_ERROR = s('■', 'x'); - -const symbol = (state: State) => { - switch (state) { - case 'initial': - case 'active': - return color.cyan(S_STEP_ACTIVE); - case 'cancel': - return color.red(S_STEP_CANCEL); - case 'error': - return color.yellow(S_STEP_ERROR); - case 'submit': - return color.green(S_STEP_SUBMIT); - } -}; - -interface LimitOptionsParams { - options: TOption[]; - maxItems: number | undefined; - cursor: number; - style: (option: TOption, active: boolean) => string; -} - -const limitOptions = (params: LimitOptionsParams): string[] => { - const { cursor, options, style } = params; - - const paramMaxItems = params.maxItems ?? Infinity; - const outputMaxItems = Math.max(process.stdout.rows - 4, 0); - // We clamp to minimum 5 because anything less doesn't make sense UX wise - const maxItems = Math.min(outputMaxItems, Math.max(paramMaxItems, 5)); - let slidingWindowLocation = 0; - - if (cursor >= slidingWindowLocation + maxItems - 3) { - slidingWindowLocation = Math.max(Math.min(cursor - maxItems + 3, options.length - maxItems), 0); - } else if (cursor < slidingWindowLocation + 2) { - slidingWindowLocation = Math.max(cursor - 2, 0); - } - - const shouldRenderTopEllipsis = maxItems < options.length && slidingWindowLocation > 0; - const shouldRenderBottomEllipsis = - maxItems < options.length && slidingWindowLocation + maxItems < options.length; - - return options - .slice(slidingWindowLocation, slidingWindowLocation + maxItems) - .map((option, i, arr) => { - const isTopLimit = i === 0 && shouldRenderTopEllipsis; - const isBottomLimit = i === arr.length - 1 && shouldRenderBottomEllipsis; - return isTopLimit || isBottomLimit - ? color.dim('...') - : style(option, i + slidingWindowLocation === cursor); - }); -}; - -export interface TextOptions { - message: string; - placeholder?: string; - defaultValue?: string; - initialValue?: string; - validate?: (value: string) => string | void; -} -export const text = (opts: TextOptions): Promise => { - return new TextPrompt({ - validate: opts.validate, - placeholder: opts.placeholder, - defaultValue: opts.defaultValue, - initialValue: opts.initialValue, - render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; - const placeholder = opts.placeholder - ? color.inverse(opts.placeholder[0]) + color.dim(opts.placeholder.slice(1)) - : color.inverse(color.hidden('_')); - const value = !this.value ? placeholder : this.valueWithCursor; - - switch (this.state) { - case 'error': - return `${title.trim()}\n${color.yellow(S_BAR)} ${value}\n${color.yellow( - S_BAR_END - )} ${color.yellow(this.error)}\n`; - case 'submit': - return `${title}${color.gray(S_BAR)} ${color.dim(this.value || opts.placeholder)}`; - case 'cancel': - return `${title}${color.gray(S_BAR)} ${color.strikethrough( - color.dim(this.value ?? '') - )}${this.value?.trim() ? '\n' + color.gray(S_BAR) : ''}`; - default: - return `${title}${color.cyan(S_BAR)} ${value}\n${color.cyan(S_BAR_END)}\n`; - } - } - }).prompt(); -}; - -export interface PasswordOptions { - message: string; - mask?: string; - validate?: (value: string) => string | void; -} -export const password = (opts: PasswordOptions): Promise => { - return new PasswordPrompt({ - validate: opts.validate, - mask: opts.mask ?? S_PASSWORD_MASK, - render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; - const value = this.valueWithCursor; - const masked = this.masked; - - switch (this.state) { - case 'error': - return `${title.trim()}\n${color.yellow(S_BAR)} ${masked}\n${color.yellow( - S_BAR_END - )} ${color.yellow(this.error)}\n`; - case 'submit': - return `${title}${color.gray(S_BAR)} ${color.dim(masked)}`; - case 'cancel': - return `${title}${color.gray(S_BAR)} ${color.strikethrough(color.dim(masked ?? ''))}${ - masked ? '\n' + color.gray(S_BAR) : '' - }`; - default: - return `${title}${color.cyan(S_BAR)} ${value}\n${color.cyan(S_BAR_END)}\n`; - } - } - }).prompt(); -}; - -export interface ConfirmOptions { - message: string; - active?: string; - inactive?: string; - initialValue?: boolean; -} -export const confirm = (opts: ConfirmOptions) => { - const active = opts.active ?? 'Yes'; - const inactive = opts.inactive ?? 'No'; - return new ConfirmPrompt({ - active, - inactive, - initialValue: opts.initialValue ?? true, - render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; - const value = this.value ? active : inactive; - - switch (this.state) { - case 'submit': - return `${title}${color.gray(S_BAR)} ${color.dim(value)}`; - case 'cancel': - return `${title}${color.gray(S_BAR)} ${color.strikethrough( - color.dim(value) - )}\n${color.gray(S_BAR)}`; - default: { - return `${title}${color.cyan(S_BAR)} ${ - this.value - ? `${color.green(S_RADIO_ACTIVE)} ${active}` - : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(active)}` - } ${color.dim('/')} ${ - !this.value - ? `${color.green(S_RADIO_ACTIVE)} ${inactive}` - : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(inactive)}` - }\n${color.cyan(S_BAR_END)}\n`; - } - } - } - }).prompt() as Promise; -}; - -type Primitive = Readonly; - -type Option = Value extends Primitive - ? { value: Value; label?: string; hint?: string } - : { value: Value; label: string; hint?: string }; - -export interface SelectOptions { - message: string; - options: Array>; - initialValue?: Value; - maxItems?: number; -} - -export const select = (opts: SelectOptions) => { - const opt = (option: Option, state: 'inactive' | 'active' | 'selected' | 'cancelled') => { - const label = option.label ?? String(option.value); - switch (state) { - case 'selected': - return color.dim(label); - case 'active': - return `${color.green(S_RADIO_ACTIVE)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' - }`; - case 'cancelled': - return color.strikethrough(color.dim(label)); - default: - return `${color.dim(S_RADIO_INACTIVE)} ${color.dim(label)}`; - } - }; - - return new SelectPrompt({ - options: opts.options, - initialValue: opts.initialValue, - render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; - - switch (this.state) { - case 'submit': - return `${title}${color.gray(S_BAR)} ${opt(this.options[this.cursor]!, 'selected')}`; - case 'cancel': - return `${title}${color.gray(S_BAR)} ${opt( - this.options[this.cursor]!, - 'cancelled' - )}\n${color.gray(S_BAR)}`; - default: { - return `${title}${color.cyan(S_BAR)} ${limitOptions({ - cursor: this.cursor, - options: this.options, - maxItems: opts.maxItems, - style: (item, active) => opt(item, active ? 'active' : 'inactive') - }).join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; - } - } - } - }).prompt() as Promise; -}; - -export const selectKey = (opts: SelectOptions) => { - const opt = ( - option: Option, - state: 'inactive' | 'active' | 'selected' | 'cancelled' = 'inactive' - ) => { - const label = option.label ?? String(option.value); - if (state === 'selected') { - return color.dim(label); - } else if (state === 'cancelled') { - return color.strikethrough(color.dim(label)); - } else if (state === 'active') { - return `${color.bgCyan(color.gray(` ${option.value} `))} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' - }`; - } - return `${color.gray(color.bgWhite(color.inverse(` ${option.value} `)))} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' - }`; - }; - - return new SelectKeyPrompt({ - options: opts.options, - initialValue: opts.initialValue, - render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; - - switch (this.state) { - case 'submit': - return `${title}${color.gray(S_BAR)} ${opt( - this.options.find((opt) => opt.value === this.value)!, - 'selected' - )}`; - case 'cancel': - return `${title}${color.gray(S_BAR)} ${opt(this.options[0]!, 'cancelled')}\n${color.gray( - S_BAR - )}`; - default: { - return `${title}${color.cyan(S_BAR)} ${this.options - .map((option, i) => opt(option, i === this.cursor ? 'active' : 'inactive')) - .join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; - } - } - } - }).prompt() as Promise; -}; - -export interface MultiSelectOptions { - message: string; - options: Array>; - initialValues?: Value[]; - maxItems?: number; - required?: boolean; - cursorAt?: Value; -} -export const multiselect = (opts: MultiSelectOptions) => { - const opt = ( - option: Option, - state: 'inactive' | 'active' | 'selected' | 'active-selected' | 'submitted' | 'cancelled' - ) => { - const label = option.label ?? String(option.value); - if (state === 'active') { - return `${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' - }`; - } else if (state === 'selected') { - return `${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`; - } else if (state === 'cancelled') { - return color.strikethrough(color.dim(label)); - } else if (state === 'active-selected') { - return `${color.green(S_CHECKBOX_SELECTED)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' - }`; - } else if (state === 'submitted') { - return color.dim(label); - } - return `${color.dim(S_CHECKBOX_INACTIVE)} ${color.dim(label)}`; - }; - - return new MultiSelectPrompt({ - options: opts.options, - initialValues: opts.initialValues, - required: opts.required ?? true, - cursorAt: opts.cursorAt, - validate(selected: Value[]) { - if (this.required && selected.length === 0) - return `Please select at least one option.\n${color.reset( - color.dim( - `Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray( - color.bgWhite(color.inverse(' enter ')) - )} to submit` - ) - )}`; - }, - render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; - - const styleOption = (option: Option, active: boolean) => { - const selected = this.value.includes(option.value); - if (active && selected) { - return opt(option, 'active-selected'); - } - if (selected) { - return opt(option, 'selected'); - } - return opt(option, active ? 'active' : 'inactive'); - }; - - switch (this.state) { - case 'submit': { - return `${title}${color.gray(S_BAR)} ${ - this.options - .filter(({ value }) => this.value.includes(value)) - .map((option) => opt(option, 'submitted')) - .join(color.dim(', ')) || color.dim('none') - }`; - } - case 'cancel': { - const label = this.options - .filter(({ value }) => this.value.includes(value)) - .map((option) => opt(option, 'cancelled')) - .join(color.dim(', ')); - return `${title}${color.gray(S_BAR)} ${ - label.trim() ? `${label}\n${color.gray(S_BAR)}` : '' - }`; - } - case 'error': { - const footer = this.error - .split('\n') - .map((ln, i) => - i === 0 ? `${color.yellow(S_BAR_END)} ${color.yellow(ln)}` : ` ${ln}` - ) - .join('\n'); - return ( - title + - color.yellow(S_BAR) + - ' ' + - limitOptions({ - options: this.options, - cursor: this.cursor, - maxItems: opts.maxItems, - style: styleOption - }).join(`\n${color.yellow(S_BAR)} `) + - '\n' + - footer + - '\n' - ); - } - default: { - return `${title}${color.cyan(S_BAR)} ${limitOptions({ - options: this.options, - cursor: this.cursor, - maxItems: opts.maxItems, - style: styleOption - }).join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; - } - } - } - }).prompt() as Promise; -}; - -export interface GroupMultiSelectOptions { - message: string; - options: Record>>; - initialValues?: Value[]; - required?: boolean; - cursorAt?: Value; - selectableGroups?: boolean; - spacedGroups?: boolean; -} - -export const groupMultiselect = (opts: GroupMultiSelectOptions) => { - const { selectableGroups = false, spacedGroups = false } = opts; - const opt = ( - option: Option, - state: - | 'inactive' - | 'active' - | 'selected' - | 'active-selected' - | 'group-active' - | 'group-active-selected' - | 'submitted' - | 'cancelled', - options: Array> = [] - ) => { - const label = option.label ?? String(option.value); - const isItem = typeof (option as any).group === 'string'; - const next = isItem && (options[options.indexOf(option) + 1] ?? { group: true }); - // @ts-ignore - const isLast = isItem && next.group === true; - const prefix = isItem ? (selectableGroups ? `${isLast ? S_BAR_END : S_BAR} ` : ' ') : ''; - const spacingPrefix = spacedGroups && !isItem ? `\n${color.cyan(S_BAR)} ` : ''; - - if (state === 'active') { - return `${spacingPrefix}${color.dim(prefix)}${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' - }`; - } else if (state === 'group-active') { - return `${spacingPrefix}${prefix}${color.cyan(S_CHECKBOX_ACTIVE)} ${color.dim(label)}`; - } else if (state === 'group-active-selected') { - return `${spacingPrefix}${prefix}${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`; - } else if (state === 'selected') { - return `${spacingPrefix}${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${color.dim( - label - )}`; - } else if (state === 'cancelled') { - return color.strikethrough(color.dim(label)); - } else if (state === 'active-selected') { - return `${spacingPrefix}${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' - }`; - } else if (state === 'submitted') { - return color.dim(label); - } - return `${spacingPrefix}${color.dim(prefix)}${ - isItem || selectableGroups ? `${color.dim(S_CHECKBOX_INACTIVE)} ` : '' - }${color.dim(label)}`; - }; - - return new GroupMultiSelectPrompt({ - options: opts.options, - initialValues: opts.initialValues, - required: opts.required ?? true, - cursorAt: opts.cursorAt, - selectableGroups, - validate(selected: Value[]) { - if (this.required && selected.length === 0) - return `Please select at least one option.\n${color.reset( - color.dim( - `Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray( - color.bgWhite(color.inverse(' enter ')) - )} to submit` - ) - )}`; - }, - render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; - - switch (this.state) { - case 'submit': { - return `${title}${color.gray(S_BAR)} ${this.options - .filter(({ value }) => this.value.includes(value)) - .map((option) => opt(option, 'submitted')) - .join(color.dim(', '))}`; - } - case 'cancel': { - const label = this.options - .filter(({ value }) => this.value.includes(value)) - .map((option) => opt(option, 'cancelled')) - .join(color.dim(', ')); - return `${title}${color.gray(S_BAR)} ${ - label.trim() ? `${label}\n${color.gray(S_BAR)}` : '' - }`; - } - case 'error': { - const footer = this.error - .split('\n') - .map((ln, i) => - i === 0 ? `${color.yellow(S_BAR_END)} ${color.yellow(ln)}` : ` ${ln}` - ) - .join('\n'); - return `${title}${color.yellow(S_BAR)} ${this.options - .map((option, i, options) => { - const selected = - this.value.includes(option.value) || - (option.group === true && this.isGroupSelected(`${option.value}`)); - const active = i === this.cursor; - const groupActive = - !active && - typeof option.group === 'string' && - this.options[this.cursor]!.value === option.group; - if (groupActive) { - return opt(option, selected ? 'group-active-selected' : 'group-active', options); - } - if (active && selected) { - return opt(option, 'active-selected', options); - } - if (selected) { - return opt(option, 'selected', options); - } - return opt(option, active ? 'active' : 'inactive', options); - }) - .join(`\n${color.yellow(S_BAR)} `)}\n${footer}\n`; - } - default: { - return `${title}${color.cyan(S_BAR)} ${this.options - .map((option, i, options) => { - const selected = - this.value.includes(option.value) || - (option.group === true && this.isGroupSelected(`${option.value}`)); - const active = i === this.cursor; - const groupActive = - !active && - typeof option.group === 'string' && - this.options[this.cursor]!.value === option.group; - if (groupActive) { - return opt(option, selected ? 'group-active-selected' : 'group-active', options); - } - if (active && selected) { - return opt(option, 'active-selected', options); - } - if (selected) { - return opt(option, 'selected', options); - } - return opt(option, active ? 'active' : 'inactive', options); - }) - .join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; - } - } - } - }).prompt() as Promise; -}; - -const strip = (str: string) => str.replace(ansiRegex(), ''); -function buildBox(message = '', title = '', dimmed = true) { - const lines = `\n${message}\n`.split('\n'); - const titleLen = strip(title).length; - const len = - Math.max( - lines.reduce((sum, ln) => { - ln = strip(ln); - return ln.length > sum ? ln.length : sum; - }, 0), - titleLen - ) + 2; - const msg = lines - .map( - (ln) => - `${color.gray(S_BAR)} ${dimmed ? color.dim(ln) : ln}${' '.repeat(len - strip(ln).length)}${color.gray(S_BAR)}` - ) - .join('\n'); - process.stdout.write( - `${color.gray(S_BAR)}\n${color.green(S_STEP_SUBMIT)} ${color.reset(title)} ${color.gray( - S_BAR_H.repeat(Math.max(len - titleLen - 1, 1)) + S_CORNER_TOP_RIGHT - )}\n${msg}\n${color.gray(S_CONNECT_LEFT + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\n` - ); -} - -export const note = (message = '', title = ''): void => buildBox(message, title, true); -export const box = (message = '', title = ''): void => buildBox(message, title, false); -export const taskLog = (title: string) => { - const BAR = color.dim(S_BAR); - const ACTIVE = color.green(S_STEP_SUBMIT); - const SUCCESS = color.green(S_SUCCESS); - const ERROR = color.red(S_ERROR); - - // heading - process.stdout.write(`${BAR}\n`); - process.stdout.write(`${ACTIVE} ${title}\n`); - - let output = ''; - let frame = ''; - - // clears previous output - const clear = (eraseTitle = false): void => { - if (!frame) return; - const terminalWidth = process.stdout.columns; - const frameHeight = frame.split('\n').reduce((height, line) => { - // accounts for line wraps - height += Math.ceil(line.length / terminalWidth); - return height; - }, 0); - const lines = frameHeight + (eraseTitle ? 1 : 0); - - process.stdout.write(cursor.up(lines)); - process.stdout.write(erase.down()); - }; - - // logs the output - const print = (limit = 0): void => { - const lines = output.split('\n').slice(-limit); - // reset frame - frame = ''; - for (const line of lines) { - frame += `${BAR} ${line}\n`; - } - process.stdout.write(color.dim(frame)); - }; - - return { - set text(data: string) { - clear(); - output += data; - // half the height of the terminal - const frameHeight = Math.ceil(process.stdout.rows / 2); - print(frameHeight); - }, - fail(message: string): void { - clear(true); - process.stdout.write(`${ERROR} ${message}\n`); - print(); // log the output on failure - }, - success(message: string): void { - clear(true); - process.stdout.write(`${SUCCESS} ${message}\n`); - } - }; -}; - -export const cancel = (message = ''): void => { - process.stdout.write(`${color.gray(S_BAR_END)} ${color.red(message)}\n\n`); -}; - -export const intro = (title = ''): void => { - process.stdout.write(`${color.gray(S_BAR_START)} ${title}\n`); -}; - -export const outro = (message = ''): void => { - process.stdout.write(`${color.gray(S_BAR)}\n${color.gray(S_BAR_END)} ${message}\n\n`); -}; - -export type LogMessageOptions = { - symbol?: string; -}; -export const log = { - message: (message = '', { symbol = color.gray(S_BAR) }: LogMessageOptions = {}): void => { - const parts = [color.gray(S_BAR)]; - if (message) { - const [firstLine, ...lines] = message.split('\n'); - parts.push(`${symbol} ${firstLine}`, ...lines.map((ln) => `${color.gray(S_BAR)} ${ln}`)); - } - process.stdout.write(`${parts.join('\n')}\n`); - }, - info: (message: string): void => { - log.message(message, { symbol: color.blue(S_INFO) }); - }, - success: (message: string): void => { - log.message(message, { symbol: color.green(S_SUCCESS) }); - }, - step: (message: string): void => { - log.message(message, { symbol: color.green(S_STEP_SUBMIT) }); - }, - warn: (message: string): void => { - log.message(message, { symbol: color.yellow(S_WARN) }); - }, - /** alias for `log.warn()`. */ - warning: (message: string): void => { - log.warn(message); - }, - error: (message: string): void => { - log.message(message, { symbol: color.red(S_ERROR) }); - } -}; - -export const spinner = (): { - start: (msg?: string) => void; - stop: (msg?: string, code?: number) => void; - message: (msg?: string) => void; -} => { - const frames = unicode ? ['◒', '◐', '◓', '◑'] : ['•', 'o', 'O', '0']; - const delay = unicode ? 80 : 120; - - let unblock: () => void; - let loop: NodeJS.Timeout; - let isSpinnerActive: boolean = false; - let _message: string = ''; - - const handleExit = (code: number) => { - const msg = code > 1 ? 'Something went wrong' : 'Canceled'; - if (isSpinnerActive) stop(msg, code); - }; - - const errorEventHandler = () => { - handleExit(2); - }; - const signalEventHandler = () => { - handleExit(1); - }; - - const registerHooks = () => { - // Reference: https://nodejs.org/api/process.html#event-uncaughtexception - process.on('uncaughtExceptionMonitor', errorEventHandler); - // Reference: https://nodejs.org/api/process.html#event-unhandledrejection - process.on('unhandledRejection', errorEventHandler); - // Reference Signal Events: https://nodejs.org/api/process.html#signal-events - process.on('SIGINT', signalEventHandler); - process.on('SIGTERM', signalEventHandler); - process.on('exit', handleExit); - }; - - const clearHooks = () => { - process.removeListener('uncaughtExceptionMonitor', errorEventHandler); - process.removeListener('unhandledRejection', errorEventHandler); - process.removeListener('SIGINT', signalEventHandler); - process.removeListener('SIGTERM', signalEventHandler); - process.removeListener('exit', handleExit); - }; - - const start = (msg: string = ''): void => { - isSpinnerActive = true; - unblock = block(); - _message = msg.replace(/\.+$/, ''); - process.stdout.write(`${color.gray(S_BAR)}\n`); - let frameIndex = 0; - let dotsTimer = 0; - registerHooks(); - loop = setInterval(() => { - const frame = color.magenta(frames[frameIndex]); - const loadingDots = '.'.repeat(Math.floor(dotsTimer)).slice(0, 3); - process.stdout.write(cursor.move(-999, 0)); - process.stdout.write(erase.down(1)); - process.stdout.write(`${frame} ${_message}${loadingDots}`); - frameIndex = frameIndex + 1 < frames.length ? frameIndex + 1 : 0; - dotsTimer = dotsTimer < frames.length ? dotsTimer + 0.125 : 0; - }, delay); - }; - - const stop = (msg: string = '', code: number = 0): void => { - _message = msg ?? _message; - isSpinnerActive = false; - clearInterval(loop); - const step = - code === 0 - ? color.green(S_STEP_SUBMIT) - : code === 1 - ? color.red(S_STEP_CANCEL) - : color.red(S_STEP_ERROR); - process.stdout.write(cursor.move(-999, 0)); - process.stdout.write(erase.down(1)); - process.stdout.write(`${step} ${_message}\n`); - clearHooks(); - unblock(); - }; - - const message = (msg: string = ''): void => { - _message = msg ?? _message; - }; - - return { - start, - stop, - message - }; -}; - -// Adapted from https://github.com/chalk/ansi-regex -// @see LICENSE -function ansiRegex() { - const pattern = [ - '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', - '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))' - ].join('|'); - - return new RegExp(pattern, 'g'); -} - -export type PromptGroupAwaitedReturn = { - [P in keyof T]: Exclude, symbol>; -}; - -export interface PromptGroupOptions { - /** - * Control how the group can be canceled - * if one of the prompts is canceled. - */ - onCancel?: (opts: { results: Prettify>> }) => void; -} - -type Prettify = { - [P in keyof T]: T[P]; -} & {}; - -export type PromptGroup = { - [P in keyof T]: (opts: { - results: Prettify>>>; - }) => void | Promise; -}; - -/** - * Define a group of prompts to be displayed - * and return a results of objects within the group - */ -export const group = async ( - prompts: PromptGroup, - opts?: PromptGroupOptions -): Promise>> => { - const results = {} as any; - const promptNames = Object.keys(prompts); - - for (const name of promptNames) { - const prompt = prompts[name as keyof T]; - const result = await prompt({ results })?.catch((e) => { - throw e; - }); - - // Pass the results to the onCancel function - // so the user can decide what to do with the results - // TODO: Switch to callback within core to avoid isCancel Fn - if (typeof opts?.onCancel === 'function' && isCancel(result)) { - results[name] = 'canceled'; - opts.onCancel({ results }); - continue; - } - - results[name] = result; - } - - return results; -}; - -export type Task = { - /** - * Task title - */ - title: string; - /** - * Task function - */ - task: (message: (string: string) => void) => string | Promise | void | Promise; - - /** - * If enabled === false the task will be skipped - */ - enabled?: boolean; -}; - -/** - * Define a group of tasks to be executed - */ -export const tasks = async (tasks: Task[]): Promise => { - for (const task of tasks) { - if (task.enabled === false) continue; - - const s = spinner(); - s.start(task.title); - const result = await task.task(s.message); - s.stop(result || task.title); - } -}; diff --git a/packages/clack-prompts/package.json b/packages/clack-prompts/package.json deleted file mode 100644 index 733d8445..00000000 --- a/packages/clack-prompts/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@sveltejs/clack-prompts", - "private": true, - "version": "0.9.1", - "type": "module", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/sveltejs/cli/tree/main/packages/clack-prompts" - }, - "bugs": "https://github.com/sveltejs/cli/issues", - "scripts": { - "check": "tsc", - "format": "pnpm lint --write", - "lint": "prettier --check . --config ../../prettier.config.js --ignore-path ../../.gitignore --ignore-path .gitignore --ignore-path ../../.prettierignore" - }, - "files": [ - "dist" - ], - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "./package.json": "./package.json" - }, - "devDependencies": { - "@sveltejs/clack-core": "workspace:*", - "is-unicode-supported": "^1.3.0", - "picocolors": "^1.1.1", - "sisteransi": "^1.0.5" - } -} diff --git a/packages/clack-prompts/tsconfig.json b/packages/clack-prompts/tsconfig.json deleted file mode 100644 index a1fcf6c9..00000000 --- a/packages/clack-prompts/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "checkJs": false, - "isolatedDeclarations": true, - "declaration": true - } -} diff --git a/packages/cli/commands/add/index.ts b/packages/cli/commands/add/index.ts index 3359ef7e..ea1dc6ea 100644 --- a/packages/cli/commands/add/index.ts +++ b/packages/cli/commands/add/index.ts @@ -4,7 +4,7 @@ import process from 'node:process'; import pc from 'picocolors'; import * as v from 'valibot'; import * as pkg from 'empathic/package'; -import * as p from '@sveltejs/clack-prompts'; +import * as p from '@clack/prompts'; import { Command, Option } from 'commander'; import { officialAddons, @@ -92,7 +92,7 @@ export const add = new Command('add') const selectedAddons = transformAliases(specifiedAddons); common.runCommand(async () => { const { nextSteps } = await runAddCommand(options, selectedAddons); - if (nextSteps) p.box(nextSteps, 'Next steps'); + if (nextSteps) p.note(nextSteps, 'Next steps'); }); }); diff --git a/packages/cli/commands/create.ts b/packages/cli/commands/create.ts index 72a27f66..cd968899 100644 --- a/packages/cli/commands/create.ts +++ b/packages/cli/commands/create.ts @@ -3,7 +3,7 @@ import path from 'node:path'; import process from 'node:process'; import * as v from 'valibot'; import { Command, Option } from 'commander'; -import * as p from '@sveltejs/clack-prompts'; +import * as p from '@clack/prompts'; import pc from 'picocolors'; import { create as createKit, @@ -93,8 +93,8 @@ export const create = new Command('create') `Stuck? Visit us at ${pc.cyan('https://svelte.dev/chat')}` ]; - p.box(steps.join('\n'), 'Project next steps'); - if (addOnNextSteps) p.box(addOnNextSteps, 'Add-on next steps'); + p.note(steps.join('\n'), 'Project next steps'); + if (addOnNextSteps) p.note(addOnNextSteps, 'Add-on next steps'); }); }); diff --git a/packages/cli/lib/install.ts b/packages/cli/lib/install.ts index 9c0bfb7e..6574130b 100644 --- a/packages/cli/lib/install.ts +++ b/packages/cli/lib/install.ts @@ -9,7 +9,7 @@ import type { AddonWithoutExplicitArgs } from '@sveltejs/cli-core'; import pc from 'picocolors'; -import * as p from '@sveltejs/clack-prompts'; +import * as p from '@clack/prompts'; import { exec, NonZeroExitError } from 'tinyexec'; import { resolveCommand } from 'package-manager-detector'; import { TESTING } from '../utils/env.ts'; diff --git a/packages/cli/package.json b/packages/cli/package.json index 189e847d..8210a564 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -30,8 +30,8 @@ } }, "devDependencies": { + "@clack/prompts": "https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96", "@sveltejs/addons": "workspace:*", - "@sveltejs/clack-prompts": "workspace:*", "@sveltejs/cli-core": "workspace:*", "@sveltejs/create": "workspace:*", "@types/degit": "^2.8.6", diff --git a/packages/cli/utils/common.ts b/packages/cli/utils/common.ts index 935024bf..b2282f97 100644 --- a/packages/cli/utils/common.ts +++ b/packages/cli/utils/common.ts @@ -1,6 +1,6 @@ import pc from 'picocolors'; import pkg from '../package.json' with { type: 'json' }; -import * as p from '@sveltejs/clack-prompts'; +import * as p from '@clack/prompts'; import type { Argument, HelpConfiguration, Option } from 'commander'; import { UnsupportedError } from './errors.ts'; import process from 'node:process'; diff --git a/packages/cli/utils/package-manager.ts b/packages/cli/utils/package-manager.ts index 90829424..f53e4878 100644 --- a/packages/cli/utils/package-manager.ts +++ b/packages/cli/utils/package-manager.ts @@ -4,7 +4,7 @@ import process from 'node:process'; import * as find from 'empathic/find'; import { exec } from 'tinyexec'; import { Option } from 'commander'; -import * as p from '@sveltejs/clack-prompts'; +import * as p from '@clack/prompts'; import { AGENTS, COMMANDS, @@ -46,7 +46,12 @@ export async function packageManagerPrompt(cwd: string): Promise { - const task = p.taskLog(`Installing dependencies with ${agent}...`); + const task = p.taskLog({ + title: `Installing dependencies with ${agent}...`, + limit: Math.ceil(process.stdout.rows / 2), + spacing: 0, + retainLog: true + }); try { const { command, args } = constructCommand(COMMANDS[agent].install, [])!; @@ -56,17 +61,17 @@ export async function installDependencies(agent: AgentName, cwd: string): Promis }); proc.process?.stdout?.on('data', (data) => { - task.text = data; + task.message(data.toString(), { raw: true }); }); proc.process?.stderr?.on('data', (data) => { - task.text = data; + task.message(data.toString(), { raw: true }); }); await proc; task.success('Successfully installed dependencies'); } catch { - task.fail('Failed to install dependencies'); + task.error('Failed to install dependencies'); p.cancel('Operation failed.'); process.exit(2); } diff --git a/packages/core/index.ts b/packages/core/index.ts index e68a90e8..e22448cb 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -1,5 +1,5 @@ export { defineAddon, defineAddonOptions } from './addon/config.ts'; -export { log } from '@sveltejs/clack-prompts'; +export { log } from '@clack/prompts'; export { default as colors } from 'picocolors'; export { default as dedent } from 'dedent'; export * as utils from './utils.ts'; diff --git a/packages/core/package.json b/packages/core/package.json index 41232984..79fa2930 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -42,8 +42,8 @@ } }, "devDependencies": { + "@clack/prompts": "https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96", "@sveltejs/acorn-typescript": "^1.0.1", - "@sveltejs/clack-prompts": "workspace:*", "@types/estree": "^1.0.6", "acorn": "^8.14.0", "decircular": "^1.0.0", diff --git a/packages/migrate/package.json b/packages/migrate/package.json index 65ede312..248519d3 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -27,7 +27,7 @@ "svelte-migrate": "./bin.js" }, "dependencies": { - "@clack/prompts": "^0.9.1", + "@clack/prompts": "https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96", "import-meta-resolve": "^4.1.0", "magic-string": "^0.30.17", "package-manager-detector": "^0.2.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ad25f51..741ccd1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,41 +91,14 @@ importers: specifier: workspace:* version: link:../core - packages/clack-core: - devDependencies: - picocolors: - specifier: ^1.1.1 - version: 1.1.1 - sisteransi: - specifier: ^1.0.5 - version: 1.0.5 - wrap-ansi: - specifier: ^8.1.0 - version: 8.1.0 - - packages/clack-prompts: - devDependencies: - '@sveltejs/clack-core': - specifier: workspace:* - version: link:../clack-core - is-unicode-supported: - specifier: ^1.3.0 - version: 1.3.0 - picocolors: - specifier: ^1.1.1 - version: 1.1.1 - sisteransi: - specifier: ^1.0.5 - version: 1.0.5 - packages/cli: devDependencies: + '@clack/prompts': + specifier: https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96 + version: https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96 '@sveltejs/addons': specifier: workspace:* version: link:../addons - '@sveltejs/clack-prompts': - specifier: workspace:* - version: link:../clack-prompts '@sveltejs/cli-core': specifier: workspace:* version: link:../core @@ -171,12 +144,12 @@ importers: packages/core: devDependencies: + '@clack/prompts': + specifier: https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96 + version: https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96 '@sveltejs/acorn-typescript': specifier: ^1.0.1 version: 1.0.5(acorn@8.14.1) - '@sveltejs/clack-prompts': - specifier: workspace:* - version: link:../clack-prompts '@types/estree': specifier: ^1.0.6 version: 1.0.7 @@ -238,8 +211,8 @@ importers: packages/migrate: dependencies: '@clack/prompts': - specifier: ^0.9.1 - version: 0.9.1 + specifier: https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96 + version: https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96 import-meta-resolve: specifier: ^4.1.0 version: 4.1.0 @@ -349,11 +322,13 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@clack/core@0.4.1': - resolution: {integrity: sha512-Pxhij4UXg8KSr7rPek6Zowm+5M22rbd2g1nfojHJkxp5YkFqiZ2+YLEM/XGVIzvGOcM0nqjIFxrpDwWRZYWYjA==} + '@clack/core@https://pkg.pr.new/bombshell-dev/clack/@clack/core@f111d9646e0d0eddd88f4433544ae8d244ce8b2d': + resolution: {tarball: https://pkg.pr.new/bombshell-dev/clack/@clack/core@f111d9646e0d0eddd88f4433544ae8d244ce8b2d} + version: 0.4.1 - '@clack/prompts@0.9.1': - resolution: {integrity: sha512-JIpyaboYZeWYlyP0H+OoPPxd6nqueG/CmN6ixBiNFsIDHREevjIf0n0Ohh5gr5C8pEDknzgvz+pIJ8dMhzWIeg==} + '@clack/prompts@https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96': + resolution: {tarball: https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96} + version: 0.10.0 '@emnapi/core@1.4.0': resolution: {integrity: sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==} @@ -1579,10 +1554,6 @@ packages: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -2498,14 +2469,14 @@ snapshots: human-id: 4.1.1 prettier: 2.8.8 - '@clack/core@0.4.1': + '@clack/core@https://pkg.pr.new/bombshell-dev/clack/@clack/core@f111d9646e0d0eddd88f4433544ae8d244ce8b2d': dependencies: picocolors: 1.1.1 sisteransi: 1.0.5 - '@clack/prompts@0.9.1': + '@clack/prompts@https://pkg.pr.new/bombshell-dev/clack/@clack/prompts@f111d96': dependencies: - '@clack/core': 0.4.1 + '@clack/core': https://pkg.pr.new/bombshell-dev/clack/@clack/core@f111d9646e0d0eddd88f4433544ae8d244ce8b2d picocolors: 1.1.1 sisteransi: 1.0.5 @@ -3639,8 +3610,6 @@ snapshots: dependencies: better-path-resolve: 1.0.0 - is-unicode-supported@1.3.0: {} - is-windows@1.0.2: {} isexe@2.0.0: {} diff --git a/rolldown.config.js b/rolldown.config.js index 3ffbb1da..a9eb3aa7 100644 --- a/rolldown.config.js +++ b/rolldown.config.js @@ -118,13 +118,7 @@ function getConfig(project) { } /** @type {RolldownOptions[]} */ -export default [ - getConfig('clack-core'), - getConfig('clack-prompts'), - getConfig('create'), - getConfig('core'), - getConfig('cli') -]; +export default [getConfig('create'), getConfig('core'), getConfig('cli')]; /** * @param {PackageJson} pkg