@redocly/openapi-core
Version:
See https://github.com/Redocly/redocly-cli
121 lines • 4.56 kB
JavaScript
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