Tom Butcher ee14c420df
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good
Added CSV support.
2026-03-03 01:20:38 +00:00

55 lines
1.7 KiB
JavaScript

/**
* Convert a value to a CSV cell-friendly format.
* Primitives pass through; objects/arrays are stringified; dates are formatted.
*/
function toCsvValue(val) {
if (val === null || val === undefined) return '';
if (val instanceof Date) return val.toISOString();
if (typeof val === 'number' || typeof val === 'boolean') return String(val);
if (typeof val === 'string') return val;
if (typeof val === 'object') {
if (Array.isArray(val)) return val.map(toCsvValue).join(', ');
return JSON.stringify(val);
}
return String(val);
}
/**
* Escape a CSV field per RFC 4180: wrap in double quotes, escape internal quotes by doubling.
*/
function escapeCsvField(str) {
if (str == null) return '""';
const s = String(str);
if (s.includes('"') || s.includes(',') || s.includes('\n') || s.includes('\r')) {
return '"' + s.replace(/"/g, '""') + '"';
}
return s;
}
/**
* Generate a CSV buffer from tabular data.
* @param {Array<Object>} data - Array of row objects (keys = column headers)
* @param {Object} options - Options
* @param {string[]} [options.columnOrder] - Optional column order (uses Object.keys of first row if not provided)
* @returns {Buffer} CSV file as buffer
*/
export function generateCsvTable(data, options = {}) {
const { columnOrder } = options;
if (!data || data.length === 0) {
return Buffer.from('', 'utf8');
}
const keys = columnOrder || Object.keys(data[0]).filter((k) => !k.startsWith('@'));
const headerRow = keys.map((k) => escapeCsvField(k)).join(',');
const lines = [headerRow];
for (const row of data) {
const values = keys.map((key) => escapeCsvField(toCsvValue(row[key])));
lines.push(values.join(','));
}
const csv = lines.join('\n');
return Buffer.from(csv, 'utf8');
}