UNPKG

@redocly/openapi-core

Version:

See https://github.com/Redocly/redocly-cli

121 lines 4.56 kB
import { isRef, parseRef } from '../../ref-utils.js'; import { isEmptyObject } from '../../utils/is-empty-object.js'; import { hasComponent } from '../../utils/oas-has-component.js'; function getComponentKey(pointer) { if (!pointer.startsWith('#/components/')) return; const [_component, type, name] = parseRef(pointer).pointer; if (!type || !name) return; return `${type}/${name}`; } export const RemoveUnusedComponents = () => { const components = new Map(); function registerComponent(componentType, name, referencesDiscriminator = false) { const key = `${componentType}/${name}`; components.set(key, { usedIn: components.get(key)?.usedIn ?? [], componentType, name, referencesDiscriminator, }); } function removeUnusedComponents(root, removedKeys = new Set()) { const removedCountBefore = removedKeys.size; for (const [key, { usedIn, name, componentType, referencesDiscriminator }] of components) { const used = usedIn.some((sourceKey) => sourceKey !== key && !removedKeys.has(sourceKey)) || referencesDiscriminator; if (!used && componentType && root.components && hasComponent(root.components, componentType)) { removedKeys.add(key); const componentChild = root.components[componentType]; delete componentChild[name]; components.delete(key); if (isEmptyObject(componentChild)) { delete root.components[componentType]; } } } return removedKeys.size > removedCountBefore ? removeUnusedComponents(root, removedKeys) : removedKeys.size; } return { ref: { leave(ref, { location, type, key }) { if ([ 'Schema', 'Header', 'Parameter', 'Response', 'Example', 'RequestBody', 'MediaTypesMap', ].includes(type.name)) { const targetPointer = getComponentKey(ref.$ref); if (!targetPointer) return; const sourcePointer = getComponentKey(location.pointer) ?? location.pointer; const registered = components.get(targetPointer); if (registered) { registered.usedIn.push(sourcePointer); } else { components.set(targetPointer, { usedIn: [sourcePointer], name: key.toString(), }); } } }, }, Root: { leave(root, ctx) { const data = ctx.getVisitorData(); data.removedCount = removeUnusedComponents(root); if (isEmptyObject(root.components)) { delete root.components; } }, }, NamedSchemas: { Schema(schema, ctx) { const referencesDiscriminator = schema.allOf?.some((ref) => isRef(ref) && ctx.resolve(ref)?.node?.discriminator); registerComponent('schemas', ctx.key.toString(), referencesDiscriminator); }, }, NamedParameters: { Parameter(_parameter, { key }) { registerComponent('parameters', key.toString()); }, }, NamedResponses: { Response(_response, { key }) { registerComponent('responses', key.toString()); }, }, NamedExamples: { Example(_example, { key }) { registerComponent('examples', key.toString()); }, }, NamedRequestBodies: { RequestBody(_requestBody, { key }) { registerComponent('requestBodies', key.toString()); }, }, NamedHeaders: { Header(_header, { key }) { registerComponent('headers', key.toString()); }, }, NamedMediaTypes: { MediaTypesMap(_mediaTypesMap, { key }) { registerComponent('mediaTypes', key.toString()); }, }, }; }; //# sourceMappingURL=remove-unused-components.js.map