UNPKG

@redocly/openapi-core

Version:

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

255 lines 9.86 kB
import { isAbsoluteUrl, replaceRef, isExternalValue, isRef, refBaseName } from '../ref-utils.js'; import { makeRefId } from '../utils/make-ref-id.js'; import { reportUnresolvedRef } from '../rules/common/no-unresolved-refs.js'; import { isTruthy } from '../utils/is-truthy.js'; import { dequal } from '../utils/dequal.js'; export function mapTypeToComponent(typeName, version) { switch (version) { case 'oas3': switch (typeName) { case 'Schema': return 'schemas'; case 'Parameter': return 'parameters'; case 'Response': return 'responses'; case 'Example': return 'examples'; case 'RequestBody': return 'requestBodies'; case 'Header': return 'headers'; case 'SecuritySchema': return 'securitySchemes'; case 'Link': return 'links'; case 'Callback': return 'callbacks'; default: return null; } case 'oas2': switch (typeName) { case 'Schema': return 'definitions'; case 'Parameter': return 'parameters'; case 'Response': return 'responses'; default: return null; } case 'async2': switch (typeName) { case 'Schema': return 'schemas'; case 'Parameter': return 'parameters'; default: return null; } case 'async3': switch (typeName) { case 'Schema': return 'schemas'; case 'Parameter': return 'parameters'; default: return null; } case 'arazzo1': switch (typeName) { case 'Root.workflows_items.parameters_items': case 'Root.workflows_items.steps_items.parameters_items': return 'parameters'; default: return null; } case 'overlay1': switch (typeName) { default: return null; } case 'openrpc1': switch (typeName) { case 'ContentDescriptor': return 'contentDescriptors'; case 'Schema': return 'schemas'; case 'Example': return 'examples'; case 'Link': return 'links'; case 'ErrorObject': return 'errors'; case 'ExamplePairing': return 'examplePairingObjects'; case 'Tag': return 'tags'; default: return null; } } } export function makeBundleVisitor(version, dereference, rootDocument, resolvedRefMap, keepUrlRefs) { let components; let rootLocation; const visitor = { ref: { leave(node, ctx, resolved) { if (!resolved.location || resolved.node === undefined) { reportUnresolvedRef(resolved, ctx.report, ctx.location); return; } if (resolved.location.source === rootDocument.source && resolved.location.source === ctx.location.source && ctx.type.name !== 'scalar' && !dereference) { // Normalize explicit self-file refs to internal pointer if (node.$ref !== resolved.location.pointer) { node.$ref = resolved.location.pointer; } return; } if (keepUrlRefs && isAbsoluteUrl(node.$ref)) { return; } const componentType = mapTypeToComponent(ctx.type.name, version); if (!componentType) { replaceRef(node, resolved, ctx); } else { if (dereference) { saveComponent(componentType, resolved, ctx); replaceRef(node, resolved, ctx); } else { node.$ref = saveComponent(componentType, resolved, ctx); resolveBundledComponent(node, resolved, ctx); } } }, }, Example: { leave(node, ctx) { if (isExternalValue(node) && node.value === undefined) { const resolved = ctx.resolve({ $ref: node.externalValue }); if (!resolved.location || resolved.node === undefined) { reportUnresolvedRef(resolved, ctx.report, ctx.location); return; } if (keepUrlRefs && isAbsoluteUrl(node.externalValue)) { return; } node.value = ctx.resolve({ $ref: node.externalValue }).node; delete node.externalValue; } }, }, Root: { enter(root, ctx) { rootLocation = ctx.location; if (version === 'oas3') { components = root.components = root.components || {}; } else if (version === 'oas2') { components = root; } else if (version === 'async2') { components = root.components = root.components || {}; } else if (version === 'async3') { components = root.components = root.components || {}; } else if (version === 'arazzo1') { components = root.components = root.components || {}; } else if (version === 'openrpc1') { components = root.components = root.components || {}; } }, }, }; if (version === 'oas3') { visitor.DiscriminatorMapping = { leave(mapping, ctx) { for (const name of Object.keys(mapping)) { const $ref = mapping[name]; const resolved = ctx.resolve({ $ref }); if (!resolved.location || resolved.node === undefined) { reportUnresolvedRef(resolved, ctx.report, ctx.location.child(name)); return; } const componentType = mapTypeToComponent('Schema', version); mapping[name] = saveComponent(componentType, resolved, ctx); } }, }; } function resolveBundledComponent(node, resolved, ctx) { const newRefId = makeRefId(ctx.location.source.absoluteRef, node.$ref); resolvedRefMap.set(newRefId, { document: rootDocument, isRemote: false, node: resolved.node, nodePointer: node.$ref, resolved: true, }); } function saveComponent(componentType, target, ctx) { components[componentType] = components[componentType] || {}; const name = getComponentName(target, componentType, ctx); components[componentType][name] = target.node; if (version === 'oas3' || version === 'async2' || version === 'async3' || version === 'openrpc1') { return `#/components/${componentType}/${name}`; } else { return `#/${componentType}/${name}`; } } function isEqualOrEqualRef(node, target, ctx) { if (isRef(node) && ctx.resolve(node, rootLocation.absolutePointer).location?.absolutePointer === target.location.absolutePointer) { return true; } return dequal(node, target.node); } function getComponentName(target, componentType, ctx) { const [fileRef, pointer] = [target.location.source.absoluteRef, target.location.pointer]; const componentsGroup = components[componentType]; let name = ''; const refParts = pointer.slice(2).split('/').filter(isTruthy); // slice(2) removes "#/" while (refParts.length > 0) { name = refParts.pop() + (name ? `-${name}` : ''); if (!componentsGroup || !componentsGroup[name] || isEqualOrEqualRef(componentsGroup[name], target, ctx)) { return name; } } name = refBaseName(fileRef) + (name ? `_${name}` : ''); if (!componentsGroup[name] || isEqualOrEqualRef(componentsGroup[name], target, ctx)) { return name; } const prevName = name; let serialId = 2; while (componentsGroup[name] && !isEqualOrEqualRef(componentsGroup[name], target, ctx)) { name = `${prevName}-${serialId}`; serialId++; } if (!componentsGroup[name]) { ctx.report({ message: `Two schemas are referenced with the same name but different content. Renamed ${prevName} to ${name}.`, location: ctx.location, forceSeverity: 'warn', }); } return name; } return visitor; } //# sourceMappingURL=bundle-visitor.js.map