npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

json2any

v2.0.2

Published

Fast JSON to CSV converter — Excel-safe, streaming, two-pass GB-scale support. ESM + CJS + TypeScript.

Readme

json2any

Convert JSON to clean, Excel-compatible CSV — with full streaming support for datasets of any size.

Currently supports: JSON → CSV Planned: JSON → Excel, XML, and more.


Why this exists

Most JSON → CSV tools either:

  • break on nested data
  • produce messy or inconsistent output
  • ignore Excel-specific issues (encoding, formulas, quoting)
  • run out of memory on large files

This fixes all of those problems with a simple API, solid defaults, and a two-pass streaming engine that keeps memory constant regardless of how large your data is.


Features

  • Nested object flattening (dot notation)
  • Safe array serialization
  • Excel-compatible output (BOM, CRLF, formula protection)
  • Correct CSV escaping (quotes, commas, newlines in values)
  • Stable column order (first-seen, insertion order)
  • Heterogeneous schemas — missing fields filled with empty string
  • Two-pass streaming — handles GB-scale files with O(h) memory
  • Works with NDJSON and JSON arrays
  • Full TypeScript support — ships a .d.ts definition file
  • Dual ESM + CommonJS — works in any Node.js project
  • Zero external dependencies

Installation

npm install json2any

Quick start

import { toCSV } from 'json2any';

const csv = toCSV([
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
]);

console.log(csv);
id,name
1,Alice
2,Bob

Usage

Nested objects

Nested objects are flattened using dot notation automatically.

const csv = toCSV([
  { user: { name: 'Alice', age: 30 }, active: true }
]);
user.name,user.age,active
Alice,30,true

Arrays

const csv = toCSV([
  { tags: ['js', 'ts', 'node'] }
]);
tags
js|ts|node
  • Primitive arrays → pipe-separated string
  • Arrays of objects → JSON string (not expanded into columns)

Heterogeneous schemas

Rows with different keys are handled correctly. Missing fields become empty strings.

const csv = toCSV([
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob', role: 'admin' }
]);
id,name,role
1,Alice,
2,Bob,admin

Excel safety

const csv = toCSV([
  { formula: '=SUM(A1:A10)', note: 'hello, world' }
]);
formula,note
'=SUM(A1:A10),"hello, world"
  • Formula prefixes (=, +, -, @) are escaped with a leading apostrophe
  • Fields containing commas, quotes, or newlines are RFC-4180 quoted
  • UTF-8 BOM prepended so Excel opens the file with correct encoding

Options

toCSV(data, {
  flatten:   true,   // flatten nested objects (default: true)
  separator: '.',    // key separator for nested fields (default: '.')
  delimiter: ',',    // CSV field delimiter (default: ',')
  bom:       true,   // prepend UTF-8 BOM for Excel (default: true)
  excelSafe: true,   // escape formula injection prefixes (default: true)
})

Streaming

Standard streaming — toCSVStream

Returns a readable CSV stream. Suitable for small to medium datasets.

import fs from 'fs';
import { Readable } from 'stream';
import { toCSVStream } from 'json2any';

const source = Readable.from([
  '{"id":1,"name":"Alice"}\n',
  '{"id":2,"name":"Bob"}\n'
]);

toCSVStream(source).pipe(fs.createWriteStream('output.csv'));

Two-pass streaming — for large files

Use streamJSONToCSV with twoPass: true for datasets that don't fit in memory.

Instead of buffering rows, it spills flattened rows to a temp file during Pass 1, then streams the formatted CSV output in Pass 2. The temp file is deleted automatically.

Memory stays O(h) regardless of row count — where h is the number of unique columns.

import fs from 'fs';
import { streamJSONToCSV } from 'json2any';

const source = fs.createReadStream('large-input.ndjson');
const dest   = fs.createWriteStream('output.csv');

await streamJSONToCSV(source, dest, { twoPass: true });

Both NDJSON and JSON arrays are supported as input.

// NDJSON — one object per line
{"id":1,"name":"Alice"}
{"id":2,"name":"Bob"}

// JSON array
[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]

Two-pass options

