import type { LoaderContext } from '@rspack/core'; import { parse } from 'csv-parse/sync'; import { parseSchema, createValidator, parseValue } from '../index.js'; export interface CsvLoaderOptions { delimiter?: string; quote?: string; escape?: string; } interface PropertyConfig { name: string; schema: any; validator: (value: unknown) => boolean; parser: (valueString: string) => unknown; } export default function csvLoader( this: LoaderContext, content: string ): string { const options = this.getOptions() || {}; const delimiter = options.delimiter ?? ','; const quote = options.quote ?? '"'; const escape = options.escape ?? '\\'; const records = parse(content, { delimiter, quote, escape, relax_column_count: true, }); if (records.length < 2) { throw new Error('CSV must have at least 2 rows: headers and schemas'); } const headers = records[0]; const schemas = records[1]; if (headers.length !== schemas.length) { throw new Error( `Header count (${headers.length}) does not match schema count (${schemas.length})` ); } const propertyConfigs: PropertyConfig[] = headers.map((header: string, index: number) => { const schemaString = schemas[index]; const schema = parseSchema(schemaString); return { name: header, schema, validator: createValidator(schema), parser: (valueString: string) => parseValue(schema, valueString), }; }); const dataRows = records.slice(2); const objects = dataRows.map((row: string[], rowIndex: number) => { const obj: Record = {}; propertyConfigs.forEach((config, colIndex) => { const rawValue = row[colIndex] ?? ''; try { const parsed = config.parser(rawValue); if (!config.validator(parsed)) { throw new Error( `Validation failed for property "${config.name}" at row ${rowIndex + 3}: ${rawValue}` ); } obj[config.name] = parsed; } catch (error) { if (error instanceof Error) { throw new Error( `Failed to parse property "${config.name}" at row ${rowIndex + 3}, column ${colIndex + 1}: ${error.message}` ); } throw error; } }); return obj; }); const json = JSON.stringify(objects, null, 2); return `export default ${json};`; }