@stacksjs/cli 0.59.10 → 0.61.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/dist/index.js CHANGED
@@ -20,6 +20,100 @@ function cli(name, options) {
20
20
  }
21
21
  return new CAC(name || "buddy");
22
22
  }
23
+ // src/run.ts
24
+ import {ExitCode as ExitCode2} from "@stacksjs/types";
25
+
26
+ // src/console.ts
27
+ import {log, logger} from "@stacksjs/logging";
28
+ import prompts from "prompts";
29
+
30
+ class Prompt {
31
+ required;
32
+ constructor() {
33
+ this.required = false;
34
+ }
35
+ require() {
36
+ this.required = true;
37
+ return this;
38
+ }
39
+ isRequired() {
40
+ return this.required;
41
+ }
42
+ async select(message, options) {
43
+ if (this.isRequired())
44
+ return logger.prompt(message, {
45
+ ...options,
46
+ type: "select",
47
+ required: true
48
+ });
49
+ return logger.prompt(message, { ...options, type: "select" });
50
+ }
51
+ async checkbox(message, options) {
52
+ if (this.isRequired())
53
+ return logger.prompt(message, {
54
+ ...options,
55
+ type: "multiselect",
56
+ required: true
57
+ });
58
+ return logger.prompt(message, { ...options, type: "multiselect" });
59
+ }
60
+ async confirm(message, options) {
61
+ if (this.isRequired())
62
+ return logger.prompt(message, {
63
+ ...options,
64
+ type: "confirm",
65
+ required: true
66
+ });
67
+ return logger.prompt(message, { ...options, type: "confirm" });
68
+ }
69
+ async input(message, options) {
70
+ if (this.isRequired())
71
+ return logger.prompt(message, {
72
+ ...options,
73
+ type: "text",
74
+ required: true
75
+ });
76
+ return logger.prompt(message, { ...options, type: "text" });
77
+ }
78
+ async password(message, options) {
79
+ if (this.isRequired())
80
+ return logger.prompt(message, {
81
+ ...options,
82
+ type: "password",
83
+ required: true
84
+ });
85
+ return logger.prompt(message, { ...options, type: "password" });
86
+ }
87
+ async number(message, options) {
88
+ if (this.isRequired())
89
+ return logger.prompt(message, {
90
+ ...options,
91
+ type: "numeral",
92
+ required: true
93
+ });
94
+ return logger.prompt(message, { ...options, type: "numeral" });
95
+ }
96
+ async multiselect(message, options) {
97
+ if (this.isRequired())
98
+ return logger.prompt(message, {
99
+ ...options,
100
+ type: "multiselect",
101
+ required: true
102
+ });
103
+ return logger.prompt(message, { ...options, type: "multiselect" });
104
+ }
105
+ async autocomplete(message, options) {
106
+ if (this.isRequired())
107
+ return logger.prompt(message, {
108
+ ...options,
109
+ type: "autocomplete",
110
+ required: true
111
+ });
112
+ return logger.prompt(message, { ...options, type: "autocomplete" });
113
+ }
114
+ }
115
+ var prompt = () => new Prompt;
116
+
23
117
  // src/exec.ts
24
118
  import process2 from "process";
25
119
  import {err, handleError, ok} from "@stacksjs/error-handling";
