fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement conditional schema name prefixing

Only add file prefix to external schema names when there's a naming conflict. This results in cleaner schema names like "User" instead of "file1_User" when there's no ambiguity.

Changes:
- Modified remap() to try unprefixed names first
- Only add file prefix when the unprefixed name is already taken
- Updated fixDanglingRefs() to handle both prefixed and unprefixed names
- Updated test snapshots to reflect new cleaner naming
- Verified conflict resolution still works correctly

Example:
- No conflict: "SchemaA" (was "file1_SchemaA")
- With conflict: First gets "User", second gets "file2_User"

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>

+49 -25
+5 -5
packages/json-schema-ref-parser/src/__tests__/__snapshots__/cross-file-ref-main.json
··· 13 13 "content": { 14 14 "application/json": { 15 15 "schema": { 16 - "$ref": "#/components/schemas/cross-file-ref-file1_SchemaA" 16 + "$ref": "#/components/schemas/SchemaA" 17 17 } 18 18 } 19 19 } ··· 29 29 "content": { 30 30 "application/json": { 31 31 "schema": { 32 - "$ref": "#/components/schemas/cross-file-ref-file2_SchemaB" 32 + "$ref": "#/components/schemas/SchemaB" 33 33 } 34 34 } 35 35 } ··· 40 40 }, 41 41 "components": { 42 42 "schemas": { 43 - "cross-file-ref-file1_SchemaA": { 43 + "SchemaA": { 44 44 "type": "object", 45 45 "properties": { 46 46 "typeField": { 47 - "$ref": "#/components/schemas/cross-file-ref-file2_SchemaB" 47 + "$ref": "#/components/schemas/SchemaB" 48 48 }, 49 49 "name": { 50 50 "type": "string" 51 51 } 52 52 } 53 53 }, 54 - "cross-file-ref-file2_SchemaB": { 54 + "SchemaB": { 55 55 "type": "string", 56 56 "enum": [ 57 57 "TypeA",
+6 -6
packages/json-schema-ref-parser/src/__tests__/__snapshots__/main-with-external-siblings.json
··· 14 14 "content": { 15 15 "application/json": { 16 16 "schema": { 17 - "$ref": "#/components/schemas/external-with-siblings_ResolutionStep" 17 + "$ref": "#/components/schemas/ResolutionStep" 18 18 } 19 19 } 20 20 } ··· 31 31 "content": { 32 32 "application/json": { 33 33 "schema": { 34 - "$ref": "#/components/schemas/external-with-siblings_ActionInfo" 34 + "$ref": "#/components/schemas/ActionInfo" 35 35 } 36 36 } 37 37 } ··· 42 42 }, 43 43 "components": { 44 44 "schemas": { 45 - "external-with-siblings_ActionInfo": { 45 + "ActionInfo": { 46 46 "type": "object", 47 47 "properties": { 48 48 "ActionId": { ··· 50 50 } 51 51 } 52 52 }, 53 - "external-with-siblings_ResolutionStep": { 53 + "ResolutionStep": { 54 54 "type": "object", 55 55 "properties": { 56 56 "ResolutionType": { 57 57 "oneOf": [ 58 58 { 59 - "$ref": "#/components/schemas/external-with-siblings_ResolutionType" 59 + "$ref": "#/components/schemas/ResolutionType" 60 60 } 61 61 ] 62 62 }, ··· 65 65 } 66 66 } 67 67 }, 68 - "external-with-siblings_ResolutionType": { 68 + "ResolutionType": { 69 69 "type": "string", 70 70 "enum": [ 71 71 "ContactVendor",
+3 -3
packages/json-schema-ref-parser/src/__tests__/__snapshots__/multiple-refs.json
··· 5 5 "summary": "First endpoint using the same pathId schema", 6 6 "parameters": [ 7 7 { 8 - "$ref": "#/components/parameters/path-parameter_pathId" 8 + "$ref": "#/components/parameters/pathId" 9 9 } 10 10 ], 11 11 "responses": { ··· 20 20 "summary": "Second endpoint using the same pathId schema", 21 21 "parameters": [ 22 22 { 23 - "$ref": "#/components/parameters/path-parameter_pathId" 23 + "$ref": "#/components/parameters/pathId" 24 24 } 25 25 ], 26 26 "responses": { ··· 33 33 }, 34 34 "components": { 35 35 "parameters": { 36 - "path-parameter_pathId": { 36 + "pathId": { 37 37 "name": "pathId", 38 38 "in": "path", 39 39 "required": true,
+7 -7
packages/json-schema-ref-parser/src/__tests__/__snapshots__/redfish-like.json
··· 15 15 "content": { 16 16 "application/json": { 17 17 "schema": { 18 - "$ref": "#/components/schemas/ResolutionStep_v1_0_1_ResolutionStep_v1_0_1_ResolutionStep" 18 + "$ref": "#/components/schemas/ResolutionStep_v1_0_1_ResolutionStep" 19 19 } 20 20 } 21 21 } ··· 35 35 "content": { 36 36 "application/json": { 37 37 "schema": { 38 - "$ref": "#/components/schemas/ResolutionStep_v1_0_1_ResolutionStep_v1_0_1_ActionParameters" 38 + "$ref": "#/components/schemas/ResolutionStep_v1_0_1_ActionParameters" 39 39 } 40 40 } 41 41 } ··· 46 46 }, 47 47 "components": { 48 48 "schemas": { 49 - "ResolutionStep_v1_0_1_ResolutionStep_v1_0_1_ActionParameters": { 49 + "ResolutionStep_v1_0_1_ActionParameters": { 50 50 "type": "object", 51 51 "properties": { 52 52 "ActionId": { 53 53 "type": "string" 54 54 }, 55 55 "ActionType": { 56 - "$ref": "#/components/schemas/ResolutionStep_v1_0_1_ResolutionStep_v1_0_1_ResolutionType" 56 + "$ref": "#/components/schemas/ResolutionStep_v1_0_1_ResolutionType" 57 57 } 58 58 } 59 59 }, 60 - "ResolutionStep_v1_0_1_ResolutionStep_v1_0_1_ResolutionStep": { 60 + "ResolutionStep_v1_0_1_ResolutionStep": { 61 61 "type": "object", 62 62 "properties": { 63 63 "ResolutionType": { 64 64 "oneOf": [ 65 65 { 66 - "$ref": "#/components/schemas/ResolutionStep_v1_0_1_ResolutionStep_v1_0_1_ResolutionType" 66 + "$ref": "#/components/schemas/ResolutionStep_v1_0_1_ResolutionType" 67 67 } 68 68 ] 69 69 }, ··· 73 73 } 74 74 } 75 75 }, 76 - "ResolutionStep_v1_0_1_ResolutionStep_v1_0_1_ResolutionType": { 76 + "ResolutionStep_v1_0_1_ResolutionType": { 77 77 "type": "string", 78 78 "enum": [ 79 79 "ContactVendor",
+28 -4
packages/json-schema-ref-parser/src/bundle.ts
··· 586 586 } catch { 587 587 // Ignore errors 588 588 } 589 - const proposed = `${proposedBase}_${lastToken(entry.hash)}`; 589 + 590 + // Try without prefix first (cleaner names) 591 + const schemaName = lastToken(entry.hash); 592 + let proposed = schemaName; 593 + 594 + // Check if this name would conflict with existing schemas from other files 595 + if (!usedNamesByObj.has(container)) { 596 + usedNamesByObj.set(container, new Set<string>(Object.keys(container || {}))); 597 + } 598 + const used = usedNamesByObj.get(container)!; 599 + 600 + // If the name is already used, add the file prefix 601 + if (used.has(proposed)) { 602 + proposed = `${proposedBase}_${schemaName}`; 603 + } 604 + 590 605 defName = uniqueName(container, proposed); 591 606 namesForPrefix.set(targetKey, defName); 592 607 // Store the resolved value under the container ··· 634 649 ].filter((c) => c.obj && typeof c.obj === 'object'); 635 650 636 651 // Build a map of simple schema names to their hoisted full names 637 - // E.g., "SchemaB" -> "file2_SchemaB" 652 + // Since we now use unprefixed names when there's no conflict, we need to handle both cases 638 653 const schemaNameMap = new Map<string, Array<{ fullName: string; prefix: string }>>(); 639 654 640 655 for (const container of containers) { 641 656 for (const fullName of Object.keys(container.obj)) { 642 - // Extract the original schema name from the hoisted name 643 - // Hoisted names are typically "filename_SchemaName" 657 + // First, add the full name itself as a candidate (for unprefixed schemas) 658 + if (!schemaNameMap.has(fullName)) { 659 + schemaNameMap.set(fullName, []); 660 + } 661 + schemaNameMap.get(fullName)!.push({ 662 + fullName, 663 + prefix: container.prefix, 664 + }); 665 + 666 + // Extract the original schema name from the hoisted name if it has a prefix 667 + // Hoisted names with conflicts are "filename_SchemaName" 644 668 // Try to match the pattern and extract SchemaName 645 669 const parts = fullName.split('_'); 646 670 if (parts.length >= 2) {