diff --git a/packages/csv-parse/lib/index.js b/packages/csv-parse/lib/index.js index f85363d6..f3a6bf7b 100644 --- a/packages/csv-parse/lib/index.js +++ b/packages/csv-parse/lib/index.js @@ -42,7 +42,12 @@ class Parser extends Transform { // Let buffered records be consumed before closing the writable side. // The source can still write chunks before the readable end event. this.on("end", () => { - this.end(() => this.destroy()); + this.end(() => { + // The stream polyfill of the browser builds has no `destroy` (#409) + if (typeof this.destroy === "function") { + this.destroy(); + } + }); }); }, ); @@ -70,7 +75,9 @@ class Parser extends Transform { }, () => { this.push(null); - this.on("end", this.destroy); + if (typeof this.destroy === "function") { + this.on("end", this.destroy); + } }, ); callback(err); diff --git a/packages/csv-parse/test/api.stream.destroy.ts b/packages/csv-parse/test/api.stream.destroy.ts index b5942783..a8130d90 100644 --- a/packages/csv-parse/test/api.stream.destroy.ts +++ b/packages/csv-parse/test/api.stream.destroy.ts @@ -2,7 +2,7 @@ import fs from "fs"; import os from "os"; import "should"; import { generate } from "csv-generate"; -import { parse, Parser } from "../lib/index.js"; +import { parse, Parser, Callback, Options } from "../lib/index.js"; interface ParserPrivate { _readableState: { @@ -73,4 +73,42 @@ describe("API destroy", function () { }); generate({ length: 2, seed: 1, columns: 2, fixed_size: true }).pipe(parser); }); + + describe("stream without destroy", function () { + // The browser builds bundle a stream polyfill which doesn't implement + // `destroy`, see https://github.com/adaltas/node-csv/issues/409 + const parseWithoutDestroy = (options: Options, callback: Callback) => { + // Node.js calls `destroy` itself unless `autoDestroy` is disabled + const parser = parse({ ...options, autoDestroy: false }, callback); + Object.defineProperty(parser, "destroy", { value: undefined }); + return parser; + }; + + it("option `to`", function (next) { + const parser = parseWithoutDestroy({ to: 1 }, (err, records) => { + if (err) return next(err); + records.should.eql([["a", "b"]]); + next(); + }); + parser.write("a,b\n1,2\n"); + }); + + it("option `to_line`", function (next) { + const parser = parseWithoutDestroy({ to_line: 1 }, (err, records) => { + if (err) return next(err); + records.should.eql([["a", "b"]]); + next(); + }); + parser.write("a,b\n1,2\n"); + }); + + it("empty input", function (next) { + const parser = parseWithoutDestroy({}, (err, records) => { + if (err) return next(err); + records.should.eql([]); + next(); + }); + parser.end(); + }); + }); });