@azure-tools/typespec-autorest 0.42.0-dev.4 → 0.42.0-dev.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,44 +1,14 @@
1
1
  import { extractLroStates, getArmResourceIdentifierConfig, getAsEmbeddingVector, getLroMetadata, getPagedResult, getUnionAsEnum, isFixed, } from "@azure-tools/typespec-azure-core";
2
- import { createSdkContext, getClientNameOverride, shouldFlattenProperty, } from "@azure-tools/typespec-client-generator-core";
3
- import { NoTarget, SyntaxKind, TwoLevelMap, compilerAssert, emitFile, getAllTags, getDirectoryPath, getDiscriminator, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMinItems, getMinLength, getMinValue, getNamespaceFullName, getPattern, getProjectedName, getProperty, getPropertyType, getRelativePathFromDirectory, getRootLength, getService, getSummary, getVisibility, ignoreDiagnostics, interpolatePath, isArrayModelType, isDeprecated, isErrorModel, isErrorType, isGlobalNamespace, isNeverType, isNullType, isNumericType, isRecordModelType, isSecret, isService, isStringType, isTemplateDeclaration, isTemplateDeclarationOrInstance, isVoidType, listServices, navigateTypesInNamespace, projectProgram, resolveEncodedName, resolvePath, stringTemplateToString, } from "@typespec/compiler";
2
+ import { shouldFlattenProperty } from "@azure-tools/typespec-client-generator-core";
3
+ import { NoTarget, SyntaxKind, compilerAssert, createDiagnosticCollector, getAllTags, getDirectoryPath, getDiscriminator, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMinItems, getMinLength, getMinValue, getPattern, getProjectedName, getProperty, getPropertyType, getRelativePathFromDirectory, getRootLength, getSummary, getVisibility, ignoreDiagnostics, interpolatePath, isArrayModelType, isDeprecated, isErrorModel, isErrorType, isGlobalNamespace, isNeverType, isNullType, isNumericType, isRecordModelType, isSecret, isService, isStringType, isTemplateDeclaration, isTemplateDeclarationOrInstance, isVoidType, navigateTypesInNamespace, resolveEncodedName, resolvePath, stringTemplateToString, } from "@typespec/compiler";
4
+ import { TwoLevelMap } from "@typespec/compiler/utils";
4
5
  import { Visibility, createMetadataInfo, getAllHttpServices, getAuthentication, getHeaderFieldOptions, getQueryParamOptions, getServers, getStatusCodeDescription, getVisibilitySuffix, isContentTypeHeader, isSharedRoute, reportIfNoRoutes, resolveRequestVisibility, } from "@typespec/http";
5
6
  import { checkDuplicateTypeName, getExtensions, getExternalDocs, getOpenAPITypeName, getParameterKey, isReadonlyProperty, resolveInfo, shouldInline, } from "@typespec/openapi";
6
- import { buildVersionProjections } from "@typespec/versioning";
7
7
  import { AutorestOpenAPISchema } from "./autorest-openapi-schema.js";
8
8
  import { getExamples, getRef } from "./decorators.js";
9
9
  import { sortWithJsonSchema } from "./json-schema-sorter/sorter.js";
