diff --git a/packages/csv-parse/lib/api/index.js b/packages/csv-parse/lib/api/index.js index ca97838cd..bba5162a3 100644 --- a/packages/csv-parse/lib/api/index.js +++ b/packages/csv-parse/lib/api/index.js @@ -718,7 +718,25 @@ const transform = function (original_options = {}) { } let field = this.state.field.toString(encoding); if (rtrim === true && wasQuoting === false) { - field = field.trimRight(); + if (encoding === null) { + let end = field.length; + trimming: while (end > 0) { + for (const trimChar of this.state.timchars) { + const start = end - trimChar.length; + if ( + start >= 0 && + field.compare(trimChar, 0, trimChar.length, start, end) === 0 + ) { + end = start; + continue trimming; + } + } + break; + } + field = field.subarray(0, end); + } else { + field = field.trimRight(); + } } if (cast === true) { const [err, f] = this.__cast(field); diff --git a/packages/csv-parse/test/option.rtrim.buffer.js b/packages/csv-parse/test/option.rtrim.buffer.js new file mode 100644 index 000000000..4c7331fc4 --- /dev/null +++ b/packages/csv-parse/test/option.rtrim.buffer.js @@ -0,0 +1,43 @@ +import assert from "node:assert/strict"; +import { parse } from "../lib/index.js"; +import { parse as parseSync } from "../lib/sync.js"; + +describe("Option `rtrim` with Buffer fields", function () { + for (const encoding of [null, false]) { + for (const trimOption of ["rtrim", "trim"]) { + it(`preserves bytes with encoding=${encoding} and ${trimOption}`, async function () { + const binary = Buffer.from([0xff, 0xfe, 0xa0]); + const input = Buffer.concat([ + Buffer.from(' \ta\u2003 ," b "\n'), + binary, + Buffer.from("\u00a0, \t"), + ]); + const options = { encoding, [trimOption]: true }; + const expected = [ + [ + Buffer.from(trimOption === "trim" ? "a" : " \ta"), + Buffer.from(" b "), + ], + [binary, Buffer.alloc(0)], + ]; + assert.deepEqual(parseSync(input, options), expected); + const records = await new Promise((resolve, reject) => { + const parser = parse(options, (error, result) => { + if (error) reject(error); + else resolve(result); + }); + for (const byte of input) parser.write(Buffer.from([byte])); + parser.end(); + }); + assert.deepEqual(records, expected); + }); + } + + it(`preserves quoted whitespace with encoding=${encoding}`, function () { + assert.deepEqual( + parseSync('" a\u2003"," \t "', { encoding, rtrim: true }), + [[Buffer.from(" a\u2003"), Buffer.from(" \t ")]], + ); + }); + } +});