feat: add TypeScript type definition generation for csv-loader
- Add emitTypes option (default true) to generate .d.ts files - Add typesOutputDir option for custom output directory - Generate interface based on CSV schema definitions - Export RowType for explicit type imports Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import type { LoaderContext } from '@rspack/core';
|
||||
import { parse } from 'csv-parse/sync';
|
||||
import { parseSchema, createValidator, parseValue } from '../index.js';
|
||||
import type { Schema } from '../types.js';
|
||||
import * as path from 'path';
|
||||
|
||||
export interface CsvLoaderOptions {
|
||||
delimiter?: string;
|
||||
@@ -9,6 +11,10 @@ export interface CsvLoaderOptions {
|
||||
bom?: boolean;
|
||||
comment?: string | false;
|
||||
trim?: boolean;
|
||||
/** Generate TypeScript declaration file (.d.ts) */
|
||||
emitTypes?: boolean;
|
||||
/** Output directory for generated type files (relative to output path) */
|
||||
typesOutputDir?: string;
|
||||
}
|
||||
|
||||
interface PropertyConfig {
|
||||
@@ -18,6 +24,57 @@ interface PropertyConfig {
|
||||
parser: (valueString: string) => unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a schema to TypeScript type string
|
||||
*/
|
||||
function schemaToTypeString(schema: Schema): string {
|
||||
switch (schema.type) {
|
||||
case 'string':
|
||||
return 'string';
|
||||
case 'number':
|
||||
return 'number';
|
||||
case 'boolean':
|
||||
return 'boolean';
|
||||
case 'array':
|
||||
if (schema.element.type === 'tuple') {
|
||||
const tupleElements = schema.element.elements.map(schemaToTypeString);
|
||||
return `[${tupleElements.join(', ')}]`;
|
||||
}
|
||||
return `${schemaToTypeString(schema.element)}[]`;
|
||||
case 'tuple':
|
||||
const tupleElements = schema.elements.map(schemaToTypeString);
|
||||
return `[${tupleElements.join(', ')}]`;
|
||||
default:
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate TypeScript interface for the CSV data
|
||||
*/
|
||||
function generateTypeDefinition(
|
||||
resourceName: string,
|
||||
propertyConfigs: PropertyConfig[]
|
||||
): string {
|
||||
const interfaceName = path.basename(resourceName, path.extname(resourceName))
|
||||
.replace(/[^a-zA-Z0-9_$]/g, '_')
|
||||
.replace(/^(\d)/, '_$1');
|
||||
|
||||
const properties = propertyConfigs
|
||||
.map((config) => ` ${config.name}: ${schemaToTypeString(config.schema)};`)
|
||||
.join('\n');
|
||||
|
||||
return `export interface ${interfaceName} {
|
||||
${properties}
|
||||
}
|
||||
|
||||
export type RowType = ${interfaceName};
|
||||
|
||||
declare const data: ${interfaceName}[];
|
||||
export default data;
|
||||
`;
|
||||
}
|
||||
|
||||
export default function csvLoader(
|
||||
this: LoaderContext<CsvLoaderOptions>,
|
||||
content: string
|
||||
@@ -29,6 +86,8 @@ export default function csvLoader(
|
||||
const bom = options?.bom ?? true;
|
||||
const comment = options?.comment === false ? undefined : (options?.comment ?? '#');
|
||||
const trim = options?.trim ?? true;
|
||||
const emitTypes = options?.emitTypes ?? true;
|
||||
const typesOutputDir = options?.typesOutputDir ?? '';
|
||||
|
||||
const records = parse(content, {
|
||||
delimiter,
|
||||
@@ -90,5 +149,16 @@ export default function csvLoader(
|
||||
});
|
||||
|
||||
const json = JSON.stringify(objects, null, 2);
|
||||
|
||||
// Emit type definition file if enabled
|
||||
if (emitTypes) {
|
||||
const dtsContent = generateTypeDefinition(this.resourcePath, propertyConfigs);
|
||||
const relativePath = this.resourcePath.replace(this.context, '');
|
||||
const dtsFileName = relativePath.replace(/\.csv$/, '.d.ts');
|
||||
const outputPath = path.join(typesOutputDir, dtsFileName);
|
||||
|
||||
this.emitFile?.(outputPath, dtsContent);
|
||||
}
|
||||
|
||||
return `export default ${json};`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user