You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					142 lines
				
				3.7 KiB
			
		
		
			
		
	
	
					142 lines
				
				3.7 KiB
			| 
											3 years ago
										 | /* | ||
|  | Copyright (c) 2014-2021, Matteo Collina <hello@matteocollina.com> | ||
|  | 
 | ||
|  | Permission to use, copy, modify, and/or distribute this software for any | ||
|  | purpose with or without fee is hereby granted, provided that the above | ||
|  | copyright notice and this permission notice appear in all copies. | ||
|  | 
 | ||
|  | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
|  | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
|  | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
|  | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
|  | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
|  | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR | ||
|  | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
|  | */ | ||
|  | 
 | ||
|  | 'use strict' | ||
|  | 
 | ||
|  | const { Transform } = require('stream') | ||
|  | const { StringDecoder } = require('string_decoder') | ||
|  | const kLast = Symbol('last') | ||
|  | const kDecoder = Symbol('decoder') | ||
|  | 
 | ||
|  | function transform (chunk, enc, cb) { | ||
|  |   let list | ||
|  |   if (this.overflow) { // Line buffer is full. Skip to start of next line.
 | ||
|  |     const buf = this[kDecoder].write(chunk) | ||
|  |     list = buf.split(this.matcher) | ||
|  | 
 | ||
|  |     if (list.length === 1) return cb() // Line ending not found. Discard entire chunk.
 | ||
|  | 
 | ||
|  |     // Line ending found. Discard trailing fragment of previous line and reset overflow state.
 | ||
|  |     list.shift() | ||
|  |     this.overflow = false | ||
|  |   } else { | ||
|  |     this[kLast] += this[kDecoder].write(chunk) | ||
|  |     list = this[kLast].split(this.matcher) | ||
|  |   } | ||
|  | 
 | ||
|  |   this[kLast] = list.pop() | ||
|  | 
 | ||
|  |   for (let i = 0; i < list.length; i++) { | ||
|  |     try { | ||
|  |       push(this, this.mapper(list[i])) | ||
|  |     } catch (error) { | ||
|  |       return cb(error) | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   this.overflow = this[kLast].length > this.maxLength | ||
|  |   if (this.overflow && !this.skipOverflow) { | ||
|  |     cb(new Error('maximum buffer reached')) | ||
|  |     return | ||
|  |   } | ||
|  | 
 | ||
|  |   cb() | ||
|  | } | ||
|  | 
 | ||
|  | function flush (cb) { | ||
|  |   // forward any gibberish left in there
 | ||
|  |   this[kLast] += this[kDecoder].end() | ||
|  | 
 | ||
|  |   if (this[kLast]) { | ||
|  |     try { | ||
|  |       push(this, this.mapper(this[kLast])) | ||
|  |     } catch (error) { | ||
|  |       return cb(error) | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   cb() | ||
|  | } | ||
|  | 
 | ||
|  | function push (self, val) { | ||
|  |   if (val !== undefined) { | ||
|  |     self.push(val) | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function noop (incoming) { | ||
|  |   return incoming | ||
|  | } | ||
|  | 
 | ||
|  | function split (matcher, mapper, options) { | ||
|  |   // Set defaults for any arguments not supplied.
 | ||
|  |   matcher = matcher || /\r?\n/ | ||
|  |   mapper = mapper || noop | ||
|  |   options = options || {} | ||
|  | 
 | ||
|  |   // Test arguments explicitly.
 | ||
|  |   switch (arguments.length) { | ||
|  |     case 1: | ||
|  |       // If mapper is only argument.
 | ||
|  |       if (typeof matcher === 'function') { | ||
|  |         mapper = matcher | ||
|  |         matcher = /\r?\n/ | ||
|  |       // If options is only argument.
 | ||
|  |       } else if (typeof matcher === 'object' && !(matcher instanceof RegExp)) { | ||
|  |         options = matcher | ||
|  |         matcher = /\r?\n/ | ||
|  |       } | ||
|  |       break | ||
|  | 
 | ||
|  |     case 2: | ||
|  |       // If mapper and options are arguments.
 | ||
|  |       if (typeof matcher === 'function') { | ||
|  |         options = mapper | ||
|  |         mapper = matcher | ||
|  |         matcher = /\r?\n/ | ||
|  |       // If matcher and options are arguments.
 | ||
|  |       } else if (typeof mapper === 'object') { | ||
|  |         options = mapper | ||
|  |         mapper = noop | ||
|  |       } | ||
|  |   } | ||
|  | 
 | ||
|  |   options = Object.assign({}, options) | ||
|  |   options.autoDestroy = true | ||
|  |   options.transform = transform | ||
|  |   options.flush = flush | ||
|  |   options.readableObjectMode = true | ||
|  | 
 | ||
|  |   const stream = new Transform(options) | ||
|  | 
 | ||
|  |   stream[kLast] = '' | ||
|  |   stream[kDecoder] = new StringDecoder('utf8') | ||
|  |   stream.matcher = matcher | ||
|  |   stream.mapper = mapper | ||
|  |   stream.maxLength = options.maxLength | ||
|  |   stream.skipOverflow = options.skipOverflow || false | ||
|  |   stream.overflow = false | ||
|  |   stream._destroy = function (err, cb) { | ||
|  |     // Weird Node v12 bug that we need to work around
 | ||
|  |     this._writableState.errorEmitted = false | ||
|  |     cb(err) | ||
|  |   } | ||
|  | 
 | ||
|  |   return stream | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = split |