From c27f939a375ed0fb4e4ec83c5c21b20ca097e2f8 Mon Sep 17 00:00:00 2001 From: Laurin Weger Date: Wed, 10 Sep 2025 13:14:12 +0300 Subject: [PATCH] fix collision property issues --- .../typing/ShexJTypingTransformerCompact.ts | 28 ++++++++++++------- .../test/testData/pluralAnonymous.ts | 8 +++--- .../test/testData/propertyCollision.ts | 27 +++++++++++++++++- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/packages/schema-converter-shex/src/typing/ShexJTypingTransformerCompact.ts b/packages/schema-converter-shex/src/typing/ShexJTypingTransformerCompact.ts index 24f70d8..6831451 100644 --- a/packages/schema-converter-shex/src/typing/ShexJTypingTransformerCompact.ts +++ b/packages/schema-converter-shex/src/typing/ShexJTypingTransformerCompact.ts @@ -65,22 +65,29 @@ const predicateIriByProp = new WeakMap(); function resolveCollisions(props: dom.PropertyDeclaration[]): void { const groups = new Map(); props.forEach((p) => { - const base = p.name.replace(/\d+$/, ""); - if (!groups.has(base)) groups.set(base, []); - groups.get(base)!.push(p); + const name = p.name; + if (!groups.has(name)) groups.set(name, []); + groups.get(name)!.push(p); }); groups.forEach((list) => { if (list.length < 2) return; + + const predicateIris = new Set( + list.map((p) => predicateIriByProp.get(p)).filter(Boolean) + ); + if (predicateIris.size < 2) { + return; + } + // First pass rename using second last segment list.forEach((prop) => { - const iri = predicateIriByProp.get(prop) || prop.name; + const iri = predicateIriByProp.get(prop); + if (!iri) return; const segs = iri.split(/[#:\/]/).filter(Boolean); - if (!segs.length) return; + if (segs.length < 2) return; const local = segs.at(-1)!; - const secondLast = segs.length > 1 ? segs.at(-2)! : undefined; - if (secondLast) { - prop.name = `${secondLast}_${local}`; - } + const secondLast = segs.at(-2)!; + prop.name = `${secondLast}_${local}`; }); // Detect any remaining duplicates after first pass const nameCounts = new Map(); @@ -89,7 +96,8 @@ function resolveCollisions(props: dom.PropertyDeclaration[]): void { ); list.forEach((p) => { if (nameCounts.get(p.name)! > 1) { - p.name = predicateIriByProp.get(p) || p.name; + const iri = predicateIriByProp.get(p); + if (iri) p.name = iri; } }); }); diff --git a/packages/schema-converter-shex/test/testData/pluralAnonymous.ts b/packages/schema-converter-shex/test/testData/pluralAnonymous.ts index 5a96f28..eef9954 100644 --- a/packages/schema-converter-shex/test/testData/pluralAnonymous.ts +++ b/packages/schema-converter-shex/test/testData/pluralAnonymous.ts @@ -3,7 +3,7 @@ import type { TestData } from "./testData.js"; export const pluralAnonymous: TestData = { name: "plural anonymous", shexc: ` - PREFIX ex: + PREFIX ex: ex:ConfigHolderShape { ex:configs { ex:key . ; ex:val . }* } `, sampleTurtle: ``, @@ -15,16 +15,16 @@ export const pluralAnonymous: TestData = { export interface ConfigHolder { id?: IRI; /** - * Original IRI: http://ex/configs + * Original IRI: http://ex.org/configs */ configs?: Record; diff --git a/packages/schema-converter-shex/test/testData/propertyCollision.ts b/packages/schema-converter-shex/test/testData/propertyCollision.ts index 6a9ef4f..23ed998 100644 --- a/packages/schema-converter-shex/test/testData/propertyCollision.ts +++ b/packages/schema-converter-shex/test/testData/propertyCollision.ts @@ -14,5 +14,30 @@ export const propertyCollision: TestData = { baseNode: "http://ex/c1", successfulContext: {} as any, successfulTypings: "", - successfulCompactTypings: `export type IRI = string;\n\nexport interface C {\n id?: IRI;\n /**\n * Original IRI: http://ex/label\n */\n ex_label: any;\n /**\n * Original IRI: http://ex2/label\n */\n ex2_label: any;\n /**\n * Original IRI: http://xmlns.com/foaf/0.1/label\n */\n "0.1_label": any;\n /**\n * Original IRI: http://example.com/v1#label\n */\n v1_label: any;\n /**\n * Original IRI: http://api.example.com/v2.1:label\n */\n "v2.1:label": any;\n}\n\n`, + successfulCompactTypings: `export type IRI = string; + +export interface C { + id?: IRI; + /** + * Original IRI: http://ex/label + */ + label: any; + /** + * Original IRI: http://ex2/label + */ + label2: any; + /** + * Original IRI: http://xmlns.com/foaf/0.1/label + */ + label3: any; + /** + * Original IRI: http://example.com/v1#label + */ + label4: any; + /** + * Original IRI: http://api.example.com/v2.1:label + */ + "v2.1:label": any; +} +`, };