@@ -28,18 +122,17 @@ async function exec(command, options) {
28
122
  const cmd = Array.isArray(command) ? command : command.match(/(?:[^\s"]+|"[^"]*")+/g);
29
123
  if (!cmd)
30
124
  return err(handleError(`Failed to parse command: ${cmd}`, options));
31
- log2.debug("exec:", Array.isArray(command) ? command.join(" ") : command);
32
- log2.debug("cmd:", cmd);
33
- log2.debug("exec options:", options);
125
+ log.debug("exec:", Array.isArray(command) ? command.join(" ") : command);
126
+ log.debug("cmd:", cmd);
127
+ log.debug("exec options:", options);
34
128
  const proc = Bun.spawn(cmd, {
35
129
  ...options,
36
130
  stdout: options?.silent || options?.quiet ? "ignore" : options?.stdin ? options.stdin : options?.stdout || "inherit",
37
131
  stderr: options?.silent || options?.quiet ? "ignore" : options?.stderr || "inherit",
38
132
  detached: options?.background || false,
39
- cwd: options?.cwd || import.meta.dir,
40
- onExit(_subprocess, exitCode, _signalCode, _error) {
41
- if (exitCode && exitCode !== ExitCode.Success)
42
- process2.exit(exitCode);
133
+ cwd: options?.cwd || process2.cwd(),
134
+ onExit(subprocess, exitCode, signalCode, error) {
135
+ exitHandler("spawn", subprocess, exitCode, signalCode, error);
43
136
  }
44
137
  });
45
138
  if (options?.stdin === "pipe" && options.input) {
@@ -54,11 +147,11 @@ async function exec(command, options) {
54
147
  return err(handleError(`Failed to execute command: ${cmd.join(" ")}`));
55
148
  }
56
149
  async function execSync(command, options) {
57
- log2.debug("Running ExecSync:", command);
58
- log2.debug("ExecSync Options:", options);
150
+ log.debug("Running ExecSync:", command);
151
+ log.debug("ExecSync Options:", options);
59
152
  const cmd = Array.isArray(command) ? command : command.match(/(?:[^\s"]+|"[^"]*")+/g);
60
153
  if (!cmd) {
61
- log2.error(`Failed to parse command: ${cmd}`, options);
154
+ log.error(`Failed to parse command: ${cmd}`, options);
62
155
  process2.exit(ExitCode.FatalError);
63
156
  }
64
157
  const proc = Bun.spawnSync(cmd, {
@@ -66,14 +159,25 @@ async function execSync(command, options) {
66
159
  stdin: options?.stdin ?? "inherit",
67
160
  stdout: options?.stdout ?? "pipe",
68
161
  stderr: options?.stderr ?? "inherit",
69
- cwd: options?.cwd ?? import.meta.dir,
70
- onExit(_subprocess, exitCode, _signalCode, _error) {
71
- if (exitCode !== ExitCode.Success && exitCode)
72
- process2.exit(exitCode);
162
+ cwd: options?.cwd ?? process2.cwd(),
163
+ onExit(subprocess, exitCode, signalCode, error) {
164
+ exitHandler("spawnSync", subprocess, exitCode, signalCode, error);
73
165
  }
74
166
  });
75
167
  return proc.stdout.toString();
76
168
  }
169
+ var exitHandler = function(type, subprocess, exitCode, signalCode, error) {
170
+ log.debug(`exitHandler: ${type}`);
171
+ log.debug("subprocess", subprocess);
172
+ log.debug("exitCode", exitCode);
173
+ log.debug("signalCode", signalCode);
174
+ if (error) {
175
+ log.error(error);
176
+ process2.exit(ExitCode.FatalError);
177
+ }
178
+ if (exitCode !== ExitCode.Success && exitCode)
179
+ process2.exit(exitCode);
180
+ };
77
181
 
78
182
  // src/utils.ts
79
183
  import {collect} from "@stacksjs/collections";
@@ -405,86 +509,33 @@ var quotes = collect([
405
509
  "Security is mostly a superstition. Life is either a daring adventure or nothing."
406
510
  ]);
407
511
 
408
- // src/console.ts
409
- import {log as log2, logger} from "@stacksjs/logging";
410
- import prompts from "prompts";
411
-
412
- class Prompt {
413
- required;
414
- constructor() {
415
- this.required = false;
416
- }
417
- require() {
418
- this.required = true;
419
- return this;
420
- }
421
- isRequired() {
422
- return this.required;
423
- }
424
- async select(message, options) {
425
- if (this.isRequired())
426
- return logger.prompt(message, { ...options, type: "select", required: true });
427
- return logger.prompt(message, { ...options, type: "select" });
428
- }
429
- async checkbox(message, options) {
430
- if (this.isRequired())
431
- return logger.prompt(message, { ...options, type: "multiselect", required: true });
432
- return logger.prompt(message, { ...options, type: "multiselect" });
433
- }
434
- async confirm(message, options) {
435
- if (this.isRequired())
436
- return logger.prompt(message, { ...options, type: "confirm", required: true });
437
- return logger.prompt(message, { ...options, type: "confirm" });
438
- }
439
- async input(message, options) {
440
- if (this.isRequired())
441
- return logger.prompt(message, { ...options, type: "text", required: true });
442
- return logger.prompt(message, { ...options, type: "text" });
443
- }
444
- async password(message, options) {
445
- if (this.isRequired())
446
- return logger.prompt(message, { ...options, type: "password", required: true });
447
- return logger.prompt(message, { ...options, type: "password" });
448
- }
449
- async number(message, options) {
450
- if (this.isRequired())
451
- return logger.prompt(message, { ...options, type: "numeral", required: true });
452
- return logger.prompt(message, { ...options, type: "numeral" });
453
- }
454
- async multiselect(message, options) {
455
- if (this.isRequired())
456
- return logger.prompt(message, { ...options, type: "multiselect", required: true });
457
- return logger.prompt(message, { ...options, type: "multiselect" });
458
- }
459
- async autocomplete(message, options) {
460
- if (this.isRequired())
461
- return logger.prompt(message, { ...options, type: "autocomplete", required: true });
462
- return logger.prompt(message, { ...options, type: "autocomplete" });
463
- }
464
- }
465
- var prompt = () => new Prompt;
466
-
467
512
  // src/run.ts
468
513
  async function runCommand(command, options) {
469
- log2.debug("runCommand:", command);
470
- log2.debug("options:", options);
514
+ log.debug("runCommand:", command);
515
+ log.debug("options:", options);
471
516
  return await exec(command, options);
472
517
  }
473
518
  async function runProcess(command, options) {
474
- log2.debug("runProcess:", italic(command));
475
- log2.debug("runProcess Options:", options);
519
+ log.debug("runProcess:", italic(command));
520
+ log.debug("runProcess Options:", options);
476
521
  return await exec(command, options);
477
522
  }
478
523
  async function runCommandSync(command, options) {
479
- log2.debug("runCommandSync:", italic(command));
480
- log2.debug("runCommandSync Options:", options);
524
+ log.debug("runCommandSync:", italic(command));
525
+ log.debug("runCommandSync Options:", options);
481
526
  const result = await execSync(command, options);
482
527
  return result;
483
528
  }
484
529
  async function runCommands(commands, options) {
485
530
  const results = [];
486
- for (const command of commands)
487
- results.push(await runCommand(command, options));
531
+ for (const command of commands) {
532
+ const result = await runCommand(command, options);
533
+ if (result.isErr()) {
534
+ log.error(result.error);
535
+ process.exit(ExitCode2.FatalError);
536
+ }
537
+ results.push(result);
538
+ }
488
539
  return results;
489
540
  }
490
541
 
@@ -514,13 +565,12 @@ var command = {
514
565
  }
515
566
  };
516
567
  // src/helpers.ts
517
- import {config as config2} from "@stacksjs/config";
518
568
  import {handleError as handleError2} from "@stacksjs/error-handling";
519
- import {log as log3} from "@stacksjs/logging";
520
- import {ExitCode as ExitCode2} from "@stacksjs/types";
569
+ import {log as log2} from "@stacksjs/logging";
570
+ import {ExitCode as ExitCode3} from "@stacksjs/types";
521
571
  import {bgCyan as bgCyan2, bold as bold2, cyan as cyan2, dim as dim2, gray as gray2, green as green2, italic as italic2} from "kolorist";
522
572
  // package.json
523
- var version = "0.59.10";
573
+ var version = "0.61.0";
524
574
 
525
575
  // src/helpers.ts
526
576
  async function intro(command2, options) {
@@ -530,10 +580,7 @@ async function intro(command2, options) {
530
580
  console.log(cyan2(bold2("Stacks CLI")) + dim2(` v${version}`));
531
581
  console.log();
532
582
  }
533
- let msg = `Running ${bgCyan2(italic2(bold2(` ${command2} `)))}`;
534
- if (command2 === "buddy deploy")
535
- msg = `Running ${bgCyan2(italic2(bold2(` ${command2} `)))} for ${bold2(`${config2.app.name}`)} ${italic2(`via ${config2.app.url}`)}`;
536
- log3.info(msg);
583
+ log2.info(`Running ${bgCyan2(italic2(bold2(` ${command2} `)))}`);
537
584
  if (options?.showPerformance === false || options?.quiet)
538
585
  return resolve(0);
539
586
  return resolve(performance.now());
@@ -556,24 +603,25 @@ function outro(text, options, error) {
556
603
  time = Math.round(time * 100) / 100;
557
604
  }
558
605
  if (opts.quiet === true)
559
- return resolve(ExitCode2.Success);
606
+ return resolve(ExitCode3.Success);
560
607
  if (error)
561
- log3.error(`[${time.toFixed(2)}${opts.useSeconds ? "s" : "ms"}] Failed`);
608
+ log2.error(`[${time.toFixed(2)}${opts.useSeconds ? "s" : "ms"}] Failed`);
562
609
  else if (opts.type === "info")
563
- log3.info(`${dim2(gray2(`[${time.toFixed(2)}${opts.useSeconds ? "s" : "ms"}]`))} ${opts.message ?? "Complete"}`);
610
+ log2.info(`${dim2(gray2(`[${time.toFixed(2)}${opts.useSeconds ? "s" : "ms"}]`))} ${opts.message ?? "Complete"}`);
564
611
  else
565
- log3.success(`${dim2(gray2(bold2(`[${time.toFixed(2)}${opts.useSeconds ? "s" : "ms"}]`)))} ${bold2(green2(opts.message ?? "Complete"))}`);
612
+ log2.success(`${dim2(gray2(bold2(`[${time.toFixed(2)}${opts.useSeconds ? "s" : "ms"}]`)))} ${bold2(green2(opts.message ?? "Complete"))}`);
566
613
  } else {
567
614
  if (opts?.type === "info")
568
- log3.info(text);
615
+ log2.info(text);
569
616
  else if (opts?.type === "success" && opts?.quiet !== true)
570
- log3.success(text);
617
+ log2.success(text);
571
618
  }
572
- return resolve(ExitCode2.Success);
619
+ return resolve(ExitCode3.Success);
573
620
  });
574
621
  }
575
622
  // src/parse.ts
576
623
  import process3 from "process";
624
+ import {log as log3} from "@stacksjs/logging";
577
625
  var isLongOption = function(arg) {
578
626
  if (!arg)
579
627
  return false;
@@ -596,7 +644,7 @@ var parseLongOption = function(arg, argv2, index, options) {
596
644
  const [key, value] = arg.slice(2).split("=");
597
645
  if (value !== undefined) {
598
646
  options[key] = parseValue(value);
599
- } else if (index + 1 < argv2.length && !argv2[index + 1].startsWith("-")) {
647
+ } else if (index + 1 < argv2.length && !argv2[index + 1]?.startsWith("-")) {
600
648
  options[key] = argv2[index + 1];
601
649
  index++;
602
650
  } else {
@@ -613,7 +661,7 @@ var parseShortOption = function(arg, argv2, index, options) {
613
661
  options[key[j]] = parseValue(value);
614
662
  } else {
615
663
  for (let j = 0;j < key.length; j++) {
616
- if (index + 1 < argv2.length && j === key.length - 1 && !argv2[index + 1].startsWith("-")) {
664
+ if (index + 1 < argv2.length && j === key.length - 1 && !argv2[index + 1]?.startsWith("-")) {
617
665
  options[key[j]] = parseValue(argv2[index + 1]);
618
666
  index++;
619
667
  } else {
@@ -686,9 +734,9 @@ function buddyOptions(options) {
686
734
  options.shift();
687
735
  }
688
736
  if (options?.verbose) {
689
- log.debug("process.argv", process3.argv);
690
- log.debug("process.argv.slice(2)", process3.argv.slice(2));
691
- log.debug("options inside buddyOptions", options);
737
+ log3.debug("process.argv", process3.argv);
738
+ log3.debug("process.argv.slice(2)", process3.argv.slice(2));
739
+ log3.debug("options inside buddyOptions", options);
692
740
  }
693
741
  return options.join(" ");
694
742
  }
@@ -721,7 +769,7 @@ export {
721
769
  outro,
722
770
  magenta,
723
771
  logger,
724
- log2 as log,
772
+ log,
725
773
  link,
726
774
  lightYellow,
727
775
  lightRed,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/cli",
3
3
  "type": "module",
4
- "version": "0.59.10",
4
+ "version": "0.61.0",
5
5
  "description": "TypeScript framework for CLI artisans. Build beautiful console apps with ease.",
6
6
  "author": "Chris Breuer",
7
7
  "license": "MIT",
@@ -57,7 +57,8 @@
57
57
  "prepublishOnly": "bun run build"
58
58
  },
59
59
  "peerDependencies": {
60
- "@antfu/install-pkg": "^0.3.1",
60
+ "@antfu/install-pkg": "^0.3.3",
61
+ "@clack/prompts": "^0.7.0",
61
62
  "@stacksjs/config": "latest",
62
63
  "@stacksjs/error-handling": "latest",
63
64
  "@stacksjs/logging": "latest",
@@ -71,7 +72,8 @@
71
72
  "prompts": "^2.4.2"
72
73
  },
73
74
  "dependencies": {
74
- "@antfu/install-pkg": "^0.3.1",
75
+ "@antfu/install-pkg": "^0.3.3",
76
+ "@clack/prompts": "^0.7.0",
75
77
  "@stacksjs/collections": "latest",
76
78
  "@stacksjs/config": "latest",
77
79
  "@stacksjs/error-handling": "latest",
@@ -1,5 +1,5 @@
1
- import type { ExecaReturnValue } from 'execa'
2
1
  import { installPackage as installPkg } from '@antfu/install-pkg'
2
+ import type { ExecaReturnValue } from 'execa'
3
3
 
4
4
  interface InstallPackageOptions {
5
5
  cwd?: string
@@ -18,9 +18,8 @@ interface InstallPackageOptions {
18
18
  * @param options - The options to pass to the install.The options to pass to the install.
19
19
  * @returns The result of the install.
20
20
  */
21
- export async function installPackage(name: string, options?: InstallPackageOptions): Promise<ExecaReturnValue<string>> {
22
- if (options)
23
- return await installPkg(name, options)
21
+ export async function installPackage(name: string, options?: InstallPackageOptions) {
22
+ if (options) return await installPkg(name, options)
24
23
 
25
24
  return await installPkg(name, { silent: true })
26
25
  }
@@ -33,8 +32,7 @@ export async function installPackage(name: string, options?: InstallPackageOptio
33
32
  * @returns The result of the install.
34
33
  */
35
34
  export async function installStack(name: string, options?: InstallPackageOptions) {
36
- if (options)
37
- return await installPkg(`@stacksjs/${name}`, options)
35
+ if (options) return await installPkg(`@stacksjs/${name}`, options)
38
36
 
39
37
  return await installPkg(`@stacksjs/${name}`, { silent: true })
40
38
  }
package/src/console.ts CHANGED
@@ -19,56 +19,88 @@ export class Prompt {
19
19
 
20
20
  async select(message: any, options: any) {
21
21
  if (this.isRequired())
22
- return logger.prompt(message, { ...options, type: 'select', required: true })
22
+ return logger.prompt(message, {
23
+ ...options,
24
+ type: 'select',
25
+ required: true,
26
+ })
23
27
 
24
28
  return logger.prompt(message, { ...options, type: 'select' })
25
29
  }
26
30
 
27
31
  async checkbox(message: any, options: any) {
28
32
  if (this.isRequired())
29
- return logger.prompt(message, { ...options, type: 'multiselect', required: true })
33
+ return logger.prompt(message, {
34
+ ...options,
35
+ type: 'multiselect',
36
+ required: true,
37
+ })
30
38
 
31
39
  return logger.prompt(message, { ...options, type: 'multiselect' })
32
40
  }
33
41
 
34
42
  async confirm(message: any, options: any) {
35
43
  if (this.isRequired())
36
- return logger.prompt(message, { ...options, type: 'confirm', required: true })
44
+ return logger.prompt(message, {
45
+ ...options,
46
+ type: 'confirm',
47
+ required: true,
48
+ })
37
49
 
38
50
  return logger.prompt(message, { ...options, type: 'confirm' })
39
51
  }
40
52
 
41
53
  async input(message: any, options: any) {
42
54
  if (this.isRequired())
43
- return logger.prompt(message, { ...options, type: 'text', required: true })
55
+ return logger.prompt(message, {
56
+ ...options,
57
+ type: 'text',
58
+ required: true,
59
+ })
44
60
 
45
61
  return logger.prompt(message, { ...options, type: 'text' })
46
62
  }
47
63
 
48
64
  async password(message: any, options: any) {
49
65
  if (this.isRequired())
50
- return logger.prompt(message, { ...options, type: 'password', required: true })
66
+ return logger.prompt(message, {
67
+ ...options,
68
+ type: 'password',
69
+ required: true,
70
+ })
51
71
 
52
72
  return logger.prompt(message, { ...options, type: 'password' })
53
73
  }
54
74
 
55
75
  async number(message: any, options: any) {
56
76
  if (this.isRequired())
57
- return logger.prompt(message, { ...options, type: 'numeral', required: true })
77
+ return logger.prompt(message, {
78
+ ...options,
79
+ type: 'numeral',
80
+ required: true,
81
+ })
58
82
 
59
83
  return logger.prompt(message, { ...options, type: 'numeral' })
60
84
  }
61
85
 
62
86
  async multiselect(message: any, options: any) {
63
87
  if (this.isRequired())
64
- return logger.prompt(message, { ...options, type: 'multiselect', required: true })
88
+ return logger.prompt(message, {
89
+ ...options,
90
+ type: 'multiselect',
91
+ required: true,
92
+ })
65
93
 
66
94
  return logger.prompt(message, { ...options, type: 'multiselect' })
67
95
  }
68
96
 
69
97
  async autocomplete(message: any, options: any) {
70
98
  if (this.isRequired())
71
- return logger.prompt(message, { ...options, type: 'autocomplete', required: true })
99
+ return logger.prompt(message, {
100
+ ...options,
101
+ type: 'autocomplete',
102
+ required: true,
103
+ })
72
104
 
73
105
  return logger.prompt(message, { ...options, type: 'autocomplete' })
74
106
  }
package/src/exec.ts CHANGED
@@ -25,12 +25,9 @@ import { log } from './'
25
25
  * ```
26
26
  */
27
27
  export async function exec(command: string | string[], options?: CliOptions): Promise<Result<Subprocess, Error>> {
28
- const cmd = Array.isArray(command)
29
- ? command
30
- : command.match(/(?:[^\s"]+|"[^"]*")+/g)
28
+ const cmd = Array.isArray(command) ? command : command.match(/(?:[^\s"]+|"[^"]*")+/g)
31
29
 
32
- if (!cmd)
33
- return err(handleError(`Failed to parse command: ${cmd}`, options))
30
+ if (!cmd) return err(handleError(`Failed to parse command: ${cmd}`, options))
34
31
 
35
32
  log.debug('exec:', Array.isArray(command) ? command.join(' ') : command)
36
33
  log.debug('cmd:', cmd)
@@ -38,14 +35,14 @@ export async function exec(command: string | string[], options?: CliOptions): Pr
38
35
 
39
36
  const proc = Bun.spawn(cmd, {
40
37
  ...options,
41
- stdout: (options?.silent || options?.quiet) ? 'ignore' : (options?.stdin ? options.stdin : (options?.stdout || 'inherit')),
42
- stderr: (options?.silent || options?.quiet) ? 'ignore' : (options?.stderr || 'inherit'),
38
+ stdout:
39
+ options?.silent || options?.quiet ? 'ignore' : options?.stdin ? options.stdin : options?.stdout || 'inherit',
40
+ stderr: options?.silent || options?.quiet ? 'ignore' : options?.stderr || 'inherit',
43
41
  detached: options?.background || false,
44
- cwd: options?.cwd || import.meta.dir,
42
+ cwd: options?.cwd || process.cwd(),
45
43
  // env: { ...e, ...options?.env },
46
- onExit(_subprocess, exitCode, _signalCode, _error) {
47
- if (exitCode && exitCode !== ExitCode.Success)
48
- process.exit(exitCode)
44
+ onExit(subprocess, exitCode, signalCode, error) {
45
+ exitHandler('spawn', subprocess, exitCode, signalCode, error)
49
46
  },
50
47
  })
51
48
 
@@ -61,8 +58,7 @@ export async function exec(command: string | string[], options?: CliOptions): Pr
61
58
  }
62
59
 
63
60
  const exited = await proc.exited
64
- if (exited === ExitCode.Success)
65
- return ok(proc)
61
+ if (exited === ExitCode.Success) return ok(proc)
66
62
 
67
63
  return err(handleError(`Failed to execute command: ${cmd.join(' ')}`))
68
64
  }
@@ -87,9 +83,7 @@ export async function execSync(command: string | string[], options?: CliOptions)
87
83
  log.debug('Running ExecSync:', command)
88
84
  log.debug('ExecSync Options:', options)
89
85
 
90
- const cmd = Array.isArray(command)
91
- ? command
92
- : command.match(/(?:[^\s"]+|"[^"]*")+/g)
86
+ const cmd = Array.isArray(command) ? command : command.match(/(?:[^\s"]+|"[^"]*")+/g)
93
87
 
94
88
  if (!cmd) {
95
89
  log.error(`Failed to parse command: ${cmd}`, options)
@@ -101,14 +95,27 @@ export async function execSync(command: string | string[], options?: CliOptions)
101
95
  stdin: options?.stdin ?? 'inherit',
102
96
  stdout: options?.stdout ?? 'pipe',
103
97
  stderr: options?.stderr ?? 'inherit',
104
- cwd: options?.cwd ?? import.meta.dir,
98
+ cwd: options?.cwd ?? process.cwd(),
105
99
  // env: { ...Bun.env, ...options?.env },
106
- onExit(_subprocess, exitCode, _signalCode, _error) {
107
- // console.log('onExit', { subprocess, exitCode, signalCode, error })
108
- if (exitCode !== ExitCode.Success && exitCode)
109
- process.exit(exitCode)
100
+ onExit(subprocess, exitCode, signalCode, error) {
101
+ exitHandler('spawnSync', subprocess, exitCode, signalCode, error)
110
102
  },
111
103
  })
112
104
 
113
105
  return proc.stdout.toString()
114
106
  }
107
+
108
+ // @ts-expect-error - missing types is okay here but can be improved later on
109
+ function exitHandler(type: 'spawn' | 'spawnSync', subprocess, exitCode, signalCode, error) {
110
+ log.debug(`exitHandler: ${type}`)
111
+ log.debug('subprocess', subprocess)
112
+ log.debug('exitCode', exitCode)
113
+ log.debug('signalCode', signalCode)
114
+
115
+ if (error) {
116
+ log.error(error)
117
+ process.exit(ExitCode.FatalError)
118
+ }
119
+
120
+ if (exitCode !== ExitCode.Success && exitCode) process.exit(exitCode)
121
+ }
package/src/helpers.ts CHANGED
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-console */
2
- import { config } from '@stacksjs/config'
3
1
  import { handleError } from '@stacksjs/error-handling'
4
2
  import { log } from '@stacksjs/logging'
5
3
  import type { IntroOptions, OutroOptions } from '@stacksjs/types'
@@ -18,14 +16,9 @@ export async function intro(command: string, options?: IntroOptions): Promise<nu
18
16
  console.log()
19
17
  }
20
18
 
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}`)}`
19
+ log.info(`Running ${bgCyan(italic(bold(` ${command} `)))}`)
24
20
 
25
- log.info(msg)
26
-
27
- if (options?.showPerformance === false || options?.quiet)
28
- return resolve(0)
21
+ if (options?.showPerformance === false || options?.quiet) return resolve(0)
29
22
 
30
23
  return resolve(performance.now())
31
24
  })
@@ -44,8 +37,7 @@ export function outro(text: string, options?: OutroOptions, error?: Error | stri
44
37
  opts.message = options?.message || text
45
38
 
46
39
  return new Promise((resolve) => {
47
- if (error)
48
- return handleError(error)
40
+ if (error) return handleError(error)
49
41
 
50
42
  if (opts?.startTime) {
51
43
  let time = performance.now() - opts.startTime
@@ -55,24 +47,21 @@ export function outro(text: string, options?: OutroOptions, error?: Error | stri
55
47
  time = Math.round(time * 100) / 100 // https://stackoverflow.com/a/11832950/7811162
56
48
  }
57
49
 
58
- if (opts.quiet === true)
59
- return resolve(ExitCode.Success)
50
+ if (opts.quiet === true) return resolve(ExitCode.Success)
60
51
 
61
- if (error)
62
- log.error(`[${time.toFixed(2)}${opts.useSeconds ? 's' : 'ms'}] Failed`)
52
+ if (error) log.error(`[${time.toFixed(2)}${opts.useSeconds ? 's' : 'ms'}] Failed`)
63
53
  else if (opts.type === 'info')
64
54
  log.info(`${dim(gray(`[${time.toFixed(2)}${opts.useSeconds ? 's' : 'ms'}]`))} ${opts.message ?? 'Complete'}`)
65
55
  else
66
- log.success(`${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
- log.info(text)
72
-
56
+ log.success(
57
+ `${dim(gray(bold(`[${time.toFixed(2)}${opts.useSeconds ? 's' : 'ms'}]`)))} ${bold(
58
+ green(opts.message ?? 'Complete'),
59
+ )}`,
60
+ )
61
+ } else {
62
+ if (opts?.type === 'info') log.info(text)
73
63
  // the following condition triggers in the case of "Cleaned up" messages
74
- else if (opts?.type === 'success' && opts?.quiet !== true)
75
- log.success(text)
64
+ else if (opts?.type === 'success' && opts?.quiet !== true) log.success(text)
76
65
  }
77
66
 
78
67
  return resolve(ExitCode.Success)
package/src/parse.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import process from 'node:process'
2
+ import { log } from '@stacksjs/logging'
2
3
 
3
4
  interface ParsedArgv {
4
5
  args: string[]
@@ -8,8 +9,7 @@ interface ParsedArgv {
8
9
  }
9
10
 
10
11
  function isLongOption(arg?: string): boolean {
11
- if (!arg)
12
- return false
12
+ if (!arg) return false
13
13
 
14
14
  return arg.startsWith('--')
15
15
  }
@@ -19,52 +19,53 @@ function isShortOption(arg: string): boolean {
19
19
  }
20
20
 
21
21
  function parseValue(value: string): string | boolean | number {
22
- if (value === 'true')
23
- return true
22
+ if (value === 'true') return true
24
23
 
25
- if (value === 'false')
26
- return false
24
+ if (value === 'false') return false
27
25
 
28
26
  const numberValue = Number.parseFloat(value)
29
- if (!Number.isNaN(numberValue))
30
- return numberValue
27
+ if (!Number.isNaN(numberValue)) return numberValue
31
28
 
32
29
  return value.replace(/"/g, '')
33
30
  }
34
31
 
35
- function parseLongOption(arg: string, argv: string[], index: number, options: { [k: string]: string | boolean | number }): number {
32
+ function parseLongOption(
33
+ arg: string,
34
+ argv: string[],
35
+ index: number,
36
+ options: { [k: string]: string | boolean | number },
37
+ ): number {
36
38
  const [key, value] = arg.slice(2).split('=')
37
39
  if (value !== undefined) {
38
40
  options[key as string] = parseValue(value)
39
- }
40
- else if (index + 1 < argv.length && !argv[index + 1]!.startsWith('-')) {
41
+ } else if (index + 1 < argv.length && !argv[index + 1]?.startsWith('-')) {
41
42
  options[key as string] = argv[index + 1] as string
42
43
  index++
43
- }
44
- else {
44
+ } else {
45
45
  options[key as string] = true
46
46
  }
47
47
  return index
48
48
  }
49
49
 
50
- function parseShortOption(arg: string, argv: string[], index: number, options: { [k: string]: string | boolean | number }): number {
50
+ function parseShortOption(
51
+ arg: string,
52
+ argv: string[],
53
+ index: number,
54
+ options: { [k: string]: string | boolean | number },
55
+ ): number {
51
56
  const [key, value] = arg.slice(1).split('=')
52
57
 
53
58
  // Check if key is undefined and handle it
54
- if (key === undefined)
55
- return index
59
+ if (key === undefined) return index
56
60
 
57
61
  if (value !== undefined && key !== undefined) {
58
- for (let j = 0; j < key.length; j++)
59
- options[key[j] as string] = parseValue(value)
60
- }
61
- else {
62
+ for (let j = 0; j < key.length; j++) options[key[j] as string] = parseValue(value)
63
+ } else {
62
64
  for (let j = 0; j < key.length; j++) {
63
- if (index + 1 < argv.length && j === key.length - 1 && !argv[index + 1]!.startsWith('-')) {
64
- options[key[j] as string] = parseValue(argv[index + 1]!)
65
+ if (index + 1 < argv.length && j === key.length - 1 && !argv[index + 1]?.startsWith('-')) {
66
+ options[key[j] as string] = parseValue(argv[index + 1] as string)
65
67
  index++
66
- }
67
- else {
68
+ } else {
68
69
  options[key[j] as string] = true
69
70
  }
70
71
  }
@@ -74,30 +75,24 @@ function parseShortOption(arg: string, argv: string[], index: number, options: {
74
75
  }
75
76
 
76
77
  export function parseArgv(argv?: string[]): ParsedArgv {
77
- if (argv === undefined)
78
- argv = process.argv.slice(2)
78
+ if (argv === undefined) argv = process.argv.slice(2)
79
79
 
80
80
  const args: string[] = []
81
81
  const options: { [k: string]: string | boolean | number } = {}
82
82
 
83
83
  for (let i = 0; i < argv.length; i++) {
84
84
  const arg = argv[i]
85
- if (!arg)
86
- continue
87
- if (isLongOption(arg))
88
- i = parseLongOption(arg, argv, i, options)
89
- else if (isShortOption(arg))
90
- i = parseShortOption(arg, argv, i, options)
91
- else
92
- args.push(arg)
85
+ if (!arg) continue
86
+ if (isLongOption(arg)) i = parseLongOption(arg, argv, i, options)
87
+ else if (isShortOption(arg)) i = parseShortOption(arg, argv, i, options)
88
+ else args.push(arg)
93
89
  }
94
90
 
95
91
  return { args, options }
96
92
  }
97
93
 
98
94
  export function parseArgs(argv?: string[]): string[] {
99
- if (argv === undefined)
100
- argv = process.argv.slice(2)
95
+ if (argv === undefined) argv = process.argv.slice(2)
101
96
 
102
97
  return parseArgv(argv).args
103
98
  }
@@ -119,38 +114,35 @@ export function parseOptions(options?: CliOptions): CliOptions {
119
114
  const key = arg.substring(2) // remove the --
120
115
  const camelCaseKey = key.replace(
121
116
  /-([a-z])/gi,
122
- g => (g[1] ? g[1].toUpperCase() : ''), // convert kebab-case to camelCase
117
+ (g) => (g[1] ? g[1].toUpperCase() : ''), // convert kebab-case to camelCase
123
118
  )
124
119
 
125
- if (i + 1 < args.length) { // if the next arg exists
126
- if (args[i + 1] === 'true' || args[i + 1] === 'false') { // if the next arg is a boolean
120
+ if (i + 1 < args.length) {
121
+ // if the next arg exists
122
+ if (args[i + 1] === 'true' || args[i + 1] === 'false') {
123
+ // if the next arg is a boolean
127
124
  options[camelCaseKey] = args[i + 1] === 'true' // set the value to the boolean
128
125
  i++
129
- }
130
- else {
126
+ } else {
131
127
  options[camelCaseKey] = args[i + 1]
132
128
  i++
133
129
  }
134
- }
135
- else {
130
+ } else {
136
131
  options[camelCaseKey] = true
137
132
  }
138
133
  }
139
134
  }
140
135
 
141
136
  // if options has no keys, return undefined, e.g. `buddy release`
142
- if (Object.keys(options).length === 0)
143
- return { dryRun: false, quiet: false, verbose: false }
137
+ if (Object.keys(options).length === 0) return { dryRun: false, quiet: false, verbose: false }
144
138
 
145
139
  // convert the string 'true' or 'false' to a boolean
146
140
  Object.keys(options).forEach((key) => {
147
- if (!options)
148
- return { dryRun: false, quiet: false, verbose: false }
141
+ if (!options) return { dryRun: false, quiet: false, verbose: false }
149
142
 
150
143
  const value = options[key]
151
144
 
152
- if (value === 'true' || value === 'false')
153
- options[key] = value === 'true'
145
+ if (value === 'true' || value === 'false') options[key] = value === 'true'
154
146
  })
155
147
 
156
148
  return options
@@ -165,8 +157,7 @@ export function buddyOptions(options?: any): string {
165
157
  options = Array.from(new Set(options))
166
158
  // delete the 0 element if it does not start with a -
167
159
  // e.g. is used when buddy changelog --dry-run is used
168
- if (options[0] && !options[0].startsWith('-'))
169
- options.shift()
160
+ if (options[0] && !options[0].startsWith('-')) options.shift()
170
161
  }
171
162
 
172
163
  if (options?.verbose) {
package/src/run.ts CHANGED
@@ -1,8 +1,9 @@
1
- import type { CliOptions, CommandError, Subprocess } from '@stacksjs/types'
2
1
  import type { Result } from '@stacksjs/error-handling'
2
+ import type { CliOptions, CommandError, Subprocess } from '@stacksjs/types'
3
+ import { ExitCode } from '@stacksjs/types'
4
+ import { log } from './console'
3
5
  import { exec, execSync } from './exec'
4
6
  import { italic } from './utils'
5
- import { log } from './console'
6
7
 
7
8
  /**
8
9
  * Run a command.
@@ -92,8 +93,16 @@ export async function runCommandSync(command: string, options?: CliOptions): Pro
92
93
  export async function runCommands(commands: string[], options?: CliOptions) {
93
94
  const results = []
94
95
 
95
- for (const command of commands)
96
- results.push(await runCommand(command, options))
96
+ for (const command of commands) {
97
+ const result = await runCommand(command, options)
98
+
99
+ if (result.isErr()) {
100
+ log.error(result.error)
101
+ process.exit(ExitCode.FatalError)
102
+ }
103
+
104
+ results.push(result)
105
+ }
97
106
 
98
107
  return results
99
108
  }
package/src/utils.ts CHANGED
@@ -63,7 +63,8 @@ export {
63
63
  stripColors,
64
64
  } from 'kolorist'
65
65
 
66
- export const quotes = collect([ // could be queried from any API or database
66
+ export const quotes = collect([
67
+ // could be queried from any API or database
67
68
  'The best way to get started is to quit talking and begin doing.',
68
69
  'The pessimist sees difficulty in every opportunity. The optimist sees opportunity in every difficulty.',
69
70
  'Don’t let yesterday take up too much of today.',
@@ -1 +0,0 @@
1
- export * from './install';
@@ -1,27 +0,0 @@
1
- import type { ExecaReturnValue } from 'execa';
2
- interface InstallPackageOptions {
3
- cwd?: string;
4
- dev?: boolean;
5
- silent?: boolean;
6
- packageManager?: string;
7
- packageManagerVersion?: string;
8
- preferOffline?: boolean;
9
- additionalArgs?: string[];
10
- }
11
- /**
12
- * Install an npm package.
13
- *
14
- * @param name - The package name to install.
15
- * @param options - The options to pass to the install.The options to pass to the install.
16
- * @returns The result of the install.
17
- */
18
- export declare function installPackage(name: string, options?: InstallPackageOptions): Promise<ExecaReturnValue<string>>;
19
- /**
20
- * Install a Stack into your project.
21
- *
22
- * @param name - The Stack name to install.
23
- * @param options - The options to pass to the install.
24
- * @returns The result of the install.
25
- */
26
- export declare function installStack(name: string, options?: InstallPackageOptions): Promise<ExecaReturnValue<string>>;
27
- export {};
package/dist/cli.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import { CAC } from 'cac';
2
- export interface ParsedArgv {
3
- args: ReadonlyArray<string>;
4
- options: {
5
- [k: string]: any;
6
- };
7
- }
8
- interface CliOptions {
9
- name?: string;
10
- }
11
- export declare function cli(name?: string | CliOptions, options?: CliOptions): CAC;
12
- export { CAC };
package/dist/command.d.ts DELETED
@@ -1,33 +0,0 @@
1
- import type { CliOptions } from '@stacksjs/types';
2
- type CommandOptionTuple = [string, string, {
3
- default: boolean;
4
- }];
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
- export declare class Command {
21
- name: Options['name'];
22
- description: Options['description'];
23
- options: Options['options'];
24
- run: Options['run'];
25
- onFail: Options['onFail'];
26
- onSuccess: Options['onSuccess'];
27
- constructor({ name, description, options, run, onFail, onSuccess }: Options);
28
- }
29
- export declare const command: {
30
- run: (command: string, options?: any) => Promise<Result<Subprocess, CommandError>>;
31
- runSync: (command: string, options?: any) => Promise<Result<Subprocess, CommandError>>;
32
- };
33
- export {};
package/dist/console.d.ts DELETED
@@ -1,18 +0,0 @@
1
- import { log, logger } from '@stacksjs/logging';
2
- import prompts from 'prompts';
3
- export declare class Prompt {
4
- private required;
5
- constructor();
6
- require(): this;
7
- isRequired(): boolean;
8
- select(message: any, options: any): Promise<any>;
9
- checkbox(message: any, options: any): Promise<any>;
10
- confirm(message: any, options: any): Promise<any>;
11
- input(message: any, options: any): Promise<any>;
12
- password(message: any, options: any): Promise<any>;
13
- number(message: any, options: any): Promise<any>;
14
- multiselect(message: any, options: any): Promise<any>;
15
- autocomplete(message: any, options: any): Promise<any>;
16
- }
17
- export { prompts, logger, log };
18
- export declare const prompt: () => Prompt;
package/dist/exec.d.ts DELETED
@@ -1,40 +0,0 @@
1
- import { type Result } from '@stacksjs/error-handling';
2
- import type { CliOptions, Subprocess } from '@stacksjs/types';
3
- /**
4
- * Execute a command.
5
- *
6
- * @param command The command to execute.
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 exec('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 exec('ls', { cwd: '/home' })
21
- * ```
22
- */
23
- export declare function exec(command: string | string[], options?: CliOptions): Promise<Result<Subprocess, Error>>;
24
- /**
25
- * Execute a command and return result.
26
- *
27
- * @param command The command to execute.
28
- * @returns The result of the command.
29
- * @example
30
- * ```ts
31
- * const output = execSync('ls')
32
- *
33
- * console.log(output)
34
- * ```
35
- * @example
36
- * ```ts
37
- * const output = execSync('ls', { cwd: '/home' })
38
- * ```
39
- */
40
- export declare function execSync(command: string | string[], options?: CliOptions): Promise<string>;
package/dist/helpers.d.ts DELETED
@@ -1,9 +0,0 @@
1
- import type { IntroOptions, OutroOptions } from '@stacksjs/types';
2
- /**
3
- * Prints the intro message.
4
- */
5
- export declare function intro(command: string, options?: IntroOptions): Promise<number>;
6
- /**
7
- * Prints the outro message.
8
- */
9
- export declare function outro(text: string, options?: OutroOptions, error?: Error | string): Promise<unknown>;
package/dist/index.d.ts DELETED
@@ -1,10 +0,0 @@
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 './utils';
package/dist/parse.d.ts DELETED
@@ -1,17 +0,0 @@
1
- interface ParsedArgv {
2
- args: string[];
3
- options: {
4
- [k: string]: string | boolean | number;
5
- };
6
- }
7
- export declare function parseArgv(argv?: string[]): ParsedArgv;
8
- export declare function parseArgs(argv?: string[]): string[];
9
- interface CliOptions {
10
- dryRun?: boolean;
11
- quiet?: boolean;
12
- verbose?: boolean;
13
- [k: string]: string | boolean | number | undefined;
14
- }
15
- export declare function parseOptions(options?: CliOptions): CliOptions;
16
- export declare function buddyOptions(options?: any): string;
17
- export {};
package/dist/run.d.ts DELETED
@@ -1,63 +0,0 @@
1
- import type { CliOptions, CommandError, Subprocess } from '@stacksjs/types';
2
- import type { Result } from '@stacksjs/error-handling';
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<any[]>;
package/dist/spinner.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import ora from 'ora';
2
- export declare const spinner: typeof ora;
package/dist/utils.d.ts DELETED
@@ -1,4 +0,0 @@
1
- export * as kolorist from 'kolorist';
2
- export { stripAnsi, centerAlign, rightAlign, leftAlign, align, box, colors, getColor, colorize, } from 'consola/utils';
3
- 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';
4
- export declare const quotes: any;