UNPKG

json-data-to-csv

Version:

A TypeScript library to convert array of objects to CSV format with customizable delimiter

115 lines (114 loc) 4.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JsonToCsvError = void 0; exports.parse = parse; /** * Custom error class for JSON to CSV conversion errors */ class JsonToCsvError extends Error { constructor(message, code) { super(message); this.code = code; this.name = 'JsonToCsvError'; } } exports.JsonToCsvError = JsonToCsvError; /** * Escapes CSV field values that contain special characters * @param value - The value to escape * @param delimiter - The delimiter being used * @returns Escaped value */ function escapeCSVField(value, delimiter) { // If the value contains delimiter, newline, or double quote, wrap in quotes if (value.includes(delimiter) || value.includes('\n') || value.includes('\r') || value.includes('"')) { // Escape any existing double quotes by doubling them const escapedValue = value.replace(/"/g, '""'); return `"${escapedValue}"`; } return value; } /** * Converts a value to a string representation suitable for CSV * @param value - The value to convert * @returns String representation of the value */ function valueToString(value) { if (value === null || value === undefined) { return ''; } if (typeof value === 'object') { try { return JSON.stringify(value); } catch (error) { throw new JsonToCsvError('Failed to serialize object value', 'SERIALIZATION_ERROR'); } } return String(value); } /** * Parses an array of objects into CSV format * @param data - Array of objects to convert to CSV * @param options - Options for parsing (delimiter) * @returns CSV string with header row and data rows */ function parse(data, options = {}) { var _a; // Validate input if (!Array.isArray(data)) { throw new JsonToCsvError('Input data must be an array', 'INVALID_INPUT'); } if (data.length === 0) { throw new JsonToCsvError('Input data cannot be empty', 'EMPTY_INPUT'); } // Validate delimiter before applying default if (options.delimiter !== undefined && (typeof options.delimiter !== 'string' || options.delimiter.length === 0)) { throw new JsonToCsvError('Delimiter must be a non-empty string', 'INVALID_DELIMITER'); } const delimiter = (_a = options.delimiter) !== null && _a !== void 0 ? _a : ','; try { // Extract all unique keys from all objects to create comprehensive headers const allKeys = new Set(); for (const row of data) { if (typeof row !== 'object' || row === null || Array.isArray(row)) { throw new JsonToCsvError('All data items must be objects', 'INVALID_DATA_TYPE'); } Object.keys(row).forEach((key) => allKeys.add(key)); } if (allKeys.size === 0) { throw new JsonToCsvError('No valid properties found in data objects', 'NO_PROPERTIES'); } const headers = Array.from(allKeys); // Create CSV header row const headerRow = headers .map((header) => escapeCSVField(header, delimiter)) .join(delimiter); // Create CSV data rows const dataRows = data.map((row, index) => { try { return headers .map((header) => { const value = valueToString(row[header]); return escapeCSVField(value, delimiter); }) .join(delimiter); } catch (error) { throw new JsonToCsvError(`Error processing row ${index + 1}: ${error instanceof Error ? error.message : 'Unknown error'}`, 'ROW_PROCESSING_ERROR'); } }); // Combine header and data rows return [headerRow, ...dataRows].join('\n'); } catch (error) { if (error instanceof JsonToCsvError) { throw error; } throw new JsonToCsvError(`Unexpected error during parsing: ${error instanceof Error ? error.message : 'Unknown error'}`, 'UNEXPECTED_ERROR'); } }