43 lines · 1.2 KB
export type Table = { rows: string[][]; truncated: boolean };

/** RFC 4180 style parser (quoted fields, doubled quotes, CRLF), stopping after `maxRows` rows. */
export function parseDelimited(text: string, delimiter: string, maxRows: number): Table {
  const rows: string[][] = [];
  let row: string[] = [];
  let field = "";
  let quoted = false;
  let index = 0;
  while (index < text.length) {
    const char = text[index];
    if (quoted) {
      if (char === '"' && text[index + 1] === '"') {
        field += '"';
        index += 2;
        continue;
      }
      if (char === '"') quoted = false;
      else field += char;
      index++;
      continue;
    }
    if (char === '"' && field === "") quoted = true;
    else if (char === delimiter) {
      row.push(field);
      field = "";
    } else if (char === "\n" || char === "\r") {
      row.push(field);
      rows.push(row);
      row = [];
      field = "";
      if (char === "\r" && text[index + 1] === "\n") index++;
      if (rows.length >= maxRows) return { rows, truncated: index + 1 < text.length };
    } else field += char;
    index++;
  }
  if (field !== "" || row.length > 0) {
    row.push(field);
    rows.push(row);
  }