├── README.md ├── line-transform-stream.js ├── package.json └── typings └── index.d.ts /README.md: -------------------------------------------------------------------------------- 1 | # Line Transform Stream 2 | 3 | A transform stream class to conveniently modify streamable data line by line. 4 | Written in ES2015/ES6. 5 | 6 | - Encodes input data chunk as string 7 | - Splits input at newline characters and buffers incomplete lines 8 | - Runs callback on each complete line 9 | - Outputs processed lines 10 | 11 | ## Usage 12 | 13 | Pass your transform function to the constructor. This example reads lines from `stdin`, replaces whitespaces by underscores and writes back to `stdout`. 14 | 15 | ```javascript 16 | // import module 17 | const LineTransformStream = require( 'line-transform-stream' ) 18 | 19 | // create new line transform stream 20 | const filter = new LineTransformStream( ( line ) => 21 | { 22 | // replace every whitespace by an underscore 23 | return line.replace( /\s/g, '_' ) 24 | }) 25 | 26 | // say hello 27 | process.stdout.write( "Type a phrase and hit Enter:\n" ) 28 | 29 | // connect stdin via transform stream to stdout 30 | process.stdin.pipe( filter ).pipe( process.stdout ) 31 | ``` 32 | 33 | ## Reference 34 | 35 | ### new LineTransformStream( transformCallback, stringEncoding, newlineCharacter ) 36 | 37 | - **transformCallback** `Function` Callback to process a line of text. Called with a `String` as single argument, without the newline at the end. Must return a `String` or throw an `Error`. Any thrown `Error` is caught and emitted as stream error event. 38 | - **stringEncoding** `String` Optional. Specifies a valid encoding option for [Buffer.toString()](https://nodejs.org/api/buffer.html#buffer_buf_tostring_encoding_start_end). Defaults to `"utf8"`. 39 | - **newlineCharacter** `String` Optional. Specifies a string to split the input at. Defaults to `"\n"`. 40 | -------------------------------------------------------------------------------- /line-transform-stream.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Line Transform Stream 3 | * 4 | * A transform stream class to conveniently modify streamable data line by line. 5 | * Written in ES2015 (ES6). 6 | * 7 | * Nick Schwarzenberg 8 | * mtripg6666tdr 9 | * v1.0.1, 07/2023 10 | * 11 | * License: MIT 12 | */ 13 | 14 | 'use strict' 15 | 16 | 17 | // built-in module dependency 18 | const Transform = require( 'stream' ).Transform 19 | 20 | 21 | // extend Transform stream class 22 | class LineTransformStream extends Transform 23 | { 24 | constructor( transformCallback, stringEncoding='utf8', newlineCharacter='\n' ) 25 | { 26 | // fail if callback is not a function 27 | if ( typeof transformCallback != 'function' ) 28 | { 29 | // throw type error 30 | throw new TypeError( "Callback must be a function." ) 31 | } 32 | 33 | // initialize parent 34 | super() 35 | 36 | // set callback for transforming lines 37 | this.transformCallback = transformCallback 38 | 39 | // set string encoding 40 | this.stringEncoding = stringEncoding 41 | 42 | // set newline character 43 | this.newlineCharacter = newlineCharacter 44 | 45 | // initialize internal line buffer 46 | this.lineBuffer = '' 47 | } 48 | 49 | // implement transform method (input encoding will be ignored in favor of encoding set in constructor) 50 | _transform( data, encoding, streamCallback ) 51 | { 52 | // convert buffer to string 53 | const text = data.toString( this.stringEncoding ) 54 | 55 | // split data at newline 56 | const lines = text.split( this.newlineCharacter ) 57 | 58 | // prepend previously buffered data to first line 59 | lines[0] = this.lineBuffer + lines[0] 60 | 61 | // last "line" is probably not a complete line, 62 | // remove it from the processing array and store it for next time 63 | this.lineBuffer = lines.pop() 64 | 65 | // process and push data with adding newline back 66 | this.handleLines( streamCallback, this.transformCallback, lines, this.newlineCharacter ) 67 | } 68 | 69 | // implement flush method to catch end of stream 70 | _flush( streamCallback ) 71 | { 72 | // anything remaining in line buffer? 73 | if ( this.lineBuffer != '' ) 74 | { 75 | // pass remaining buffer contents as single line 76 | const lines = [ this.lineBuffer ] 77 | 78 | // process and push data 79 | this.handleLines( streamCallback, this.transformCallback, [ this.lineBuffer ], '' ) 80 | } 81 | else 82 | { 83 | // otherwise run callback immediately 84 | streamCallback( null ) 85 | } 86 | } 87 | 88 | // handle array of lines of text 89 | handleLines( streamCallback, transformCallback, lines, appendToOutput ) 90 | { 91 | // processed output will be collected 92 | let processedOutput = '' 93 | 94 | try 95 | { 96 | // process line by line 97 | lines.forEach( line => 98 | { 99 | // pass line through processing callback 100 | processedOutput += transformCallback( line ) 101 | 102 | // add back whatever has been removed from line 103 | // (will be the newline character unless at the end of stream) 104 | processedOutput += appendToOutput 105 | }) 106 | } 107 | catch ( error ) 108 | { 109 | // catch processing errors and emit as stream error 110 | streamCallback( error ) 111 | 112 | // don't fire the callback again below 113 | return 114 | } 115 | 116 | // indicate end of processing and push aggregated output 117 | streamCallback( null, processedOutput ) 118 | } 119 | } 120 | 121 | 122 | // export custom stream class 123 | module.exports = LineTransformStream 124 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "line-transform-stream", 3 | "version": "1.0.1", 4 | "description": "A transform stream class to conveniently modify streamable data line by line.", 5 | "author": "Nick Schwarzenberg ", 6 | "license": "MIT", 7 | "main": "./line-transform-stream.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/bitfasching/node-line-transform-stream" 11 | }, 12 | "keywords": [ 13 | "line", "transform", "stream", "stdin", "stdout", "readline" 14 | ], 15 | "preferGlobal": false, 16 | "private": false, 17 | "types": "./typings/index.d.ts" 18 | } 19 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "line-transform-stream" { 2 | import { Transform } from "stream"; 3 | type transformCallback = (string:string)=>string; 4 | class LineTransformStream extends Transform{ 5 | constructor( transformCallback:transformCallback, stringEncoding?:string) 6 | } 7 | export = LineTransformStream; 8 | } --------------------------------------------------------------------------------