From 3db62b00171de4e3cf3ac9c6a6c6a0fc0284d164 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Mon, 3 Jun 2024 12:32:09 +0900 Subject: [PATCH] docs(csv): improve API docs (#4920) Co-authored-by: Asher Gomez --- _tools/check_docs.ts | 1 + csv/_io.ts | 80 +++++++++++++++++++++++++++++++++++-- csv/csv_parse_stream.ts | 80 +++++++++++++++++++++++++++++++++---- csv/csv_stringify_stream.ts | 35 ++++++++++++++-- csv/parse.ts | 36 +++++++---------- csv/stringify.ts | 59 ++++++++++++++++----------- 6 files changed, 231 insertions(+), 60 deletions(-) diff --git a/_tools/check_docs.ts b/_tools/check_docs.ts index a216737d2..5c5d6e1be 100644 --- a/_tools/check_docs.ts +++ b/_tools/check_docs.ts @@ -33,6 +33,7 @@ const ENTRY_POINTS = [ "../cli/mod.ts", "../crypto/mod.ts", "../collections/mod.ts", + "../csv/mod.ts", "../data_structures/mod.ts", "../datetime/mod.ts", "../encoding/mod.ts", diff --git a/csv/_io.ts b/csv/_io.ts index f21e6399b..a158113a5 100644 --- a/csv/_io.ts +++ b/csv/_io.ts @@ -215,16 +215,88 @@ function runeCount(s: string): number { /** * A ParseError is returned for parsing errors. * Line numbers are 1-indexed and columns are 0-indexed. + * + * @example Usage + * ```ts + * import { parse, ParseError } from "@std/csv/parse"; + * + * try { + * parse(`a "word","b"`); + * } catch (error) { + * if (error instanceof ParseError) { + * console.error(error.message); + * } + * } + * ``` */ export class ParseError extends SyntaxError { - /** Line where the record starts*/ + /** + * Line where the record starts. + * + * @example Usage + * ```ts + * import { parse, ParseError } from "@std/csv/parse"; + * + * try { + * parse(`a "word","b"`); + * } catch (error) { + * if (error instanceof ParseError) { + * console.error(error.startLine); + * } + * } + * ``` + */ startLine: number; - /** Line where the error occurred */ + /** + * Line where the error occurred. + * + * @example Usage + * ```ts + * import { parse, ParseError } from "@std/csv/parse"; + * + * try { + * parse(`a "word","b"`); + * } catch (error) { + * if (error instanceof ParseError) { + * console.error(error.line); + * } + * } + * ``` + */ line: number; - /** Column (rune index) where the error occurred */ + /** + * Column (rune index) where the error occurred. + * + * @example Usage + * ```ts + * import { parse, ParseError } from "@std/csv/parse"; + * + * try { + * parse(`a "word","b"`); + * } catch (error) { + * if (error instanceof ParseError) { + * console.error(error.column); + * } + * } + * ``` + */ column: number | null; - /** Constructs a new instance. */ + /** + * Constructs a new instance. + * + * @example Usage + * ```ts no-eval + * import { ParseError } from "@std/csv/parse"; + * + * throw new ParseError(1, 2, 3, "error message"); + * ``` + * + * @param start Line where the record starts + * @param line Line where the error occurred + * @param column Column The index where the error occurred + * @param message Error message + */ constructor( start: number, line: number, diff --git a/csv/csv_parse_stream.ts b/csv/csv_parse_stream.ts index 94451717b..f52c30a5a 100644 --- a/csv/csv_parse_stream.ts +++ b/csv/csv_parse_stream.ts @@ -66,14 +66,20 @@ export type RowType = T extends undefined ? string[] * A `CsvParseStream` expects input conforming to * {@link https://www.rfc-editor.org/rfc/rfc4180.html | RFC 4180}. * - * @example + * @example Usage * ```ts * import { CsvParseStream } from "@std/csv/csv-parse-stream"; - * const res = await fetch("https://example.com/data.csv"); - * const parts = res.body! - * .pipeThrough(new TextDecoderStream()) - * .pipeThrough(new CsvParseStream()); + * + * const source = ReadableStream.from([ + * "name,age", + * "Alice,34", + * "Bob,24", + * "Charlie,45", + * ]); + * const parts = source.pipeThrough(new CsvParseStream()); * ``` + * + * @typeParam T The type of options for the stream. */ export class CsvParseStream< const T extends CsvParseStreamOptions | undefined = undefined, @@ -89,7 +95,23 @@ export class CsvParseStream< #headers: readonly string[] = []; - /** Construct a new instance. */ + /** Construct a new instance. + * + * @example Usage + * ```ts + * import { CsvParseStream } from "@std/csv/csv-parse-stream"; + * + * const source = ReadableStream.from([ + * "name,age", + * "Alice,34", + * "Bob,24", + * "Charlie,45", + * ]); + * const parts = source.pipeThrough(new CsvParseStream()); + * ``` + * + * @param options Options for the stream. + */ constructor(options?: T) { this.#options = { ...defaultReadOptions, @@ -170,12 +192,54 @@ export class CsvParseStream< } } - /** The instance's {@linkcode ReadableStream}. */ + /** + * The instance's {@linkcode ReadableStream}. + * + * @example Usage + * ```ts + * import { CsvParseStream } from "@std/csv/csv-parse-stream"; + * + * const source = ReadableStream.from([ + * "name,age", + * "Alice,34", + * "Bob,24", + * "Charlie,45", + * ]); + * const parseStream = new CsvParseStream(); + * const parts = source.pipeTo(parseStream.writable); + * for await (const part of parseStream.readable) { + * console.log(part); + * } + * ``` + * + * @returns The instance's {@linkcode ReadableStream}. + */ get readable(): ReadableStream> { return this.#readable as ReadableStream>; } - /** The instance's {@linkcode WritableStream}. */ + /** + * The instance's {@linkcode WritableStream}. + * + * @example Usage + * ```ts + * import { CsvParseStream } from "@std/csv/csv-parse-stream"; + * + * const source = ReadableStream.from([ + * "name,age", + * "Alice,34", + * "Bob,24", + * "Charlie,45", + * ]); + * const parseStream = new CsvParseStream(); + * const parts = source.pipeTo(parseStream.writable); + * for await (const part of parseStream.readable) { + * console.log(part); + * } + * ``` + * + * @returns The instance's {@linkcode WritableStream}. + */ get writable(): WritableStream { return this.#lines.writable; } diff --git a/csv/csv_stringify_stream.ts b/csv/csv_stringify_stream.ts index 4d3e02531..7dc476976 100644 --- a/csv/csv_stringify_stream.ts +++ b/csv/csv_stringify_stream.ts @@ -22,11 +22,13 @@ export interface CsvStringifyStreamOptions { /** * Convert each chunk to a CSV record. * - * @example + * @example Usage * ```ts * import { CsvStringifyStream } from "@std/csv/csv-stringify-stream"; * - * const file = await Deno.open("data.csv", { create: true, write: true }); + * const path = await Deno.makeTempFile(); + * + * const file = await Deno.open(path, { create: true, write: true }); * const readable = ReadableStream.from([ * { id: 1, name: "one" }, * { id: 2, name: "two" }, @@ -37,7 +39,9 @@ export interface CsvStringifyStreamOptions { * .pipeThrough(new CsvStringifyStream({ columns: ["id", "name"] })) * .pipeThrough(new TextEncoderStream()) * .pipeTo(file.writable); - * ```` + * ``` + * + * @typeParam TOptions The type of options for the stream. */ export class CsvStringifyStream extends TransformStream< @@ -45,7 +49,30 @@ export class CsvStringifyStream : Array, string > { - /** Construct a new instance. */ + /** + * Construct a new instance. + * + * @example Usage + * ```ts + * import { CsvStringifyStream } from "@std/csv/csv-stringify-stream"; + * + * const path = await Deno.makeTempFile(); + * + * const file = await Deno.open(path, { create: true, 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); + * ``` + * + * @param options Options for the stream. + */ constructor(options?: TOptions) { const { separator, diff --git a/csv/parse.ts b/csv/parse.ts index 5c1d4a004..9d753729c 100644 --- a/csv/parse.ts +++ b/csv/parse.ts @@ -304,45 +304,39 @@ export interface ParseOptions extends ReadOptions { * Csv parse helper to manipulate data. * Provides an auto/custom mapper for columns. * - * @example + * @example Usage * ```ts * import { parse } from "@std/csv/parse"; + * import { assertEquals } from "@std/assert/assert-equals"; + * * const string = "a,b,c\nd,e,f"; * - * console.log( - * await parse(string, { - * skipFirstRow: false, - * }), - * ); - * // output: - * // [["a", "b", "c"], ["d", "e", "f"]] + * assertEquals(parse(string), [["a", "b", "c"], ["d", "e", "f"]]); * ``` * - * @param input Input to parse. - * @returns If you don't provide `opt.skipFirstRow` and `opt.columns`, it returns `string[][]`. - * If you provide `opt.skipFirstRow` or `opt.columns`, it returns `Record[]`. + * @param input The input to parse. + * @returns The parsed data. */ export function parse(input: string): string[][]; /** * Csv parse helper to manipulate data. * Provides an auto/custom mapper for columns. * - * @example + * @example Usage * ```ts * import { parse } from "@std/csv/parse"; + * import { assertEquals } from "@std/assert/assert-equals"; + * * const string = "a,b,c\nd,e,f"; * - * console.log( - * await parse(string, { - * skipFirstRow: false, - * }), - * ); - * // output: - * // [["a", "b", "c"], ["d", "e", "f"]] + * assertEquals(parse(string, { skipFirstRow: false }), [["a", "b", "c"], ["d", "e", "f"]]); + * assertEquals(parse(string, { skipFirstRow: true }), [{ a: "d", b: "e", c: "f" }]); + * assertEquals(parse(string, { columns: ["x", "y", "z"] }), [{ x: "a", y: "b", z: "c" }, { x: "d", y: "e", z: "f" }]); * ``` * - * @param input Input to parse. - * @param opt options of the parser. + * @typeParam T The options' type for parsing. + * @param input The input to parse. + * @param opt The options for parsing. * @returns If you don't provide `opt.skipFirstRow` and `opt.columns`, it returns `string[][]`. * If you provide `opt.skipFirstRow` or `opt.columns`, it returns `Record[]`. */ diff --git a/csv/stringify.ts b/csv/stringify.ts index 92375e7e3..fc327e069 100644 --- a/csv/stringify.ts +++ b/csv/stringify.ts @@ -168,9 +168,35 @@ function normalizeColumn(column: Column): NormalizedColumn { return { header, prop }; } -/** Error thrown in {@linkcode stringify}. */ +/** + * Error thrown in {@linkcode stringify}. + * + * @example Usage + * ```ts + * import { stringify, StringifyError } from "@std/csv/stringify"; + * + * try { + * stringify([{ a: 1 }, { a: 2 }], { separator: "\r\n" }); + * } catch (error) { + * if (error instanceof StringifyError) { + * console.error(error.message); + * } + * } + * ``` + */ export class StringifyError extends Error { - /** Construct a new instance. */ + /** + * Construct a new instance. + * + * @example Usage + * ```ts no-eval + * import { StringifyError } from "@std/csv/stringify"; + * + * throw new StringifyError("An error occurred"); + * ``` + * + * @param message The error message. + */ constructor(message?: string) { super(message); this.name = "StringifyError"; @@ -222,29 +248,15 @@ function getValuesFromItem( } /** - * Write data using CSV encoding. + * Converts an array of objects into a CSV string. * - * @param data The source data to stringify. It's an array of items which are - * plain objects or arrays. - * - * `DataItem: Record | unknown[]` - * - * ```ts - * const data = [ - * { - * name: "Deno", - * repo: { org: "denoland", name: "deno" }, - * runsOn: ["Rust", "TypeScript"], - * }, - * ]; - * ``` - * - * @example + * @example Usage * ```ts * import { * Column, * stringify, * } from "@std/csv/stringify"; + * import { assertEquals } from "@std/assert/assert-equals"; * * type Character = { * age: number; @@ -276,11 +288,12 @@ function getValuesFromItem( * "age", * ]; * - * console.log(stringify(data, { columns })); - * // first,age - * // Rick,70 - * // Morty,14 + * assertEquals(stringify(data, { columns }), `first,age\r\nRick,70\r\nMorty,14\r\n`); * ``` + * + * @param data The source data to stringify. It's an array of items which are + * plain objects or arrays. + * @returns A CSV string. */ export function stringify( data: DataItem[],