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 */