Skip to content
Source
Sign inSign up
Open History to see the last commit

delimitedPreview.ts

73 lines2.3 KB

1import { GENERIC_MIME_TYPES } from "./image.ts";
2​
3export function filePreviewDelimiter(file: { name: string; mimeType?: string }): "," | "\t" | null {
4 const mime = file.mimeType?.split(";", 1)[0]?.trim().toLowerCase();
5 if (mime === "text/csv") return ",";
6 if (mime === "text/tab-separated-values") return "\t";
7 if (mime && mime !== "text/plain" && !GENERIC_MIME_TYPES.has(mime)) return null;
8 if (/\.csv$/i.test(file.name)) return ",";
9 if (/\.tsv$/i.test(file.name)) return "\t";
10 return null;
12​
13export function parseDelimitedPreview(
14 text: string,
15 delimiter: "," | "\t",
16 limits: { maxRows: number; maxColumns: number; maxCellLength: number } = {
17 maxRows: 100,
18 maxColumns: 30,
19 maxCellLength: 2000,
20 },
21) {
22 const rows: string[][] = [];
23 let row: string[] = [];
24 let cell = "";
25 let quoted = false;
26 let truncated = false;
27 let rowStart = text.charCodeAt(0) === 0xfeff ? 1 : 0;
28 const endCell = () => {
29 if (cell.length > limits.maxCellLength) {
30 let end = limits.maxCellLength;
31 const before = cell.charCodeAt(end - 1);
32 const after = cell.charCodeAt(end);
33 if (before >= 0xd800 && before <= 0xdbff && after >= 0xdc00 && after <= 0xdfff) end--;
34 cell = cell.slice(0, end);
35 truncated = true;
36 }
37 if (row.length < limits.maxColumns) row.push(cell);
38 else truncated = true;
39 cell = "";
40 };
41 for (let index = rowStart; index < text.length; index++) {
42 const char = text[index];
43 if (char === '"') {
44 if (quoted && text[index + 1] === '"') {
45 if (cell.length <= limits.maxCellLength) cell += '"';
46 else truncated = true;
47 index++;
48 continue;
49 }
50 if (quoted || cell === "") {
51 quoted = !quoted;
52 continue;
53 }
54 }
55 if (!quoted && (char === delimiter || char === "\n" || char === "\r")) {
56 endCell();
57 if (char !== delimiter) {
58 rows.push(row);
59 row = [];
60 if (char === "\r" && text[index + 1] === "\n") index++;
61 rowStart = index + 1;
62 if (rows.length === limits.maxRows)
63 return { rows, truncated: truncated || index < text.length - 1 };
64 }
65 } else if (cell.length <= limits.maxCellLength) cell += char;
66 else truncated = true;
67 }
68 if (rowStart < text.length) {
69 endCell();
70 rows.push(row);
71 }
72 return { rows, truncated: truncated || quoted };