| [62b2964] | 1 | /*
|
|---|
| 2 | Copyright (c) 2014-2021, Matteo Collina <hello@matteocollina.com>
|
|---|
| 3 |
|
|---|
| 4 | Permission to use, copy, modify, and/or distribute this software for any
|
|---|
| 5 | purpose with or without fee is hereby granted, provided that the above
|
|---|
| 6 | copyright notice and this permission notice appear in all copies.
|
|---|
| 7 |
|
|---|
| 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|---|
| 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|---|
| 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|---|
| 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|---|
| 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|---|
| 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|---|
| 14 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|---|
| 15 | */
|
|---|
| 16 |
|
|---|
| 17 | 'use strict'
|
|---|
| 18 |
|
|---|
| 19 | const { Transform } = require('stream')
|
|---|
| 20 | const { StringDecoder } = require('string_decoder')
|
|---|
| 21 | const kLast = Symbol('last')
|
|---|
| 22 | const kDecoder = Symbol('decoder')
|
|---|
| 23 |
|
|---|
| 24 | function transform (chunk, enc, cb) {
|
|---|
| 25 | let list
|
|---|
| 26 | if (this.overflow) { // Line buffer is full. Skip to start of next line.
|
|---|
| 27 | const buf = this[kDecoder].write(chunk)
|
|---|
| 28 | list = buf.split(this.matcher)
|
|---|
| 29 |
|
|---|
| 30 | if (list.length === 1) return cb() // Line ending not found. Discard entire chunk.
|
|---|
| 31 |
|
|---|
| 32 | // Line ending found. Discard trailing fragment of previous line and reset overflow state.
|
|---|
| 33 | list.shift()
|
|---|
| 34 | this.overflow = false
|
|---|
| 35 | } else {
|
|---|
| 36 | this[kLast] += this[kDecoder].write(chunk)
|
|---|
| 37 | list = this[kLast].split(this.matcher)
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| 40 | this[kLast] = list.pop()
|
|---|
| 41 |
|
|---|
| 42 | for (let i = 0; i < list.length; i++) {
|
|---|
| 43 | try {
|
|---|
| 44 | push(this, this.mapper(list[i]))
|
|---|
| 45 | } catch (error) {
|
|---|
| 46 | return cb(error)
|
|---|
| 47 | }
|
|---|
| 48 | }
|
|---|
| 49 |
|
|---|
| 50 | this.overflow = this[kLast].length > this.maxLength
|
|---|
| 51 | if (this.overflow && !this.skipOverflow) {
|
|---|
| 52 | cb(new Error('maximum buffer reached'))
|
|---|
| 53 | return
|
|---|
| 54 | }
|
|---|
| 55 |
|
|---|
| 56 | cb()
|
|---|
| 57 | }
|
|---|
| 58 |
|
|---|
| 59 | function flush (cb) {
|
|---|
| 60 | // forward any gibberish left in there
|
|---|
| 61 | this[kLast] += this[kDecoder].end()
|
|---|
| 62 |
|
|---|
| 63 | if (this[kLast]) {
|
|---|
| 64 | try {
|
|---|
| 65 | push(this, this.mapper(this[kLast]))
|
|---|
| 66 | } catch (error) {
|
|---|
| 67 | return cb(error)
|
|---|
| 68 | }
|
|---|
| 69 | }
|
|---|
| 70 |
|
|---|
| 71 | cb()
|
|---|
| 72 | }
|
|---|
| 73 |
|
|---|
| 74 | function push (self, val) {
|
|---|
| 75 | if (val !== undefined) {
|
|---|
| 76 | self.push(val)
|
|---|
| 77 | }
|
|---|
| 78 | }
|
|---|
| 79 |
|
|---|
| 80 | function noop (incoming) {
|
|---|
| 81 | return incoming
|
|---|
| 82 | }
|
|---|
| 83 |
|
|---|
| 84 | function split (matcher, mapper, options) {
|
|---|
| 85 | // Set defaults for any arguments not supplied.
|
|---|
| 86 | matcher = matcher || /\r?\n/
|
|---|
| 87 | mapper = mapper || noop
|
|---|
| 88 | options = options || {}
|
|---|
| 89 |
|
|---|
| 90 | // Test arguments explicitly.
|
|---|
| 91 | switch (arguments.length) {
|
|---|
| 92 | case 1:
|
|---|
| 93 | // If mapper is only argument.
|
|---|
| 94 | if (typeof matcher === 'function') {
|
|---|
| 95 | mapper = matcher
|
|---|
| 96 | matcher = /\r?\n/
|
|---|
| 97 | // If options is only argument.
|
|---|
| 98 | } else if (typeof matcher === 'object' && !(matcher instanceof RegExp) && !matcher[Symbol.split]) {
|
|---|
| 99 | options = matcher
|
|---|
| 100 | matcher = /\r?\n/
|
|---|
| 101 | }
|
|---|
| 102 | break
|
|---|
| 103 |
|
|---|
| 104 | case 2:
|
|---|
| 105 | // If mapper and options are arguments.
|
|---|
| 106 | if (typeof matcher === 'function') {
|
|---|
| 107 | options = mapper
|
|---|
| 108 | mapper = matcher
|
|---|
| 109 | matcher = /\r?\n/
|
|---|
| 110 | // If matcher and options are arguments.
|
|---|
| 111 | } else if (typeof mapper === 'object') {
|
|---|
| 112 | options = mapper
|
|---|
| 113 | mapper = noop
|
|---|
| 114 | }
|
|---|
| 115 | }
|
|---|
| 116 |
|
|---|
| 117 | options = Object.assign({}, options)
|
|---|
| 118 | options.autoDestroy = true
|
|---|
| 119 | options.transform = transform
|
|---|
| 120 | options.flush = flush
|
|---|
| 121 | options.readableObjectMode = true
|
|---|
| 122 |
|
|---|
| 123 | const stream = new Transform(options)
|
|---|
| 124 |
|
|---|
| 125 | stream[kLast] = ''
|
|---|
| 126 | stream[kDecoder] = new StringDecoder('utf8')
|
|---|
| 127 | stream.matcher = matcher
|
|---|
| 128 | stream.mapper = mapper
|
|---|
| 129 | stream.maxLength = options.maxLength
|
|---|
| 130 | stream.skipOverflow = options.skipOverflow || false
|
|---|
| 131 | stream.overflow = false
|
|---|
| 132 | stream._destroy = function (err, cb) {
|
|---|
| 133 | // Weird Node v12 bug that we need to work around
|
|---|
| 134 | this._writableState.errorEmitted = false
|
|---|
| 135 | cb(err)
|
|---|
| 136 | }
|
|---|
| 137 |
|
|---|
| 138 | return stream
|
|---|
| 139 | }
|
|---|
| 140 |
|
|---|
| 141 | module.exports = split
|
|---|