v6
 1// Copyright 2021 Google LLC
 2//
 3// Licensed under the Apache License, Version 2.0 (the "License");
 4// you may not use this file except in compliance with the License.
 5// You may obtain a copy of the License at
 6//
 7//     https://www.apache.org/licenses/LICENSE-2.0
 8//
 9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import { ProcessOutput, $, Zx } from './core.js'
16import { sleep } from './goods.js'
17import { isString } from './util.js'
18import { getCtx, runInCtx } from './context.js'
19import { log } from './print.js'
20
21export { log }
22
23// Retries a command a few times. Will return after the first
24// successful attempt, or will throw after specifies attempts count.
25export function retry(count = 5, delay = 0) {
26  return async function (cmd: TemplateStringsArray, ...args: any[]) {
27    while (count-- > 0)
28      try {
29        return await $(cmd, ...args)
30      } catch (p) {
31        if (count === 0) throw p
32        if (delay) await sleep(delay)
33      }
34    return
35  }
36}
37
38// Runs and sets a timeout for a cmd
39export function withTimeout(timeout: number, signal: string) {
40  return async function (cmd: TemplateStringsArray, ...args: any[]) {
41    let p = $(cmd, ...args)
42    if (!timeout) return p
43
44    let timer = setTimeout(() => p.kill(signal), timeout)
45
46    return p.finally(() => clearTimeout(timer))
47  }
48}
49
50// A console.log() alternative which can take ProcessOutput.
51export function echo(pieces: TemplateStringsArray, ...args: any[]) {
52  let msg
53  let lastIdx = pieces.length - 1
54  if (
55    Array.isArray(pieces) &&
56    pieces.every(isString) &&
57    lastIdx === args.length
58  ) {
59    msg =
60      args.map((a, i) => pieces[i] + stringify(a)).join('') + pieces[lastIdx]
61  } else {
62    msg = [pieces, ...args].map(stringify).join(' ')
63  }
64  console.log(msg)
65}
66
67function stringify(arg: ProcessOutput | any) {
68  if (arg instanceof ProcessOutput) {
69    return arg.toString().replace(/\n$/, '')
70  }
71  return `${arg}`
72}
73
74// Starts a simple CLI spinner, and returns stop() func.
75export function startSpinner(title = '') {
76  let i = 0,
77    spin = () => process.stdout.write(`  ${'⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'[i++ % 10]} ${title}\r`)
78  return (
79    (id) => () =>
80      clearInterval(id)
81  )(setInterval(spin, 100))
82}
83
84export function ctx<R extends any>(cb: (_$: Zx) => R, ref: Zx = $.bind(null)): R {
85  const _$ = Object.assign(ref, getCtx())
86
87  return runInCtx(_$, cb, _$)
88}