@ende/csv
ende add jsr:@ende/csvVersion 2026.10.15 on JSR.
Reads and writes comma-separated values (CSV) files.
Parsing CSV
import { parse } from "@ende/csv/parse";
import { assertEquals } from "@ende/assert";
const string = "a,b,c\nd,e,f";
// Parse as array of arrays (default)
assertEquals(parse(string, { skipFirstRow: false }), [["a", "b", "c"], ["d", "e", "f"]]);
// Parse csv file with headers into array of objects
assertEquals(parse(string, { skipFirstRow: true }), [{ a: "d", b: "e", c: "f" }]);
// Parse with custom column names
assertEquals(parse(string, { columns: ["x", "y", "z"] }), [
{ x: "a", y: "b", z: "c" },
{ x: "d", y: "e", z: "f" }
]);
// Parse tab-separated values
const tsvString = "name\tage\tcity\njohn\t30\tnew york\nmary\t25\tlos angeles";
assertEquals(parse(tsvString, { separator: "\t", skipFirstRow: true }), [
{ name: "john", age: "30", city: "new york" },
{ name: "mary", age: "25", city: "los angeles" }
]);
// Parse a CSV file which has comments
const csvWithComments = "# This is a comment\nname,age,city\n# Another comment\njohn,30,new york\nmary,25,los angeles";
assertEquals(parse(csvWithComments, { comment: "#", skipFirstRow: true }), [
{ name: "john", age: "30", city: "new york" },
{ name: "mary", age: "25", city: "los angeles" }
]);
Parsing CSV from a Stream
import { CsvParseStream } from "@ende/csv/parse-stream";
import { assertEquals } from "@ende/assert";
// Parse from a stream (useful for large files)
const source = ReadableStream.from([
"name,age,city\n",
"john,30,new york\n",
"mary,25,los angeles\n"
]);
const csvStream = source
.pipeThrough(new CsvParseStream({ skipFirstRow: true }));
const records = await Array.fromAsync(csvStream);
assertEquals(records, [
{ name: "john", age: "30", city: "new york" },
{ name: "mary", age: "25", city: "los angeles" }
]);
// Or process records one by one
// for await (const record of csvStream) {
// console.log(record);
// }
Stringifying Data to CSV
import { stringify } from "@ende/csv/stringify";
import { assertEquals } from "@ende/assert";
// Convert array of arrays to CSV
const arrayData = [["name", "age", "city"], ["john", "30", "new york"], ["mary", "25", "los angeles"]];
const csvString = stringify(arrayData);
assertEquals(csvString, "name,age,city\r\njohn,30,new york\r\nmary,25,los angeles\r\n");
// Convert array of objects to CSV
const objectData = [
{ name: "john", age: "30", city: "new york" },
{ name: "mary", age: "25", city: "los angeles" }
];
// When using an array of objects, you must specify columns to use
const customColumns = stringify(objectData, { columns: ["city", "name", "age"] });
assertEquals(customColumns, "city,name,age\r\nnew york,john,30\r\nlos angeles,mary,25\r\n");
Streaming Stringify Data to CSV
import { CsvStringifyStream } from "@ende/csv/stringify-stream";
import { assertEquals } from "@ende/assert";
async function writeCsvToTempFile(): Promise<string> {
const path = await Ende.makeTempFile();
using file = await Ende.open(path, { write: true });
const readable = ReadableStream.from([
{ id: 1, name: "one" },
{ id: 2, name: "two" },
{ id: 3, name: "three" },
]);
await readable
.pipeThrough(new CsvStringifyStream({ columns: ["id", "name"] }))
.pipeThrough(new TextEncoderStream())
.pipeTo(file.writable);
return path;
}
const path = await writeCsvToTempFile();
const content = await Ende.readTextFile(path);
assertEquals(content, "id,name\r\n1,one\r\n2,two\r\n3,three\r\n");
CSV Format Information
There are many kinds of CSV files; this module supports the format described in RFC 4180.
A csv file contains zero or more records of one or more fields per record. Each record is separated by the newline character. The final record may optionally be followed by a newline character.
field1,field2,field3
White space is considered part of a field.
Carriage returns before newline characters are silently removed.
Blank lines are ignored. A line with only whitespace characters (excluding the ending newline character) is not considered a blank line.
Fields which start and stop with the quote character " are called quoted-fields. The beginning and ending quote are not part of the field.
The source:
normal string,"quoted-field"
results in the fields
[`normal string`, `quoted-field`]
Within a quoted-field a quote character followed by a second quote character is considered a single quote.
"the ""word"" is true","a ""quoted-field"""
results in
[`the "word" is true`, `a "quoted-field"`]
Newlines and commas may be included in a quoted-field
"Multi-line
field","comma is ,"
results in
[`Multi-line
field`, `comma is ,`]
Modules
Exports
Column— The most essential aspect of a column is accessing the property holding the data for that column on each object in the data array. If that member is at the top level, `Column` can simply be a property accessor, which is either a `string` (if it's a plain object) or a `number` (if it's an array).ColumnDetails— Column information.CsvParseStream— `CsvParseStream` transforms a stream of CSV-encoded text into a stream of parsed objects.CsvParseStreamOptions— Options for CsvParseStream.CsvStringifyStream— Convert each chunk to a CSV record.CsvStringifyStreamOptions— Options for CsvStringifyStream.DataItem— An object (plain or array)parse— Parses CSV string into an array of arrays of strings.ParseOptions— Options for parse.ParseResult— Parse result type for parse and CsvParseStream.PropertyAccessor— Array index or record key corresponding to a value for a data object.RecordWithColumn— Record type with column type.RowType— Row return type.stringify— Converts an array of objects into a CSV string.StringifyOptions— Options for stringify.