From 1a84622f11ad4bbe3759bb1e9f09bbec8c00da3a Mon Sep 17 00:00:00 2001 From: Sri Charan Date: Mon, 12 Oct 2026 13:52:15 +0000 Subject: [PATCH] fix(csv-stringify): preserve caller cast configuration --- .../lib/api/normalize_options.js | 17 ++++++- packages/csv-stringify/test/option.cast.js | 44 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/packages/csv-stringify/lib/api/normalize_options.js b/packages/csv-stringify/lib/api/normalize_options.js index 32e63033c..339387c23 100644 --- a/packages/csv-stringify/lib/api/normalize_options.js +++ b/packages/csv-stringify/lib/api/normalize_options.js @@ -199,8 +199,21 @@ const normalize_options = function (opts) { // Normalize option `cast` if (options.cast === undefined || options.cast === null) { options.cast = {}; - } else { - // todo + } else if ( + typeof options.cast === "object" || + typeof options.cast === "function" + ) { + options.cast = { + ...options.cast, + bigint: options.cast.bigint, + boolean: options.cast.boolean, + date: options.cast.date, + number: options.cast.number, + object: options.cast.object, + string: options.cast.string, + null: options.cast.null, + undefined: options.cast.undefined, + }; } // Normalize option cast.bigint if (options.cast.bigint === undefined || options.cast.bigint === null) { diff --git a/packages/csv-stringify/test/option.cast.js b/packages/csv-stringify/test/option.cast.js index 83fcef78a..ec4cd77fe 100644 --- a/packages/csv-stringify/test/option.cast.js +++ b/packages/csv-stringify/test/option.cast.js @@ -1,8 +1,52 @@ import "should"; +import assert from "node:assert/strict"; import { stringify } from "../lib/index.js"; import { stringify as stringifySync } from "../lib/sync.js"; describe("Option `cast`", function () { + describe("configuration", function () { + for (const frozen of [false, true]) { + it(`preserves ${frozen ? "frozen" : "mutable"} options in the sync API`, function () { + const string = (value) => value.toUpperCase(); + const cast = { string, number: null }; + if (frozen) Object.freeze(cast); + for (let i = 0; i < 2; i++) { + assert.equal(stringifySync([["value", 42]], { cast }), "VALUE,42\n"); + } + assert.deepEqual(cast, { string, number: null }); + }); + + it(`preserves ${frozen ? "frozen" : "mutable"} options in the callback API`, function (next) { + const string = (value) => value.toUpperCase(); + const cast = { string }; + if (frozen) Object.freeze(cast); + stringify([["value", 42]], { cast }, (err, output) => { + if (err) return next(err); + assert.equal(output, "VALUE,42\n"); + assert.deepEqual(cast, { string }); + next(); + }); + }); + } + + it("preserves inherited and non-enumerable custom casters", function () { + const cast = Object.create({ string: (value) => value.toUpperCase() }); + Object.defineProperty(cast, "number", { value: () => "number" }); + assert.equal(stringifySync([["value", 42]], { cast }), "VALUE,number\n"); + }); + + it("preserves custom null and undefined casters", function () { + const cast = { + null: () => "null", + undefined: () => "missing", + }; + assert.equal( + stringifySync([[null, undefined]], { cast }), + "null,missing\n", + ); + }); + }); + describe("udf", function () { it("boolean must return a string", function (next) { stringify(