@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
JavaScript
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