pax_global_header 0000666 0000000 0000000 00000000064 13451570411 0014513 g ustar 00root root 0000000 0000000 52 comment=a61e22616bbf4036d662e56cd0ec5bfd2c5a76d8
hook-std-2.0.0/ 0000775 0000000 0000000 00000000000 13451570411 0013242 5 ustar 00root root 0000000 0000000 hook-std-2.0.0/.editorconfig 0000664 0000000 0000000 00000000257 13451570411 0015723 0 ustar 00root root 0000000 0000000 root = 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/.gitattributes 0000664 0000000 0000000 00000000023 13451570411 0016130 0 ustar 00root root 0000000 0000000 * text=auto eol=lf
hook-std-2.0.0/.gitignore 0000664 0000000 0000000 00000000027 13451570411 0015231 0 ustar 00root root 0000000 0000000 node_modules
yarn.lock
hook-std-2.0.0/.npmrc 0000664 0000000 0000000 00000000023 13451570411 0014355 0 ustar 00root root 0000000 0000000 package-lock=false
hook-std-2.0.0/.travis.yml 0000664 0000000 0000000 00000000054 13451570411 0015352 0 ustar 00root root 0000000 0000000 language: node_js
node_js:
- '10'
- '8'
hook-std-2.0.0/index.d.ts 0000664 0000000 0000000 00000007435 13451570411 0015154 0 ustar 00root root 0000000 0000000 ///
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.js 0000664 0000000 0000000 00000003154 13451570411 0014712 0 ustar 00root root 0000000 0000000 '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.ts 0000664 0000000 0000000 00000005720 13451570411 0016124 0 ustar 00root root 0000000 0000000 import {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/license 0000664 0000000 0000000 00000002125 13451570411 0014607 0 ustar 00root root 0000000 0000000 MIT 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.json 0000664 0000000 0000000 00000001263 13451570411 0015532 0 ustar 00root root 0000000 0000000 {
"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.md 0000664 0000000 0000000 00000004157 13451570411 0015030 0 ustar 00root root 0000000 0000000 # hook-std [](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.js 0000664 0000000 0000000 00000015374 13451570411 0014571 0 ustar 00root root 0000000 0000000 import {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;
});