csv.ts
Language TypeScript1.7 KB62 lines
/*** RFC 4180 CSV, because the tracker's cells contain the things a naive split* breaks on: commas inside task titles, newlines inside notes, and quotes* inside both.** A row shaped like the ones in the sheet:** "Refresh the vinyl banner gallery images* Recrop the hero image (subject too close to the edge...)"** That is one cell containing a newline. Splitting on '\n' first turns it into* two rows, the second of which has no owner, so it lands in "unowned work"* and someone goes looking for who dropped it. Parse properly or not at all.*/export function parseCsv(text: string): string[][] {const rows: string[][] = []let row: string[] = []let cell = ''let quoted = false// Strip a BOM: Google does not send one today, but a hand-saved re-export// will, and it would silently corrupt the first header name.const s = text.charCodeAt(0) === 0xfeff ? text.slice(1) : textfor (let i = 0; i < s.length; i++) {const c = s[i]!if (quoted) {if (c === '"') {if (s[i + 1] === '"') {cell += '"'i++} else {quoted = false}} else {cell += c}continue}if (c === '"') {quoted = true} else if (c === ',') {row.push(cell)cell = ''} else if (c === '\n' || c === '\r') {// Swallow CRLF as one break rather than emitting a phantom empty row.if (c === '\r' && s[i + 1] === '\n') i++row.push(cell)rows.push(row)row = []cell = ''} else {cell += c}}// A file that does not end in a newline still has a last row.if (cell !== '' || row.length) {row.push(cell)rows.push(row)}return rows}