pax_global_header00006660000000000000000000000064134515704110014513gustar00rootroot0000000000000052 comment=a61e22616bbf4036d662e56cd0ec5bfd2c5a76d8 hook-std-2.0.0/000077500000000000000000000000001345157041100132425ustar00rootroot00000000000000hook-std-2.0.0/.editorconfig000066400000000000000000000002571345157041100157230ustar00rootroot00000000000000root = true [*] indent_style = tab end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.yml] indent_style = space indent_size = 2 hook-std-2.0.0/.gitattributes000066400000000000000000000000231345157041100161300ustar00rootroot00000000000000* text=auto eol=lf hook-std-2.0.0/.gitignore000066400000000000000000000000271345157041100152310ustar00rootroot00000000000000node_modules yarn.lock hook-std-2.0.0/.npmrc000066400000000000000000000000231345157041100143550ustar00rootroot00000000000000package-lock=false hook-std-2.0.0/.travis.yml000066400000000000000000000000541345157041100153520ustar00rootroot00000000000000language: node_js node_js: - '10' - '8' hook-std-2.0.0/index.d.ts000066400000000000000000000074351345157041100151540ustar00rootroot00000000000000/// declare namespace hookStd { /** `unhook()` method which, when called, unhooks from a stream and resolves the Promise. */ type Unhook = () => void; /** @param output - String from stream output. @param unhook - Method which, when called, unhooks from stream. @returns A Buffer or string to modify the value in the stream. */ type Transform = ( output: string, unhook: Unhook ) => Buffer | string | void; /** @param output - String from stream output. @param unhook - Method which, when called, unhooks from stream. @returns A boolean to influence the return value of `.write(…)`. */ type SilentTransform = ( output: string, unhook: Unhook ) => boolean | void; interface Options { /** Automatically unhooks after the first call. @default false */ readonly once?: boolean; /** Suppress stdout/stderr output. @default true */ readonly silent?: boolean; } interface StreamsOptions extends Options { /** Writable streams to hook. This can be useful for libraries allowing users to configure a Writable Stream to write to. @default [process.stdout, process.stderr] */ readonly streams?: NodeJS.WritableStream[]; } interface SilentFalseOptions extends Options { /** Suppress stdout/stderr output. @default true */ readonly silent: false; } interface SilentTrueOptions extends Options { /** Suppress stdout/stderr output. @default true */ readonly silent?: true; } interface StreamsSilentFalseOptions extends StreamsOptions { /** Suppress stdout/stderr output. @default true */ readonly silent: false; } interface StreamsSilentTrueOptions extends StreamsOptions { /** Suppress stdout/stderr output. @default true */ readonly silent?: true; } /** Promise with a `unhook()` method which, when called, resolves the Promise with an empty result. */ interface HookPromise extends Promise { unhook: Unhook; } } declare const hookStd: { /** Hooks streams in options or stdout & stderr if none are specified. @returns A `Promise` with a `unhook()` method which, when called, unhooks the streams and resolves the `Promise`. */ (transform: hookStd.SilentTransform): hookStd.HookPromise; ( options: hookStd.StreamsSilentFalseOptions, transform: hookStd.Transform ): hookStd.HookPromise; ( options: hookStd.StreamsSilentTrueOptions, transform: hookStd.SilentTransform ): hookStd.HookPromise; /** Hooks stdout. @returns A `Promise` with a `unhook()` method which, when called, unhooks the streams and resolves the `Promise`. @example ``` import * as assert from 'assert'; import hookStd = require('hook-std'); (async () => { const promise = hookStd.stdout(output => { promise.unhook(); assert.strictEqual(output.trim(), 'unicorn'); }); console.log('unicorn'); await promise; })(); // You can also unhook using the second `transform` method parameter: (async () => { const promise = hookStd.stdout((output, unhook) => { unhook(); assert.strictEqual(output.trim(), 'unicorn'); }); console.log('unicorn'); await promise; })(); ``` */ stdout(transform: hookStd.SilentTransform): hookStd.HookPromise; stdout( options: hookStd.SilentFalseOptions, transform: hookStd.Transform ): hookStd.HookPromise; stdout( options: hookStd.SilentTrueOptions, transform: hookStd.SilentTransform ): hookStd.HookPromise; /** Hooks stderr. @returns A `Promise` with a `unhook()` method which, when called, unhooks the streams and resolves the `Promise`. */ stderr(transform: hookStd.SilentTransform): hookStd.HookPromise; stderr( options: hookStd.SilentFalseOptions, transform: hookStd.Transform ): hookStd.HookPromise; stderr( options: hookStd.SilentTrueOptions, transform: hookStd.SilentTransform ): hookStd.HookPromise; }; export = hookStd; hook-std-2.0.0/index.js000066400000000000000000000031541345157041100147120ustar00rootroot00000000000000'use strict'; const hook = (stream, options, transform) => { if (typeof options !== 'object') { transform = options; options = {}; } options = { silent: true, once: false, ...options }; let unhookFunction; const promise = new Promise(resolve => { const {write} = stream; const unhook = () => { stream.write = write; resolve(); }; stream.write = (output, encoding, callback) => { const callbackReturnValue = transform(String(output), unhook); if (options.once) { unhook(); } if (options.silent) { return typeof callbackReturnValue === 'boolean' ? callbackReturnValue : true; } let returnValue; if (typeof callbackReturnValue === 'string') { returnValue = typeof encoding === 'string' ? Buffer.from(callbackReturnValue).toString(encoding) : callbackReturnValue; } returnValue = returnValue || (Buffer.isBuffer(callbackReturnValue) ? callbackReturnValue : output); return write.call(stream, returnValue, encoding, callback); }; unhookFunction = unhook; }); promise.unhook = unhookFunction; return promise; }; const hookStd = (options, transform) => { const streams = options.streams || [process.stdout, process.stderr]; const streamPromises = streams.map(stream => hook(stream, options, transform)); const promise = Promise.all(streamPromises); promise.unhook = () => { for (const streamPromise of streamPromises) { streamPromise.unhook(); } }; return promise; }; hookStd.stdout = (...arguments_) => hook(process.stdout, ...arguments_); hookStd.stderr = (...arguments_) => hook(process.stderr, ...arguments_); module.exports = hookStd; hook-std-2.0.0/index.test-d.ts000066400000000000000000000057201345157041100161240ustar00rootroot00000000000000import {expectType, expectError} from 'tsd'; import hookStd = require('.'); import {HookPromise, Unhook, Transform, SilentTransform} from '.'; expectType(() => null); expectType(() => undefined); expectType(() => 0); expectType(() => '0'); expectType((output: string, unhook: Unhook) => undefined); expectType((output: string, unhook: Unhook) => {}); expectType((output: string, unhook: Unhook) => 'foo'); expectType((output: string, unhook: Unhook) => Buffer.from('foo')); expectError((output: string, unhook: Unhook) => true); expectType((output: string, unhook: Unhook) => undefined); expectType((output: string, unhook: Unhook) => {}); expectType((output: string, unhook: Unhook) => true); expectError((output: string, unhook: Unhook) => 'foo'); expectError((output: string, unhook: Unhook) => Buffer.from('foo') ); expectType(hookStd({once: true}, () => true)); expectError(hookStd({once: true}, () => 'foo')); expectType(hookStd(() => true)); expectError(hookStd(() => 'foo')); expectType(hookStd({silent: false}, () => 'foo')); expectType(hookStd({silent: true}, () => true)); expectError(hookStd({silent: false}, () => true)); expectError(hookStd({silent: true}, () => 'foo')); expectType(hookStd({streams: [process.stderr]}, () => true)); expectError(hookStd({streams: [process.stderr]}, () => 'foo')); expectType( hookStd({silent: false, streams: [process.stderr]}, () => 'foo') ); expectType( hookStd({silent: true, streams: [process.stderr]}, () => true) ); expectError(hookStd({silent: false, streams: [process.stderr]}, () => true)); expectError(hookStd({silent: true, streams: [process.stderr]}, () => 'foo')); expectType(hookStd.stdout({once: true}, () => true)); expectError(hookStd.stdout({once: true}, () => 'foo')); expectType(hookStd.stdout(() => true)); expectError(hookStd.stdout(() => 'foo')); expectType(hookStd.stdout({silent: false}, () => 'foo')); expectType(hookStd.stdout({silent: true}, () => true)); expectError(hookStd.stdout({silent: false}, () => true)); expectError(hookStd.stdout({silent: true}, () => 'foo')); expectError( hookStd.stdout({silent: false, streams: [process.stderr]}, () => 'foo') ); expectType(hookStd.stderr({once: true}, () => true)); expectError(hookStd.stderr({once: true}, () => 'foo')); expectType(hookStd.stderr(() => true)); expectError(hookStd.stderr(() => 'foo')); expectType(hookStd.stderr({silent: false}, () => 'foo')); expectType(hookStd.stderr({silent: true}, () => true)); expectError(hookStd.stderr({silent: false}, () => true)); expectError(hookStd.stderr({silent: true}, () => 'foo')); expectError( hookStd.stderr({silent: false, streams: [process.stderr]}, () => 'foo') ); hook-std-2.0.0/license000066400000000000000000000021251345157041100146070ustar00rootroot00000000000000MIT License Copyright (c) Sindre Sorhus (sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hook-std-2.0.0/package.json000066400000000000000000000012631345157041100155320ustar00rootroot00000000000000{ "name": "hook-std", "version": "2.0.0", "description": "Hook and modify stdout/stderr", "license": "MIT", "repository": "sindresorhus/hook-std", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", "url": "sindresorhus.com" }, "engines": { "node": ">=8" }, "scripts": { "test": "xo && ava && tsd" }, "files": [ "index.js", "index.d.ts" ], "keywords": [ "hook", "intercept", "capture", "modify", "change", "test", "assert", "check", "std", "stdio", "stdout", "stderr", "console", "log", "process" ], "devDependencies": { "@types/node": "^11.13.0", "ava": "^1.4.1", "tsd": "^0.7.2", "xo": "^0.24.0" } } hook-std-2.0.0/readme.md000066400000000000000000000041571345157041100150300ustar00rootroot00000000000000# hook-std [![Build Status](https://travis-ci.org/sindresorhus/hook-std.svg?branch=master)](https://travis-ci.org/sindresorhus/hook-std) > Hook and modify stdout and stderr ## Install ``` $ npm install hook-std ``` ## Usage ```js const assert = require('assert'); const hookStd = require('hook-std'); (async () => { const promise = hookStd.stdout(output => { promise.unhook(); assert.strictEqual(output.trim(), 'unicorn'); }); console.log('unicorn'); await promise; })(); ``` You can also unhook using the second `transform` method parameter: ```js (async () => { const promise = hookStd.stdout((output, unhook) => { unhook(); assert.strictEqual(output.trim(), 'unicorn'); }); console.log('unicorn'); await promise; })(); ``` ## API ### hookStd([options], transform) Hook streams in [streams option](#streams), by default stdout and stderr. Returns a `Promise` with a `unhook()` method which, when called, unhooks both stdout and stderr and resolves the `Promise` with an empty result. ### hookStd.stdout([options], transform) Hook stdout. Returns a `Promise` with a `unhook()` method which, when called, resolves the `Promise` with an empty result. ### hookStd.stderr([options], transform) Hook stderr. Returns a `Promise` with a `unhook()` method which, when called, resolves the `Promise` with an empty result. #### options Type: `Object` ##### silent Type: `boolean`
Default: `true` Suppress stdout/stderr output. ##### once Type: `boolean`
Default: `false` Automatically unhooks after the first call. ##### streams Type: `stream.Writable[]`
Default: `[process.stdout, process.stderr]` [Writable streams](https://nodejs.org/api/stream.html#stream_writable_streams) to hook. This can be useful for libraries allowing users to configure a Writable Stream to write to. #### transform Type: `Function` Receives stdout/stderr as the first argument and the unhook method as the second argument. Return a string to modify it. Optionally, when in silent mode, you may return a `boolean` to influence the return value of `.write(...)`. ## License MIT © [Sindre Sorhus](https://sindresorhus.com) hook-std-2.0.0/test.js000066400000000000000000000153741345157041100145710ustar00rootroot00000000000000import {serial as test} from 'ava'; import hookStd from '.'; const {stdout, stderr} = process; function restore() { // This craziness is required because these properties only have getters by default Object.defineProperties(process, { stdout: { configurable: true, writable: true, value: stdout }, stderr: { configurable: true, writable: true, value: stderr } }); } test.beforeEach(restore); test.afterEach(restore); test.cb('hook stdout & stderr', t => { t.plan(2); let i = 0; const promise = hookStd(string => { if (string === 'foo' || string === 'bar') { t.pass(); } if (++i === 2) { promise.unhook(); t.end(); } }); process.stdout.write('foo'); process.stderr.write('bar'); }); test.cb('hook stdout', t => { t.plan(1); const promise = hookStd.stdout(string => { t.is(string, 'foo'); promise.unhook(); t.end(); }); process.stdout.write('foo'); }); test.cb('hook stderr', t => { t.plan(1); const promise = hookStd.stderr(string => { t.is(string, 'foo'); promise.unhook(); t.end(); }); process.stderr.write('foo'); }); test.cb('hook custom stream', t => { t.plan(1); const streams = [{write: () => {}}]; let i = 0; const promise = hookStd({streams}, string => { if (string === 'foo') { t.pass(); } if (++i === 1) { promise.unhook(); t.end(); } }); streams[0].write('foo'); }); function loggingWrite(log, returnValueValue) { return (...items) => { while (items[items.length - 1] === undefined) { items.pop(); } log.push(items); return returnValueValue(); }; } test('passes through the return value of the underlying write call', t => { t.plan(3); const log = []; let returnValue = false; process.stdout = { write: loggingWrite(log, () => returnValue) }; hookStd.stdout({silent: false}, string => string); t.false(process.stdout.write('foo')); returnValue = true; t.true(process.stdout.write('bar')); t.deepEqual(log, [['foo'], ['bar']]); }); test('if silent, returns true by default', t => { t.plan(2); const log = []; process.stdout = { write: () => t.fail() }; hookStd.stdout(string => { log.push(string); return string; }); t.true(process.stdout.write('foo')); t.deepEqual(log, ['foo']); }); test('if silent, callback can return a boolean', t => { t.plan(3); const log = []; let returnValue = true; process.stdout = { write: () => t.fail() }; hookStd.stdout(string => { log.push(string); return returnValue; }); t.true(process.stdout.write('foo')); returnValue = false; t.false(process.stdout.write('bar')); t.deepEqual(log, ['foo', 'bar']); }); test('callback can return a buffer', t => { t.plan(3); const log = []; process.stdout = { write: loggingWrite(log, () => true) }; hookStd.stdout({silent: false}, string => Buffer.from(string)); t.true(process.stdout.write('foo')); t.true(process.stdout.write('bar')); t.deepEqual(log, [[Buffer.from('foo')], [Buffer.from('bar')]]); }); test('if no options are assigned, behave as silent', t => { t.plan(1); const log = []; let returnValue = false; process.stdout = { write: loggingWrite(log, () => returnValue) }; hookStd.stdout(string => string); process.stdout.write('foo'); returnValue = true; t.deepEqual(log, []); }); test('if once option is true, only the first write is silent', t => { t.plan(1); let returnValue; const log = []; process.stdout = { write: loggingWrite(log, () => returnValue) }; hookStd.stdout({once: true}, string => string); process.stdout.write('foo'); process.stdout.write('bar'); process.stdout.write('unicorn'); t.deepEqual(log, [['bar'], ['unicorn']]); }); test('if once option is true and silent is false, hook only prints the first write and std prints all writes', t => { t.plan(4); let hookReturnValue; const log = []; process.stdout = { write: loggingWrite(log, () => true) }; hookStd.stdout({silent: false, once: true}, string => { hookReturnValue = string; return string; }); process.stdout.write('foo'); t.deepEqual(hookReturnValue, 'foo'); t.deepEqual(log, [['foo']]); hookReturnValue = false; process.stdout.write('bar'); t.deepEqual(hookReturnValue, false); t.deepEqual(log, [['foo'], ['bar']]); }); test('output is converted to string', t => { t.plan(4); const log = []; hookStd.stdout(string => log.push(string)); process.stdout.write('foo'); t.deepEqual(log, ['foo']); process.stdout.write(123); t.deepEqual(log, ['foo', '123']); process.stdout.write({}); t.deepEqual(log, ['foo', '123', '[object Object]']); process.stdout.write(true); t.deepEqual(log, ['foo', '123', '[object Object]', 'true']); }); test('string returned by callback is converted to correct encoding', t => { t.plan(2); process.stdout = { write: output => output }; hookStd.stdout({silent: false}, () => 'tést'); t.is(process.stdout.write('foo', 'hex'), '74c3a97374'); t.is(process.stdout.write('bar', 'ascii'), 'tC)st'); }); test('string returned by callback is not converted if encoding is invalid', t => { t.plan(4); process.stdout = { write: output => output }; hookStd.stdout({silent: false}, () => 'tést'); t.is(process.stdout.write('foo', 123), 'tést'); t.is(process.stdout.write('bar', null), 'tést'); t.is(process.stdout.write('ping', {}), 'tést'); t.is(process.stdout.write('pong', () => {}), 'tést'); }); test('promise resolves when stdout & stderr are hooked and released via promise unhook method', async t => { t.plan(1); const log = []; const promise = hookStd(string => log.push(string)); process.stdout.write('foo'); process.stderr.write('bar'); t.deepEqual(log, ['foo', 'bar']); promise.unhook(); await promise; }); test('promise resolves when stdout & stderr are hooked and released via callback', async t => { t.plan(1); const log = []; const promise = hookStd((string, unhook) => { log.push(string); unhook(); }); process.stdout.write('foo'); process.stderr.write('bar'); t.deepEqual(log, ['foo', 'bar']); await promise; }); test('promise resolves when stdout is released via promise unhook method', async t => { t.plan(1); const promise = hookStd.stdout(string => { t.is(string, 'foo'); }); process.stdout.write('foo'); promise.unhook(); await promise; }); test('promise resolves when stderr is released via promise unhook method', async t => { t.plan(1); const promise = hookStd.stderr(string => { t.is(string, 'foo'); }); process.stderr.write('foo'); promise.unhook(); await promise; }); test('promise resolves when streams are hooked and released via callback', async t => { t.plan(1); const log = []; const streams = [{write: () => {}}, {write: () => {}}]; const promise = hookStd({streams}, (string, unhook) => { log.push(string); unhook(); }); streams[0].write('foo'); streams[1].write('bar'); t.deepEqual(log, ['foo', 'bar']); await promise; });