diff --git a/packages/openapi-ts-tests/main/test/3.0.x.test.ts b/packages/openapi-ts-tests/main/test/3.0.x.test.ts index 7f2d5e89f..22fff3851 100644 --- a/packages/openapi-ts-tests/main/test/3.0.x.test.ts +++ b/packages/openapi-ts-tests/main/test/3.0.x.test.ts @@ -618,11 +618,10 @@ describe(`OpenAPI ${version}`, () => { output: 'transforms-schemas-name-collision', parser: { transforms: { - schemaName: (name: string) => + schemaName: (name: string) => // Try to rename all _vX_User schemas to "User" // This should cause collisions since "User" already exists - name.replace(/_v\d+_User$/, '') - , + name.replace(/_v\d+_User$/, ''), }, }, plugins: ['@hey-api/typescript'], diff --git a/packages/shared/src/openApi/shared/transforms/schemas.ts b/packages/shared/src/openApi/shared/transforms/schemas.ts index 568bf3cc9..d89f810a3 100644 --- a/packages/shared/src/openApi/shared/transforms/schemas.ts +++ b/packages/shared/src/openApi/shared/transforms/schemas.ts @@ -8,9 +8,6 @@ type SchemaNameConfig = Parser['transforms']['schemaName']; /** * Recursively walks the entire spec object and replaces all $ref strings * according to the provided rename mapping. - * - * @param node - Current node being visited - * @param renameMap - Map from old pointer to new pointer */ const rewriteRefs = (node: unknown, renameMap: Record) => { if (node instanceof Array) { @@ -18,7 +15,6 @@ const rewriteRefs = (node: unknown, renameMap: Record) => { } else if (node && typeof node === 'object') { for (const [key, value] of Object.entries(node)) { if (key === '$ref' && typeof value === 'string' && value in renameMap) { - // Replace the $ref with the new name (node as Record)[key] = renameMap[value]; } else { rewriteRefs(value, renameMap); @@ -28,18 +24,9 @@ const rewriteRefs = (node: unknown, renameMap: Record) => { }; /** - * Applies the schema name transform to rename schema component keys and - * update all $ref pointers throughout the spec. - * - * This transform: - * 1. Iterates all schema keys in components.schemas (or definitions for Swagger 2.0) - * 2. Applies the name transformer to compute new names - * 3. Handles name collisions (skips rename if new name already exists) - * 4. Renames schema keys in the schemas object - * 5. Updates all $ref pointers throughout the spec to use the new names - * - * @param config - The schema name transformer - * @param spec - The OpenAPI spec object to transform + * Renames schema component keys and updates all $ref pointers throughout + * the spec. Handles collisions by skipping renames when the target name + * already exists or conflicts with another rename. */ export const schemaNameTransform = ({ config, @@ -62,63 +49,38 @@ export const schemaNameTransform = ({ return; } - // Build rename map: oldPointer -> newPointer const renameMap: Record = {}; const newNames = new Set(); + const namingConfig = { name: config }; - // Create a simple config object for applyNaming - const namingConfig = typeof config === 'function' ? { name: config } : { name: config }; - - // First pass: compute all new names and check for collisions for (const oldName of Object.keys(schemasObj)) { const newName = applyNaming(oldName, namingConfig); - // Skip if name doesn't change if (newName === oldName) { continue; } - // Skip if new name collides with an existing schema if (newName in schemasObj) { continue; } - // Skip if new name collides with another renamed schema if (newNames.has(newName)) { continue; } - // Record the rename renameMap[`${schemasPointerNamespace}${oldName}`] = `${schemasPointerNamespace}${newName}`; newNames.add(newName); } - // Second pass: rename schema keys - // We need to be careful about the order to avoid overwriting - const renamedSchemas: Record = {}; - const processedOldNames = new Set(); - for (const [oldPointer, newPointer] of Object.entries(renameMap)) { const oldName = oldPointer.slice(schemasPointerNamespace.length); const newName = newPointer.slice(schemasPointerNamespace.length); + const schema = schemasObj[oldName]; - // Store the schema under the new name - renamedSchemas[newName] = schemasObj[oldName]; - processedOldNames.add(oldName); - } - - // Add all schemas that weren't renamed - for (const [name, schema] of Object.entries(schemasObj)) { - if (!processedOldNames.has(name)) { - renamedSchemas[name] = schema; - } + delete schemasObj[oldName]; + schemasObj[newName] = schema; } - // Replace the entire schemas object with the renamed version - Object.keys(schemasObj).forEach((key) => delete schemasObj[key]); - Object.assign(schemasObj, renamedSchemas); - - // Third pass: rewrite all $ref pointers throughout the spec if (Object.keys(renameMap).length > 0) { rewriteRefs(spec, renameMap); }