feat(export): configurable CSV field quoting mode
This commit is contained in:
32 files changed
+442
-56
No files matched your search
@@ -0,0 +1,16 @@
|
||||
export type CsvQuoteMode = "all" | "necessary";
|
||||
|
||||
export const DEFAULT_CSV_QUOTE_MODE: CsvQuoteMode = "all";
|
||||
|
||||
export function normalizeCsvQuoteMode(value: unknown): CsvQuoteMode {
|
||||
return value === "necessary" ? "necessary" : DEFAULT_CSV_QUOTE_MODE;
|
||||
}
|
||||
|
||||
export function csvFieldNeedsQuotes(value: string): boolean {
|
||||
return /[",\r\n]/.test(value);
|
||||
}
|
||||
|
||||
export function escapeCsvField(value: string, quoteMode: CsvQuoteMode): string {
|
||||
if (quoteMode === "necessary" && !csvFieldNeedsQuotes(value)) return value;
|
||||
return `"${value.replace(/"/g, '""')}"`;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { DatabaseType, QueryResult } from "@/types/database";
|
||||
import * as api from "@/lib/backend/api";
|
||||
import { escapeCsvField, type CsvQuoteMode } from "@/lib/export/csvQuoteMode";
|
||||
|
||||
export type ExportCellValue = string | number | boolean | null;
|
||||
|
||||
export function formatCsv(columns: string[], rows: ExportCellValue[][]): string {
|
||||
const esc = (v: string) => `"${v.replace(/"/g, '""')}"`;
|
||||
const header = columns.map(esc).join(",");
|
||||
const body = rows.map((row) => row.map((c) => (c === null ? "" : esc(String(c)))).join(",")).join("\n");
|
||||
export function formatCsv(columns: string[], rows: ExportCellValue[][], quoteMode: CsvQuoteMode = "all"): string {
|
||||
const header = columns.map((column) => escapeCsvField(column, quoteMode)).join(",");
|
||||
const body = rows.map((row) => row.map((cell) => (cell === null ? "" : escapeCsvField(String(cell), quoteMode))).join(",")).join("\n");
|
||||
return `${header}\n${body}`;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user