diff --git a/src/core.ts b/src/core.ts index cfdc00a928..80a1009290 100644 --- a/src/core.ts +++ b/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 { ) } + json(): Promise { + return this.then((p) => p.json()) + } + + text(encoding?: Encoding): Promise { + return this.then((p) => p.text(encoding)) + } + + lines(): Promise { + return this.then((p) => p.lines()) + } + + buffer(): Promise { + return this.then((p) => p.buffer()) + } + + blob(type?: string): Promise { + return this.then((p) => p.blob(type)) + } + then( onfulfilled?: | ((value: ProcessOutput) => PromiseLike | R) @@ -515,16 +536,30 @@ export class ProcessOutput extends Error { return this._combined } - json() { + json(): 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() { diff --git a/test/core.test.js b/test/core.test.js index 00779dafe5..515670e660 100644 --- a/test/core.test.js +++ b/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 }) })