Compare commits

..

No commits in common. "e0317946d5262c434ea65b3662389aaebd2ac048" and "f101c1209715fcf399561d0dbf8d3751071e6bd7" have entirely different histories.

4 changed files with 12 additions and 125 deletions

View File

@ -160,11 +160,7 @@ export function parseCsv(
} }
// Parse type declarations with expansion of type name references // Parse type declarations with expansion of type name references
const typeDeclarationsParsed: { const typeDeclarationsParsed: { name: string; schema: Schema }[] = [];
name: string;
schema: Schema;
schemaString: string;
}[] = [];
for (const decl of typeDeclarationsRaw) { for (const decl of typeDeclarationsRaw) {
// Expand any type name references before parsing // Expand any type name references before parsing
const expandedSchema = expandSchemaString( const expandedSchema = expandSchemaString(
@ -172,11 +168,7 @@ export function parseCsv(
declaredSchemaStrings, declaredSchemaStrings,
); );
const schema = parseSchema(expandedSchema.trim()); const schema = parseSchema(expandedSchema.trim());
typeDeclarationsParsed.push({ typeDeclarationsParsed.push({ name: decl.typeName, schema });
name: decl.typeName,
schema,
schemaString: decl.schemaString,
});
} }
// Build declared types map // Build declared types map
@ -189,11 +181,7 @@ export function parseCsv(
const typeDeclarations: TypeDeclaration[] = []; const typeDeclarations: TypeDeclaration[] = [];
for (const decl of typeDeclarationsParsed) { for (const decl of typeDeclarationsParsed) {
const resolvedSchema = resolveTypeReferences(decl.schema, declaredTypes); const resolvedSchema = resolveTypeReferences(decl.schema, declaredTypes);
typeDeclarations.push({ typeDeclarations.push({ name: decl.name, schema: resolvedSchema });
name: decl.name,
schema: resolvedSchema,
schemaString: decl.schemaString,
});
} }
// Update declaredTypes with resolved schemas for column schema lookup // Update declaredTypes with resolved schemas for column schema lookup
@ -209,21 +197,11 @@ export function parseCsv(
// Check if schema string matches a declared type name // Check if schema string matches a declared type name
let schema: Schema; let schema: Schema;
let declaredTypeName: string | undefined; let declaredTypeName: string | undefined;
let columnSchemaString: string | undefined;
if (declaredTypes.has(schemaString)) { if (declaredTypes.has(schemaString)) {
schema = declaredTypes.get(schemaString)!; schema = declaredTypes.get(schemaString)!;
declaredTypeName = schemaString; declaredTypeName = schemaString;
} else { } else {
// Expand any custom type name references before parsing schema = parseSchema(schemaString);
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 = { const config: PropertyConfig = {
@ -232,7 +210,6 @@ export function parseCsv(
validator: createValidator(schema), validator: createValidator(schema),
parser: (valueString: string) => parseValue(schema, valueString), parser: (valueString: string) => parseValue(schema, valueString),
declaredTypeName, declaredTypeName,
schemaString: columnSchemaString,
}; };
if (schema.type === "reference") { if (schema.type === "reference") {

View File

@ -56,9 +56,9 @@ describe("parseCsv - type declarations", () => {
currentFilePath: path.join(fixturesDir, "card.csv"), currentFilePath: path.join(fixturesDir, "card.csv"),
}); });
expect(result.typeDefinition).toContain("export type Trigger ="); expect(result.typeDefinition).toContain("type Trigger =");
expect(result.typeDefinition).toContain( expect(result.typeDefinition).toContain(
"'onPlay' | 'onDraw' | 'onDiscard'", '"onPlay" | "onDraw" | "onDiscard"',
); );
}); });
@ -93,8 +93,8 @@ describe("parseCsv - type declarations", () => {
currentFilePath: path.join(fixturesDir, "card.csv"), currentFilePath: path.join(fixturesDir, "card.csv"),
}); });
expect(result.typeDefinition).toContain("export type Trigger ="); expect(result.typeDefinition).toContain("type Trigger =");
expect(result.typeDefinition).toContain("export type Status ="); expect(result.typeDefinition).toContain("type Status =");
}); });
it("should ignore comment lines that are not type declarations", () => { it("should ignore comment lines that are not type declarations", () => {
@ -196,96 +196,11 @@ describe("parseCsv - type declarations", () => {
}); });
// Both type declarations should appear // Both type declarations should appear
expect(result.typeDefinition).toContain("export type Trigger ="); expect(result.typeDefinition).toContain("type Trigger =");
expect(result.typeDefinition).toContain("export type Effect ="); expect(result.typeDefinition).toContain("type Effect =");
// Column should use the declared type name, not expanded union // Column should use the declared type name, not expanded union
expect(result.typeDefinition).toContain("readonly trigger: Trigger;"); expect(result.typeDefinition).toContain("readonly trigger: Trigger;");
}); });
it("should expand custom type names inside tuples and arrays", () => {
const csv = [
"# type IntentEffectTarget = 'user' | 'eachEnemy' | 'randomEnemy' | 'player'",
"# type IntentEffect = [IntentEffectTarget; string; int]",
"# type IntentEffects = IntentEffect[]",
"id,effects",
"string,IntentEffects",
"boost,[user;spike;1];[user;defend;4]",
].join("\n");
const result = parseCsv(csv, { emitTypes: false });
expect(result.typeDeclarations).toHaveLength(3);
const intentEffect = result.typeDeclarations.find(
(d) => d.name === "IntentEffect",
)!;
expect(intentEffect.schema.type).toBe("tuple");
// First element should be resolved to union, not a string "IntentEffectTarget"
const firstEl = (
intentEffect.schema as { elements: { schema: { type: string } }[] }
).elements[0].schema;
expect(firstEl.type).toBe("union");
});
it("should use declared type names in generated type definition for tuple arrays", () => {
const csv = [
"# type IntentEffectTarget = 'user' | 'eachEnemy' | 'randomEnemy' | 'player'",
"# type IntentEffect = [IntentEffectTarget; string; int]",
"# type IntentEffects = IntentEffect[]",
"id,effects",
"string,IntentEffects",
"boost,[user;spike;1];[user;defend;4]",
].join("\n");
const result = parseCsv(csv, {
emitTypes: true,
resourceName: "intent",
currentFilePath: path.join(fixturesDir, "intent.csv"),
});
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;");
});
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", () => { describe("parseCsv - type declarations with resolveReferences: false", () => {

