From 5038ec5e277cc8b61cb8fe92427b4a4e963553b0 Mon Sep 17 00:00:00 2001 From: Anton Golub Date: Tue, 17 Sep 2024 14:54:34 +0300 Subject: [PATCH] feat: enable stringLiterals for `pipe()` API (#900) --- .size-limit.json | 2 +- src/core.ts | 10 +++++-- src/util.ts | 2 ++ src/vendor-core.ts | 2 +- test/core.test.js | 66 ++++++++++++++++++++++++++-------------------- 5 files changed, 50 insertions(+), 32 deletions(-) diff --git a/.size-limit.json b/.size-limit.json index 66d4e8c322..4a7b4c4d31 100644 --- a/.size-limit.json +++ b/.size-limit.json @@ -9,7 +9,7 @@ { "name": "zx/index", "path": "build/*.{js,cjs}", - "limit": "795 kB", + "limit": "796 kB", "brotli": false, "gzip": false }, diff --git a/src/core.ts b/src/core.ts index f7dbd80c75..3ccaf0544d 100644 --- a/src/core.ts +++ b/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 { 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 { } return dest } else { - this._postrun = () => this.stdout.pipe(dest) + this._postrun = () => this.stdout.pipe(dest as Writable) return this } } diff --git a/src/util.ts b/src/util.ts index 03a3a55597..c5262f2f10 100644 --- a/src/util.ts +++ b/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 }) diff --git a/src/vendor-core.ts b/src/vendor-core.ts index 5376b5e6c9..b1b19571cc 100644 --- a/src/vendor-core.ts +++ b/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[0] export type RequestInit = Parameters[1] diff --git a/test/core.test.js b/test/core.test.js index d1a46cd4cc..9060a05b48 100644 --- a/test/core.test.js +++ b/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()', () => {