mirror of
https://github.com/ae-utbm/sith.git
synced 2026-07-17 03:58:35 +00:00
53 lines
1.4 KiB
TypeScript
53 lines
1.4 KiB
TypeScript
import type { NestedKeyOf } from "#core:types/nested-key";
|
|
|
|
interface StringifyOptions<T extends object> {
|
|
/** The columns to include in the resulting CSV. */
|
|
columns: readonly NestedKeyOf<T>[];
|
|
/** Content of the first row */
|
|
titleRow?: readonly string[];
|
|
}
|
|
|
|
function getNested<T extends { [key: string]: unknown }>(obj: T, key: NestedKeyOf<T>) {
|
|
const path = key.split(".");
|
|
let res = obj[path.shift() as string] as { [key: string]: unknown } | undefined;
|
|
for (const node of path) {
|
|
if (res === undefined) {
|
|
break;
|
|
}
|
|
res = res[node] as { [key: string]: unknown } | undefined;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Convert the content the string to make sure it won't break
|
|
* the resulting csv.
|
|
* cf. https://en.wikipedia.org/wiki/Comma-separated_values#Basic_rules
|
|
*/
|
|
function sanitizeCell(content: string): string {
|
|
return `"${content.replace(/"/g, '""')}"`;
|
|
}
|
|
|
|
export const csv = {
|
|
stringify: <T extends { [key: string]: unknown }>(
|
|
objs: T[],
|
|
options?: StringifyOptions<T>,
|
|
) => {
|
|
const columns = options?.columns;
|
|
const content = objs
|
|
.map((obj) => {
|
|
return (columns ?? [])
|
|
.map((col) => {
|
|
return sanitizeCell((getNested(obj, col) ?? "").toString());
|
|
})
|
|
.join(",");
|
|
})
|
|
.join("\n");
|
|
if (!options?.titleRow) {
|
|
return content;
|
|
}
|
|
const firstRow = options.titleRow.map(sanitizeCell).join(",");
|
|
return `${firstRow}\n${content}`;
|
|
},
|
|
};
|