├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── intercept-stdout.js ├── package.json └── test └── intercept.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *node_modules* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Steve Farthing 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js Intercept stdout 2 | 3 | `intercept-stdout` captures or modifies stdout and/or stderr. 4 | 5 | Based on [this](https://gist.github.com/benbuckman/2758563) gist. 6 | 7 | ## Capture 8 | ```javascript 9 | // Start capturing stdout. 10 | var intercept = require("intercept-stdout"); 11 | var captured_text = ""; 12 | 13 | var unhook_intercept = intercept(function(text) { 14 | captured_text += text; 15 | }); 16 | 17 | console.log("This text is being captured."); 18 | 19 | // Stop capturing stdout. 20 | unhook_intercept(); 21 | 22 | console.log("This text is not being captured."); 23 | 24 | // This is the text that was captured. 25 | console.log("CAPTURED:", captured_text); 26 | ``` 27 | 28 | ## Modify 29 | ```javascript 30 | // Start capturing stdout. 31 | var intercept = require("intercept-stdout"); 32 | var modified_text = ""; 33 | 34 | var unhook_intercept = intercept(function(text) { 35 | modified_text += text.replace(/captured/i, "modified"); 36 | }); 37 | 38 | console.log("This text is being captured."); 39 | 40 | // Stop capturing stdout. 41 | unhook_intercept(); 42 | 43 | // This is the modified text. 44 | console.log("MODIFIED:", modified_text); 45 | ``` 46 | 47 | ## Suppress output 48 | 49 | By default, the captured text is still sent to stdout. To avoid this, return an empty string in the interceptor: 50 | 51 | ```javascript 52 | var intercept = require("intercept-stdout"); 53 | 54 | var logs = []; 55 | 56 | var unhook_intercept = intercept(function(text) { 57 | logs.push(text); 58 | return ''; 59 | }); 60 | 61 | console.log("This text won't be sent to stdout."); 62 | 63 | // Stop capturing stdout. 64 | unhook_intercept(); 65 | 66 | // Strings sent to stdout 67 | console.log("Logs:", logs); 68 | ``` 69 | 70 | ## Test 71 | ``` 72 | npm install 73 | npm test 74 | ``` 75 | 76 | ## Separating Error Text 77 | 78 | Starting in Version 0.1.2, you may now specify two interceptor callbacks. If a second interceptor callback is specified, the second callback will be invoked for `stderr` output. 79 | 80 | ## Errors and Warnings 81 | 82 | Versions > 0.1.1 hook both `stdout` and `stderr`. This change enables capturing of `console.log`, `console.info`, `console.warn`, and `console.error`. This change may break pre-existing interceptors if your interceptor expected output to be a full line of text. 83 | 84 | ## About Colorization 85 | 86 | Popular modules such as [`mocha`](http://mochajs.org/) and [`winston`](https://github.com/winstonjs/winston) may colorize output by inserting ANSI escape codes into the output stream. Both `mocha` and `winston` make multiple calls to the output streams while colorizing a line -- in order to be robust, your code should anticipate and deal with this. 87 | -------------------------------------------------------------------------------- /intercept-stdout.js: -------------------------------------------------------------------------------- 1 | // Borrowed. 2 | // https://gist.github.com/benbuckman/2758563 3 | 4 | 5 | var toArray = require('lodash.toarray'), 6 | util = require('util'); 7 | 8 | // Intercept stdout and stderr to pass output thru callback. 9 | // 10 | // Optionally, takes two callbacks. 11 | // If two callbacks are specified, 12 | // the first intercepts stdout, and 13 | // the second intercepts stderr. 14 | // 15 | // returns an unhook() function, call when done intercepting 16 | module.exports = function (stdoutIntercept, stderrIntercept) { 17 | stderrIntercept = stderrIntercept || stdoutIntercept; 18 | 19 | var old_stdout_write = process.stdout.write; 20 | var old_stderr_write = process.stderr.write; 21 | 22 | process.stdout.write = (function(write) { 23 | return function(string, encoding, fd) { 24 | var args = toArray(arguments); 25 | args[0] = interceptor( string, stdoutIntercept ); 26 | write.apply(process.stdout, args); 27 | }; 28 | }(process.stdout.write)); 29 | 30 | process.stderr.write = (function(write) { 31 | return function(string, encoding, fd) { 32 | var args = toArray(arguments); 33 | args[0] = interceptor( string, stderrIntercept ); 34 | write.apply(process.stderr, args); 35 | }; 36 | }(process.stderr.write)); 37 | 38 | function interceptor(string, callback) { 39 | // only intercept the string 40 | var result = callback(string); 41 | if (typeof result == 'string') { 42 | string = result.replace( /\n$/ , '' ) + (result && (/\n$/).test( string ) ? '\n' : ''); 43 | } 44 | return string; 45 | } 46 | 47 | // puts back to original 48 | return function unhook() { 49 | process.stdout.write = old_stdout_write; 50 | process.stderr.write = old_stderr_write; 51 | }; 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "intercept-stdout", 3 | "version": "0.1.2", 4 | "description": "Hooking Node.js stdout", 5 | "main": "intercept-stdout.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "mocha" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/sfarthin/intercept-stdout" 15 | }, 16 | "keywords": [ 17 | "stdout", 18 | "stderr" 19 | ], 20 | "author": "Steven Farthing", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/sfarthin/intercept-stdout/issues" 24 | }, 25 | "dependencies": { 26 | "lodash.toarray": "^3.0.0" 27 | }, 28 | "devDependencies": { 29 | "chai": "~1.8.1", 30 | "mocha": "~1.17.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/intercept.js: -------------------------------------------------------------------------------- 1 | var expect = require("chai").expect, 2 | Intercept = require("../intercept-stdout.js"), 3 | os = require('os'); 4 | 5 | describe("intercept-stdout", function() { 6 | 7 | describe('when one interceptor are specified', function() { 8 | 9 | it("should capture stdout when initialized and not when it is unhooked", function() { 10 | 11 | // Lets set up our intercept 12 | var captured_text = ""; 13 | var unhook = Intercept(function(txt) { 14 | captured_text += txt; 15 | }); 16 | 17 | // Lets try each of these pieces of text 18 | var arr = ["capture-this!", "日本語", "-k21.12-0k-ª–m-md1∆º¡∆ªº"]; 19 | arr.forEach(function(txt) { 20 | 21 | // Make sure we don't see the captured text yet. 22 | expect(captured_text).to.not.have.string(txt); 23 | 24 | // send to stdout. 25 | console.log(txt); 26 | 27 | // Make sure we have the captured text. 28 | expect(captured_text).to.have.string(txt); 29 | 30 | }); 31 | 32 | unhook(); 33 | captured_text = ""; 34 | 35 | arr = ["capture-this!", "日本語", "-k21.12-0k-ª–m-md1∆º¡∆ªº"]; 36 | arr.forEach(function(txt) { 37 | 38 | // send to stdout. 39 | console.log(txt); 40 | 41 | // Make sure we have not captured text. 42 | expect(captured_text).to.be.equal(""); 43 | 44 | }); 45 | 46 | }); 47 | 48 | it("should modify output if callback returns a string", function() { 49 | 50 | var captured = []; 51 | var unhook = Intercept(function(txt) { 52 | var mod = modified[captured.length]; 53 | captured.push(mod); 54 | return mod; 55 | }); 56 | 57 | var arr = ["capture-this!", "日本語", "-k21.12-0k-ª–m-md1∆º¡∆ªº"]; 58 | var modified = ["print-this!", "asdf", ""]; 59 | 60 | arr.forEach(function(txt) { 61 | // send to stdout. 62 | console.log(txt); 63 | // make sure captured doesn't contain the original text 64 | expect(captured).to.not.contain(txt); 65 | }); 66 | 67 | expect(captured).to.eql(modified); 68 | 69 | unhook(); 70 | 71 | }); 72 | 73 | it("should do the same for console.error", function() { 74 | 75 | var captured = []; 76 | var unhook = Intercept(function(txt) { 77 | var mod = modified[captured.length]; 78 | captured.push(mod); 79 | return mod; 80 | }); 81 | 82 | var arr = ["capture-this!", "日本語", "-k21.12-0k-ª–m-md1∆º¡∆ªº"]; 83 | var modified = ["print-this!", "asdf", ""]; 84 | 85 | arr.forEach(function(txt) { 86 | console.error(txt); 87 | }); 88 | 89 | expect(captured).to.eql(modified); 90 | 91 | unhook(); 92 | 93 | }); 94 | }); 95 | 96 | describe('when two interceptors are specified', function() { 97 | 98 | var outputs = []; 99 | var errors = []; 100 | var unhook; 101 | 102 | var expectedOutputs = ["capture-this!", "日本語", "-k21.12-0k-ª–m-md1∆º¡∆ªº"]; 103 | var expectedErrors = ["errors here!", "日本語 < huh?", "-k21.12-0k-ª–m-md1∆º¡∆ªº <-- that's some wierd stuff"]; 104 | 105 | before(function() { 106 | unhook = Intercept( 107 | function(txt) { 108 | outputs.push(txt); 109 | }, 110 | function(txt) { 111 | errors.push(txt); 112 | } ); 113 | 114 | expectedOutputs.forEach(function(txt) { 115 | console.log(txt); 116 | }); 117 | 118 | expectedErrors.forEach(function(txt) { 119 | console.error(txt); 120 | }); 121 | 122 | }); 123 | 124 | it("stdout is passed thru first interceptor", function(done) { 125 | 126 | expectedOutputs.forEach(function(txt) { 127 | expect(outputs).to.contain(txt + os.EOL); 128 | }); 129 | 130 | expectedErrors.forEach(function(txt) { 131 | expect(outputs).to.not.contain(txt + os.EOL); 132 | }); 133 | 134 | done(); 135 | }); 136 | 137 | it("stderr is passed thru second interceptor", function(done) { 138 | 139 | expectedErrors.forEach(function(txt) { 140 | expect(errors).to.contain(txt + os.EOL); 141 | }); 142 | 143 | expectedOutputs.forEach(function(txt) { 144 | expect(errors).to.not.contain(txt + os.EOL); 145 | }); 146 | 147 | done(); 148 | }); 149 | 150 | describe("after the interceptors are unhooked", function() { 151 | 152 | before(function() { 153 | unhook(); 154 | outputs = []; 155 | errors = []; 156 | 157 | expectedOutputs.forEach(function(txt) { 158 | console.log(txt); 159 | }); 160 | 161 | expectedErrors.forEach(function(txt) { 162 | console.error(txt); 163 | }); 164 | }); 165 | 166 | it("stdout is no longer passed thru first interceptor", function(done) { 167 | 168 | expect(outputs.length).to.eql(0); 169 | expect(errors.length).to.eql(0); 170 | 171 | done(); 172 | }); 173 | 174 | }); 175 | 176 | }); 177 | 178 | }); 179 | --------------------------------------------------------------------------------