Commit 5a3e890

Anton Medvedev <anton@medv.io>
2022-06-06 16:49:16
Add snapshots and simplify logger
1 parent 3688634
src/core.ts
@@ -22,9 +22,12 @@ import { chalk, which } from './goods.js'
 import { log } from './log.js'
 import { exitCodeInfo, noop, psTree, quote, substitute } from './util.js'
 
-type Shell = (pieces: TemplateStringsArray, ...args: any[]) => ProcessPromise
+export type Shell = (
+  pieces: TemplateStringsArray,
+  ...args: any[]
+) => ProcessPromise
 
-type Options = {
+export type Options = {
   verbose: boolean
   cwd: string
   env: NodeJS.ProcessEnv
@@ -93,7 +96,7 @@ export const $ = new Proxy<Shell & Options>(
       }
       cmd += s + pieces[++i]
     }
-    promise._bind(cmd, $.cwd, from, resolve!, reject!)
+    promise._bind(cmd, from, resolve!, reject!, getStore())
     // Make sure all subprocesses are started, if not explicitly by await or then().
     setImmediate(() => promise._run())
     return promise
@@ -115,10 +118,10 @@ type IO = StdioPipe | StdioNull
 export class ProcessPromise extends Promise<ProcessOutput> {
   child?: ChildProcess
   private _command = ''
-  private _cwd = ''
   private _from = ''
   private _resolve: Resolve = noop
   private _reject: Resolve = noop
+  private _snapshot = getStore()
   private _stdio: [IO, IO, IO] = ['inherit', 'pipe', 'pipe']
   private _nothrow = false
   private _quiet = false
@@ -129,24 +132,29 @@ export class ProcessPromise extends Promise<ProcessOutput> {
 
   _bind(
     cmd: string,
-    cwd: string,
     from: string,
     resolve: Resolve,
-    reject: Resolve
+    reject: Resolve,
+    options: Options
   ) {
     this._command = cmd
-    this._cwd = cwd
     this._from = from
     this._resolve = resolve
     this._reject = reject
+    this._snapshot = { ...options }
   }
 
   _run() {
+    const $ = this._snapshot
     if (this.child) return // The _run() called from two places: then() and setImmediate().
     this._prerun() // In case $1.pipe($2), the $2 returned, and on $2._run() invoke $1._run().
-    $.log('cmd', this._command, { source: this })
+    $.log({
+      kind: 'cmd',
+      cmd: this._command,
+      verbose: $.verbose && !this._quiet,
+    })
     this.child = spawn($.prefix + this._command, {
-      cwd: this._cwd,
+      cwd: $.cwd,
       shell: typeof $.shell === 'string' ? $.shell : true,
       stdio: this._stdio,
       windowsHide: true,
@@ -182,12 +190,12 @@ export class ProcessPromise extends Promise<ProcessOutput> {
       stderr = '',
       combined = ''
     let onStdout = (data: any) => {
-      $.log('stdout', data, { source: this })
+      $.log({ kind: 'stdout', data, verbose: $.verbose && !this._quiet })
       stdout += data
       combined += data
     }
     let onStderr = (data: any) => {
-      $.log('stderr', data, { source: this })
+      $.log({ kind: 'stderr', data, verbose: $.verbose && !this._quiet })
       stderr += data
       combined += data
     }
src/goods.ts
@@ -40,12 +40,12 @@ globbyModule)
 export const glob = globby
 
 export async function fetch(url: RequestInfo, init?: RequestInit) {
-  $.log('fetch', url.toString(), { init })
+  $.log({ kind: 'fetch', url, init })
   return nodeFetch(url, init)
 }
 
 export function cd(dir: string) {
-  $.log('cd', dir)
+  $.log({ kind: 'cd', dir })
   $.cwd = path.resolve($.cwd, dir)
   process.chdir($.cwd)
 }
src/index.ts
@@ -14,7 +14,14 @@
 
 import { ProcessPromise } from './core.js'
 
-export { $, ProcessPromise, ProcessOutput, within } from './core.js'
+export {
+  $,
+  Shell,
+  Options,
+  ProcessPromise,
+  ProcessOutput,
+  within,
+} from './core.js'
 
 export {
   argv,
@@ -34,7 +41,7 @@ export {
   YAML,
 } from './goods.js'
 
-export { log, formatCmd, LogKind, LogExtra } from './log.js'
+export { log, formatCmd, LogEntry } from './log.js'
 
 /**
  *  @deprecated Use $.nothrow() instead.
src/log.ts
@@ -12,38 +12,54 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import { RequestInit } from 'node-fetch'
+import { RequestInfo, RequestInit } from 'node-fetch'
 import { inspect } from 'node:util'
-import { $, ProcessPromise } from './core.js'
+import { $ } from './core.js'
 import { colorize } from './util.js'
 
-export type LogKind = 'cmd' | 'stdout' | 'stderr' | 'cd' | 'fetch'
-export type LogExtra = {
-  source?: ProcessPromise
-  init?: RequestInit
-}
-
-export function log(kind: LogKind, data: string, extra: LogExtra = {}) {
-  if (extra.source?.isQuiet()) return
-  if ($.verbose) {
-    switch (kind) {
-      case 'cmd':
-        process.stderr.write(formatCmd(data))
-        break
-      case 'stdout':
-      case 'stderr':
-        process.stderr.write(data)
-        break
-      case 'cd':
-        process.stderr.write('$ ' + colorize(`cd ${data}`) + '\n')
-        break
-      case 'fetch':
-        const init = extra.init ? ' ' + inspect(extra.init) : ''
-        process.stderr.write('$ ' + colorize(`fetch ${data}`) + init + '\n')
-        break
-      default:
-        throw new Error(`Unknown log kind "${kind}".`)
+export type LogEntry =
+  | {
+      kind: 'cmd'
+      verbose: boolean
+      cmd: string
+    }
+  | {
+      kind: 'stdout' | 'stderr'
+      verbose: boolean
+      data: Buffer
     }
+  | {
+      kind: 'cd'
+      dir: string
+    }
+  | {
+      kind: 'fetch'
+      url: RequestInfo
+      init?: RequestInit
+    }
+
+export function log(entry: LogEntry) {
+  switch (entry.kind) {
+    case 'cmd':
+      if (!entry.verbose) return
+      process.stderr.write(formatCmd(entry.cmd))
+      break
+    case 'stdout':
+    case 'stderr':
+      if (!entry.verbose) return
+      process.stderr.write(entry.data)
+      break
+    case 'cd':
+      if (!$.verbose) return
+      process.stderr.write('$ ' + colorize(`cd ${entry.dir}`) + '\n')
+      break
+    case 'fetch':
+      if (!$.verbose) return
+      const init = entry.init ? ' ' + inspect(entry.init) : ''
+      process.stderr.write('$ ' + colorize(`fetch ${entry.url}`) + init + '\n')
+      break
+    default:
+      throw new Error(`Unknown log kind.`)
   }
 }
 
test/index.test.js
@@ -467,4 +467,15 @@ test('stdio() works', async () => {
   assert.is((await b).stdout, 'bar')
 })
 
+test('snapshots works', async () => {
+  await within(async () => {
+    $.prefix += 'echo success;'
+    let p = $`:`
+    $.prefix += 'echo fail;'
+    let out = await p
+    assert.is(out.stdout, 'success\n')
+    assert.not.match(out.stdout, 'fail')
+  })
+})
+
 test.run()