A comprehensive CSV to JSON conversion library for Node.js with streaming support, extensive customization, and high-performance parsing capabilities.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Custom error handling system with specific error types for parsing issues, validation failures, and comprehensive error information for debugging CSV parsing problems.
Custom error class extending native Error with CSV-specific error information and context.
/**
* Custom error class for CSV parsing errors
* @extends Error
*/
class CSVError extends Error {
/** Error message describing the issue */
err: string;
/** Line number where error occurred (zero-based) */
line: number;
/** Additional context information about the error */
extra?: string;
/**
* Creates a new CSV parsing error
* @param err - Error message
* @param line - Line number where error occurred
* @param extra - Optional additional error context
*/
constructor(err: string, line: number, extra?: string);
/**
* Convert error to JSON for serialization
* @returns Object with error details
*/
toJSON(): {err: string, line: number, extra?: string};
}Usage Examples:
import csvtojson from "csvtojson";
try {
const jsonArray = await csvtojson({
checkColumn: true,
maxRowLength: 1000
})
.fromString('name,age,city\nAlice,25\nBob,30,London,Extra');
} catch (error) {
if (error instanceof CSVError) {
console.log('CSV Error:', error.err);
console.log('Line:', error.line);
if (error.extra) {
console.log('Context:', error.extra);
}
// Serialize error
const errorData = error.toJSON();
console.log('Error data:', errorData);
}
}Predefined static methods for creating specific types of CSV errors.
/**
* Create error for column count mismatch
* @param index - Line index where mismatch occurred
* @param extra - Optional additional context
* @returns CSVError instance
*/
static column_mismatched(index: number, extra?: string): CSVError;
/**
* Create error for unclosed quote
* @param index - Line index where unclosed quote occurred
* @param extra - Optional additional context
* @returns CSVError instance
*/
static unclosed_quote(index: number, extra?: string): CSVError;
/**
* Create CSVError from JSON object (for deserialization)
* @param obj - Object with err, line, and optional extra properties
* @returns CSVError instance
*/
static fromJSON(obj: {err: string, line: number, extra?: string}): CSVError;Usage Examples:
import csvtojson, { CSVError } from "csvtojson";
// Handle different error types
csvtojson({
checkColumn: true,
quote: '"'
})
.fromFile('./problematic-data.csv')
.subscribe(
(jsonObj) => {
console.log('Parsed:', jsonObj);
},
(error) => {
if (error instanceof CSVError) {
switch (error.err) {
case 'column_mismatched':
console.error(`Column count mismatch at line ${error.line + 1}`);
if (error.extra) {
console.error(`Expected columns but got: ${error.extra}`);
}
break;
case 'unclosed_quote':
console.error(`Unclosed quote at line ${error.line + 1}`);
if (error.extra) {
console.error(`Near: ${error.extra}`);
}
break;
default:
console.error(`Parsing error: ${error.err} at line ${error.line + 1}`);
}
}
}
);Comprehensive error information for debugging complex parsing issues.
interface CSVError extends Error {
/** The specific error type identifier */
err: 'column_mismatched' | 'unclosed_quote' | 'row_exceed' | string;
/** Zero-based line number where error occurred */
line: number;
/** Additional context like partial data or expected vs actual values */
extra?: string;
}Usage Examples:
import csvtojson from "csvtojson";
async function parseWithDetailedErrorHandling(filePath: string) {
try {
const result = await csvtojson({
checkColumn: true,
maxRowLength: 10000,
quote: '"'
})
.fromFile(filePath);
return result;
} catch (error) {
if (error instanceof CSVError) {
// Create detailed error report
const errorReport = {
type: error.err,
lineNumber: error.line + 1, // Convert to 1-based
message: error.message,
context: error.extra,
file: filePath,
timestamp: new Date().toISOString()
};
// Log structured error
console.error('CSV Parsing Error:', JSON.stringify(errorReport, null, 2));
// Handle specific error types
switch (error.err) {
case 'column_mismatched':
console.error('Suggestion: Check if delimiters are consistent');
break;
case 'unclosed_quote':
console.error('Suggestion: Check for unescaped quotes in data');
break;
case 'row_exceed':
console.error('Suggestion: Increase maxRowLength or check for corrupted data');
break;
}
throw errorReport;
}
// Re-throw non-CSV errors
throw error;
}
}Strategies for handling and recovering from parsing errors in production.
/**
* Error handling patterns for robust CSV processing
*/
interface ErrorRecoveryOptions {
/** Continue processing after errors */
continueOnError?: boolean;
/** Maximum errors before aborting */
maxErrors?: number;
/** Callback for handling individual errors */
onError?: (error: CSVError) => boolean; // Return true to continue
}Usage Examples:
import csvtojson, { CSVError } from "csvtojson";
// Graceful error handling with recovery
async function robustCSVParsing(filePath: string) {
const results: any[] = [];
const errors: CSVError[] = [];
let errorCount = 0;
const maxErrors = 10;
return new Promise((resolve, reject) => {
csvtojson({
checkColumn: false, // More lenient parsing
ignoreEmpty: true, // Skip empty rows
maxRowLength: 100000
})
.fromFile(filePath)
.subscribe(
(jsonObj, lineNumber) => {
try {
// Validate data before adding
if (isValidRecord(jsonObj)) {
results.push(jsonObj);
} else {
console.warn(`Invalid record at line ${lineNumber}:`, jsonObj);
}
} catch (validationError) {
console.error(`Validation error at line ${lineNumber}:`, validationError);
}
},
(csvError) => {
errorCount++;
errors.push(csvError);
console.error(`Error ${errorCount}/${maxErrors} at line ${csvError.line + 1}: ${csvError.err}`);
if (errorCount >= maxErrors) {
reject(new Error(`Too many errors (${errorCount}). Aborting.`));
}
},
() => {
// Parsing completed
console.log(`Parsing completed with ${results.length} records and ${errorCount} errors`);
resolve({
data: results,
errors: errors,
stats: {
totalRecords: results.length,
errorCount: errorCount,
successRate: (results.length / (results.length + errorCount)) * 100
}
});
}
);
});
}
function isValidRecord(record: any): boolean {
// Implement your validation logic
return record && typeof record === 'object' && Object.keys(record).length > 0;
}Handle errors in streaming scenarios with proper cleanup and resource management.
/**
* Stream-specific error handling for large file processing
*/
interface StreamErrorHandling {
/** Handle stream errors without stopping processing */
onStreamError?: (error: Error) => void;
/** Handle individual record errors */
onRecordError?: (error: CSVError, record: any) => boolean;
}Usage Examples:
import csvtojson from "csvtojson";
import { createWriteStream } from "fs";
// Streaming with error handling and logging
function processLargeCSVWithErrorLogging(inputFile: string, outputFile: string) {
const outputStream = createWriteStream(outputFile);
const errorLogStream = createWriteStream(`${outputFile}.errors.log`);
let processedCount = 0;
let errorCount = 0;
csvtojson({
checkType: true,
trim: true
})
.fromFile(inputFile)
.subscribe(
async (jsonObj, lineNumber) => {
try {
// Process and validate data
const processedObj = await processRecord(jsonObj);
// Write to output
outputStream.write(JSON.stringify(processedObj) + '\n');
processedCount++;
if (processedCount % 1000 === 0) {
console.log(`Processed ${processedCount} records`);
}
} catch (processingError) {
errorCount++;
const errorEntry = {
line: lineNumber + 1,
error: processingError.message,
data: jsonObj,
timestamp: new Date().toISOString()
};
errorLogStream.write(JSON.stringify(errorEntry) + '\n');
}
},
(csvError) => {
// Handle CSV parsing errors
errorCount++;
const errorEntry = {
line: csvError.line + 1,
type: 'csv_parse_error',
error: csvError.err,
extra: csvError.extra,
timestamp: new Date().toISOString()
};
errorLogStream.write(JSON.stringify(errorEntry) + '\n');
console.error(`CSV parsing error at line ${csvError.line + 1}: ${csvError.err}`);
},
() => {
// Cleanup and final reporting
outputStream.end();
errorLogStream.end();
console.log(`\nProcessing completed:`);
console.log(`- Processed records: ${processedCount}`);
console.log(`- Errors: ${errorCount}`);
console.log(`- Success rate: ${((processedCount / (processedCount + errorCount)) * 100).toFixed(2)}%`);
}
);
}
async function processRecord(record: any): Promise<any> {
// Implement your record processing logic
// Throw errors for invalid records
if (!record.email || !record.email.includes('@')) {
throw new Error('Invalid email address');
}
return {
...record,
processed_at: new Date().toISOString()
};
}Install with Tessl CLI
npx tessl i tessl/npm-csvtojson