forked from unjs/nitro
-
Notifications
You must be signed in to change notification settings - Fork 0
/
openapi.ts
98 lines (84 loc) · 2.2 KB
/
openapi.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { eventHandler } from "h3";
import type {
OpenAPI3,
PathItemObject,
OperationObject,
ParameterObject,
PathsObject,
} from "openapi-typescript";
import { handlersMeta } from "#internal/nitro/virtual/server-handlers";
import { useRuntimeConfig } from "#internal/nitro";
// Served as /_nitro/openapi.json
export default eventHandler((event) => {
const base = useRuntimeConfig()?.app?.baseURL;
return <OpenAPI3>{
openapi: "3.0.0",
info: {
title: "Nitro Server Routes",
version: null,
},
servers: [
{
url: `http://localhost:3000${base}`,
description: "Local Development Server",
variables: {},
},
],
schemes: ["http"],
paths: getPaths(),
};
});
function getPaths(): PathsObject {
const paths: PathsObject = {};
for (const h of handlersMeta) {
const { route, parameters } = normalizeRoute(h.route);
const tags = defaultTags(h.route);
const method = (h.method || "get").toLowerCase();
const item: PathItemObject = {
[method]: <OperationObject>{
tags,
parameters,
responses: {
200: { description: "OK" },
},
},
};
if (paths[route] === undefined) {
paths[route] = item;
} else {
Object.assign(paths[route], item);
}
}
return paths;
}
function normalizeRoute(_route: string) {
const parameters: ParameterObject[] = [];
let anonymousCtr = 0;
const route = _route
.replace(/:(\w+)/g, (_, name) => `{${name}}`)
.replace(/\/(\*)\//g, () => `/{param${++anonymousCtr}}/`)
.replace(/\*\*{/, "{")
.replace(/\/(\*\*)$/g, () => `/{*param${++anonymousCtr}}`);
const paramMatches = route.matchAll(/{(\*?\w+)}/g);
for (const match of paramMatches) {
const name = match[1];
if (!parameters.some((p) => p.name === name)) {
parameters.push({ name, in: "path", required: true, schema: { type: 'string' } });
}
}
return {
route,
parameters,
};
}
function defaultTags(route: string) {
const tags: string[] = [];
if (route.startsWith("/api/")) {
tags.push("API Routes");
} else if (route.startsWith("/_")) {
tags.push("Internal");
} else {
tags.push("App Routes");
}
return tags;
}