@portable/csv
Functions
Csv.decode
Decodes CSV bytes or text into rows. A row is a string[], or an object keyed by column name
when header is set. Values stay strings.
function decode<
const header extends DecoderStream.Header = false,
relax extends boolean = false,
>(
input: string | Uint8Array,
options: decode.Options<header, relax> = {},
): DecoderStream.Row<header, relax>[];| Option | Type | Default | Description |
|---|---|---|---|
delimiter | string | ',' | Field separator, one character. Use '\t' for TSV. |
encoding | string | 'utf-8' | Text encoding of byte input, as a TextDecoder label. |
header | header | false | Emit each row as an object keyed by column name. |
maxLength | number | Infinity | Longest allowed row, in UTF-16 code units of input text, not counting the line break. |
quote | string | false | '"' | Quote character, one character, or false to read quotes as ordinary text. |
relaxColumnCount | relax | false | Accept rows whose field count differs from the first row's or the header's. |
skipEmpty | boolean | true | Drop lines with no characters instead of reading each as a row with one empty field. |
Throws
InvalidInputErrorWhen text follows a closing quote, a row's field count differs from the first row's (unlessrelaxColumnCountis set), or the header repeats a name.TruncatedErrorWhen the input ends inside a quoted field.MaxLengthErrorWhen a row is longer thanmaxLength.
import * as Csv from '@portable/csv';
Csv.decode('name,age\r\n"Smith, J",42\r\n'); // [['name', 'age'], ['Smith, J', '42']]
Csv.decode('name,age\nAda,36\n', { header: true }); // [{ name: 'Ada', age: '36' }]
Csv.decode('a\tb\n', { delimiter: '\t' }); // [['a', 'b']]Csv.encode
Encodes rows as CSV text. A field is quoted only when it holds the delimiter, the quote or a line
break, and quotes inside it are doubled. Every row, including the last, ends with newline.
function encode(
rows: readonly encode.Row[],
options: encode.Options = {},
): string;| Option | Type | Default | Description |
|---|---|---|---|
bom | boolean | false | Start the output with a byte order mark, which Excel needs to read UTF-8. |
delimiter | string | ',' | Field separator, one character. Use '\t' for TSV. |
escapeFormulas | boolean | false | Prefix fields that start with =, +, -, @, tab or CR with ' and quote them, so spreadsheets don't run them as formulas. |
header | boolean | readonly string[] | false | Write a header row. true takes the names from the first object row's keys. An array gives the names and the order in which object rows' values are written. Without a header row, object rows are written in the first row's key order. |
newline | "\r\n" | "\n" | '\r\n' | Line break written after every row. |
quote | string | false | '"' | Quote character, or false to never quote, which throws on a field that needs quoting. |
Throws
InvalidInputErrorWhenquoteisfalseand a field needs quoting, a value isn't anencode.Valueor is an invalidDate, or an object row follows array rows andheaderisn't an array.TypeErrorWhenheaderistruewith array rows.RangeErrorWhendelimiter,quoteornewlineisn't allowed.
import * as Csv from '@portable/csv';
Csv.encode([
['name', 'note'],
['Ada', 'said "hi", left'],
]); // 'name,note\r\nAda,"said ""hi"", left"\r\n'
Csv.encode([{ name: 'Ada', age: 36 }], { header: true }); // 'name,age\r\nAda,36\r\n'Classes
Csv.DecoderStream
A TransformStream that decodes CSV bytes or text into rows. A row is a string[], or an
object keyed by column name when header is set. Quoted fields may hold delimiters, doubled
quotes and line breaks. Rows end in CRLF, LF or CR, and a leading byte order mark is dropped.
The output is the same however the input is split into chunks.
class DecoderStream extends TransformStream<
Uint8Array | string,
DecoderStream.Row<header, relax>
> {
constructor(options: DecoderStream.Options<header, relax> = {});
}| Option | Type | Default | Description |
|---|---|---|---|
delimiter | string | ',' | Field separator, one character. Use '\t' for TSV. |
encoding | string | 'utf-8' | Text encoding of byte input, as a TextDecoder label. |
header | header | false | Emit each row as an object keyed by column name. |
maxLength | number | Infinity | Longest allowed row, in UTF-16 code units of input text, not counting the line break. |
quote | string | false | '"' | Quote character, one character, or false to read quotes as ordinary text. |
relaxColumnCount | relax | false | Accept rows whose field count differs from the first row's or the header's. |
skipEmpty | boolean | true | Drop lines with no characters instead of reading each as a row with one empty field. |
signal | AbortSignal | Errors the stream with signal.reason when the signal aborts, which makes pipeThrough cancel the source. |
import * as Csv from '@portable/csv';
import * as Streams from '@portable/streams';
const response = await fetch('https://example.com/users.csv');
const rows = response.body!.pipeThrough(
new Csv.DecoderStream({ header: true }),
);
for await (const row of Streams.toAsyncIterable(rows)) {
console.log(row.email);
}Csv.EncoderStream
A TransformStream that encodes each row as one line of CSV text, as Csv.encode does, and
emits it as UTF-8 bytes.
class EncoderStream extends TransformStream<
encode.Row,
Uint8Array<ArrayBuffer>
> {
constructor(options: EncoderStream.Options = {});
}| Option | Type | Default | Description |
|---|---|---|---|
bom | boolean | false | Start the output with a byte order mark, which Excel needs to read UTF-8. |
delimiter | string | ',' | Field separator, one character. Use '\t' for TSV. |
escapeFormulas | boolean | false | Prefix fields that start with =, +, -, @, tab or CR with ' and quote them, so spreadsheets don't run them as formulas. |
header | boolean | readonly string[] | false | Write a header row. true takes the names from the first object row's keys. An array gives the names and the order in which object rows' values are written. Without a header row, object rows are written in the first row's key order. |
newline | "\r\n" | "\n" | '\r\n' | Line break written after every row. |
quote | string | false | '"' | Quote character, or false to never quote, which throws on a field that needs quoting. |
signal | AbortSignal | Errors the stream with signal.reason when the signal aborts, which makes pipeThrough cancel the source. |
import * as Csv from '@portable/csv';
import * as Streams from '@portable/streams';
const body = Streams.fromIterable(users).pipeThrough(
new Csv.EncoderStream({ header: ['id', 'email'] }),
);
return new Response(body, { headers: { 'content-type': 'text/csv' } });Errors
Csv.InvalidInputError
Thrown for malformed CSV: text between a closing quote and the next delimiter or line break, a row whose field count differs from the first row's, or a repeated header name.
class InvalidInputError extends Errors.InvalidInputError {
readonly kind: string;
}import * as Csv from '@portable/csv';
try {
Csv.decode('a,b\n1,2,3\n');
} catch (error) {
if (error instanceof Csv.InvalidInputError) {
console.log(error.message);
} // 'Row 2 has 3 fields, but row 1 has 2.'
}Csv.MaxLengthError
Thrown when a row is longer than maxLength characters.
class MaxLengthError extends Errors.MaxLengthError {
constructor(options: Errors.MaxLengthError.Options);
readonly kind: string;
}import * as Csv from '@portable/csv';
try {
Csv.decode('a,b\nfar too long,x\n', { maxLength: 8 });
} catch (error) {
if (error instanceof Csv.MaxLengthError) {
console.log(error.maxLength);
} // 8
}Csv.TruncatedError
Thrown when the input ends inside a quoted field.
class TruncatedError extends Errors.TruncatedError {
readonly kind: string;
}import * as Csv from '@portable/csv';
try {
Csv.decode('a,"b\n');
} catch (error) {
if (error instanceof Csv.TruncatedError) {
console.log('unclosed quote');
}
}