Zum Inhalt springen

Diese Seite ist noch nicht übersetzt und wird auf Englisch angezeigt.

elide:csv

The elide:csv module: parse, stream and write CSV in Elide, with typed columns, header-keyed rows, errors and TypeScript types.

Offline lesenelide help --lang de api-csv

Auf dieser Seite

elide:csv parses, streams and writes CSV. Rows are keyed by header name, and declared columns are converted to numbers, bigints and booleans while parsing. There is nothing to install.

js
import { parse, stringify } from "elide:csv";

const users = parse("id,name,joined\n1,ada,2024\n2,lin,2025\n", {
  columns: { id: "integer", name: "string" },
});
users.length; // 2
users[0].id; // 1
users[1].name; // "lin"
users[0].joined; // undefined: unlisted columns are skipped
stringify(users.toArray()); // "id,name\r\n1,ada\r\n2,lin\r\n"
utf-8 · js · Lines: 10

Import it as elide:csv. A bare "csv" specifier resolves to the npm package of that name.

Read a file

text
# users.csv
id,name,joined
1,ada,2024
2,lin,2025
utf-8 · text · Lines: 4
js
// users.mjs
import { parseFile } from "elide:csv";

const users = parseFile("users.csv", { columns: { id: "integer", name: "string" } });
try {
  for (const user of users) console.log(user.id, user.name);
} finally {
  users.close();
}
utf-8 · js · Lines: 9

elide run users.mjs prints:

text
1 ada
2 lin
utf-8 · text · Lines: 2

Parsing

  • parse(input, options?) reads a string, Uint8Array, ArrayBuffer or DataView (UTF-8) and returns a table.
  • parseFile(path, options?) reads a file path or file: URL. Call table.close() when done: until then the table holds the file, and only the code that opened it can read it.
  • open(source, options?) streams a path, file: URL, bytes, or a ReadableStream of bytes. See Streaming.

A table is array-like: table.length, table[i], table.at(i) (negative counts from the end) and for...of work as they do on arrays. table.columns lists the column names, table.toArray() copies every row into a plain object, and table.close() releases it; reading a closed table throws. Each index read returns a new row object, so table[0] !== table[0].

A row is an object keyed by column name: Object.keys(row) and JSON.stringify(row) see the names only. Without a header (header: false), columns are named "0", "1", and so on, so row[0] works.

Typed columns

Without columns, every cell is a string. With columns, the listed columns are converted and the rest of the header is skipped; a listed column missing from the header throws.

TypeAcceptsReads as
"string"any textstring
"number"decimal or exponent notationnumber
"integer"digits within ±(253−1)number
"bigint"digits within 64-bit rangebigint
"boolean"true or false, any caseboolean

Give a column as a type name, or as { type, nullable, optional }. A nullable column reads an empty unquoted field (or a nullValues token) as null. An optional column may be absent from the header or from a short row; its key is then missing from the row.

js
parse("id,email\n1,\n", {
  columns: { id: "bigint", email: { type: "string", nullable: true } },
})[0]; // { id: 1n, email: null }
utf-8 · js · Lines: 3

Options

OptionDefaultMeaning
delimiter","Field separator
quote'"'Quote character; null reads quotes as text
escapenoneEscape character inside fields
commentnoneSkip lines starting with this character
headertrueTake names from the first record, number the columns (false), or use these names (string[])
skipHeaderfalseWith header names, skip the first record
trimfalseStrip spaces and tabs around fields
skipEmptyLinestrueSkip empty lines
flexiblefalseAccept rows shorter than the header; their missing cells are absent
nullValuesnoneValues that read as null
maxRowsallStop after this many rows
duplicateHeaders"error"Repeated header names: "error", "allowEmpty" (blank names only) or "allow"
format"rfc4180""excel", "tdf", "mysql" or "postgresql"
limitssee belowResource bounds
strictsee belowRFC 4180 strictness

Reads are strict by default: a quote inside an unquoted field, text after a closing quote, an unterminated quote, or a row wider than the header throws. escape, comment, a multi-character delimiter, skipEmptyLines: false and every format other than "rfc4180" read in the more lenient style of those formats instead; combining one with strict: true throws a TypeError.

limits bounds maxBytes (input size, 64 MiB), maxRecordBytes (one record, 1 MiB), maxColumns (4096) and maxRows (rows per table, about a million).

Streaming

open() returns a reader. Iterate rows with for await, or whole tables with batches():

js
import { open } from "elide:csv";

let total = 0n;
for await (const row of open("orders.csv", { columns: { amount: "bigint" } })) {
  total += row.amount;
}

for await (const table of open("orders.csv", { batchBytes: 1 << 20 }).batches()) {
  console.log(table.length, table.columns);
}
utf-8 · js · Lines: 10

When it runs out of rows, the reader reads the next batch, about batchBytes (default 1 MiB) of input, in the background. reader.columns is null until the header has been read. Breaking out of the loop, await reader.close() and await using reader = open(...) all stop reading and release the source. Bytes passed to open() are copied first.

Writing

stringify(rows, options?) writes objects by key, arrays by position, or a parsed table:

js
import { stringify } from "elide:csv";

stringify([{ id: 1, note: "a, b" }, { id: 2, note: null }]);
// 'id,note\r\n1,"a, b"\r\n2,\r\n'

stringify([[1, 2n, true]], { recordSeparator: "\n" }); // '1,2,true\n'
utf-8 · js · Lines: 6
OptionDefaultMeaning
headeron for objects and tablesWrite a header line, or these names
columnsfirst object’s keysKeys (or table columns) to write, in order
delimiter, quote, escape",", '"', noneAs for parsing
quoteMode"minimal""all", "allNonNull", "nonNumeric" (numbers bare) or "none"
recordSeparator"\r\n"Line ending
nullValue""Written for null and undefined

Cells must be strings, numbers, bigints, booleans, null or undefined; anything else throws a TypeError.

Errors

Failures throw errors with a code:

CodeErrorCause
ERR_CSV_SYNTAXSyntaxErrorMalformed CSV, invalid UTF-8, or a bad header; the message names the record and byte
ERR_CSV_CONTRACTErrorA cell that does not fit its column, or a listed column missing from the header
ERR_CSV_LIMITErrorA limits bound was exceeded
ERR_CSV_CLOSEDErrorA closed table or reader was read
ERR_CSV_IOErrorThe stream failed while reading
ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUETypeErrorAn invalid argument or option
ERR_OUT_OF_RANGERangeErrorA numeric option out of range

File errors (ENOENT, EACCES, EISDIR) match node:fs. Messages never include cell contents.

TypeScript

@elide-dev/types declares the module. Rows are typed from columns, or checked against a row type you supply:

ts
import { parse } from "elide:csv";

interface User {
  id: number;
  email: string | null;
}

const users = parse<User>(text, {
  columns: { id: "integer", email: { type: "string", nullable: true } },
});
utf-8 · ts · Lines: 10

With elide:yaml

Rows are plain data, so elide:yaml writes them directly:

js
// to-yaml.mjs
import { parseFile } from "elide:csv";
import { stringify } from "elide:yaml";

const users = parseFile("users.csv", { columns: { id: "integer", name: "string" } });
try {
  process.stdout.write(stringify(users.toArray()));
} finally {
  users.close();
}
utf-8 · js · Lines: 10

elide run to-yaml.mjs prints:

yaml
- id: 1
  name: ada
- id: 2
  name: lin
utf-8 · yaml · Lines: 4

War diese Seite hilfreich?