├── .gitignore ├── LICENSE.md ├── README.md ├── lib └── line-input-stream.js ├── package.json └── test └── fs-wrapper.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | npm-debug.log -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | SOFTWARE LICENSE 2 | 3 | The node-line-input-stream JavaScript library is licensed under the Apache License, Version 2.0: 4 | 5 | Copyright 2012 Philip Tellis 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-line-input-stream 2 | ====================== 3 | 4 | Convert a Node.JS Readable Stream into a Line Buffered Input Stream 5 | 6 | Install 7 | --- 8 | 9 | ``` 10 | npm install line-input-stream 11 | ``` 12 | 13 | Usage 14 | --- 15 | 16 | Like [Readable Stream](http://nodejs.org/api/stream.html#stream_readable_stream) with a `line` event. 17 | 18 | ```javascript 19 | var LineInputStream = require('line-input-stream'), 20 | fs = require('fs'); 21 | 22 | var stream = LineInputStream(fs.createReadStream("foo.txt", { flags: "r" })); 23 | stream.setEncoding("utf8"); 24 | stream.setDelimiter("\n"); // optional string, defaults to "\n" 25 | 26 | stream.on("error", function(err) { 27 | console.log(err); 28 | }); 29 | 30 | stream.on("data", function(chunk) { 31 | // You don't need to use this event 32 | }); 33 | 34 | stream.on("line", function(line) { 35 | // Sends you lines from the stream delimited by delimiter 36 | }); 37 | 38 | stream.on("end", function() { 39 | // No more data, all line events emitted before this event 40 | }); 41 | 42 | stream.on("close", function() { 43 | // Same as ReadableStream's close event 44 | }); 45 | 46 | if(stream.readable) { 47 | console.log("stream is readable"); 48 | } 49 | 50 | // Also available: pause(), resume(), destroy(), pipe() 51 | ``` 52 | 53 | You can also attach listeners to any event specific to the underlying stream, ie, 54 | you can listen to the `open` event for streams created by [`fs.createReadStream()`](http://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options) 55 | or the `connect` event for [Net](http://nodejs.org/api/net.html) streams. 56 | 57 | A side effect of this is that you can add a listener for any junk string and `LineInputStream` will 58 | pretend that it worked. The event listener may never be called though. 59 | 60 | Caveats & Notes 61 | --- 62 | - Calling `pause()` might not stop `line` events from firing immediately. It will stop reading of data 63 | from the underlying stream, but any data that has already been read will still be split into lines and 64 | a `line` event will be fired for each of them. 65 | - The delimiter is not included in the line passed to the `line` handler 66 | - Even though this is called `line-input-stream`, you can delimit by anything, so for example, 67 | setting delimiter to `"\n\n"` will read by paragraph (sort of). 68 | - You can set the delimiter to a regular expression, which let's you do cool things like drop multiple blank lines: `/[\r\n]+/` 69 | - All methods return `this`, so can be chained 70 | 71 | 72 | Copyright 73 | --- 74 | Philip Tellis [@bluesmoon](https://twitter.com/bluesmoon) 75 | 76 | License 77 | --- 78 | (Apache License)[LICENSE.md] 79 | -------------------------------------------------------------------------------- /lib/line-input-stream.js: -------------------------------------------------------------------------------- 1 | var EventEmitter = require('events').EventEmitter, 2 | Stream = require('stream'), 3 | util = require('util'); 4 | 5 | 6 | var _events = { 7 | "line": function(line) { 8 | this.emit("line", line); 9 | }, 10 | "end": function() { 11 | this.emit("end"); 12 | } 13 | }; 14 | 15 | function LineInputStream(underlyingStream, delimiter) 16 | { 17 | if(!(this instanceof LineInputStream)) { 18 | return new LineInputStream(underlyingStream); 19 | } 20 | if(!underlyingStream) { 21 | throw new Error("LineInputStream requires an underlying stream"); 22 | } 23 | 24 | Stream.call(this); 25 | 26 | this.underlyingStream = underlyingStream; 27 | this.delimiter = delimiter? delimiter: "\n"; 28 | 29 | var self = this, 30 | data = ""; 31 | 32 | this.underlyingStream.on("data", function(chunk) { 33 | data += chunk; 34 | var lines = data.split(self.delimiter); 35 | data = lines.pop(); 36 | lines.forEach(_events.line, self); 37 | }); 38 | this.underlyingStream.on("end", function() { 39 | if(data.length > 0) { 40 | var lines = data.split(self.delimiter); 41 | lines.forEach(_events.line, self); 42 | } 43 | _events.end.call(self); 44 | }); 45 | 46 | Object.defineProperty(this, "readable", { 47 | get: function() { 48 | return self.underlyingStream.readable; 49 | }, 50 | enumerable: true, 51 | 52 | }); 53 | 54 | Object.defineProperty(this, "paused", { 55 | get: function() { 56 | return self.underlyingStream.paused; 57 | }, 58 | enumerable: true, 59 | 60 | }); 61 | } 62 | 63 | util.inherits(LineInputStream, Stream); 64 | 65 | // Start overriding EventEmitter methods so we can pass through to underlyingStream 66 | // If we get a request for an event we don't know about, pass it to the underlyingStream 67 | 68 | LineInputStream.prototype.addListener = function(type, listener) { 69 | if(!(type in _events)) { 70 | this.underlyingStream.on(type, listener); 71 | } 72 | EventEmitter.prototype.on.call(this, type, listener); 73 | 74 | return this; 75 | }; 76 | 77 | LineInputStream.prototype.on = LineInputStream.prototype.addListener; 78 | 79 | LineInputStream.prototype.removeListener = function(type, listener) { 80 | if(!(type in _events)) { 81 | this.underlyingStream.removeListener(type, listener); 82 | } 83 | EventEmitter.prototype.removeListener.call(this, type, listener); 84 | 85 | return this; 86 | }; 87 | 88 | LineInputStream.prototype.removeAllListeners = function(type) { 89 | if(!(type in _events)) { 90 | this.underlyingStream.removeAllListeners(type); 91 | } 92 | EventEmitter.prototype.removeAllListeners.call(this, type); 93 | 94 | return this; 95 | }; 96 | 97 | // End overriding EventEmitter methods 98 | 99 | // Start passthrough of Readable Stream methods for underlying stream 100 | LineInputStream.prototype.pause = function() { 101 | if(this.underlyingStream.pause) 102 | this.underlyingStream.pause(); 103 | 104 | return this; 105 | }; 106 | 107 | LineInputStream.prototype.resume = function() { 108 | if(this.underlyingStream.resume) 109 | this.underlyingStream.resume(); 110 | 111 | return this; 112 | }; 113 | 114 | LineInputStream.prototype.destroy = function() { 115 | if(this.underlyingStream.destroy) 116 | this.underlyingStream.destroy(); 117 | 118 | return this; 119 | }; 120 | 121 | LineInputStream.prototype.setEncoding = function(encoding) { 122 | if(this.underlyingStream.setEncoding) 123 | this.underlyingStream.setEncoding(encoding); 124 | 125 | return this; 126 | }; 127 | 128 | // End passthrough of Readable Stream methods 129 | 130 | LineInputStream.prototype.setDelimiter = function(delimiter) { 131 | this.delimiter = delimiter; 132 | 133 | return this; 134 | }; 135 | 136 | module.exports = LineInputStream; 137 | 138 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "line-input-stream", 3 | "version" : "1.0.1", 4 | "description" : "Convert a Node.JS Readable Stream into a Line Buffered Input Stream", 5 | "keywords" : ["stream", "input", "line", "line-buffered", "readable stream"], 6 | "homepage" : "https://github.com/bluesmoon/node-line-input-stream", 7 | "author" : "Philip Tellis (http://bluesmoon.info/)", 8 | "files" : ["lib/" ], 9 | "main" : "lib/line-input-stream.js", 10 | "repository" : { "type": "git", "url": "git://github.com/bluesmoon/node-line-input-stream.git" }, 11 | "engines" : { "node": ">=0.8.8" } 12 | } 13 | -------------------------------------------------------------------------------- /test/fs-wrapper.js: -------------------------------------------------------------------------------- 1 | var LineInputStream = require('../lib/line-input-stream'), 2 | fs = require('fs'); 3 | 4 | var stream = LineInputStream(fs.createReadStream(__filename, { flags: "r" })); 5 | 6 | console.log(stream.readable); 7 | console.log(stream.paused); 8 | 9 | var n=0; 10 | 11 | stream.on("error", function(err) { 12 | console.log(err); 13 | }); 14 | 15 | stream.on("open", function(fd) { 16 | console.log("opened: ", fd); 17 | }) 18 | 19 | stream.on("line", function(line) { 20 | console.log("line %d: %s", n, line); 21 | n++; 22 | }); 23 | 24 | stream.on("end", function() { 25 | console.log("ended"); 26 | stream.destroy(); 27 | console.log(stream.readable); 28 | }); 29 | 30 | stream.pipe(process.stdout); 31 | --------------------------------------------------------------------------------