From e0317946d5262c434ea65b3662389aaebd2ac048 Mon Sep 17 00:00:00 2001 From: hypercross Date: Wed, 22 Apr 2026 17:58:36 +0800 Subject: [PATCH] feat(csv-loader): expand custom type references in schemas Allow custom type names to be expanded before parsing the schema string. This enables using declared types within complex structures like tuples or arrays. The original schema string is preserved for type generation to ensure the output uses the named type rather than the expanded inline definition. --- src/csv-loader/loader.ts | 13 +++++++- .../tests/parseCsv-typeDeclarations.test.ts | 32 +++++++++++++++++++ src/csv-loader/type-gen.ts | 3 +- src/csv-loader/types.ts | 2 ++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/csv-loader/loader.ts b/src/csv-loader/loader.ts index 259d28a..789148c 100644 --- a/src/csv-loader/loader.ts +++ b/src/csv-loader/loader.ts @@ -209,11 +209,21 @@ export function parseCsv( // Check if schema string matches a declared type name let schema: Schema; let declaredTypeName: string | undefined; + let columnSchemaString: string | undefined; if (declaredTypes.has(schemaString)) { schema = declaredTypes.get(schemaString)!; declaredTypeName = schemaString; } else { - schema = parseSchema(schemaString); + // Expand any custom type name references before parsing + const expandedSchema = expandSchemaString( + schemaString, + declaredSchemaStrings, + ); + schema = parseSchema(expandedSchema.trim()); + // Only preserve the original schema string if expansion actually changed it + if (expandedSchema !== schemaString) { + columnSchemaString = schemaString; + } } const config: PropertyConfig = { @@ -222,6 +232,7 @@ export function parseCsv( validator: createValidator(schema), parser: (valueString: string) => parseValue(schema, valueString), declaredTypeName, + schemaString: columnSchemaString, }; if (schema.type === "reference") { diff --git a/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts b/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts index f489b3b..ed5eecd 100644 --- a/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts +++ b/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts @@ -254,6 +254,38 @@ describe("parseCsv - type declarations", () => { // Column should reference IntentEffects, not inline expansion expect(result.typeDefinition).toContain("readonly effects: IntentEffects;"); }); + + it("should handle custom type inside a tuple used as an array element", () => { + const csv = [ + '# type Type = "apple" | "orange"', + "id,items", + "string,[Type; int][]", + "001,[apple;2];[orange;3]", + ].join("\n"); + + const result = parseCsv(csv, { emitTypes: false }); + + expect(result.data).toHaveLength(1); + expect(result.data[0]).toEqual({ + id: "001", + items: [ + ["apple", 2], + ["orange", 3], + ], + }); + + const typeResult = parseCsv(csv, { + emitTypes: true, + resourceName: "item", + currentFilePath: path.join(fixturesDir, "item.csv"), + }); + + expect(typeResult.typeDefinition).toContain("export type Type ="); + expect(typeResult.typeDefinition).toContain("[Type; int][]"); + expect(typeResult.typeDefinition).toContain( + "readonly items: [Type; int][];", + ); + }); }); describe("parseCsv - type declarations with resolveReferences: false", () => { diff --git a/src/csv-loader/type-gen.ts b/src/csv-loader/type-gen.ts index da3389c..91be4b0 100644 --- a/src/csv-loader/type-gen.ts +++ b/src/csv-loader/type-gen.ts @@ -60,7 +60,8 @@ export function generateTypeDefinition( .map((config) => { const typeStr = config.declaredTypeName ? config.declaredTypeName - : schemaToTypeString(config.schema, resourceNames); + : (config.schemaString ?? + schemaToTypeString(config.schema, resourceNames)); return ` readonly ${config.name}: ${typeStr};`; }) .join("\n"); diff --git a/src/csv-loader/types.ts b/src/csv-loader/types.ts index 34f75a1..41b7334 100644 --- a/src/csv-loader/types.ts +++ b/src/csv-loader/types.ts @@ -78,6 +78,8 @@ export interface PropertyConfig { reverseReferenceForeignKey?: string; /** When a column uses a declared type name, this stores that name */ declaredTypeName?: string; + /** The original schema string for the column (preserves type name references for output) */ + schemaString?: string; } /** Parsed reverse reference declaration from a comment line */