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.
This commit is contained in:
hypercross 2026-04-22 17:58:36 +08:00
parent 46504a53dd
commit e0317946d5
4 changed files with 48 additions and 2 deletions

View File

@ -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") {

View File

@ -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", () => {

View File

@ -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");

View File

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