Skip to content

Commit 0b9ccca

Browse files
committed
chore: use bigint parse/stringify
chore: move cli generator into this repo
1 parent f654e4b commit 0b9ccca

5 files changed

Lines changed: 181 additions & 27 deletions

File tree

cli.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env bash
2+
":"; //# comment; exec /usr/bin/env node --input-type=module - "$@" < "$0"
3+
4+
import { readFile, writeFile } from "fs/promises";
5+
import openapi from "openapi-typescript";
6+
// import { resolve } from "path";
7+
// import { cwd } from "process";
8+
9+
const NOTICE = `// This file was auto-generated by @insertish/oapi!\n`;
10+
11+
readFile("OpenAPI.json").then((data) => {
12+
// Load and patch anything we need to
13+
let source = data.toString();
14+
15+
if (process.env.REWRITE_ANYOF) {
16+
source = source.replace(/"anyOf"/g, '"oneOf"');
17+
}
18+
19+
// Parse spec
20+
const spec = JSON.parse(source);
21+
22+
// Copy index.ts
23+
// readFile(
24+
// resolve(cwd(), "node_modules", "@insertish", "oapi", "src", "index.ts")
25+
// ).then((data) => writeFile("src/index.ts", data));
26+
27+
// Generate Schema
28+
openapi(spec).then((data) => writeFile("src/schema.ts", data));
29+
30+
// Route Types + Data
31+
{
32+
const entries = [
33+
"import { paths } from './schema';",
34+
"export type APIRoutes =",
35+
];
36+
const paths = Object.keys(spec.paths);
37+
const queryData = {};
38+
39+
for (const path of paths) {
40+
const data = spec.paths[path];
41+
const methods = Object.keys(data);
42+
43+
let template = path.replace(/\{\w+\}/g, "${string}");
44+
45+
for (const method of methods) {
46+
const OPERATION = `paths['${path}']['${method}']`;
47+
48+
const route = data[method];
49+
const response =
50+
Object.keys(route["responses"]).find((x) => x !== "default") ||
51+
"default";
52+
const contentType = Object.keys(
53+
route["responses"][response]["content"] || {}
54+
)[0];
55+
const RESPONSE =
56+
response === "204" || !contentType
57+
? "undefined"
58+
: `${OPERATION}['responses']['${response}']['content']['${contentType}']`;
59+
60+
let queryParams = [];
61+
let hasBody = false;
62+
63+
if (route["parameters"]) {
64+
for (const parameter of route["parameters"]) {
65+
if (parameter.in === "query") {
66+
queryParams.push(parameter.name);
67+
}
68+
}
69+
}
70+
71+
if (route["requestBody"]?.["content"]?.["application/json"]) {
72+
hasBody = true;
73+
}
74+
75+
let params = "undefined";
76+
if (hasBody || queryParams.length > 0) {
77+
let entries = [];
78+
79+
if (queryParams.length > 0) {
80+
entries.push(`${OPERATION}['parameters']['query']`);
81+
}
82+
83+
if (hasBody) {
84+
entries.push(
85+
`${OPERATION}['requestBody']['content']['application/json']`
86+
);
87+
}
88+
89+
params = entries.join("|");
90+
}
91+
92+
const parts = path.split("").filter((x) => x === "/").length;
93+
entries.push(
94+
`| { method: '${method}', path: \`${template}\`, parts: ${parts}, params: ${params}, response: ${RESPONSE} }`
95+
);
96+
97+
if (/\{\w+\}/.test(path)) {
98+
entries.push(
99+
`| { method: '${method}', path: '-${path}', parts: ${parts}, params: ${params}, response: ${RESPONSE} }`
100+
);
101+
}
102+
103+
queryData[path] = {
104+
...queryData[path],
105+
[method]: queryParams,
106+
};
107+
}
108+
}
109+
110+
const pathResolve = {};
111+
for (const path of paths) {
112+
const segments = path.split("/");
113+
segments.shift();
114+
pathResolve[segments.length] = [
115+
...(pathResolve[segments.length] || []),
116+
segments.map((key) => (/\{.*\}/.test(key) ? [key] : key)),
117+
];
118+
}
119+
120+
writeFile("src/routes.ts", NOTICE + entries.join("\n") + ";");
121+
writeFile(
122+
"src/params.ts",
123+
NOTICE +
124+
"export const pathResolve = " +
125+
JSON.stringify(pathResolve) +
126+
";\n" +
127+
"export const queryParams = " +
128+
JSON.stringify(queryData) +
129+
";"
130+
);
131+
}
132+
133+
// Type Exports
134+
{
135+
const entries = ["import { components } from './schema';"];
136+
const schemas = spec.components.schemas;
137+
138+
for (const schema of Object.keys(schemas)) {
139+
entries.push(
140+
`export type ${schema.replace(
141+
/\s/g,
142+
"_"
143+
)} = components['schemas']['${schema}'];`
144+
);
145+
}
146+
147+
writeFile("src/types.ts", NOTICE + entries.join("\n") + ";");
148+
}
149+
150+
// Default Base URL
151+
const baseURL = spec["servers"]?.[0]?.["url"];
152+
writeFile(
153+
"src/baseURL.ts",
154+
NOTICE +
155+
`export const defaultBaseURL = ${
156+
baseURL ? '"' + baseURL + '"' : "undefined"
157+
};`
158+
);
159+
});

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"prepublish": "in-publish && pnpm build || echo Skipping build."
1717
},
1818
"devDependencies": {
19-
"@insertish/oapi": "0.2.6",
2019
"in-publish": "^2.0.1",
2120
"openapi-typescript": "^5.4.2",
2221
"typescript": "^5.8.3"
@@ -28,5 +27,8 @@
2827
"LICENSE",
2928
"README.md"
3029
],
31-
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
30+
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
31+
"dependencies": {
32+
"json-with-bigint": "^3.4.4"
33+
}
3234
}

pnpm-lock.yaml

Lines changed: 9 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// This file was auto-generated by @insertish/oapi!
22
import type { APIRoutes } from "./routes.js";
3+
export type { APIRoutes } from "./routes.js";
34

45
export * from "./types.js";
56

67
import { defaultBaseURL } from "./baseURL.js";
78
import { pathResolve, queryParams } from "./params.js";
89

10+
import { JSONParse, JSONStringify } from 'json-with-bigint';
11+
912
type Methods = APIRoutes["method"];
1013
type PickRoutes<Method extends Methods> = APIRoutes & { method: Method };
1114

@@ -189,7 +192,7 @@ export class API {
189192
}
190193
const passbody = ["head", "get"].includes(method)
191194
? undefined
192-
: JSON.stringify(body);
195+
: JSONStringify(body);
193196

194197
let fetchpath = `${path}?${query.toString()}`;
195198
if (fetchpath.startsWith("/")) {
@@ -205,13 +208,14 @@ export class API {
205208
body: passbody,
206209
});
207210

211+
const respType = config?.responseType || "json";
208212
const data =
209213
fetchdata.status === 204
210214
? null
211-
: await fetchdata[config?.responseType || "json"]();
215+
: await fetchdata[respType === 'json' ? 'text' : respType]();
212216

213217
if (fetchdata.ok) {
214-
return data;
218+
return respType === 'json' ? JSONParse(data as string) : data as never;
215219
} else {
216220
throw data;
217221
}

tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"compilerOptions": {
33
"target": "ES2022",
4-
"module": "ES6",
5-
"moduleResolution": "node",
4+
"module": "es6",
5+
"moduleResolution": "bundler",
66
"rootDir": "./src",
77
"declaration": true,
88
"declarationMap": true,

0 commit comments

Comments
 (0)