import * as path from "path"; import * as fs from "fs"; import { csvToModule } from "./module-gen"; import { CsvLoaderOptions } from "./types"; export interface CsvRollupOptions extends CsvLoaderOptions { /** Include pattern for CSV files (default: /\.csv$/) */ include?: RegExp | string | Array; /** Exclude pattern for CSV files */ exclude?: RegExp | string | Array; /** Base directory for resolving referenced CSV files (default: directory of current file) */ refBaseDir?: string; /** Primary key field name for referenced tables (default: 'id') */ defaultPrimaryKey?: string; } function matchesPattern( id: string, pattern: RegExp | string | Array | undefined, ): boolean { if (!pattern) return true; const patterns = Array.isArray(pattern) ? pattern : [pattern]; return patterns.some((p) => { if (p instanceof RegExp) { return p.test(id); } return id.includes(p); }); } interface TransformResult { code: string; map: null; } interface EmitFileOptions { type: "asset"; fileName: string; source: string; } interface PluginContext { emitFile: (options: EmitFileOptions) => string; } interface RollupPlugin { name: string; transform: ( this: PluginContext, code: string, id: string, ) => TransformResult | null; } /** * Rollup plugin for loading CSV files with typed-csv validation. * Works with both Vite and Tsup (esbuild). */ export function csvLoader(options: CsvRollupOptions = {}): RollupPlugin { const { include = /\.csv$/, exclude, emitTypes = true, typesOutputDir = "", writeToDisk = false, ...parseOptions } = options; return { name: "typed-csv", transform(code: string, id: string) { // Check if file matches the include/exclude patterns if (!matchesPattern(id, include)) return null; if (exclude && matchesPattern(id, exclude)) return null; // Only process .csv files if (!id.endsWith(".csv")) return null; // Infer resource name from filename const fileName = path.basename(id, ".csv").split(".")[0]; const resourceName = fileName .replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : "")) .replace(/^(.)/, (_, char) => char.toUpperCase()); const result = csvToModule(code, { ...parseOptions, emitTypes, resourceName, currentFilePath: id, refBaseDir: options.refBaseDir, defaultPrimaryKey: options.defaultPrimaryKey, }); // Emit type definition file if enabled if (emitTypes && result.dts) { const dtsPath = typesOutputDir ? path.join(typesOutputDir, path.basename(id) + ".d.ts") : id + ".d.ts"; if (writeToDisk) { // Write directly to disk const absolutePath = path.isAbsolute(dtsPath) ? dtsPath : path.join(process.cwd(), dtsPath); fs.mkdirSync(path.dirname(absolutePath), { recursive: true }); fs.writeFileSync(absolutePath, result.dts); } else { // Emit to Rollup's virtual module system this.emitFile({ type: "asset", fileName: dtsPath, source: result.dts, }); } } return { code: result.js, map: null, }; }, }; } export default csvLoader;