Compare commits
3 Commits
f101c12097
...
e0317946d5
| Author | SHA1 | Date |
|---|---|---|
|
|
e0317946d5 | |
|
|
46504a53dd | |
|
|
ea362d4229 |
|
|
@ -160,7 +160,11 @@ export function parseCsv(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse type declarations with expansion of type name references
|
// 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) {
|
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(
|
||||||
|
|
@ -168,7 +172,11 @@ export function parseCsv(
|
||||||
declaredSchemaStrings,
|
declaredSchemaStrings,
|
||||||
);
|
);
|
||||||
const schema = parseSchema(expandedSchema.trim());
|
const schema = parseSchema(expandedSchema.trim());
|
||||||
typeDeclarationsParsed.push({ name: decl.typeName, schema });
|
typeDeclarationsParsed.push({
|
||||||
|
name: decl.typeName,
|
||||||
|
schema,
|
||||||
|
schemaString: decl.schemaString,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build declared types map
|
// Build declared types map
|
||||||
|
|
@ -181,7 +189,11 @@ 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({ name: decl.name, schema: resolvedSchema });
|
typeDeclarations.push({
|
||||||
|
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
|
||||||
|
|
@ -197,11 +209,21 @@ 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 {
|
||||||
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 = {
|
const config: PropertyConfig = {
|
||||||
|
|
@ -210,6 +232,7 @@ 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") {
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,9 @@ describe("parseCsv - type declarations", () => {
|
||||||
currentFilePath: path.join(fixturesDir, "card.csv"),
|
currentFilePath: path.join(fixturesDir, "card.csv"),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.typeDefinition).toContain("type Trigger =");
|
expect(result.typeDefinition).toContain("export 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("type Trigger =");
|
expect(result.typeDefinition).toContain("export type Trigger =");
|
||||||
expect(result.typeDefinition).toContain("type Status =");
|
expect(result.typeDefinition).toContain("export type Status =");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should ignore comment lines that are not type declarations", () => {
|
it("should ignore comment lines that are not type declarations", () => {
|
||||||
|
|
@ -196,11 +196,96 @@ describe("parseCsv - type declarations", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Both type declarations should appear
|
// Both type declarations should appear
|
||||||
expect(result.typeDefinition).toContain("type Trigger =");
|
expect(result.typeDefinition).toContain("export type Trigger =");
|
||||||
expect(result.typeDefinition).toContain("type Effect =");
|
expect(result.typeDefinition).toContain("export 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", () => {
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ export function generateTypeDefinition(
|
||||||
? typeDeclarations
|
? typeDeclarations
|
||||||
.map(
|
.map(
|
||||||
(decl) =>
|
(decl) =>
|
||||||
`export type ${decl.name} = ${schemaToTypeString(decl.schema, resourceNames)};`,
|
`export type ${decl.name} = ${decl.schemaString ?? schemaToTypeString(decl.schema, resourceNames)};`,
|
||||||
)
|
)
|
||||||
.join("\n") + "\n\n"
|
.join("\n") + "\n\n"
|
||||||
: "";
|
: "";
|
||||||
|
|
@ -60,7 +60,8 @@ export function generateTypeDefinition(
|
||||||
.map((config) => {
|
.map((config) => {
|
||||||
const typeStr = config.declaredTypeName
|
const typeStr = config.declaredTypeName
|
||||||
? config.declaredTypeName
|
? config.declaredTypeName
|
||||||
: schemaToTypeString(config.schema, resourceNames);
|
: (config.schemaString ??
|
||||||
|
schemaToTypeString(config.schema, resourceNames));
|
||||||
return ` readonly ${config.name}: ${typeStr};`;
|
return ` readonly ${config.name}: ${typeStr};`;
|
||||||
})
|
})
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,8 @@ 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 */
|
||||||
|
|
@ -98,6 +100,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue