Commit 1f7ce4e

Anton Golub <antongolub@antongolub.com>
2024-05-21 09:31:14
feat: provide format methods for `ProcessPromise` (#815)
continues #811 relates #764
1 parent d09edf8
Changed files (2)
src/core.ts
@@ -14,6 +14,7 @@
 
 import assert from 'node:assert'
 import { spawn, spawnSync, StdioOptions, IOType } from 'node:child_process'
+import { type Encoding } from 'node:crypto'
 import { AsyncHook, AsyncLocalStorage, createHook } from 'node:async_hooks'
 import { Readable, Writable } from 'node:stream'
 import { inspect } from 'node:util'
@@ -374,6 +375,26 @@ export class ProcessPromise extends Promise<ProcessOutput> {
     )
   }
 
+  json<T = any>(): Promise<T> {
+    return this.then((p) => p.json<T>())
+  }
+
+  text(encoding?: Encoding): Promise<string> {
+    return this.then((p) => p.text(encoding))
+  }
+
+  lines(): Promise<string[]> {
+    return this.then((p) => p.lines())
+  }
+
+  buffer(): Promise<Buffer> {
+    return this.then((p) => p.buffer())
+  }
+
+  blob(type?: string): Promise<Blob> {
+    return this.then((p) => p.blob(type))
+  }
+
   then<R = ProcessOutput, E = ProcessOutput>(
     onfulfilled?:
       | ((value: ProcessOutput) => PromiseLike<R> | R)
@@ -515,16 +536,30 @@ export class ProcessOutput extends Error {
     return this._combined
   }
 
-  json() {
+  json<T = any>(): T {
     return JSON.parse(this._combined)
   }
 
   buffer() {
-    return Buffer.from(this._combined, 'utf8')
+    return Buffer.from(this._combined)
   }
 
-  text() {
-    return this._combined
+  blob(type = 'text/plain') {
+    if (!globalThis.Blob)
+      throw new Error(
+        'Blob is not supported in this environment. Provide a polyfill'
+      )
+    return new Blob([this.buffer()], { type })
+  }
+
+  text(encoding: Encoding = 'utf8') {
+    return encoding === 'utf8'
+      ? this.toString()
+      : this.buffer().toString(encoding)
+  }
+
+  lines() {
+    return this.valueOf().split(/\r?\n/)
   }
 
   valueOf() {
test/core.test.js
@@ -465,33 +465,76 @@ describe('core', () => {
         assert.equal(signal, 'SIGKILL')
       })
     })
-  })
 
-  describe('ProcessOutput', () => {
-    test('implements toString()', async () => {
-      const p = $`echo foo`
-      assert.equal((await p).toString(), 'foo\n')
+    test('json()', async () => {
+      assert.deepEqual(await $`echo '{"key":"value"}'`.json(), { key: 'value' })
     })
 
-    test('implements valueOf()', async () => {
+    test('text()', async () => {
       const p = $`echo foo`
-      assert.equal((await p).valueOf(), 'foo')
-      assert.ok((await p) == 'foo')
+      assert.equal(await p.text(), 'foo\n')
+      assert.equal(await p.text('hex'), '666f6f0a')
     })
 
-    test('implements json()', async () => {
-      const p = $`echo '{"key":"value"}'`
-      assert.deepEqual((await p).json(), { key: 'value' })
+    test('lines()', async () => {
+      const p = $`echo 'foo\nbar\r\nbaz'`
+      assert.deepEqual(await p.lines(), ['foo', 'bar', 'baz'])
     })
 
-    test('implements text()', async () => {
-      const p = $`echo foo`
-      assert.equal((await p).toString(), 'foo\n')
+    test('buffer()', async () => {
+      assert.equal(
+        (await $`echo foo`.buffer()).compare(Buffer.from('foo\n', 'utf-8')),
+        0
+      )
     })
 
-    test('implements buffer()', async () => {
+    test('blob()', async () => {
       const p = $`echo foo`
-      assert.equal((await p).buffer().compare(Buffer.from('foo\n', 'utf-8')), 0)
+      assert.equal(await (await p.blob()).text(), 'foo\n')
+    })
+  })
+
+  describe('ProcessOutput', () => {
+    test('toString()', async () => {
+      const o = new ProcessOutput(null, null, '', '', 'foo\n')
+      assert.equal(o.toString(), 'foo\n')
+    })
+
+    test('valueOf()', async () => {
+      const o = new ProcessOutput(null, null, '', '', 'foo\n')
+      assert.equal(o.valueOf(), 'foo')
+      assert.ok(o == 'foo')
+    })
+
+    test('json()', async () => {
+      const o = new ProcessOutput(null, null, '', '', '{"key":"value"}')
+      assert.deepEqual(o.json(), { key: 'value' })
+    })
+
+    test('text()', async () => {
+      const o = new ProcessOutput(null, null, '', '', 'foo\n')
+      assert.equal(o.text(), 'foo\n')
+      assert.equal(o.text('hex'), '666f6f0a')
+    })
+
+    test('lines()', async () => {
+      const o = new ProcessOutput(null, null, '', '', 'foo\nbar\r\nbaz\n')
+      assert.deepEqual(o.lines(), ['foo', 'bar', 'baz'])
+    })
+
+    test('buffer()', async () => {
+      const o = new ProcessOutput(null, null, '', '', 'foo\n')
+      assert.equal(o.buffer().compare(Buffer.from('foo\n', 'utf-8')), 0)
+    })
+
+    test('blob()', async () => {
+      const o = new ProcessOutput(null, null, '', '', 'foo\n')
+      assert.equal(await o.blob().text(), 'foo\n')
+
+      const { Blob } = globalThis
+      globalThis.Blob = undefined
+      assert.throws(() => o.blob(), /Blob is not supported/)
+      globalThis.Blob = Blob
     })
   })