| Option | Default | Description | |---------|---------------|--------------------------------------------------| | twoPass | false | Enable two-pass streaming engine | | tempDir | os.tmpdir() | Directory for the temp spill file | | cleanup | true | Delete temp file after completion (or on error) |


API

toCSV(data, options?)

Synchronous, in-memory conversion. Returns a CSV string.

toCSV(data: object | object[], options?: CSVOptions): string

Use for small to medium datasets that comfortably fit in memory.


toCSVStream(source, options?)

Returns a readable CSV stream. Buffers all rows before emitting output.

toCSVStream(source: Readable, options?: CSVOptions): Readable

streamJSONToCSV(source, dest, options?)

Async pipeline — streams JSON input to a writable destination.

streamJSONToCSV(source: Readable, dest: Writable, options?: CSVOptions): Promise<void>
  • Without twoPass — buffered, same memory behaviour as toCSVStream
  • With twoPass: true — two-pass engine, constant memory, GB-scale safe

TypeScript

The package ships a .d.ts file. No @types/ package needed.

import { toCSV, streamJSONToCSV } from 'json2any';
import type { CSVOptions, FlatRecord } from 'json2any';

const opts: CSVOptions = {
  twoPass:   true,
  delimiter: ',',
  bom:       true,
};

await streamJSONToCSV(source, dest, opts);

ESM and CommonJS

Both module systems are supported with zero configuration.

// ESM
import { toCSV } from 'json2any';

// CommonJS
const { toCSV } = require('json2any');

Architecture

src/
  index.js              → public API
  core/
    flatten.js          → iterative JSON flattener (no recursion)
    formatter.js        → CSV formatting, Excel safety, RFC-4180 quoting
    parser.js           → NDJSON + JSON array stream parser
    csvWriter.js        → buffered streaming pipeline
    twoPassWriter.js    → two-pass streaming engine
dist/
  esm/                  → ESM build (tree-shakeable)
  cjs/                  → CommonJS build
  types/                → TypeScript definitions

Flattening

Uses an iterative stack — no recursion, no call-stack limits.

  • Objects → dot-notation keys (user.address.city)
  • Arrays → serialized immediately (pipe-separated string)
  • Complexity: O(k) where k = total leaf values across the object tree

Formatting

All formatting is pure and stateless.

  • RFC-4180 quoting (fields with commas, quotes, or newlines)
  • Double-quote escaping (""")
  • Excel formula protection (=, +, -, @)
  • UTF-8 BOM
  • CRLF line endings

Two-pass streaming engine

Pass 1 — Scan + Spill                     Memory: O(h)
──────────────────────────────────────────────────────
Input stream
  → NDJSONParser        (emit one JS object per record)
  → Pass1Spiller        (flatten → collect header keys → write line to disk)
  → temp file

Pass 2 — Emit CSV                         Memory: O(1) per row
──────────────────────────────────────────────────────
temp file (read stream)
  → Pass2CSVFormatter   (parse line → format CSV row → emit immediately)
  → output stream

The header Map is the only structure that grows with the dataset — one entry per unique column name, never per row.


Performance

  • ~25–30 MB/s on typical datasets
  • Linear with respect to output size
  • No recursion — iterative flattening throughout
  • Under 15 MB heap growth measured at 100k rows on the two-pass path

Behavior reference

| Input | Output | |---------------------|-------------------------------------------| | Nested object | Dot-notation keys (user.name) | | Array of primitives | Pipe-separated (a\|b\|c) | | Array of objects | JSON string per cell | | null / undefined| Empty string | | Date | ISO 8601 string | | Boolean | "true" or "false" | | Formula prefix | Apostrophe-prefixed ('=SUM(...)) | | Missing field | Empty string |


Limitations

  • Arrays of objects are not expanded into sub-columns
  • Header order follows first-seen insertion order
  • Two-pass mode writes a temp file roughly equal in size to the input

When to use this

  • You need predictable, Excel-ready CSV output
  • Your data is nested and other tools mangle it
  • You are working with large files and need constant-memory streaming
  • You want TypeScript types without extra packages
  • You want zero production dependencies

Roadmap

  • JSON → Excel (.xlsx)
  • CSV → JSON
  • Worker-thread parallel processing

License

MIT