10
- import { getTracer, reportDiagnostic } from "./lib.js";
11
- import { resolveOperationId } from "./utils.js";
12
- const defaultOptions = {
13
- "output-file": "{azure-resource-provider-folder}/{service-name}/{version-status}/{version}/openapi.json",
14
- "new-line": "lf",
15
- "include-x-typespec-name": "never",
16
- };
17
- export async function $onEmit(context) {
18
- const resolvedOptions = { ...defaultOptions, ...context.options };
19
- const tcgcSdkContext = createSdkContext(context, "@azure-tools/typespec-autorest");
20
- const armTypesDir = interpolatePath(resolvedOptions["arm-types-dir"] ?? "{project-root}/../../common-types/resource-management", {
21
- "project-root": context.program.projectRoot,
22
- "emitter-output-dir": context.emitterOutputDir,
23
- });
24
- const options = {
25
- outputFile: resolvedOptions["output-file"],
26
- outputDir: context.emitterOutputDir,
27
- azureResourceProviderFolder: resolvedOptions["azure-resource-provider-folder"],
28
- examplesDirectory: resolvedOptions["examples-directory"],
29
- version: resolvedOptions["version"],
30
- newLine: resolvedOptions["new-line"],
31
- omitUnreachableTypes: resolvedOptions["omit-unreachable-types"],
32
- includeXTypeSpecName: resolvedOptions["include-x-typespec-name"],
33
- armTypesDir,
34
- useReadOnlyStatusSchema: resolvedOptions["use-read-only-status-schema"],
35
- };
36
- const emitter = createOAPIEmitter(context.program, tcgcSdkContext, options);
37
- await emitter.emitOpenAPI();
38
- }
39
- function getEmitterDetails(program) {
40
- return [{ emitter: "@azure-tools/typespec-autorest" }];
41
- }
10
+ import { createDiagnostic, reportDiagnostic } from "./lib.js";
11
+ import { getClientName, resolveOperationId } from "./utils.js";
42
12
  /**
43
13
  * Represents a node that will hold a JSON reference. The value is computed
44
14
  * at the end so that we can defer decisions about the name that is
@@ -51,113 +21,111 @@ class Ref {
51
21
  return this.value;
52
22
  }
53
23
  }
54
- function createOAPIEmitter(program, tcgcSdkContext, options) {
55
- const tracer = getTracer(program);
56
- tracer.trace("options", JSON.stringify(options, null, 2));
24
+ export async function getOpenAPIForService(context, options) {
25
+ const { program, service } = context;
57
26
  const typeNameOptions = {
58
27
  // shorten type names by removing TypeSpec and service namespace
59
28
  namespaceFilter(ns) {
60
29
  return !isService(program, ns);
61
30
  },
62
31
  };
63
- let root;
64
- let currentService;
32
+ const info = resolveInfo(program, service.type);
33
+ const auth = processAuth(service.type);
34
+ const root = {
35
+ swagger: "2.0",
36
+ info: {
37
+ title: "(title)",
38
+ ...info,
39
+ version: context.version ?? info?.version ?? "0000-00-00",
40
+ "x-typespec-generated": [{ emitter: "@azure-tools/typespec-autorest" }],
41
+ },
42
+ schemes: ["https"],
43
+ ...resolveHost(program, service.type),
44
+ externalDocs: getExternalDocs(program, service.type),
45
+ produces: [], // Pre-initialize produces and consumes so that
46
+ consumes: [], // they show up at the top of the document
47
+ security: auth?.security,
48
+ securityDefinitions: auth?.securitySchemes ?? {},
49
+ tags: [],
50
+ paths: {},
51
+ "x-ms-paths": {},
52
+ definitions: {},
53
+ parameters: {},
54
+ };
65
55
  let currentEndpoint;
66
56
  let currentConsumes;
67
57
  let currentProduces;
68
- let metadataInfo;
58
+ const metadataInfo = createMetadataInfo(program, {
59
+ canonicalVisibility: Visibility.Read,
60
+ canShareProperty: canSharePropertyUsingReadonlyOrXMSMutability,
61
+ });
69
62
  // Keep a map of all Types+Visibility combinations that were encountered
70
63
  // that need schema definitions.
71
- let pendingSchemas = new TwoLevelMap();
64
+ const pendingSchemas = new TwoLevelMap();
72
65
  // Reuse a single ref object per Type+Visibility combination.
73
- let refs = new TwoLevelMap();
66
+ const refs = new TwoLevelMap();
74
67
  // Keep track of inline types still in the process of having their schema computed
75
68
  // This is used to detect cycles in inline types, which is an
76
- let inProgressInlineTypes = new Set();
69
+ const inProgressInlineTypes = new Set();
77
70
  // Map model properties that represent shared parameters to their parameter
78
71
  // definition that will go in #/parameters. Inlined parameters do not go in
79
72
  // this map.
80
- let params;
73
+ const params = new Map();
81
74
  // Keep track of models that have had properties spread into parameters. We won't
82
75
  // consider these unreferenced when emitting unreferenced types.
83
- let paramModels;
76
+ const paramModels = new Set();
84
77
  // De-dupe the per-endpoint tags that will be added into the #/tags
85
- let tags;
78
+ const tags = new Set();
86
79
  // The set of produces/consumes values found in all operations
87
80
  const globalProduces = new Set(["application/json"]);
88
81
  const globalConsumes = new Set(["application/json"]);
89
- let operationExamplesMap;
90
- let operationIdsWithExample;
91
- let outputFile;
92
- let context;
93
- async function emitOpenAPI() {
94
- const services = listServices(program);
95
- if (services.length === 0) {
96
- services.push({ type: program.getGlobalNamespaceType() });
97
- }
98
- for (const service of services) {
99
- currentService = service;
100
- const originalProgram = program;
101
- const versions = buildVersionProjections(program, service.type).filter((v) => !options.version || options.version === v.version);
102
- for (const record of versions) {
103
- let projectedProgram;
104
- if (record.projections.length > 0) {
105
- projectedProgram = program = projectProgram(originalProgram, record.projections);
106
- }
107
- context = {
108
- program,
109
- service,
110
- version: record.version,
111
- getClientName,
112
- };
113
- const projectedServiceNs = projectedProgram
114
- ? projectedProgram.projector.projectedTypes.get(service.type)
115
- : service.type;
116
- await emitOpenAPIFromVersion(projectedServiceNs === program.getGlobalNamespaceType()
117
- ? { type: program.getGlobalNamespaceType() }
118
- : getService(program, projectedServiceNs), services.length > 1, record.version);
119
- }
120
- }
121
- }
122
- return { emitOpenAPI };
123
- function initializeEmitter(service, multipleService, version) {
124
- const auth = processAuth(service.type);
125
- const info = resolveInfo(program, service.type);
126
- root = {
127
- swagger: "2.0",
128
- info: {
129
- title: "(title)",
130
- ...info,
131
- version: version ?? info?.version ?? "0000-00-00",
132
- "x-typespec-generated": getEmitterDetails(program),
133
- },
134
- schemes: ["https"],
135
- ...resolveHost(program, service.type),
136
- externalDocs: getExternalDocs(program, service.type),
137
- produces: [], // Pre-initialize produces and consumes so that
138
- consumes: [], // they show up at the top of the document
139
- security: auth?.security,
140
- securityDefinitions: auth?.securitySchemes ?? {},
141
- tags: [],
142
- paths: {},
143
- "x-ms-paths": {},
144
- definitions: {},
145
- parameters: {},
146
- };
147
- pendingSchemas = new TwoLevelMap();
148
- refs = new TwoLevelMap();
149
- metadataInfo = createMetadataInfo(program, {
150
- canonicalVisibility: Visibility.Read,
151
- canShareProperty: canSharePropertyUsingReadonlyOrXMSMutability,
152
- });
153
- inProgressInlineTypes = new Set();
154
- params = new Map();
155
- paramModels = new Set();
156
- tags = new Set();
157
- operationExamplesMap = new Map();
158
- operationIdsWithExample = new Set();
159
- outputFile = resolveOutputFile(program, service, multipleService, options, version);
160
- }
82
+ const operationIdsWithExample = new Set();
83
+ const [exampleMap, diagnostics] = await loadExamples(program.host, options, context.version);
84
+ program.reportDiagnostics(diagnostics);
85
+ const services = ignoreDiagnostics(getAllHttpServices(program));
86
+ const routes = services[0].operations;
87
+ reportIfNoRoutes(program, routes);
88
+ routes.forEach(emitOperation);
89
+ emitParameters();
90
+ emitSchemas(service.type);
91
+ emitTags();
92
+ // Finalize global produces/consumes
93
+ if (globalProduces.size > 0) {
94
+ root.produces = [...globalProduces.values()];
95
+ }
96
+ else {
97
+ delete root.produces;
98
+ }
99
+ if (globalConsumes.size > 0) {
100
+ root.consumes = [...globalConsumes.values()];
101
+ }
102
+ else {
103
+ delete root.consumes;
104
+ }
105
+ // Clean up empty entries
106
+ if (root["x-ms-paths"] && Object.keys(root["x-ms-paths"]).length === 0) {
107
+ delete root["x-ms-paths"];
108
+ }
109
+ if (root.security && Object.keys(root.security).length === 0) {
110
+ delete root["security"];
111
+ }
112
+ if (root.securityDefinitions && Object.keys(root.securityDefinitions).length === 0) {
113
+ delete root["securityDefinitions"];
114
+ }
115
+ return {
116
+ document: root,
117
+ operationExamples: [...operationIdsWithExample]
118
+ .map((operationId) => {
119
+ const data = exampleMap.get(operationId);
120
+ if (data) {
121
+ return { operationId, examples: Object.values(data) };
122
+ }
123
+ else {
124
+ return undefined;
125
+ }
126
+ })
127
+ .filter((x) => x),
128
+ };
161
129
  function resolveHost(program, namespace) {
162
130
  const servers = getServers(program, namespace);
163
131
  if (servers === undefined) {
@@ -210,83 +178,6 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
210
178
  },
211
179
  };
212
180
  }
213
- async function emitOpenAPIFromVersion(service, multipleService, version) {
214
- initializeEmitter(service, multipleService, version);
215
- try {
216
- await loadExamples(version);
217
- const services = ignoreDiagnostics(getAllHttpServices(program));
218
- const routes = services[0].operations;
219
- reportIfNoRoutes(program, routes);
220
- routes.forEach(emitOperation);
221
- emitParameters();
222
- emitSchemas(service.type);
223
- emitTags();
224
- // Finalize global produces/consumes
225
- if (globalProduces.size > 0) {
226
- root.produces = [...globalProduces.values()];
227
- }
228
- else {
229
- delete root.produces;
230
- }
231
- if (globalConsumes.size > 0) {
232
- root.consumes = [...globalConsumes.values()];
233
- }
234
- else {
235
- delete root.consumes;
236
- }
237
- // Clean up empty entries
238
- if (root["x-ms-paths"] && Object.keys(root["x-ms-paths"]).length === 0) {
239
- delete root["x-ms-paths"];
240
- }
241
- if (root.security && Object.keys(root.security).length === 0) {
242
- delete root["security"];
243
- }
244
- if (root.securityDefinitions && Object.keys(root.securityDefinitions).length === 0) {
245
- delete root["securityDefinitions"];
246
- }
247
- if (!program.compilerOptions.noEmit && !program.hasError()) {
248
- // Sort the document
249
- const sortedRoot = sortOpenAPIDocument(root);
250
- // Write out the OpenAPI document to the output path
251
- await emitFile(program, {
252
- path: outputFile,
253
- content: prettierOutput(JSON.stringify(sortedRoot, null, 2)),
254
- newLine: options.newLine,
255
- });
256
- // Copy examples to the output directory
257
- if (options.examplesDirectory && operationIdsWithExample.size > 0) {
258
- const examplesPath = resolvePath(getDirectoryPath(outputFile), "examples");
259
- const exampleDir = version
260
- ? resolvePath(options.examplesDirectory, version)
261
- : resolvePath(options.examplesDirectory);
262
- await program.host.mkdirp(examplesPath);
263
- for (const operationId of operationIdsWithExample) {
264
- const examples = operationExamplesMap.get(operationId);
265
- if (examples) {
266
- for (const [_, fileName] of Object.entries(examples)) {
267
- const content = await program.host.readFile(resolvePath(exampleDir, fileName));
268
- await emitFile(program, {
269
- path: resolvePath(examplesPath, fileName),
270
- content: content.text,
271
- newLine: options.newLine,
272
- });
273
- }
274
- }
275
- }
276
- }
277
- }
278
- }
279
- catch (err) {
280
- if (err instanceof ErrorTypeFoundError) {
281
- // Return early, there must be a parse error if an ErrorType was
282
- // inserted into the TypeSpec output
283
- return;
284
- }
285
- else {
286
- throw err;
287
- }
288
- }
289
- }
290
181
  function parseNextLinkName(paged) {
291
182
  const pathComponents = paged.nextLinkSegments;
292
183
  if (pathComponents) {
@@ -418,12 +309,12 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
418
309
  currentEndpoint["x-ms-examples"] = examples.reduce((acc, example) => ({ ...acc, [example.title]: { $ref: example.pathOrUri } }), {});
419
310
  }
420
311
  if (options.examplesDirectory) {
421
- const examples = operationExamplesMap.get(currentEndpoint.operationId);
312
+ const examples = exampleMap.get(currentEndpoint.operationId);
422
313
  if (examples && currentEndpoint.operationId) {
423
314
  operationIdsWithExample.add(currentEndpoint.operationId);
424
315
  currentEndpoint["x-ms-examples"] = currentEndpoint["x-ms-examples"] || {};
425
- for (const [title, fileName] of Object.entries(examples)) {
426
- currentEndpoint["x-ms-examples"][title] = { $ref: `./examples/${fileName}` };
316
+ for (const [title, example] of Object.entries(examples)) {
317
+ currentEndpoint["x-ms-examples"][title] = { $ref: `./examples/${example.relativePath}` };
427
318
  }
428
319
  }
429
320
  }
@@ -581,7 +472,7 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
581
472
  if (getRootLength(absoluteRef) === 0) {
582
473
  return absoluteRef; // It is already relative.
583
474
  }
584
- return getRelativePathFromDirectory(getDirectoryPath(outputFile), absoluteRef, false);
475
+ return getRelativePathFromDirectory(getDirectoryPath(context.outputFile), absoluteRef, false);
585
476
  }
586
477
  function getSchemaOrRef(type, schemaContext) {
587
478
  const refUrl = getRef(program, type, { version: context.version, service: context.service });
@@ -696,11 +587,6 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
696
587
  // `resolveEncodedName` will return the original name if no @encodedName so we have to do that check
697
588
  return encodedName === type.name ? viaProjection ?? type.name : encodedName;
698
589
  }
699
- function getClientName(type) {
700
- const viaProjection = getProjectedName(program, type, "client");
701
- const clientName = getClientNameOverride(tcgcSdkContext, type);
702
- return clientName ?? viaProjection ?? type.name;
703
- }
704
590
  function emitEndpointParameters(methodParams, visibility) {
705
591
  const consumes = methodParams.body?.contentTypes ?? [];
706
592
  for (const httpOpParam of methodParams.parameters) {
@@ -956,66 +842,6 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
956
842
  root.tags.push({ name: tag });
957
843
  }
958
844
  }
959
- async function loadExamples(version) {
960
- if (options.examplesDirectory) {
961
- const exampleDir = version
962
- ? resolvePath(options.examplesDirectory, version)
963
- : resolvePath(options.examplesDirectory);
964
- try {
965
- if (!(await program.host.stat(exampleDir)).isDirectory())
966
- return;
967
- }
968
- catch (err) {
969
- reportDiagnostic(program, {
970
- code: "example-loading",
971
- messageId: "noDirectory",
972
- format: { directory: exampleDir },
973
- target: NoTarget,
974
- });
975
- return;
976
- }
977
- const exampleFiles = await program.host.readDir(exampleDir);
978
- for (const fileName of exampleFiles) {
979
- try {
980
- const exampleFile = await program.host.readFile(resolvePath(exampleDir, fileName));
981
- const example = JSON.parse(exampleFile.text);
982
- if (!example.operationId || !example.title) {
983
- reportDiagnostic(program, {
984
- code: "example-loading",
985
- messageId: "noOperationId",
986
- format: { filename: fileName },
987
- target: NoTarget,
988
- });
989
- continue;
990
- }
991
- if (!operationExamplesMap.has(example.operationId)) {
992
- operationExamplesMap.set(example.operationId, {});
993
- }
994
- const examples = operationExamplesMap.get(example.operationId);
995
- if (example.title in examples) {
996
- reportDiagnostic(program, {
997
- code: "duplicate-example-file",
998
- target: NoTarget,
999
- format: {
1000
- filename: fileName,
1001
- operationId: example.operationId,
1002
- title: example.title,
1003
- },
1004
- });
1005
- }
1006
- examples[example.title] = fileName;
1007
- }
1008
- catch (err) {
1009
- reportDiagnostic(program, {
1010
- code: "example-loading",
1011
- messageId: "default",
1012
- format: { filename: fileName, error: err?.toString() ?? "" },
1013
- target: NoTarget,
1014
- });
1015
- }
1016
- }
1017
- }
1018
- }
1019
845
  function getSchemaForType(type, schemaContext) {
1020
846
  const builtinType = getSchemaForLiterals(type);
1021
847
  if (builtinType !== undefined) {
@@ -1288,7 +1114,7 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
1288
1114
  continue;
1289
1115
  }
1290
1116
  const jsonName = getJsonName(prop);
1291
- const clientName = getClientName(prop);
1117
+ const clientName = getClientName(context, prop);
1292
1118
  const description = getDoc(program, prop);
1293
1119
  // if this property is a discriminator property, remove it to keep autorest validation happy
1294
1120
  if (model.baseModel) {
@@ -1511,7 +1337,7 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
1511
1337
  }
1512
1338
  }
1513
1339
  if (typespecType.kind === "ModelProperty" &&
1514
- shouldFlattenProperty(tcgcSdkContext, typespecType)) {
1340
+ shouldFlattenProperty(context.tcgcSdkContext, typespecType)) {
1515
1341
  newTarget["x-ms-client-flatten"] = true;
1516
1342
  }
1517
1343
  attachExtensions(typespecType, newTarget);
@@ -1788,7 +1614,7 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
1788
1614
  reportDiagnostic(program, {
1789
1615
  code: "unsupported-auth",
1790
1616
  format: { authType: auth.type },
1791
- target: currentService.type,
1617
+ target: service.type,
1792
1618
  });
1793
1619
  return undefined;
1794
1620
  }
@@ -1809,9 +1635,6 @@ function createOAPIEmitter(program, tcgcSdkContext, options) {
1809
1635
  }
1810
1636
  }
1811
1637
  }
1812
- function prettierOutput(output) {
1813
- return output + "\n";
1814
- }
1815
1638
  class ErrorTypeFoundError extends Error {
1816
1639
  constructor() {
1817
1640
  super("Error type found in evaluated TypeSpec output");
@@ -1823,24 +1646,72 @@ export function sortOpenAPIDocument(doc) {
1823
1646
  const sorted = sortWithJsonSchema(unsorted, AutorestOpenAPISchema, "#/$defs/AutorestOpenAPISchema");
1824
1647
  return sorted;
1825
1648
  }
1826
- function resolveOutputFile(program, service, multipleServices, options, version) {
1827
- const azureResourceProviderFolder = options.azureResourceProviderFolder;
1828
- if (azureResourceProviderFolder) {
1829
- const info = resolveInfo(program, service.type);
1830
- version = version ?? info?.version ?? "0000-00-00";
1831
- }
1832
- const interpolated = interpolatePath(options.outputFile, {
1833
- "azure-resource-provider-folder": azureResourceProviderFolder,
1834
- "service-name": multipleServices || azureResourceProviderFolder
1835
- ? getNamespaceFullName(service.type)
1836
- : undefined,
1837
- "version-status": azureResourceProviderFolder
1838
- ? version?.includes("preview")
1839
- ? "preview"
1840
- : "stable"
1841
- : undefined,
1842
- version,
1843
- });
1844
- return resolvePath(options.outputDir, interpolated);
1649
+ async function loadExamples(host, options, version) {
1650
+ const diagnostics = createDiagnosticCollector();
1651
+ if (!options.examplesDirectory) {
1652
+ return diagnostics.wrap(new Map());
1653
+ }
1654
+ const exampleDir = version
1655
+ ? resolvePath(options.examplesDirectory, version)
1656
+ : resolvePath(options.examplesDirectory);
1657
+ try {
1658
+ if (!(await host.stat(exampleDir)).isDirectory())
1659
+ return diagnostics.wrap(new Map());
1660
+ }
1661
+ catch (err) {
1662
+ diagnostics.add(createDiagnostic({
1663
+ code: "example-loading",
1664
+ messageId: "noDirectory",
1665
+ format: { directory: exampleDir },
1666
+ target: NoTarget,
1667
+ }));
1668
+ return diagnostics.wrap(new Map());
1669
+ }
1670
+ const map = new Map();
1671
+ const exampleFiles = await host.readDir(exampleDir);
1672
+ for (const fileName of exampleFiles) {
1673
+ try {
1674
+ const exampleFile = await host.readFile(resolvePath(exampleDir, fileName));
1675
+ const example = JSON.parse(exampleFile.text);
1676
+ if (!example.operationId || !example.title) {
1677
+ diagnostics.add(createDiagnostic({
1678
+ code: "example-loading",
1679
+ messageId: "noOperationId",
1680
+ format: { filename: fileName },
1681
+ target: NoTarget,
1682
+ }));
1683
+ continue;
1684
+ }
1685
+ if (!map.has(example.operationId)) {
1686
+ map.set(example.operationId, {});
1687
+ }
1688
+ const examples = map.get(example.operationId);
1689
+ if (example.title in examples) {
1690
+ diagnostics.add(createDiagnostic({
1691
+ code: "duplicate-example-file",
1692
+ target: NoTarget,
1693
+ format: {
1694
+ filename: fileName,
1695
+ operationId: example.operationId,
1696
+ title: example.title,
1697
+ },
1698
+ }));
1699
+ }
1700
+ examples[example.title] = {
1701
+ relativePath: fileName,
1702
+ file: exampleFile,
1703
+ data: example,
1704
+ };
1705
+ }
1706
+ catch (err) {
1707
+ diagnostics.add(createDiagnostic({
1708
+ code: "example-loading",
1709
+ messageId: "default",
1710
+ format: { filename: fileName, error: err?.toString() ?? "" },
1711
+ target: NoTarget,
1712
+ }));
1713
+ }
1714
+ }
1715
+ return diagnostics.wrap(map);
1845
1716
  }
1846
1717
  //# sourceMappingURL=openapi.js.map