Commit 5038ec5

Anton Golub <antongolub@antongolub.com>
2024-09-17 13:54:34
feat: enable stringLiterals for `pipe()` API (#900) tag: 8.1.7
1 parent d814096
src/core.ts
@@ -30,6 +30,7 @@ import {
   chalk,
   which,
   ps,
+  isStringLiteral,
   type ChalkInstance,
   type RequestInfo,
   type RequestInit,
@@ -461,7 +462,12 @@ export class ProcessPromise extends Promise<ProcessOutput> {
     return super.catch(onrejected)
   }
 
-  pipe(dest: Writable | ProcessPromise): ProcessPromise {
+  pipe(
+    dest: Writable | ProcessPromise | TemplateStringsArray,
+    ...args: any[]
+  ): ProcessPromise {
+    if (isStringLiteral(dest))
+      return this.pipe($(dest as TemplateStringsArray, ...args))
     if (isString(dest))
       throw new Error('The pipe() method does not take strings. Forgot $?')
     if (this._resolved) {
@@ -488,7 +494,7 @@ export class ProcessPromise extends Promise<ProcessOutput> {
       }
       return dest
     } else {
-      this._postrun = () => this.stdout.pipe(dest)
+      this._postrun = () => this.stdout.pipe(dest as Writable)
       return this
     }
   }
src/util.ts
@@ -17,6 +17,8 @@ import path from 'node:path'
 import fs from 'node:fs'
 import { chalk } from './vendor-core.js'
 
+export { isStringLiteral } from './vendor-core.js'
+
 export function tempdir(prefix = `zx-${randomId()}`) {
   const dirpath = path.join(os.tmpdir(), prefix)
   fs.mkdirSync(dirpath, { recursive: true })
src/vendor-core.ts
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-export { exec, buildCmd, type TSpawnStore } from 'zurk/spawn'
+export { exec, buildCmd, isStringLiteral, type TSpawnStore } from 'zurk/spawn'
 
 export type RequestInfo = Parameters<typeof globalThis.fetch>[0]
 export type RequestInit = Parameters<typeof globalThis.fetch>[1]
test/core.test.js
@@ -317,34 +317,6 @@ describe('core', () => {
     })
 
     describe('pipe() API', () => {
-      test('is chainable', async () => {
-        let { stdout } = await $`echo "hello"`
-          .pipe($`awk '{print $1" world"}'`)
-          .pipe($`tr '[a-z]' '[A-Z]'`)
-        assert.equal(stdout, 'HELLO WORLD\n')
-      })
-
-      test('propagates rejection', async () => {
-        const p1 = $`exit 1`
-        const p2 = p1.pipe($`echo hello`)
-
-        try {
-          await p1
-        } catch (e) {
-          assert.equal(e.exitCode, 1)
-        }
-
-        try {
-          await p2
-        } catch (e) {
-          assert.equal(e.exitCode, 1)
-        }
-
-        const p3 = await $({ nothrow: true })`echo hello && exit 1`.pipe($`cat`)
-        assert.equal(p3.exitCode, 0)
-        assert.equal(p3.stdout.trim(), 'hello')
-      })
-
       test('accepts Writable', async () => {
         let contents = ''
         let stream = new Writable({
@@ -376,6 +348,16 @@ describe('core', () => {
         }
       })
 
+      test('accepts ProcessPromise', async () => {
+        const p = await $`echo foo`.pipe($`cat`)
+        assert.equal(p.stdout.trim(), 'foo')
+      })
+
+      test('accepts $ template literal', async () => {
+        const p = await $`echo foo`.pipe`cat`
+        assert.equal(p.stdout.trim(), 'foo')
+      })
+
       test('checks argument type', async () => {
         let err
         try {
@@ -389,6 +371,13 @@ describe('core', () => {
         )
       })
 
+      test('is chainable', async () => {
+        let { stdout } = await $`echo "hello"`
+          .pipe($`awk '{print $1" world"}'`)
+          .pipe($`tr '[a-z]' '[A-Z]'`)
+        assert.equal(stdout, 'HELLO WORLD\n')
+      })
+
       test('throws if already resolved', async (t) => {
         let ok = true
         let p = $`echo "Hello"`
@@ -404,6 +393,27 @@ describe('core', () => {
         }
         assert.ok(ok, 'Expected failure!')
       })
+
+      test('propagates rejection', async () => {
+        const p1 = $`exit 1`
+        const p2 = p1.pipe($`echo hello`)
+
+        try {
+          await p1
+        } catch (e) {
+          assert.equal(e.exitCode, 1)
+        }
+
+        try {
+          await p2
+        } catch (e) {
+          assert.equal(e.exitCode, 1)
+        }
+
+        const p3 = await $({ nothrow: true })`echo hello && exit 1`.pipe($`cat`)
+        assert.equal(p3.exitCode, 0)
+        assert.equal(p3.stdout.trim(), 'hello')
+      })
     })
 
     describe('abort()', () => {
.size-limit.json
@@ -9,7 +9,7 @@
   {
     "name": "zx/index",
     "path": "build/*.{js,cjs}",
-    "limit": "795 kB",
+    "limit": "796 kB",
     "brotli": false,
     "gzip": false
   },