diff --git a/core/static/bundled/utils/csv.ts b/core/static/bundled/utils/csv.ts new file mode 100644 index 00000000..b4420345 --- /dev/null +++ b/core/static/bundled/utils/csv.ts @@ -0,0 +1,43 @@ +import type { NestedKeyOf } from "#core:utils/types"; + +interface StringifyOptions { + /** The columns to include in the resulting CSV. */ + columns: readonly NestedKeyOf[]; + /** Content of the first row */ + titleRow?: readonly string[]; +} + +function getNested(obj: T, key: NestedKeyOf) { + const path: (keyof object)[] = key.split(".") as (keyof unknown)[]; + let res = obj[path.shift() as keyof T]; + for (const node of path) { + if (res === null) { + break; + } + res = res[node]; + } + return res; +} + +export const csv = { + stringify: (objs: T[], options?: StringifyOptions) => { + const columns = options.columns; + const content = objs + .map((obj) => { + return columns + .map((col) => { + return (getNested(obj, col) ?? "") + .toString() + .replace(/,/g, ",") + .replace(/\n/g, " "); + }) + .join(","); + }) + .join("\n"); + if (!options.titleRow) { + return content; + } + const firstRow = options.titleRow.map((s) => s.replace(/,/g, ",")).join(","); + return `${firstRow}\n${content}`; + }, +};