Commit 5f0874e

Anton Golub <antongolub@antongolub.com>
2024-10-30 10:56:22
refactor: simplify `promisifyStream` helper (#922)
* refactor: simplify `promisifyStream` helper * chore: bump version to 8.2.0 * test: check stream as stdio input
1 parent 3871229
src/core.ts
@@ -533,34 +533,31 @@ export class ProcessPromise extends Promise<ProcessOutput> {
 
   private static promisifyStream<D extends Writable>(
     dest: D
-  ): D & PromiseLike<void>
-  private static promisifyStream(
-    dest: Writable | ProcessPromise
-  ): (Writable & PromiseLike<void>) | ProcessPromise {
-    return dest instanceof ProcessPromise
-      ? dest
-      : (new Proxy(dest as Writable, {
-          get(target, key) {
-            if (key === 'then') {
-              return (res: any = noop, rej: any = noop) =>
-                new Promise((_res, _rej) =>
-                  target
-                    .once('error', () => _rej(rej()))
-                    .once('finish', () => _res(res()))
-                )
-            }
-            if (key === 'pipe') {
-              const pipe = Reflect.get(target, key)
-              if (typeof pipe === 'function')
-                return function (...args: any) {
-                  return ProcessPromise.promisifyStream(
-                    pipe.apply(target, args) as Writable
-                  )
-                }
+  ): D & PromiseLike<void> {
+    return new Proxy(dest as D & PromiseLike<void>, {
+      get(target, key) {
+        if (key === 'then') {
+          return (res: any = noop, rej: any = noop) =>
+            new Promise((_res, _rej) =>
+              target
+                .once('error', () => _rej(rej()))
+                .once('finish', () => _res(res()))
+            )
+        }
+        if (key === 'pipe') {
+          const pipe = Reflect.get(target, key)
+          if (typeof pipe === 'function')
+            return function (...args: any) {
+              return (
+                args[0] instanceof ProcessPromise
+                  ? noop
+                  : ProcessPromise.promisifyStream
+              )(pipe.apply(target, args) as Writable)
             }
-            return Reflect.get(target, key)
-          },
-        }) as Writable & PromiseLike<void>)
+        }
+        return Reflect.get(target, key)
+      },
+    })
   }
 }
 
test/core.test.js
@@ -267,10 +267,26 @@ describe('core', () => {
       })
     })
 
-    test('accepts `stdio`', async () => {
-      let p = $({ stdio: 'ignore' })`echo foo`
+    describe('accepts `stdio`', () => {
+      test('ignore', async () => {
+        const p = $({ stdio: 'ignore' })`echo foo`
+        assert.equal((await p).stdout, '')
+      })
 
-      assert.equal((await p).stdout, '')
+      test('file stream as stdout', async () => {
+        const createWriteStream = (f) => {
+          const stream = fs.createWriteStream(f)
+          return new Promise((resolve) => {
+            stream.on('open', () => resolve(stream))
+          })
+        }
+        const file = tempfile()
+        const stream = await createWriteStream(file)
+        const p = $({ stdio: ['pipe', stream, 'ignore'] })`echo foo`
+
+        await p
+        assert.equal((await fs.readFile(file)).toString(), 'foo\n')
+      })
     })
   })
 
@@ -339,18 +355,16 @@ describe('core', () => {
       })
 
       test('accepts WriteStream', async () => {
+        const file = tempfile()
         try {
-          await $`echo foo`.pipe(fs.createWriteStream('/tmp/output.txt'))
-          assert.equal(
-            (await fs.readFile('/tmp/output.txt')).toString(),
-            'foo\n'
-          )
+          await $`echo foo`.pipe(fs.createWriteStream(file))
+          assert.equal((await fs.readFile(file)).toString(), 'foo\n')
 
           let r = $`cat`
-          fs.createReadStream('/tmp/output.txt').pipe(r.stdin)
+          fs.createReadStream(file).pipe(r.stdin)
           assert.equal((await r).stdout, 'foo\n')
         } finally {
-          await fs.rm('/tmp/output.txt')
+          await fs.rm(file)
         }
       })
 
@@ -395,6 +409,7 @@ describe('core', () => {
       })
 
       test('is chainable (Transform/Duplex)', async () => {
+        const file = tempfile()
         const p = $`echo "hello"`
           .pipe(
             new Transform({
@@ -403,14 +418,12 @@ describe('core', () => {
               },
             })
           )
-          .pipe(fs.createWriteStream('/tmp/output2.txt'))
+          .pipe(fs.createWriteStream(file))
 
         assert.ok(p instanceof WriteStream)
         assert.equal(await p, undefined)
-        assert.equal(
-          (await fs.readFile('/tmp/output2.txt')).toString(),
-          'HELLO\n'
-        )
+        assert.equal((await fs.readFile(file)).toString(), 'HELLO\n')
+        await fs.rm(file)
       })
 
       it('supports multipiping', async () => {
package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "zx",
-  "version": "8.1.9",
+  "version": "8.2.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "zx",
-      "version": "8.1.9",
+      "version": "8.2.0",
       "license": "Apache-2.0",
       "bin": {
         "zx": "build/cli.js"
package.json
@@ -1,6 +1,6 @@
 {
   "name": "zx",
-  "version": "8.1.9",
+  "version": "8.2.0",
   "description": "A tool for writing better scripts",
   "type": "module",
   "main": "./build/index.cjs",