@stacksjs/cli 0.58.47 → 0.58.49
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/dist/index.js +1 -1
- package/package.json +11 -10
- package/src/actions/index.ts +1 -0
- package/src/actions/install.ts +40 -0
- package/src/cli.ts +52 -0
- package/src/command.ts +47 -0
- package/src/console.ts +79 -0
- package/src/exec.ts +89 -0
- package/src/helpers.ts +80 -0
- package/src/index.ts +10 -0
- package/src/parse.ts +101 -0
- package/src/run.ts +99 -0
- package/src/spinner.ts +3 -0
- package/src/utilities.ts +2 -0
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.58.
|
|
4
|
+
"version": "0.58.49",
|
|
5
5
|
"description": "TypeScript framework for CLI artisans. Build beautiful console apps with ease.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"license": "MIT",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
],
|
|
49
49
|
"files": [
|
|
50
50
|
"README.md",
|
|
51
|
-
"dist"
|
|
51
|
+
"dist",
|
|
52
|
+
"src"
|
|
52
53
|
],
|
|
53
54
|
"scripts": {
|
|
54
55
|
"build": "bun --bun build.ts",
|
|
@@ -57,13 +58,13 @@
|
|
|
57
58
|
},
|
|
58
59
|
"peerDependencies": {
|
|
59
60
|
"@antfu/install-pkg": "^0.3.1",
|
|
60
|
-
"@stacksjs/config": "
|
|
61
|
-
"@stacksjs/error-handling": "
|
|
62
|
-
"@stacksjs/logging": "
|
|
63
|
-
"@stacksjs/path": "
|
|
64
|
-
"@stacksjs/types": "
|
|
65
|
-
"@stacksjs/utils": "
|
|
66
|
-
"@stacksjs/validation": "
|
|
61
|
+
"@stacksjs/config": "latest",
|
|
62
|
+
"@stacksjs/error-handling": "latest",
|
|
63
|
+
"@stacksjs/logging": "latest",
|
|
64
|
+
"@stacksjs/path": "latest",
|
|
65
|
+
"@stacksjs/types": "latest",
|
|
66
|
+
"@stacksjs/utils": "latest",
|
|
67
|
+
"@stacksjs/validation": "latest",
|
|
67
68
|
"@types/prompts": "^2.4.9",
|
|
68
69
|
"cac": "^6.7.14",
|
|
69
70
|
"ora": "^8.0.1",
|
|
@@ -85,6 +86,6 @@
|
|
|
85
86
|
"prompts": "^2.4.2"
|
|
86
87
|
},
|
|
87
88
|
"devDependencies": {
|
|
88
|
-
"@stacksjs/development": "
|
|
89
|
+
"@stacksjs/development": "latest"
|
|
89
90
|
}
|
|
90
91
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './install'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ExecaReturnValue } from 'execa'
|
|
2
|
+
import { installPackage as installPkg } from '@antfu/install-pkg'
|
|
3
|
+
|
|
4
|
+
interface InstallPackageOptions {
|
|
5
|
+
cwd?: string
|
|
6
|
+
dev?: boolean
|
|
7
|
+
silent?: boolean
|
|
8
|
+
packageManager?: string
|
|
9
|
+
packageManagerVersion?: string
|
|
10
|
+
preferOffline?: boolean
|
|
11
|
+
additionalArgs?: string[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Install an npm package.
|
|
16
|
+
*
|
|
17
|
+
* @param name - The package name to install.
|
|
18
|
+
* @param options - The options to pass to the install.The options to pass to the install.
|
|
19
|
+
* @returns The result of the install.
|
|
20
|
+
*/
|
|
21
|
+
export async function installPackage(name: string, options?: InstallPackageOptions): Promise<ExecaReturnValue<string>> {
|
|
22
|
+
if (options)
|
|
23
|
+
return await installPkg(name, options)
|
|
24
|
+
|
|
25
|
+
return await installPkg(name, { silent: true })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Install a Stack into your project.
|
|
30
|
+
*
|
|
31
|
+
* @param name - The Stack name to install.
|
|
32
|
+
* @param options - The options to pass to the install.
|
|
33
|
+
* @returns The result of the install.
|
|
34
|
+
*/
|
|
35
|
+
export async function installStack(name: string, options?: InstallPackageOptions) {
|
|
36
|
+
if (options)
|
|
37
|
+
return await installPkg(`@stacksjs/${name}`, options)
|
|
38
|
+
|
|
39
|
+
return await installPkg(`@stacksjs/${name}`, { silent: true })
|
|
40
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// import type { CliOptions } from '@stacksjs/types'
|
|
2
|
+
import cac from 'cac'
|
|
3
|
+
import { version } from '../package.json'
|
|
4
|
+
|
|
5
|
+
interface ParsedArgv {
|
|
6
|
+
args: ReadonlyArray<string>
|
|
7
|
+
options: {
|
|
8
|
+
[k: string]: any
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface CliOptions {
|
|
13
|
+
name?: string
|
|
14
|
+
version: string
|
|
15
|
+
description: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function cli(name?: string | CliOptions, options?: CliOptions) {
|
|
19
|
+
if (typeof name === 'object') {
|
|
20
|
+
options = name
|
|
21
|
+
name = options.name
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const cli = cac(name)
|
|
25
|
+
|
|
26
|
+
cli.help()
|
|
27
|
+
cli.version(options?.version || version)
|
|
28
|
+
|
|
29
|
+
return cli
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function command(name: string, description: string, options?: CliOptions) {
|
|
33
|
+
return cli(options).command(name, description)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseArgs() {
|
|
37
|
+
return cli().parse().args
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function parseOptions(): ParsedArgv['options'] {
|
|
41
|
+
const options = cli().parse().options
|
|
42
|
+
|
|
43
|
+
// Iterate over the options and convert "true" and "false" strings to boolean
|
|
44
|
+
for (const key in options) {
|
|
45
|
+
if (options[key] === 'true')
|
|
46
|
+
options[key] = true
|
|
47
|
+
else if (options[key] === 'false')
|
|
48
|
+
options[key] = false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return options
|
|
52
|
+
}
|
package/src/command.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CliOptions } from '@stacksjs/types'
|
|
2
|
+
import { runCommand } from './run'
|
|
3
|
+
|
|
4
|
+
type CommandOptionTuple = [string, string, { default: boolean }]
|
|
5
|
+
interface CommandOptionObject {
|
|
6
|
+
name: string
|
|
7
|
+
description: string
|
|
8
|
+
default: boolean | string
|
|
9
|
+
}
|
|
10
|
+
type CommandOptions = CommandOptionTuple | CommandOptionObject[]
|
|
11
|
+
interface Options {
|
|
12
|
+
name: string
|
|
13
|
+
description: string
|
|
14
|
+
active: boolean
|
|
15
|
+
options: CommandOptions
|
|
16
|
+
run: (options?: CliOptions) => Promise<any>
|
|
17
|
+
onFail: (error: Error) => void
|
|
18
|
+
onSuccess: () => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class Command {
|
|
22
|
+
name: Options['name']
|
|
23
|
+
description: Options['description']
|
|
24
|
+
options: Options['options']
|
|
25
|
+
run: Options['run']
|
|
26
|
+
onFail: Options['onFail']
|
|
27
|
+
onSuccess: Options['onSuccess']
|
|
28
|
+
|
|
29
|
+
constructor({ name, description, options, run, onFail, onSuccess }: Options) {
|
|
30
|
+
this.name = name
|
|
31
|
+
this.description = description
|
|
32
|
+
this.options = options
|
|
33
|
+
this.run = run
|
|
34
|
+
this.onFail = onFail
|
|
35
|
+
this.onSuccess = onSuccess
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const command = {
|
|
40
|
+
run: async (command: string, options?: CliOptions) => {
|
|
41
|
+
return await runCommand(command, options)
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
runSync: async (command: string, options?: CliOptions) => {
|
|
45
|
+
return await runCommand(command, options)
|
|
46
|
+
},
|
|
47
|
+
}
|
package/src/console.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { log } from '@stacksjs/logging'
|
|
2
|
+
import prompts from 'prompts'
|
|
3
|
+
|
|
4
|
+
export class Prompt {
|
|
5
|
+
private required: boolean
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.required = false
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
require() {
|
|
12
|
+
this.required = true
|
|
13
|
+
return this
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
isRequired() {
|
|
17
|
+
return this.required
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async select(message: any, options: any) {
|
|
21
|
+
if (this.isRequired())
|
|
22
|
+
return log.prompt(message, { ...options, type: 'select', required: true })
|
|
23
|
+
|
|
24
|
+
return log.prompt(message, { ...options, type: 'select' })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async checkbox(message: any, options: any) {
|
|
28
|
+
if (this.isRequired())
|
|
29
|
+
return log.prompt(message, { ...options, type: 'multiselect', required: true })
|
|
30
|
+
|
|
31
|
+
return log.prompt(message, { ...options, type: 'multiselect' })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async confirm(message: any, options: any) {
|
|
35
|
+
if (this.isRequired())
|
|
36
|
+
return log.prompt(message, { ...options, type: 'confirm', required: true })
|
|
37
|
+
|
|
38
|
+
return log.prompt(message, { ...options, type: 'confirm' })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async input(message: any, options: any) {
|
|
42
|
+
if (this.isRequired())
|
|
43
|
+
return log.prompt(message, { ...options, type: 'text', required: true })
|
|
44
|
+
|
|
45
|
+
return log.prompt(message, { ...options, type: 'text' })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async password(message: any, options: any) {
|
|
49
|
+
if (this.isRequired())
|
|
50
|
+
return log.prompt(message, { ...options, type: 'password', required: true })
|
|
51
|
+
|
|
52
|
+
return log.prompt(message, { ...options, type: 'password' })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async number(message: any, options: any) {
|
|
56
|
+
if (this.isRequired())
|
|
57
|
+
return log.prompt(message, { ...options, type: 'numeral', required: true })
|
|
58
|
+
|
|
59
|
+
return log.prompt(message, { ...options, type: 'numeral' })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async multiselect(message: any, options: any) {
|
|
63
|
+
if (this.isRequired())
|
|
64
|
+
return log.prompt(message, { ...options, type: 'multiselect', required: true })
|
|
65
|
+
|
|
66
|
+
return log.prompt(message, { ...options, type: 'multiselect' })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async autocomplete(message: any, options: any) {
|
|
70
|
+
if (this.isRequired())
|
|
71
|
+
return log.prompt(message, { ...options, type: 'autocomplete', required: true })
|
|
72
|
+
|
|
73
|
+
return log.prompt(message, { ...options, type: 'autocomplete' })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { prompts, log }
|
|
78
|
+
|
|
79
|
+
export const prompt = new Prompt()
|
package/src/exec.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
import { type Result, err, handleError, ok } from '@stacksjs/error-handling'
|
|
3
|
+
import type { CliOptions, StacksError, Subprocess } from '@stacksjs/types'
|
|
4
|
+
import { ExitCode } from '@stacksjs/types'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Execute a command.
|
|
8
|
+
*
|
|
9
|
+
* @param command The command to execute.
|
|
10
|
+
* @param options The options to pass to the command.
|
|
11
|
+
* @returns The result of the command.
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const result = await exec('ls')
|
|
15
|
+
*
|
|
16
|
+
* if (result.isErr())
|
|
17
|
+
* console.error(result.error)
|
|
18
|
+
* else
|
|
19
|
+
* console.log(result)
|
|
20
|
+
* ```
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const result = await exec('ls', { cwd: '/home' })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export async function exec(command: string | string[], options?: CliOptions): Promise<Result<Subprocess, StacksError>> {
|
|
27
|
+
const cmd = Array.isArray(command) ? command : command.match(/(?:[^\s"]+|"[^"]*")+/g)
|
|
28
|
+
|
|
29
|
+
if (!cmd)
|
|
30
|
+
return err(handleError(`Failed to parse command: ${cmd}`, options))
|
|
31
|
+
|
|
32
|
+
if (options?.verbose)
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.log('exec', { command, cmd, options })
|
|
35
|
+
|
|
36
|
+
const proc = Bun.spawn(cmd, {
|
|
37
|
+
...options,
|
|
38
|
+
stdout: options?.silent ? 'ignore' : (options?.stdin ? options.stdin : (options?.stdout || 'inherit')),
|
|
39
|
+
stderr: options?.silent ? 'ignore' : (options?.stderr || 'inherit'),
|
|
40
|
+
detached: options?.background || false,
|
|
41
|
+
cwd: options?.cwd || import.meta.dir,
|
|
42
|
+
// env: { ...e, ...options?.env },
|
|
43
|
+
onExit(_subprocess, exitCode, _signalCode, _error) {
|
|
44
|
+
if (exitCode && exitCode !== ExitCode.Success)
|
|
45
|
+
process.exit(exitCode)
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const exited = await proc.exited
|
|
50
|
+
if (exited === ExitCode.Success)
|
|
51
|
+
return ok(proc)
|
|
52
|
+
|
|
53
|
+
return err(handleError(`Failed to execute command: ${cmd.join(' ')}`))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Execute a command and return result.
|
|
58
|
+
*
|
|
59
|
+
* @param command The command to execute.
|
|
60
|
+
* @returns The result of the command.
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const output = execSync('ls')
|
|
64
|
+
*
|
|
65
|
+
* console.log(output)
|
|
66
|
+
* ```
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const output = execSync('ls', { cwd: '/home' })
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export async function execSync(command: string | string[], options?: CliOptions): Promise<string> {
|
|
73
|
+
const cmd = Array.isArray(command) ? command : command.split(' ')
|
|
74
|
+
const proc = Bun.spawnSync(cmd, {
|
|
75
|
+
...options,
|
|
76
|
+
// stdin: 'inherit',
|
|
77
|
+
stdout: options?.stdout ?? 'pipe',
|
|
78
|
+
stderr: options?.stderr ?? 'inherit',
|
|
79
|
+
cwd: options?.cwd ?? import.meta.dir,
|
|
80
|
+
// env: { ...Bun.env, ...options?.env },
|
|
81
|
+
onExit(_subprocess, exitCode, _signalCode, _error) {
|
|
82
|
+
// console.log('onExit', { subprocess, exitCode, signalCode, error })
|
|
83
|
+
if (exitCode !== ExitCode.Success && exitCode)
|
|
84
|
+
process.exit(exitCode)
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
return proc.stdout.toString()
|
|
89
|
+
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { config } from '@stacksjs/config'
|
|
3
|
+
import { handleError } from '@stacksjs/error-handling'
|
|
4
|
+
import { log } from '@stacksjs/logging'
|
|
5
|
+
import type { IntroOptions, OutroOptions } from '@stacksjs/types'
|
|
6
|
+
import { ExitCode } from '@stacksjs/types'
|
|
7
|
+
import { bgCyan, bold, cyan, dim, gray, green, italic } from 'kolorist'
|
|
8
|
+
import { version } from '../package.json'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Prints the intro message.
|
|
12
|
+
*/
|
|
13
|
+
export async function intro(command: string, options?: IntroOptions): Promise<number> {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
if (options?.quiet === false) {
|
|
16
|
+
console.log()
|
|
17
|
+
console.log(cyan(bold('Stacks CLI')) + dim(` v${version}`))
|
|
18
|
+
console.log()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let msg = `Running ${bgCyan(italic(bold(` ${command} `)))}`
|
|
22
|
+
if (command === 'buddy deploy')
|
|
23
|
+
msg = `Running ${bgCyan(italic(bold(` ${command} `)))} for ${bold(`${config.app.name}`)} ${italic(`via ${config.app.url}`)}`
|
|
24
|
+
|
|
25
|
+
log.info(msg)
|
|
26
|
+
|
|
27
|
+
if (options?.showPerformance === false || options?.quiet)
|
|
28
|
+
return resolve(0)
|
|
29
|
+
|
|
30
|
+
return resolve(performance.now())
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Prints the outro message.
|
|
36
|
+
*/
|
|
37
|
+
export function outro(text: string, options?: OutroOptions, error?: Error | string) {
|
|
38
|
+
const opts = {
|
|
39
|
+
type: 'success',
|
|
40
|
+
useSeconds: true,
|
|
41
|
+
...options,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
opts.message = options?.message || text
|
|
45
|
+
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
if (error)
|
|
48
|
+
return handleError(error)
|
|
49
|
+
|
|
50
|
+
if (opts?.startTime) {
|
|
51
|
+
let time = performance.now() - opts.startTime
|
|
52
|
+
|
|
53
|
+
if (opts.useSeconds) {
|
|
54
|
+
time = time / 1000
|
|
55
|
+
time = Math.round(time * 100) / 100 // https://stackoverflow.com/a/11832950/7811162
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (opts.quiet === true)
|
|
59
|
+
return resolve(ExitCode.Success)
|
|
60
|
+
|
|
61
|
+
if (error)
|
|
62
|
+
log.error(`[${time.toFixed(2)}${opts.useSeconds ? 's' : 'ms'}] Failed`)
|
|
63
|
+
else if (opts.type === 'info')
|
|
64
|
+
console.log(`${dim(gray(`[${time.toFixed(2)}${opts.useSeconds ? 's' : 'ms'}]`))} ${opts.message ?? 'Complete'}`)
|
|
65
|
+
else
|
|
66
|
+
console.log(`${dim(gray(bold(`[${time.toFixed(2)}${opts.useSeconds ? 's' : 'ms'}]`)))} ${bold(green(opts.message ?? 'Complete'))}`)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
else {
|
|
70
|
+
if (opts?.type === 'info')
|
|
71
|
+
console.log(text)
|
|
72
|
+
|
|
73
|
+
// the following condition triggers in the case of "Cleaned up" messages
|
|
74
|
+
else if (opts?.type === 'success' && opts?.quiet !== true)
|
|
75
|
+
log.success(text)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return resolve(ExitCode.Success)
|
|
79
|
+
})
|
|
80
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './actions'
|
|
2
|
+
export * from './cli'
|
|
3
|
+
export * from './command'
|
|
4
|
+
export * from './console'
|
|
5
|
+
export * from './helpers'
|
|
6
|
+
export * from './parse'
|
|
7
|
+
export * from './exec'
|
|
8
|
+
export * from './run'
|
|
9
|
+
export * from './spinner'
|
|
10
|
+
export * from './utilities'
|
package/src/parse.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import process from 'node:process'
|
|
2
|
+
|
|
3
|
+
interface ParsedArgv {
|
|
4
|
+
args: string[]
|
|
5
|
+
options: {
|
|
6
|
+
[k: string]: string | boolean | number
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isLongOption(arg: string): boolean {
|
|
11
|
+
return arg.startsWith('--')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isShortOption(arg: string): boolean {
|
|
15
|
+
return arg.startsWith('-') && !isLongOption(arg)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseValue(value: string): string | boolean | number {
|
|
19
|
+
if (value === 'true')
|
|
20
|
+
return true
|
|
21
|
+
|
|
22
|
+
if (value === 'false')
|
|
23
|
+
return false
|
|
24
|
+
|
|
25
|
+
const numberValue = Number.parseFloat(value)
|
|
26
|
+
if (!Number.isNaN(numberValue))
|
|
27
|
+
return numberValue
|
|
28
|
+
|
|
29
|
+
return value.replace(/"/g, '')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseLongOption(arg: string, argv: string[], index: number, options: { [k: string]: string | boolean | number }): number {
|
|
33
|
+
const [key, value] = arg.slice(2).split('=')
|
|
34
|
+
if (value !== undefined) {
|
|
35
|
+
options[key] = parseValue(value)
|
|
36
|
+
}
|
|
37
|
+
else if (index + 1 < argv.length && !argv[index + 1].startsWith('-')) {
|
|
38
|
+
options[key] = argv[index + 1]
|
|
39
|
+
index++
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
options[key] = true
|
|
43
|
+
}
|
|
44
|
+
return index
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseShortOption(arg: string, argv: string[], index: number, options: { [k: string]: string | boolean | number }): number {
|
|
48
|
+
const [key, value] = arg.slice(1).split('=')
|
|
49
|
+
|
|
50
|
+
if (value !== undefined) {
|
|
51
|
+
for (let j = 0; j < key.length; j++)
|
|
52
|
+
options[key[j]] = parseValue(value)
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
for (let j = 0; j < key.length; j++) {
|
|
56
|
+
if (index + 1 < argv.length && j === key.length - 1 && !argv[index + 1].startsWith('-')) {
|
|
57
|
+
options[key[j]] = parseValue(argv[index + 1])
|
|
58
|
+
index++
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
options[key[j]] = true
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return index
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function parseArgv(argv?: string[]): ParsedArgv {
|
|
70
|
+
if (argv === undefined)
|
|
71
|
+
argv = process.argv.slice(2)
|
|
72
|
+
|
|
73
|
+
const args: string[] = []
|
|
74
|
+
const options: { [k: string]: string | boolean | number } = {}
|
|
75
|
+
|
|
76
|
+
for (let i = 0; i < argv.length; i++) {
|
|
77
|
+
const arg = argv[i]
|
|
78
|
+
if (isLongOption(arg))
|
|
79
|
+
i = parseLongOption(arg, argv, i, options)
|
|
80
|
+
else if (isShortOption(arg))
|
|
81
|
+
i = parseShortOption(arg, argv, i, options)
|
|
82
|
+
else
|
|
83
|
+
args.push(arg)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return { args, options }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// export function parseOptions(argv?: string[]): { [k: string]: string | boolean | number } {
|
|
90
|
+
// if (argv === undefined)
|
|
91
|
+
// argv = process.argv.slice(2)
|
|
92
|
+
|
|
93
|
+
// return parseArgv(argv).options
|
|
94
|
+
// }
|
|
95
|
+
|
|
96
|
+
export function parseArgs(argv?: string[]): string[] {
|
|
97
|
+
if (argv === undefined)
|
|
98
|
+
argv = process.argv.slice(2)
|
|
99
|
+
|
|
100
|
+
return parseArgv(argv).args
|
|
101
|
+
}
|
package/src/run.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { CliOptions, CommandError, Subprocess } from '@stacksjs/types'
|
|
2
|
+
import type { Result } from '@stacksjs/error-handling'
|
|
3
|
+
import { exec, execSync } from './exec'
|
|
4
|
+
import { italic, underline } from './utilities'
|
|
5
|
+
import { log } from './console'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Run a command.
|
|
9
|
+
*
|
|
10
|
+
* @param command The command to run.
|
|
11
|
+
* @param options The options to pass to the command.
|
|
12
|
+
* @returns The result of the command.
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const result = await runCommand('ls')
|
|
16
|
+
*
|
|
17
|
+
* if (result.isErr())
|
|
18
|
+
* console.error(result.error)
|
|
19
|
+
* else
|
|
20
|
+
* console.log(result)
|
|
21
|
+
* ```
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const result = await runCommand('ls', { cwd: '/home' })
|
|
25
|
+
*
|
|
26
|
+
* if (result.isErr())
|
|
27
|
+
* console.error(result.error)
|
|
28
|
+
* else
|
|
29
|
+
* console.log(result)
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export async function runCommand(command: string, options?: CliOptions): Promise<Result<Subprocess, CommandError>> {
|
|
33
|
+
if (options?.verbose)
|
|
34
|
+
log.debug('Running command:', underline(italic(command)), 'with options:', options)
|
|
35
|
+
|
|
36
|
+
return await exec(command, options)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function runProcess(command: string, options?: CliOptions): Promise<Result<Subprocess, CommandError>> {
|
|
40
|
+
if (options?.verbose)
|
|
41
|
+
log.debug('Running command:', underline(italic(command)), 'with options:', options)
|
|
42
|
+
|
|
43
|
+
return await exec(command, options)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Run a command.
|
|
48
|
+
*
|
|
49
|
+
* @param command The command to run.
|
|
50
|
+
* @param options The options to pass to the command.
|
|
51
|
+
* @returns The result of the command.
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* const result = runCommandSync('ls')
|
|
55
|
+
*
|
|
56
|
+
* if (result.isErr())
|
|
57
|
+
* console.error(result.error)
|
|
58
|
+
* else
|
|
59
|
+
* console.log(result)
|
|
60
|
+
* ```
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const result = runCommandSync('ls', { cwd: '/home' })
|
|
64
|
+
*
|
|
65
|
+
* if (result.isErr())
|
|
66
|
+
* console.error(result.error)
|
|
67
|
+
* else
|
|
68
|
+
* console.log(result)
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export async function runCommandSync(command: string, options?: CliOptions): Promise<string> {
|
|
72
|
+
if (options?.verbose)
|
|
73
|
+
log.debug('Running command:', underline(italic(command)), 'with options:', options)
|
|
74
|
+
|
|
75
|
+
const result = await execSync(command, options)
|
|
76
|
+
|
|
77
|
+
// if (result.isErr())
|
|
78
|
+
// return err(result.error)
|
|
79
|
+
|
|
80
|
+
// return ok(result.value)
|
|
81
|
+
|
|
82
|
+
return result
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Run many commands.
|
|
87
|
+
*
|
|
88
|
+
* @param commands The command to run.
|
|
89
|
+
* @param options The options to pass to the command.
|
|
90
|
+
* @returns The result of the command.
|
|
91
|
+
*/
|
|
92
|
+
export async function runCommands(commands: string[], options?: CliOptions) {
|
|
93
|
+
const results = []
|
|
94
|
+
|
|
95
|
+
for (const command of commands)
|
|
96
|
+
results.push(await runCommand(command, options))
|
|
97
|
+
|
|
98
|
+
return results
|
|
99
|
+
}
|
package/src/spinner.ts
ADDED
package/src/utilities.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export * as kolorist from 'kolorist'
|
|
2
|
+
export { 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, underline, white, yellow, ansi256, trueColor, trueColorBg, stripColors } from 'kolorist'
|