From eef3dbfac8ce93c61f82a0b6931701aac2dcf053 Mon Sep 17 00:00:00 2001 From: hypercross Date: Wed, 22 Apr 2026 18:15:55 +0800 Subject: [PATCH] feat(csv-loader): resolve @table references in type declarations Update type generation to transform @table references into capitalized type names when using custom schema strings. This ensures that generated TypeScript types correctly reference other CSV modules. --- .../tests/parseCsv-typeDeclarations.test.ts | 23 +++++++++++++++++++ src/csv-loader/type-gen.ts | 22 +++++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts b/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts index ed5eecd..37daa19 100644 --- a/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts +++ b/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts @@ -286,6 +286,29 @@ describe("parseCsv - type declarations", () => { "readonly items: [Type; int][];", ); }); + + it("should resolve @table references inside declared types to capitalized type names", () => { + const csv = [ + "# type Entry = [@user]", + "id,entry", + "string,Entry", + "1,1", + ].join("\n"); + + const result = parseCsv(csv, { + emitTypes: true, + resourceName: "entries", + currentFilePath: path.join(fixturesDir, "entries.csv"), + resolveReferences: false, + }); + + expect(result.typeDefinition).toContain("export type Entry = [User]"); + expect(result.typeDefinition).toContain("readonly entry: Entry;"); + // It should also import the User type + expect(result.typeDefinition).toContain( + "import type { User } from './user.csv'", + ); + }); }); describe("parseCsv - type declarations with resolveReferences: false", () => { diff --git a/src/csv-loader/type-gen.ts b/src/csv-loader/type-gen.ts index 91be4b0..94a1ffc 100644 --- a/src/csv-loader/type-gen.ts +++ b/src/csv-loader/type-gen.ts @@ -2,6 +2,21 @@ import * as path from "path"; import { schemaToTypeString } from "../index.js"; import type { PropertyConfig, TypeDeclaration } from "./types.js"; +function resolveReferencesInSchemaString( + schemaString: string, + resourceNames: Map, +): string { + return schemaString.replace( + /@([a-zA-Z0-9\-_]+)(\[\])?/g, + (_match, tableName, arraySuffix) => { + const typeName = + resourceNames.get(tableName) || + tableName.charAt(0).toUpperCase() + tableName.slice(1); + return arraySuffix ? `${typeName}[]` : typeName; + }, + ); +} + /** * Generate TypeScript interface for the CSV data */ @@ -51,7 +66,7 @@ export function generateTypeDefinition( ? typeDeclarations .map( (decl) => - `export type ${decl.name} = ${decl.schemaString ?? schemaToTypeString(decl.schema, resourceNames)};`, + `export type ${decl.name} = ${decl.schemaString ? resolveReferencesInSchemaString(decl.schemaString, resourceNames) : schemaToTypeString(decl.schema, resourceNames)};`, ) .join("\n") + "\n\n" : ""; @@ -60,8 +75,9 @@ export function generateTypeDefinition( .map((config) => { const typeStr = config.declaredTypeName ? config.declaredTypeName - : (config.schemaString ?? - schemaToTypeString(config.schema, resourceNames)); + : config.schemaString + ? resolveReferencesInSchemaString(config.schemaString, resourceNames) + : schemaToTypeString(config.schema, resourceNames); return ` readonly ${config.name}: ${typeStr};`; }) .join("\n");