From 46504a53dde11b57c20e54158ff21b54ad562296 Mon Sep 17 00:00:00 2001 From: hypercross Date: Wed, 22 Apr 2026 17:08:06 +0800 Subject: [PATCH] feat(csv-loader): preserve original schema strings in type generation Store the original schema string during CSV parsing to prevent unnecessary expansion of type name references in the generated TypeScript definitions. This ensures that declared types reference each other by name rather than inlining their full definitions. --- src/csv-loader/loader.ts | 18 ++++++++++--- .../tests/parseCsv-typeDeclarations.test.ts | 25 +++++++++++-------- src/csv-loader/type-gen.ts | 2 +- src/csv-loader/types.ts | 2 ++ 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/csv-loader/loader.ts b/src/csv-loader/loader.ts index 0bda262..259d28a 100644 --- a/src/csv-loader/loader.ts +++ b/src/csv-loader/loader.ts @@ -160,7 +160,11 @@ export function parseCsv( } // Parse type declarations with expansion of type name references - const typeDeclarationsParsed: { name: string; schema: Schema }[] = []; + const typeDeclarationsParsed: { + name: string; + schema: Schema; + schemaString: string; + }[] = []; for (const decl of typeDeclarationsRaw) { // Expand any type name references before parsing const expandedSchema = expandSchemaString( @@ -168,7 +172,11 @@ export function parseCsv( declaredSchemaStrings, ); const schema = parseSchema(expandedSchema.trim()); - typeDeclarationsParsed.push({ name: decl.typeName, schema }); + typeDeclarationsParsed.push({ + name: decl.typeName, + schema, + schemaString: decl.schemaString, + }); } // Build declared types map @@ -181,7 +189,11 @@ export function parseCsv( const typeDeclarations: TypeDeclaration[] = []; for (const decl of typeDeclarationsParsed) { const resolvedSchema = resolveTypeReferences(decl.schema, declaredTypes); - typeDeclarations.push({ name: decl.name, schema: resolvedSchema }); + typeDeclarations.push({ + name: decl.name, + schema: resolvedSchema, + schemaString: decl.schemaString, + }); } // Update declaredTypes with resolved schemas for column schema lookup diff --git a/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts b/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts index 4070eb9..f489b3b 100644 --- a/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts +++ b/src/csv-loader/tests/parseCsv-typeDeclarations.test.ts @@ -56,9 +56,9 @@ describe("parseCsv - type declarations", () => { currentFilePath: path.join(fixturesDir, "card.csv"), }); - expect(result.typeDefinition).toContain("type Trigger ="); + expect(result.typeDefinition).toContain("export type Trigger ="); expect(result.typeDefinition).toContain( - '"onPlay" | "onDraw" | "onDiscard"', + "'onPlay' | 'onDraw' | 'onDiscard'", ); }); @@ -93,8 +93,8 @@ describe("parseCsv - type declarations", () => { currentFilePath: path.join(fixturesDir, "card.csv"), }); - expect(result.typeDefinition).toContain("type Trigger ="); - expect(result.typeDefinition).toContain("type Status ="); + expect(result.typeDefinition).toContain("export type Trigger ="); + expect(result.typeDefinition).toContain("export type Status ="); }); it("should ignore comment lines that are not type declarations", () => { @@ -196,8 +196,8 @@ describe("parseCsv - type declarations", () => { }); // Both type declarations should appear - expect(result.typeDefinition).toContain("type Trigger ="); - expect(result.typeDefinition).toContain("type Effect ="); + expect(result.typeDefinition).toContain("export type Trigger ="); + expect(result.typeDefinition).toContain("export type Effect ="); // Column should use the declared type name, not expanded union expect(result.typeDefinition).toContain("readonly trigger: Trigger;"); }); @@ -242,10 +242,15 @@ describe("parseCsv - type declarations", () => { currentFilePath: path.join(fixturesDir, "intent.csv"), }); - expect(result.typeDefinition).toContain("type IntentEffectTarget ="); - expect(result.typeDefinition).toContain("type IntentEffect ="); - expect(result.typeDefinition).toContain("type IntentEffects ="); - + expect(result.typeDefinition).toContain("export type IntentEffectTarget ="); + expect(result.typeDefinition).toContain("export type IntentEffect ="); + expect(result.typeDefinition).toContain("export type IntentEffects ="); + // IntentEffect should reference IntentEffectTarget, not expand it + expect(result.typeDefinition).toContain( + "[IntentEffectTarget; string; int]", + ); + // IntentEffects should reference IntentEffect, not expand it + expect(result.typeDefinition).toContain("IntentEffect[]"); // Column should reference IntentEffects, not inline expansion expect(result.typeDefinition).toContain("readonly effects: IntentEffects;"); }); diff --git a/src/csv-loader/type-gen.ts b/src/csv-loader/type-gen.ts index a09b97e..da3389c 100644 --- a/src/csv-loader/type-gen.ts +++ b/src/csv-loader/type-gen.ts @@ -51,7 +51,7 @@ export function generateTypeDefinition( ? typeDeclarations .map( (decl) => - `export type ${decl.name} = ${schemaToTypeString(decl.schema, resourceNames)};`, + `export type ${decl.name} = ${decl.schemaString ?? schemaToTypeString(decl.schema, resourceNames)};`, ) .join("\n") + "\n\n" : ""; diff --git a/src/csv-loader/types.ts b/src/csv-loader/types.ts index ade0468..34f75a1 100644 --- a/src/csv-loader/types.ts +++ b/src/csv-loader/types.ts @@ -98,6 +98,8 @@ export interface ReverseReferenceDeclaration { export interface TypeDeclaration { /** Name of the type being defined */ name: string; + /** The original schema string (preserves type name references for output) */ + schemaString: string; /** The parsed schema for this type */ schema: Schema; }