inline-schema/src/csv-loader/loader.ts

86 lines
2.3 KiB
TypeScript

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<CsvLoaderOptions>,
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<string, unknown> = {};
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};`;
}