UNPKG

@typespec/openapi3

Version:

TypeSpec library for emitting OpenAPI 3.0 and OpenAPI 3.1 from the TypeSpec REST protocol binding and converting OpenAPI3 to TypeSpec

138 lines 5.62 kB
import { ignoreDiagnostics, navigateTypesInNamespace, } from "@typespec/compiler"; import { TwoLevelMap } from "@typespec/compiler/utils"; import { Visibility, getHttpOperation, resolveRequestVisibility, } from "@typespec/http"; export function resolveVisibilityUsage(program, metadataInfo, root, omitUnreachableTypes) { const usages = new Map(); addUsagesInContainer(program, metadataInfo, root, usages); const reachableTypes = new Set(usages.keys()); if (!omitUnreachableTypes) { // Evaluate all unreferenced types and the types they reference with Visibility.All const trackType = (type) => { if (!usages.has(type)) { navigateReferencedTypes(type, Visibility.All, (type, vis) => trackUsageExact(usages, type, vis)); } }; navigateTypesInNamespace(root, { model: trackType, scalar: trackType, enum: trackType, union: trackType, }); } return { getUsage: (type) => { const used = usages.get(type); if (used === undefined) { return undefined; } return usages.get(type); }, isUnreachable: (type) => { return !reachableTypes.has(type); }, manuallyTrack: (type, visibility) => { for (const vis of visibility) { trackUsageExact(usages, type, vis); } reachableTypes.add(type); }, }; } function addUsagesInContainer(program, metadataInfo, type, usages) { switch (type.kind) { case "Namespace": addUsagesInNamespace(program, metadataInfo, type, usages); break; case "Interface": addUsagesInInterface(program, metadataInfo, type, usages); break; case "Operation": addUsagesInOperation(program, metadataInfo, type, usages); break; } } function trackUsage(metadataInfo, usages, type, usage) { const effective = metadataInfo.getEffectivePayloadType(type, usage); trackUsageExact(usages, type, usage); if (effective !== type) { trackUsageExact(usages, effective, usage); } } function trackUsageExact(usages, type, usage) { const existingFlag = usages.get(type) ?? new Set(); existingFlag.add(usage); usages.set(type, existingFlag); } function addUsagesInNamespace(program, metadataInfo, namespace, usages) { for (const subNamespace of namespace.namespaces.values()) { addUsagesInNamespace(program, metadataInfo, subNamespace, usages); } for (const Interface of namespace.interfaces.values()) { addUsagesInInterface(program, metadataInfo, Interface, usages); } for (const operation of namespace.operations.values()) { addUsagesInOperation(program, metadataInfo, operation, usages); } } function addUsagesInInterface(program, metadataInfo, Interface, usages) { for (const operation of Interface.operations.values()) { addUsagesInOperation(program, metadataInfo, operation, usages); } } function addUsagesInOperation(program, metadataInfo, operation, usages) { const httpOperation = ignoreDiagnostics(getHttpOperation(program, operation)); const visibility = resolveRequestVisibility(program, operation, httpOperation.verb); if (httpOperation.parameters.body) { navigateReferencedTypes(httpOperation.parameters.body.type, visibility, (type, vis) => trackUsage(metadataInfo, usages, type, vis)); } for (const param of httpOperation.parameters.parameters) { navigateReferencedTypes(param.param, visibility, (type, vis) => trackUsage(metadataInfo, usages, type, vis)); } navigateReferencedTypes(operation.returnType, Visibility.Read, (type, vis) => trackUsage(metadataInfo, usages, type, vis)); } function navigateReferencedTypes(type, usage, callback, visited = new TwoLevelMap()) { if (visited.get(type)?.get(usage)) { return; } visited.getOrAdd(type, usage, () => true); switch (type.kind) { case "Model": callback(type, usage); navigateIterable(type.properties, usage, callback, visited); if (type.baseModel) { navigateReferencedTypes(type.baseModel, usage, callback, visited); } navigateIterable(type.derivedModels, usage, callback, visited); if (type.baseModel) { navigateReferencedTypes(type.baseModel, usage, callback, visited); } if (type.indexer) { if (type.indexer.key.name === "integer") { navigateReferencedTypes(type.indexer.value, usage | Visibility.Item, callback, visited); } else { navigateReferencedTypes(type.indexer.value, usage, callback, visited); } } break; case "ModelProperty": navigateReferencedTypes(type.type, usage, callback, visited); break; case "Union": callback(type, usage); navigateIterable(type.variants, usage, callback, visited); break; case "UnionVariant": navigateReferencedTypes(type.type, usage, callback, visited); break; default: callback(type, usage); break; } } function navigateIterable(map, usage, callback, visited) { for (const type of map.values()) { navigateReferencedTypes(type, usage, callback, visited); } } //# sourceMappingURL=visibility-usage.js.map