Skip to content

Commit 90c98cf

Browse files
authored
Filtering unused schemas
1 parent 6dbd9d5 commit 90c98cf

File tree

1 file changed

+67
-10
lines changed

1 file changed

+67
-10
lines changed

openapi-format.js

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -638,21 +638,78 @@ async function openapiFilter(oaObj, options) {
638638

639639
// Collect unused components
640640
const optFs = get(options, 'filterSet.unusedComponents', []) || [];
641+
642+
// First, identify components that are directly unused (not referenced anywhere)
641643
unusedComp.schemas = Object.keys(comps.schemas || {}).filter(key => !comps.schemas[key].used);
642-
if (optFs.includes('schemas')) options.unusedComp.schemas = [...options.unusedComp.schemas, ...unusedComp.schemas];
643644
unusedComp.responses = Object.keys(comps.responses || {}).filter(key => !comps.responses[key].used);
644-
if (optFs.includes('responses'))
645-
options.unusedComp.responses = [...options.unusedComp.responses, ...unusedComp.responses];
646645
unusedComp.parameters = Object.keys(comps.parameters || {}).filter(key => !comps.parameters[key].used);
647-
if (optFs.includes('parameters'))
648-
options.unusedComp.parameters = [...options.unusedComp.parameters, ...unusedComp.parameters];
649646
unusedComp.examples = Object.keys(comps.examples || {}).filter(key => !comps.examples[key].used);
650-
if (optFs.includes('examples'))
651-
options.unusedComp.examples = [...options.unusedComp.examples, ...unusedComp.examples];
652647
unusedComp.requestBodies = Object.keys(comps.requestBodies || {}).filter(key => !comps.requestBodies[key].used);
653-
if (optFs.includes('requestBodies'))
654-
options.unusedComp.requestBodies = [...options.unusedComp.requestBodies, ...unusedComp.requestBodies];
655648
unusedComp.headers = Object.keys(comps.headers || {}).filter(key => !comps.headers[key].used);
649+
650+
// Then, identify components that are only used by other unused components
651+
let foundNewUnused = true;
652+
while (foundNewUnused) {
653+
foundNewUnused = false;
654+
655+
// Check each component type
656+
for (const compType of ['schemas', 'responses', 'parameters', 'examples', 'requestBodies', 'headers']) {
657+
// Get all components of this type that are currently marked as used
658+
const usedComps = Object.keys(comps[compType] || {}).filter(
659+
key => comps[compType][key].used && !unusedComp[compType].includes(key)
660+
);
661+
662+
// For each used component, check if it's only used by unused components
663+
for (const compKey of usedComps) {
664+
let isOnlyUsedByUnusedComps = true;
665+
666+
// Check if this component is used in paths (directly used)
667+
traverse(jsonObj.paths || {}).forEach(function(node) {
668+
if (this.key === '$ref' && node === `#/components/${compType}/${compKey}`) {
669+
isOnlyUsedByUnusedComps = false;
670+
this.stop();
671+
}
672+
});
673+
674+
if (isOnlyUsedByUnusedComps) {
675+
// Check if this component is used by any component that is not in the unused list
676+
for (const otherCompType of ['schemas', 'responses', 'parameters', 'examples', 'requestBodies', 'headers']) {
677+
const otherUsedComps = Object.keys(comps[otherCompType] || {}).filter(
678+
key => comps[otherCompType][key].used && !unusedComp[otherCompType].includes(key)
679+
);
680+
681+
for (const otherCompKey of otherUsedComps) {
682+
if (otherCompKey === compKey && otherCompType === compType) continue; // Skip self-reference
683+
684+
traverse(jsonObj.components?.[otherCompType]?.[otherCompKey] || {}).forEach(function(node) {
685+
if (this.key === '$ref' && node === `#/components/${compType}/${compKey}`) {
686+
isOnlyUsedByUnusedComps = false;
687+
this.stop();
688+
}
689+
});
690+
691+
if (!isOnlyUsedByUnusedComps) break;
692+
}
693+
694+
if (!isOnlyUsedByUnusedComps) break;
695+
}
696+
}
697+
698+
// If this component is only used by unused components, mark it as unused
699+
if (isOnlyUsedByUnusedComps) {
700+
unusedComp[compType].push(compKey);
701+
foundNewUnused = true;
702+
}
703+
}
704+
}
705+
}
706+
707+
// Update options.unusedComp with the newly identified unused components
708+
if (optFs.includes('schemas')) options.unusedComp.schemas = [...options.unusedComp.schemas, ...unusedComp.schemas];
709+
if (optFs.includes('responses')) options.unusedComp.responses = [...options.unusedComp.responses, ...unusedComp.responses];
710+
if (optFs.includes('parameters')) options.unusedComp.parameters = [...options.unusedComp.parameters, ...unusedComp.parameters];
711+
if (optFs.includes('examples')) options.unusedComp.examples = [...options.unusedComp.examples, ...unusedComp.examples];
712+
if (optFs.includes('requestBodies')) options.unusedComp.requestBodies = [...options.unusedComp.requestBodies, ...unusedComp.requestBodies];
656713
if (optFs.includes('headers')) options.unusedComp.headers = [...options.unusedComp.headers, ...unusedComp.headers];
657714

658715
// Update unusedComp.meta.total after each recursion
@@ -717,7 +774,7 @@ async function openapiFilter(oaObj, options) {
717774
if (
718775
Array.isArray(filterSet.preserveEmptyObjects) &&
719776
((!['security', 'schemas', 'default'].includes(this.parent.key) &&
720-
!filterSet.preserveEmptyObjects.includes(this.key)) ||
777+
!filterSet.preserveEmptyObjects.includes(this.key)) ||
721778
!filterSet.preserveEmptyObjects.some(v => this.path.includes(v)))
722779
) {
723780
// debugFilterStep = 'Filter - Remove empty objects'

0 commit comments

Comments
 (0)