@@ -3,7 +3,6 @@ import { flattenRecord, isPlainObject } from './flatten.js';
33import type {
44 CsvColumnInput ,
55 CsvPrimitive ,
6- CsvRecord ,
76 JsonToCsvOptions ,
87 ResolvedCsvColumn ,
98 ResolvedJsonToCsvOptions
@@ -15,13 +14,14 @@ export type {
1514 CsvColumnInput ,
1615 CsvPrimitive ,
1716 CsvRecord ,
18- JsonToCsvOptions
17+ JsonToCsvOptions ,
18+ ResolvedCsvColumn
1919} from './types.js' ;
2020
2121export { escapeCsvCell } from './escape.js' ;
2222export { flattenRecord } from './flatten.js' ;
2323
24- export function jsonToCsv < TRecord extends CsvRecord > (
24+ export function jsonToCsv < TRecord extends object > (
2525 records : readonly TRecord [ ] ,
2626 options : JsonToCsvOptions < TRecord > = { }
2727) : string {
@@ -60,15 +60,15 @@ export function jsonToCsv<TRecord extends CsvRecord>(
6060
6161export const toCsv = jsonToCsv ;
6262
63- export function inferCsvColumns < TRecord extends CsvRecord > (
63+ export function inferCsvColumns < TRecord extends object > (
6464 records : readonly TRecord [ ] ,
6565 options : Pick < JsonToCsvOptions < TRecord > , 'flatten' | 'sortColumns' > = { }
6666) : Array < ResolvedCsvColumn < TRecord > > {
6767 const resolved = resolveOptions ( options ) ;
6868 return resolveColumns ( assertRecords ( records ) , resolved ) ;
6969}
7070
71- function resolveOptions < TRecord extends CsvRecord > (
71+ function resolveOptions < TRecord extends object > (
7272 options : JsonToCsvOptions < TRecord >
7373) : ResolvedJsonToCsvOptions < TRecord > {
7474 const delimiter = options . delimiter ?? ',' ;
@@ -111,7 +111,7 @@ function resolveOptions<TRecord extends CsvRecord>(
111111 } ;
112112}
113113
114- function assertRecords < TRecord extends CsvRecord > ( records : readonly TRecord [ ] ) : readonly TRecord [ ] {
114+ function assertRecords < TRecord extends object > ( records : readonly TRecord [ ] ) : readonly TRecord [ ] {
115115 if ( ! Array . isArray ( records ) ) {
116116 throw new TypeError ( 'json-csv-kit expects an array of records.' ) ;
117117 }
@@ -125,7 +125,7 @@ function assertRecords<TRecord extends CsvRecord>(records: readonly TRecord[]):
125125 return records ;
126126}
127127
128- function resolveColumns < TRecord extends CsvRecord > (
128+ function resolveColumns < TRecord extends object > (
129129 records : readonly TRecord [ ] ,
130130 options : ResolvedJsonToCsvOptions < TRecord >
131131) : Array < ResolvedCsvColumn < TRecord > > {
@@ -136,7 +136,7 @@ function resolveColumns<TRecord extends CsvRecord>(
136136 const keys = new Set < string > ( ) ;
137137
138138 for ( const record of records ) {
139- const source = options . flatten ? flattenRecord ( record ) : record ;
139+ const source : object = options . flatten ? flattenRecord ( record ) : record ;
140140
141141 for ( const key of Object . keys ( source ) ) {
142142 keys . add ( key ) ;
@@ -156,7 +156,7 @@ function resolveColumns<TRecord extends CsvRecord>(
156156 } ) ) ;
157157}
158158
159- function resolveColumn < TRecord extends CsvRecord > (
159+ function resolveColumn < TRecord extends object > (
160160 column : CsvColumnInput < TRecord >
161161) : ResolvedCsvColumn < TRecord > {
162162 if ( typeof column === 'string' ) {
@@ -176,7 +176,7 @@ function resolveColumn<TRecord extends CsvRecord>(
176176 } ;
177177}
178178
179- function readColumnValue < TRecord extends CsvRecord > (
179+ function readColumnValue < TRecord extends object > (
180180 record : TRecord ,
181181 column : ResolvedCsvColumn < TRecord > ,
182182 index : number
@@ -188,9 +188,9 @@ function readColumnValue<TRecord extends CsvRecord>(
188188 return getPath ( record , column . path ) ;
189189}
190190
191- function getPath ( record : CsvRecord , path : string ) : unknown {
191+ function getPath ( record : object , path : string ) : unknown {
192192 if ( Object . hasOwn ( record , path ) ) {
193- return record [ path ] ;
193+ return ( record as Record < string , unknown > ) [ path ] ;
194194 }
195195
196196 const segments = path . split ( '.' ) ;
@@ -201,13 +201,13 @@ function getPath(record: CsvRecord, path: string): unknown {
201201 return undefined ;
202202 }
203203
204- current = ( current as CsvRecord ) [ segment ] ;
204+ current = ( current as Record < string , unknown > ) [ segment ] ;
205205 }
206206
207207 return current ;
208208}
209209
210- function formatRow < TRecord extends CsvRecord > (
210+ function formatRow < TRecord extends object > (
211211 values : readonly string [ ] ,
212212 options : ResolvedJsonToCsvOptions < TRecord >
213213) : string {
@@ -222,7 +222,7 @@ function formatRow<TRecord extends CsvRecord>(
222222 . join ( options . delimiter ) ;
223223}
224224
225- function formatValue < TRecord extends CsvRecord > (
225+ function formatValue < TRecord extends object > (
226226 value : unknown ,
227227 options : ResolvedJsonToCsvOptions < TRecord >
228228) : string {
@@ -254,21 +254,40 @@ function formatValue<TRecord extends CsvRecord>(
254254}
255255
256256function safeJsonStringify ( value : unknown ) : string {
257- const seen = new WeakSet < object > ( ) ;
257+ return JSON . stringify ( toJsonSafe ( value , new WeakSet < object > ( ) ) ) ?? '' ;
258+ }
258259
259- return JSON . stringify ( value , ( _key , child ) => {
260- if ( typeof child === 'bigint' ) {
261- return String ( child ) ;
262- }
260+ function toJsonSafe ( value : unknown , seen : WeakSet < object > ) : unknown {
261+ if ( typeof value === 'bigint' ) {
262+ return String ( value ) ;
263+ }
263264
264- if ( child && typeof child === 'object' ) {
265- if ( seen . has ( child ) ) {
266- return '[Circular]' ;
267- }
265+ if ( ! value || typeof value !== 'object' ) {
266+ return value ;
267+ }
268268
269- seen . add ( child ) ;
270- }
269+ if ( value instanceof Date ) {
270+ return value . toISOString ( ) ;
271+ }
272+
273+ if ( seen . has ( value ) ) {
274+ return '[Circular]' ;
275+ }
276+
277+ seen . add ( value ) ;
278+
279+ if ( Array . isArray ( value ) ) {
280+ const output = value . map ( ( item ) => toJsonSafe ( item , seen ) ) ;
281+ seen . delete ( value ) ;
282+ return output ;
283+ }
284+
285+ const output : Record < string , unknown > = { } ;
286+
287+ for ( const [ key , child ] of Object . entries ( value ) ) {
288+ output [ key ] = toJsonSafe ( child , seen ) ;
289+ }
271290
272- return child ;
273- } ) ?? '' ;
291+ seen . delete ( value ) ;
292+ return output ;
274293}
0 commit comments