Commit 5038ec5
Changed files (5)
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
},