@stacksjs/cli 0.64.6 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -28
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/install.d.ts +26 -0
- package/dist/app.d.ts +100 -0
- package/dist/cli.d.ts +10 -0
- package/dist/command.d.ts +32 -0
- package/dist/console.d.ts +3 -0
- package/dist/exec.d.ts +40 -0
- package/dist/helpers.d.ts +9 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -9
- package/dist/index.js.map +18 -18
- package/dist/parse.d.ts +15 -0
- package/dist/run.d.ts +63 -0
- package/dist/spinner.d.ts +2 -0
- package/dist/utils.d.ts +5 -0
- package/package.json +15 -20
- package/src/actions/install.ts +8 -5
- package/src/app.ts +64 -60
- package/src/cli.ts +1 -1
- package/src/command.ts +10 -3
- package/src/console.ts +1 -1
- package/src/exec.ts +33 -15
- package/src/helpers.ts +21 -11
- package/src/index.ts +1 -1
- package/src/parse.ts +57 -45
- package/src/run.ts +7 -3
- package/src/spinner.ts +1 -1
- package/src/utils.ts +13 -13
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface ParsedArgv {
|
|
2
|
+
args: string[];
|
|
3
|
+
options: { [k: string]: string | boolean | number };
|
|
4
|
+
}
|
|
5
|
+
export declare function parseArgv(argv?: string[]): ParsedArgv;
|
|
6
|
+
export declare function parseArgs(argv?: string[]): string[];
|
|
7
|
+
interface CliOptions {
|
|
8
|
+
dryRun?: boolean;
|
|
9
|
+
quiet?: boolean;
|
|
10
|
+
verbose?: boolean;
|
|
11
|
+
[k: string]: string | boolean | number | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function parseOptions(options?: CliOptions): CliOptions;
|
|
14
|
+
export declare function buddyOptions(options?: string[] | Record<string, any>): string;
|
|
15
|
+
export {};
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Ok, Result } from "@stacksjs/error-handling";
|
|
2
|
+
import type { CliOptions, CommandError, Readable, Subprocess, Writable } from "@stacksjs/types";
|
|
3
|
+
/**
|
|
4
|
+
* Run a command.
|
|
5
|
+
*
|
|
6
|
+
* @param command The command to run.
|
|
7
|
+
* @param options The options to pass to the command.
|
|
8
|
+
* @returns The result of the command.
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const result = await runCommand('ls')
|
|
12
|
+
*
|
|
13
|
+
* if (result.isErr())
|
|
14
|
+
* console.error(result.error)
|
|
15
|
+
* else
|
|
16
|
+
* console.log(result)
|
|
17
|
+
* ```
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const result = await runCommand('ls', { cwd: '/home' })
|
|
21
|
+
*
|
|
22
|
+
* if (result.isErr())
|
|
23
|
+
* console.error(result.error)
|
|
24
|
+
* else
|
|
25
|
+
* console.log(result)
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function runCommand(command: string, options?: CliOptions): Promise<Result<Subprocess, CommandError>>;
|
|
29
|
+
export declare function runProcess(command: string, options?: CliOptions): Promise<Result<Subprocess, CommandError>>;
|
|
30
|
+
/**
|
|
31
|
+
* Run a command.
|
|
32
|
+
*
|
|
33
|
+
* @param command The command to run.
|
|
34
|
+
* @param options The options to pass to the command.
|
|
35
|
+
* @returns The result of the command.
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const result = runCommandSync('ls')
|
|
39
|
+
*
|
|
40
|
+
* if (result.isErr())
|
|
41
|
+
* console.error(result.error)
|
|
42
|
+
* else
|
|
43
|
+
* console.log(result)
|
|
44
|
+
* ```
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const result = runCommandSync('ls', { cwd: '/home' })
|
|
48
|
+
*
|
|
49
|
+
* if (result.isErr())
|
|
50
|
+
* console.error(result.error)
|
|
51
|
+
* else
|
|
52
|
+
* console.log(result)
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function runCommandSync(command: string, options?: CliOptions): Promise<string>;
|
|
56
|
+
/**
|
|
57
|
+
* Run many commands.
|
|
58
|
+
*
|
|
59
|
+
* @param commands The command to run.
|
|
60
|
+
* @param options The options to pass to the command.
|
|
61
|
+
* @returns The result of the command.
|
|
62
|
+
*/
|
|
63
|
+
export declare function runCommands(commands: string[], options?: CliOptions): Promise<Ok<Subprocess<Writable, Readable, Readable>, Error>[]>;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Collection } from "@stacksjs/collections";
|
|
2
|
+
export { align, box, centerAlign, colorize, colors, getColor, leftAlign, rightAlign, stripAnsi } from "consola/utils";
|
|
3
|
+
export * as kolorist from "kolorist";
|
|
4
|
+
export { ansi256, ansi256Bg, bgBlack, bgBlue, bgCyan, bgGray, bgGreen, bgLightBlue, bgLightCyan, bgLightGray, bgLightGreen, bgLightMagenta, bgLightRed, bgLightYellow, bgMagenta, bgRed, bgWhite, bgYellow, black, blue, bold, cyan, dim, gray, green, hidden, inverse, italic, lightBlue, lightCyan, lightGray, lightGreen, lightMagenta, lightRed, lightYellow, link, magenta, red, reset, strikethrough, stripColors, trueColor, trueColorBg, underline, white, yellow } from "kolorist";
|
|
5
|
+
export declare const quotes: Collection<string>;
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.65.0",
|
|
5
5
|
"description": "TypeScript framework for CLI artisans. Build beautiful console apps with ease.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
|
+
"contributors": ["Chris Breuer <chris@stacksjs.org>"],
|
|
7
8
|
"license": "MIT",
|
|
8
9
|
"funding": "https://github.com/sponsors/chrisbbreuer",
|
|
9
10
|
"homepage": "https://github.com/stacksjs/stacks/tree/main/storage/framework/core/cli#readme",
|
|
@@ -43,30 +44,24 @@
|
|
|
43
44
|
},
|
|
44
45
|
"module": "dist/index.js",
|
|
45
46
|
"types": "dist/index.d.ts",
|
|
46
|
-
"
|
|
47
|
-
"Chris Breuer <chris@stacksjs.org>"
|
|
48
|
-
],
|
|
49
|
-
"files": [
|
|
50
|
-
"README.md",
|
|
51
|
-
"dist",
|
|
52
|
-
"src"
|
|
53
|
-
],
|
|
47
|
+
"files": ["README.md", "dist", "src"],
|
|
54
48
|
"scripts": {
|
|
55
|
-
"build": "bun
|
|
56
|
-
"typecheck": "bun
|
|
49
|
+
"build": "bun build.ts",
|
|
50
|
+
"typecheck": "bun tsc --noEmit",
|
|
51
|
+
"test": "bun test",
|
|
57
52
|
"prepublishOnly": "bun run build"
|
|
58
53
|
},
|
|
59
54
|
"dependencies": {
|
|
60
55
|
"@antfu/install-pkg": "^0.4.1",
|
|
61
56
|
"@clack/core": "^0.3.4",
|
|
62
|
-
"@stacksjs/collections": "
|
|
63
|
-
"@stacksjs/config": "
|
|
64
|
-
"@stacksjs/error-handling": "
|
|
65
|
-
"@stacksjs/logging": "
|
|
66
|
-
"@stacksjs/path": "
|
|
67
|
-
"@stacksjs/types": "
|
|
68
|
-
"@stacksjs/utils": "
|
|
69
|
-
"@stacksjs/validation": "
|
|
57
|
+
"@stacksjs/collections": "0.64.6",
|
|
58
|
+
"@stacksjs/config": "0.64.6",
|
|
59
|
+
"@stacksjs/error-handling": "0.64.6",
|
|
60
|
+
"@stacksjs/logging": "0.64.6",
|
|
61
|
+
"@stacksjs/path": "0.64.6",
|
|
62
|
+
"@stacksjs/types": "0.64.6",
|
|
63
|
+
"@stacksjs/utils": "0.64.6",
|
|
64
|
+
"@stacksjs/validation": "0.64.6",
|
|
70
65
|
"cac": "^6.7.14",
|
|
71
66
|
"consola": "^3.2.3",
|
|
72
67
|
"kolorist": "1.8.0",
|
|
@@ -74,7 +69,7 @@
|
|
|
74
69
|
"prompts": "^2.4.2"
|
|
75
70
|
},
|
|
76
71
|
"devDependencies": {
|
|
77
|
-
"@stacksjs/development": "
|
|
72
|
+
"@stacksjs/development": "0.64.6",
|
|
78
73
|
"@types/prompts": "^2.4.9"
|
|
79
74
|
}
|
|
80
75
|
}
|
package/src/actions/install.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { installPackage as installPkg } from '@antfu/install-pkg'
|
|
2
|
-
import type { ExecaReturnValue } from 'execa'
|
|
3
2
|
|
|
4
3
|
interface InstallPackageOptions {
|
|
5
4
|
cwd?: string
|
|
@@ -11,6 +10,8 @@ interface InstallPackageOptions {
|
|
|
11
10
|
additionalArgs?: string[]
|
|
12
11
|
}
|
|
13
12
|
|
|
13
|
+
// TODO: improve return types here
|
|
14
|
+
|
|
14
15
|
/**
|
|
15
16
|
* Install an npm package.
|
|
16
17
|
*
|
|
@@ -18,8 +19,9 @@ interface InstallPackageOptions {
|
|
|
18
19
|
* @param options - The options to pass to the install.The options to pass to the install.
|
|
19
20
|
* @returns The result of the install.
|
|
20
21
|
*/
|
|
21
|
-
export async function installPackage(name: string, options?: InstallPackageOptions) {
|
|
22
|
-
if (options)
|
|
22
|
+
export async function installPackage(name: string, options?: InstallPackageOptions): Promise<any> {
|
|
23
|
+
if (options)
|
|
24
|
+
return await installPkg(name, options)
|
|
23
25
|
|
|
24
26
|
return await installPkg(name, { silent: true })
|
|
25
27
|
}
|
|
@@ -31,8 +33,9 @@ export async function installPackage(name: string, options?: InstallPackageOptio
|
|
|
31
33
|
* @param options - The options to pass to the install.
|
|
32
34
|
* @returns The result of the install.
|
|
33
35
|
*/
|
|
34
|
-
export async function installStack(name: string, options?: InstallPackageOptions) {
|
|
35
|
-
if (options)
|
|
36
|
+
export async function installStack(name: string, options?: InstallPackageOptions): Promise<any> {
|
|
37
|
+
if (options)
|
|
38
|
+
return await installPkg(`@stacksjs/${name}`, options)
|
|
36
39
|
|
|
37
40
|
return await installPkg(`@stacksjs/${name}`, { silent: true })
|
|
38
41
|
}
|
package/src/app.ts
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
/* eslint-disable ts/no-use-before-define, regexp/no-useless-non-capturing-group, regexp/no-trivially-nested-quantifier, regexp/no-useless-quantifier, regexp/prefer-w,regexp/no-useless-escape */
|
|
2
|
+
import process from 'node:process'
|
|
1
3
|
// slightly modified version of @clack/prompts
|
|
2
4
|
// many thanks to bombshell-dev for the original work
|
|
3
5
|
import {
|
|
6
|
+
block,
|
|
4
7
|
ConfirmPrompt,
|
|
5
8
|
GroupMultiSelectPrompt,
|
|
9
|
+
isCancel,
|
|
6
10
|
MultiSelectPrompt,
|
|
7
11
|
PasswordPrompt,
|
|
8
12
|
SelectKeyPrompt,
|
|
9
13
|
SelectPrompt,
|
|
10
14
|
type State,
|
|
11
15
|
TextPrompt,
|
|
12
|
-
block,
|
|
13
|
-
isCancel,
|
|
14
16
|
} from '@clack/core'
|
|
15
17
|
import isUnicodeSupported from 'is-unicode-supported'
|
|
16
18
|
import color from 'picocolors'
|
|
@@ -41,7 +43,7 @@ const S_CORNER_TOP_RIGHT = s('╮', '+')
|
|
|
41
43
|
const S_CONNECT_LEFT = s('├', '+')
|
|
42
44
|
const S_CORNER_BOTTOM_RIGHT = s('╯', '+')
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
function symbol(state: State) {
|
|
45
47
|
switch (state) {
|
|
46
48
|
case 'initial':
|
|
47
49
|
case 'active':
|
|
@@ -62,7 +64,7 @@ interface LimitOptionsParams<TOption> {
|
|
|
62
64
|
style: (option: TOption, active: boolean) => string
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
function limitOptions<TOption>(params: LimitOptionsParams<TOption>): string[] {
|
|
66
68
|
const { cursor, options, style } = params
|
|
67
69
|
|
|
68
70
|
const paramMaxItems = params.maxItems ?? Number.POSITIVE_INFINITY
|
|
@@ -73,7 +75,8 @@ const limitOptions = <TOption>(params: LimitOptionsParams<TOption>): string[] =>
|
|
|
73
75
|
|
|
74
76
|
if (cursor >= slidingWindowLocation + maxItems - 3) {
|
|
75
77
|
slidingWindowLocation = Math.max(Math.min(cursor - maxItems + 3, options.length - maxItems), 0)
|
|
76
|
-
}
|
|
78
|
+
}
|
|
79
|
+
else if (cursor < slidingWindowLocation + 2) {
|
|
77
80
|
slidingWindowLocation = Math.max(cursor - 2, 0)
|
|
78
81
|
}
|
|
79
82
|
|
|
@@ -92,10 +95,9 @@ export interface TextOptions {
|
|
|
92
95
|
placeholder?: string
|
|
93
96
|
defaultValue?: string
|
|
94
97
|
initialValue?: string
|
|
95
|
-
// biome-ignore lint/suspicious/noConfusingVoidType: originally shipped this
|
|
96
98
|
validate?: (value: string) => string | void
|
|
97
99
|
}
|
|
98
|
-
export
|
|
100
|
+
export function text(opts: TextOptions) {
|
|
99
101
|
return new TextPrompt({
|
|
100
102
|
validate: opts.validate,
|
|
101
103
|
placeholder: opts.placeholder,
|
|
@@ -129,10 +131,9 @@ export const text = (opts: TextOptions) => {
|
|
|
129
131
|
export interface PasswordOptions {
|
|
130
132
|
message: string
|
|
131
133
|
mask?: string
|
|
132
|
-
// biome-ignore lint/suspicious/noConfusingVoidType: originally shipped this
|
|
133
134
|
validate?: (value: string) => string | void
|
|
134
135
|
}
|
|
135
|
-
export
|
|
136
|
+
export function password(opts: PasswordOptions) {
|
|
136
137
|
return new PasswordPrompt({
|
|
137
138
|
validate: opts.validate,
|
|
138
139
|
mask: opts.mask ?? S_PASSWORD_MASK,
|
|
@@ -165,7 +166,7 @@ export interface ConfirmOptions {
|
|
|
165
166
|
inactive?: string
|
|
166
167
|
initialValue?: boolean
|
|
167
168
|
}
|
|
168
|
-
export
|
|
169
|
+
export function confirm(opts: ConfirmOptions) {
|
|
169
170
|
const active = opts.active ?? 'Yes'
|
|
170
171
|
const inactive = opts.inactive ?? 'No'
|
|
171
172
|
return new ConfirmPrompt({
|
|
@@ -200,8 +201,8 @@ export const confirm = (opts: ConfirmOptions) => {
|
|
|
200
201
|
type Primitive = Readonly<string | boolean | number>
|
|
201
202
|
|
|
202
203
|
type Option<Value> = Value extends Primitive
|
|
203
|
-
? { value: Value
|
|
204
|
-
: { value: Value
|
|
204
|
+
? { value: Value, label?: string, hint?: string }
|
|
205
|
+
: { value: Value, label: string, hint?: string }
|
|
205
206
|
|
|
206
207
|
export interface SelectOptions<Value> {
|
|
207
208
|
message: string
|
|
@@ -210,7 +211,7 @@ export interface SelectOptions<Value> {
|
|
|
210
211
|
maxItems?: number
|
|
211
212
|
}
|
|
212
213
|
|
|
213
|
-
export
|
|
214
|
+
export function select<Value>(opts: SelectOptions<Value>) {
|
|
214
215
|
const opt = (option: Option<Value>, state: 'inactive' | 'active' | 'selected' | 'cancelled') => {
|
|
215
216
|
const label = option.label ?? String(option.value)
|
|
216
217
|
switch (state) {
|
|
@@ -233,10 +234,8 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
|
|
|
233
234
|
|
|
234
235
|
switch (this.state) {
|
|
235
236
|
case 'submit':
|
|
236
|
-
// biome-ignore lint/style/noNonNullAssertion: will be set
|
|
237
237
|
return `${title}${color.gray(S_BAR)} ${opt(this.options[this.cursor]!, 'selected')}`
|
|
238
238
|
case 'cancel':
|
|
239
|
-
// biome-ignore lint/style/noNonNullAssertion: will be set
|
|
240
239
|
return `${title}${color.gray(S_BAR)} ${opt(this.options[this.cursor]!, 'cancelled')}\n${color.gray(S_BAR)}`
|
|
241
240
|
default: {
|
|
242
241
|
return `${title}${color.cyan(S_BAR)} ${limitOptions({
|
|
@@ -251,7 +250,7 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
|
|
|
251
250
|
}).prompt() as Promise<Value | symbol>
|
|
252
251
|
}
|
|
253
252
|
|
|
254
|
-
export
|
|
253
|
+
export function selectKey<Value extends string>(opts: SelectOptions<Value>) {
|
|
255
254
|
const opt = (option: Option<Value>, state: 'inactive' | 'active' | 'selected' | 'cancelled' = 'inactive') => {
|
|
256
255
|
const label = option.label ?? String(option.value)
|
|
257
256
|
|
|
@@ -283,12 +282,10 @@ export const selectKey = <Value extends string>(opts: SelectOptions<Value>) => {
|
|
|
283
282
|
switch (this.state) {
|
|
284
283
|
case 'submit':
|
|
285
284
|
return `${title}${color.gray(S_BAR)} ${opt(
|
|
286
|
-
|
|
287
|
-
this.options.find((opt) => opt.value === this.value)!,
|
|
285
|
+
this.options.find(opt => opt.value === this.value)!,
|
|
288
286
|
'selected',
|
|
289
287
|
)}`
|
|
290
288
|
case 'cancel':
|
|
291
|
-
// biome-ignore lint/style/noNonNullAssertion: will be set
|
|
292
289
|
return `${title}${color.gray(S_BAR)} ${opt(this.options[0]!, 'cancelled')}\n${color.gray(S_BAR)}`
|
|
293
290
|
default: {
|
|
294
291
|
return `${title}${color.cyan(S_BAR)} ${this.options
|
|
@@ -308,7 +305,7 @@ export interface MultiSelectOptions<Value> {
|
|
|
308
305
|
required?: boolean
|
|
309
306
|
cursorAt?: Value
|
|
310
307
|
}
|
|
311
|
-
export
|
|
308
|
+
export function multiselect<Value>(opts: MultiSelectOptions<Value>) {
|
|
312
309
|
const opt = (
|
|
313
310
|
option: Option<Value>,
|
|
314
311
|
state: 'inactive' | 'active' | 'selected' | 'active-selected' | 'submitted' | 'cancelled',
|
|
@@ -344,7 +341,7 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
|
|
|
344
341
|
required: opts.required ?? true,
|
|
345
342
|
cursorAt: opts.cursorAt,
|
|
346
343
|
validate(selected: Value[]) {
|
|
347
|
-
if (this.required && selected.length === 0)
|
|
344
|
+
if (this.required && selected.length === 0) {
|
|
348
345
|
return `Please select at least one option.\n${color.reset(
|
|
349
346
|
color.dim(
|
|
350
347
|
`Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray(
|
|
@@ -352,6 +349,7 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
|
|
|
352
349
|
)} to submit`,
|
|
353
350
|
),
|
|
354
351
|
)}`
|
|
352
|
+
}
|
|
355
353
|
},
|
|
356
354
|
render() {
|
|
357
355
|
const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`
|
|
@@ -372,14 +370,14 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
|
|
|
372
370
|
return `${title}${color.gray(S_BAR)} ${
|
|
373
371
|
this.options
|
|
374
372
|
.filter(({ value }) => this.value.includes(value))
|
|
375
|
-
.map(
|
|
373
|
+
.map(option => opt(option, 'submitted'))
|
|
376
374
|
.join(color.dim(', ')) || color.dim('none')
|
|
377
375
|
}`
|
|
378
376
|
}
|
|
379
377
|
case 'cancel': {
|
|
380
378
|
const label = this.options
|
|
381
379
|
.filter(({ value }) => this.value.includes(value))
|
|
382
|
-
.map(
|
|
380
|
+
.map(option => opt(option, 'cancelled'))
|
|
383
381
|
.join(color.dim(', '))
|
|
384
382
|
return `${title}${color.gray(S_BAR)} ${label.trim() ? `${label}\n${color.gray(S_BAR)}` : ''}`
|
|
385
383
|
}
|
|
@@ -419,7 +417,7 @@ export interface GroupMultiSelectOptions<Value> {
|
|
|
419
417
|
required?: boolean
|
|
420
418
|
cursorAt?: Value
|
|
421
419
|
}
|
|
422
|
-
export
|
|
420
|
+
export function groupMultiselect<Value>(opts: GroupMultiSelectOptions<Value>) {
|
|
423
421
|
const opt = (
|
|
424
422
|
option: Option<Value>,
|
|
425
423
|
state:
|
|
@@ -480,7 +478,7 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
|
|
|
480
478
|
required: opts.required ?? true,
|
|
481
479
|
cursorAt: opts.cursorAt,
|
|
482
480
|
validate(selected: Value[]) {
|
|
483
|
-
if (this.required && selected.length === 0)
|
|
481
|
+
if (this.required && selected.length === 0) {
|
|
484
482
|
return `Please select at least one option.\n${color.reset(
|
|
485
483
|
color.dim(
|
|
486
484
|
`Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray(
|
|
@@ -488,6 +486,7 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
|
|
|
488
486
|
)} to submit`,
|
|
489
487
|
),
|
|
490
488
|
)}`
|
|
489
|
+
}
|
|
491
490
|
},
|
|
492
491
|
render() {
|
|
493
492
|
const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`
|
|
@@ -496,13 +495,13 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
|
|
|
496
495
|
case 'submit': {
|
|
497
496
|
return `${title}${color.gray(S_BAR)} ${this.options
|
|
498
497
|
.filter(({ value }) => this.value.includes(value))
|
|
499
|
-
.map(
|
|
498
|
+
.map(option => opt(option, 'submitted'))
|
|
500
499
|
.join(color.dim(', '))}`
|
|
501
500
|
}
|
|
502
501
|
case 'cancel': {
|
|
503
502
|
const label = this.options
|
|
504
503
|
.filter(({ value }) => this.value.includes(value))
|
|
505
|
-
.map(
|
|
504
|
+
.map(option => opt(option, 'cancelled'))
|
|
506
505
|
.join(color.dim(', '))
|
|
507
506
|
return `${title}${color.gray(S_BAR)} ${label.trim() ? `${label}\n${color.gray(S_BAR)}` : ''}`
|
|
508
507
|
}
|
|
@@ -513,12 +512,11 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
|
|
|
513
512
|
.join('\n')
|
|
514
513
|
return `${title}${color.yellow(S_BAR)} ${this.options
|
|
515
514
|
.map((option, i, options) => {
|
|
516
|
-
const selected
|
|
517
|
-
this.value.includes(option.value) || (option.group === true && this.isGroupSelected(`${option.value}`))
|
|
515
|
+
const selected
|
|
516
|
+
= this.value.includes(option.value) || (option.group === true && this.isGroupSelected(`${option.value}`))
|
|
518
517
|
const active = i === this.cursor
|
|
519
|
-
const groupActive
|
|
520
|
-
|
|
521
|
-
!active && typeof option.group === 'string' && this.options[this.cursor]!.value === option.group
|
|
518
|
+
const groupActive
|
|
519
|
+
= !active && typeof option.group === 'string' && this.options[this.cursor]!.value === option.group
|
|
522
520
|
if (groupActive) {
|
|
523
521
|
return opt(option, selected ? 'group-active-selected' : 'group-active', options)
|
|
524
522
|
}
|
|
@@ -535,12 +533,11 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
|
|
|
535
533
|
default: {
|
|
536
534
|
return `${title}${color.cyan(S_BAR)} ${this.options
|
|
537
535
|
.map((option, i, options) => {
|
|
538
|
-
const selected
|
|
539
|
-
this.value.includes(option.value) || (option.group === true && this.isGroupSelected(`${option.value}`))
|
|
536
|
+
const selected
|
|
537
|
+
= this.value.includes(option.value) || (option.group === true && this.isGroupSelected(`${option.value}`))
|
|
540
538
|
const active = i === this.cursor
|
|
541
|
-
const groupActive
|
|
542
|
-
|
|
543
|
-
!active && typeof option.group === 'string' && this.options[this.cursor]!.value === option.group
|
|
539
|
+
const groupActive
|
|
540
|
+
= !active && typeof option.group === 'string' && this.options[this.cursor]!.value === option.group
|
|
544
541
|
if (groupActive) {
|
|
545
542
|
return opt(option, selected ? 'group-active-selected' : 'group-active', options)
|
|
546
543
|
}
|
|
@@ -563,11 +560,11 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
|
|
|
563
560
|
}
|
|
564
561
|
|
|
565
562
|
const strip = (str: string) => str.replace(ansiRegex(), '')
|
|
566
|
-
export
|
|
563
|
+
export function note(message = '', title = ''): void {
|
|
567
564
|
const lines = `\n${message}\n`.split('\n')
|
|
568
565
|
const titleLen = strip(title).length
|
|
569
|
-
const len
|
|
570
|
-
Math.max(
|
|
566
|
+
const len
|
|
567
|
+
= Math.max(
|
|
571
568
|
lines.reduce((sum, ln) => {
|
|
572
569
|
ln = strip(ln)
|
|
573
570
|
return ln.length > sum ? ln.length : sum
|
|
@@ -575,7 +572,7 @@ export const note = (message = '', title = '') => {
|
|
|
575
572
|
titleLen,
|
|
576
573
|
) + 2
|
|
577
574
|
const msg = lines
|
|
578
|
-
.map(
|
|
575
|
+
.map(ln => `${color.gray(S_BAR)} ${color.dim(ln)}${' '.repeat(len - strip(ln).length)}${color.gray(S_BAR)}`)
|
|
579
576
|
.join('\n')
|
|
580
577
|
process.stdout.write(
|
|
581
578
|
`${color.gray(S_BAR)}\n${color.green(S_STEP_SUBMIT)} ${color.reset(title)} ${color.gray(
|
|
@@ -584,19 +581,25 @@ export const note = (message = '', title = '') => {
|
|
|
584
581
|
)
|
|
585
582
|
}
|
|
586
583
|
|
|
587
|
-
export
|
|
584
|
+
export function cancel(message = ''): void {
|
|
588
585
|
process.stdout.write(`${color.gray(S_BAR_END)} ${color.red(message)}\n\n`)
|
|
589
586
|
}
|
|
590
587
|
|
|
591
|
-
export
|
|
588
|
+
export function intro(title = ''): void {
|
|
592
589
|
process.stdout.write(`${color.gray(S_BAR_START)} ${title}\n`)
|
|
593
590
|
}
|
|
594
591
|
|
|
595
|
-
export
|
|
592
|
+
export function outro(message = ''): void {
|
|
596
593
|
process.stdout.write(`${color.gray(S_BAR)}\n${color.gray(S_BAR_END)} ${message}\n\n`)
|
|
597
594
|
}
|
|
598
595
|
|
|
599
|
-
|
|
596
|
+
interface Spinner {
|
|
597
|
+
start: (msg: string) => void
|
|
598
|
+
stop: (msg: string, code: number) => void
|
|
599
|
+
message: (msg: string) => void
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
export function spinner(): Spinner {
|
|
600
603
|
const frames = unicode ? ['◒', '◐', '◓', '◑'] : ['•', 'o', 'O', '0']
|
|
601
604
|
const delay = unicode ? 80 : 120
|
|
602
605
|
|
|
@@ -607,7 +610,8 @@ export const spinner = () => {
|
|
|
607
610
|
|
|
608
611
|
const handleExit = (code: number) => {
|
|
609
612
|
const msg = code > 1 ? 'Something went wrong' : 'Canceled'
|
|
610
|
-
if (isSpinnerActive)
|
|
613
|
+
if (isSpinnerActive)
|
|
614
|
+
stop(msg, code)
|
|
611
615
|
}
|
|
612
616
|
|
|
613
617
|
const errorEventHandler = () => handleExit(2)
|
|
@@ -633,15 +637,16 @@ export const spinner = () => {
|
|
|
633
637
|
}
|
|
634
638
|
|
|
635
639
|
const start = (msg = ''): void => {
|
|
636
|
-
let loop: ReturnType<typeof setInterval>
|
|
637
640
|
isSpinnerActive = true
|
|
638
641
|
unblock = block()
|
|
639
642
|
_message = msg.replace(/\.+$/, '')
|
|
640
643
|
process.stdout.write(`${color.gray(S_BAR)}\n`)
|
|
641
644
|
let frameIndex = 0
|
|
642
645
|
let dotsTimer = 0
|
|
646
|
+
|
|
643
647
|
registerHooks()
|
|
644
|
-
|
|
648
|
+
|
|
649
|
+
setInterval(() => {
|
|
645
650
|
const frame = color.magenta(frames[frameIndex])
|
|
646
651
|
const loadingDots = '.'.repeat(Math.floor(dotsTimer)).slice(0, 3)
|
|
647
652
|
process.stdout.write(cursor.move(-999, 0))
|
|
@@ -656,8 +661,8 @@ export const spinner = () => {
|
|
|
656
661
|
_message = msg ?? _message
|
|
657
662
|
isSpinnerActive = false
|
|
658
663
|
clearInterval(loop as NodeJS.Timeout)
|
|
659
|
-
const step
|
|
660
|
-
code === 0 ? color.green(S_STEP_SUBMIT) : code === 1 ? color.red(S_STEP_CANCEL) : color.red(S_STEP_ERROR)
|
|
664
|
+
const step
|
|
665
|
+
= code === 0 ? color.green(S_STEP_SUBMIT) : code === 1 ? color.red(S_STEP_CANCEL) : color.red(S_STEP_ERROR)
|
|
661
666
|
process.stdout.write(cursor.move(-999, 0))
|
|
662
667
|
process.stdout.write(erase.down(1))
|
|
663
668
|
process.stdout.write(`${step} ${_message}\n`)
|
|
@@ -669,11 +674,13 @@ export const spinner = () => {
|
|
|
669
674
|
_message = msg ?? _message
|
|
670
675
|
}
|
|
671
676
|
|
|
672
|
-
|
|
677
|
+
const spinner = {
|
|
673
678
|
start,
|
|
674
679
|
stop,
|
|
675
680
|
message,
|
|
676
681
|
}
|
|
682
|
+
|
|
683
|
+
return spinner
|
|
677
684
|
}
|
|
678
685
|
|
|
679
686
|
// Adapted from https://github.com/chalk/ansi-regex
|
|
@@ -706,7 +713,6 @@ type Prettify<T> = {
|
|
|
706
713
|
export type PromptGroup<T> = {
|
|
707
714
|
[P in keyof T]: (opts: {
|
|
708
715
|
results: Prettify<Partial<PromptGroupAwaitedReturn<Omit<T, P>>>>
|
|
709
|
-
// biome-ignore lint/suspicious/noConfusingVoidType: originally shipped this
|
|
710
716
|
}) => void | Promise<T[P] | void>
|
|
711
717
|
}
|
|
712
718
|
|
|
@@ -714,10 +720,7 @@ export type PromptGroup<T> = {
|
|
|
714
720
|
* Define a group of prompts to be displayed
|
|
715
721
|
* and return a results of objects within the group
|
|
716
722
|
*/
|
|
717
|
-
export
|
|
718
|
-
prompts: PromptGroup<T>,
|
|
719
|
-
opts?: PromptGroupOptions<T>,
|
|
720
|
-
): Promise<Prettify<PromptGroupAwaitedReturn<T>>> => {
|
|
723
|
+
export async function group<T>(prompts: PromptGroup<T>, opts?: PromptGroupOptions<T>): Promise<Prettify<PromptGroupAwaitedReturn<T>>> {
|
|
721
724
|
const results = {} as any
|
|
722
725
|
const promptNames = Object.keys(prompts)
|
|
723
726
|
|
|
@@ -742,7 +745,7 @@ export const group = async <T>(
|
|
|
742
745
|
return results
|
|
743
746
|
}
|
|
744
747
|
|
|
745
|
-
export
|
|
748
|
+
export interface Task {
|
|
746
749
|
/**
|
|
747
750
|
* Task title
|
|
748
751
|
*/
|
|
@@ -761,13 +764,14 @@ export type Task = {
|
|
|
761
764
|
/**
|
|
762
765
|
* Define a group of tasks to be executed
|
|
763
766
|
*/
|
|
764
|
-
export
|
|
767
|
+
export async function tasks(tasks: Task[]): Promise<void> {
|
|
765
768
|
for (const task of tasks) {
|
|
766
|
-
if (task.enabled === false)
|
|
769
|
+
if (task.enabled === false)
|
|
770
|
+
continue
|
|
767
771
|
|
|
768
772
|
const s = spinner()
|
|
769
773
|
s.start(task.title)
|
|
770
774
|
const result = await task.task(s.message)
|
|
771
|
-
s.stop(result || task.title)
|
|
775
|
+
s.stop(result || task.title, 0)
|
|
772
776
|
}
|
|
773
777
|
}
|
package/src/cli.ts
CHANGED
|
@@ -14,7 +14,7 @@ interface CliOptions {
|
|
|
14
14
|
// description: string
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function cli(name?: string | CliOptions, options?: CliOptions) {
|
|
17
|
+
export function cli(name?: string | CliOptions, options?: CliOptions): CAC {
|
|
18
18
|
if (typeof name === 'object') {
|
|
19
19
|
options = name
|
|
20
20
|
name = options.name
|
package/src/command.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Result } from '@stacksjs/error-handling'
|
|
2
|
+
import type { CliOptions, Readable, Subprocess, Writable } from '@stacksjs/types'
|
|
2
3
|
import { runCommand } from './run'
|
|
3
4
|
|
|
4
5
|
type CommandOptionTuple = [string, string, { default: boolean }]
|
|
@@ -37,11 +38,17 @@ export class Command {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export const command = {
|
|
40
|
-
run: async (
|
|
41
|
+
run: async (
|
|
42
|
+
command: string,
|
|
43
|
+
options?: CliOptions,
|
|
44
|
+
): Promise<Result<Subprocess<Writable, Readable, Readable>, Error>> => {
|
|
41
45
|
return await runCommand(command, options)
|
|
42
46
|
},
|
|
43
47
|
|
|
44
|
-
runSync: async (
|
|
48
|
+
runSync: async (
|
|
49
|
+
command: string,
|
|
50
|
+
options?: CliOptions,
|
|
51
|
+
): Promise<Result<Subprocess<Writable, Readable, Readable>, Error>> => {
|
|
45
52
|
return await runCommand(command, options)
|
|
46
53
|
},
|
|
47
54
|
}
|
package/src/console.ts
CHANGED