View File

@ -51,7 +51,7 @@ export function generateTypeDefinition(
? typeDeclarations ? typeDeclarations
.map( .map(
(decl) => (decl) =>
`export type ${decl.name} = ${decl.schemaString ?? schemaToTypeString(decl.schema, resourceNames)};`, `export type ${decl.name} = ${schemaToTypeString(decl.schema, resourceNames)};`,
) )
.join("\n") + "\n\n" .join("\n") + "\n\n"
: ""; : "";
@ -60,8 +60,7 @@ export function generateTypeDefinition(
.map((config) => { .map((config) => {
const typeStr = config.declaredTypeName const typeStr = config.declaredTypeName
? config.declaredTypeName ? config.declaredTypeName
: (config.schemaString ?? : schemaToTypeString(config.schema, resourceNames);
schemaToTypeString(config.schema, resourceNames));
return ` readonly ${config.name}: ${typeStr};`; return ` readonly ${config.name}: ${typeStr};`;
}) })
.join("\n"); .join("\n");

View File

@ -78,8 +78,6 @@ export interface PropertyConfig {
reverseReferenceForeignKey?: string; reverseReferenceForeignKey?: string;
/** When a column uses a declared type name, this stores that name */ /** When a column uses a declared type name, this stores that name */
declaredTypeName?: string; 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 */ /** Parsed reverse reference declaration from a comment line */
@ -100,8 +98,6 @@ export interface ReverseReferenceDeclaration {
export interface TypeDeclaration { export interface TypeDeclaration {
/** Name of the type being defined */ /** Name of the type being defined */
name: string; name: string;
/** The original schema string (preserves type name references for output) */
schemaString: string;
/** The parsed schema for this type */ /** The parsed schema for this type */
schema: Schema; schema: Schema;
} }