@rcmade/hono-docs 1.0.1
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.
- package/LICENSE +21 -0
- package/README.md +302 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +464 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +449 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/core/index.d.mts +35 -0
- package/dist/core/index.d.ts +35 -0
- package/dist/core/index.js +461 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +432 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +461 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +432 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +76 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/config/index.ts
|
|
9
|
+
function defineConfig(config) {
|
|
10
|
+
return config;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/core/runGenerate.ts
|
|
14
|
+
import fs3 from "fs";
|
|
15
|
+
import path3, { resolve as resolve2 } from "path";
|
|
16
|
+
import { Project } from "ts-morph";
|
|
17
|
+
|
|
18
|
+
// src/config/loadConfig.ts
|
|
19
|
+
import { resolve, extname } from "path";
|
|
20
|
+
import { existsSync } from "fs";
|
|
21
|
+
import { pathToFileURL } from "url";
|
|
22
|
+
import { register } from "esbuild-register/dist/node";
|
|
23
|
+
async function loadConfig(configFile) {
|
|
24
|
+
const fullPath = resolve(process.cwd(), configFile);
|
|
25
|
+
if (!existsSync(fullPath)) {
|
|
26
|
+
throw new Error(`[hono-docs] Config file not found: ${fullPath}`);
|
|
27
|
+
}
|
|
28
|
+
const ext = extname(fullPath);
|
|
29
|
+
let unregister = () => {
|
|
30
|
+
};
|
|
31
|
+
if (ext === ".ts" || ext === ".tsx" || ext === ".mts") {
|
|
32
|
+
const reg = register({
|
|
33
|
+
target: "es2020",
|
|
34
|
+
jsx: "automatic"
|
|
35
|
+
});
|
|
36
|
+
unregister = reg.unregister;
|
|
37
|
+
}
|
|
38
|
+
let configModule;
|
|
39
|
+
try {
|
|
40
|
+
if (ext === ".mjs" || ext === ".mts") {
|
|
41
|
+
configModule = await import(pathToFileURL(fullPath).href);
|
|
42
|
+
} else {
|
|
43
|
+
configModule = __require(fullPath);
|
|
44
|
+
}
|
|
45
|
+
} catch (err) {
|
|
46
|
+
unregister();
|
|
47
|
+
throw new Error(
|
|
48
|
+
`[hono-docs] Failed to load config: ${err instanceof Error ? err.message : String(err)}`
|
|
49
|
+
);
|
|
50
|
+
} finally {
|
|
51
|
+
unregister();
|
|
52
|
+
}
|
|
53
|
+
const config = configModule && typeof configModule === "object" && "default" in configModule ? (
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
+
configModule.default
|
|
56
|
+
) : configModule;
|
|
57
|
+
if (!config || typeof config !== "object") {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`[hono-docs] Invalid config file. Expected an object, got: ${typeof config}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return config;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/core/generateTypes.ts
|
|
66
|
+
import fs from "fs";
|
|
67
|
+
import path from "path";
|
|
68
|
+
|
|
69
|
+
// src/utils/format.ts
|
|
70
|
+
function sanitizeApiPrefix(prefix) {
|
|
71
|
+
return prefix.replace(/^\//, "").split(/[^a-z0-9]+/i).filter(Boolean).map(
|
|
72
|
+
(seg, i) => i === 0 ? seg.toLowerCase() : seg[0].toUpperCase() + seg.slice(1).toLowerCase()
|
|
73
|
+
).join("");
|
|
74
|
+
}
|
|
75
|
+
function unwrapUnion(type) {
|
|
76
|
+
return type.isUnion() ? type.getUnionTypes() : [type];
|
|
77
|
+
}
|
|
78
|
+
function normalizeImportPaths(typeText) {
|
|
79
|
+
return typeText.replace(/from ["'].*node_modules\/(.*)["']/g, `from "$1"`);
|
|
80
|
+
}
|
|
81
|
+
function cleanDefaultResponse(operation, pathKey, method) {
|
|
82
|
+
var _a;
|
|
83
|
+
const defaultResponse = (_a = operation.responses) == null ? void 0 : _a.default;
|
|
84
|
+
if (!defaultResponse) return;
|
|
85
|
+
const desc = defaultResponse.description ?? "";
|
|
86
|
+
if (desc.includes("import(")) {
|
|
87
|
+
const content = defaultResponse.content;
|
|
88
|
+
if (content && Object.keys(content).length > 0) {
|
|
89
|
+
defaultResponse.description = "Default fallback response";
|
|
90
|
+
console.log(
|
|
91
|
+
`\u2139\uFE0F Cleaned 'default' description in ${method.toUpperCase()} ${pathKey}`
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
delete operation.responses.default;
|
|
95
|
+
console.log(
|
|
96
|
+
`\u{1F5D1}\uFE0F Removed empty 'default' in ${method.toUpperCase()} ${pathKey}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function groupBy(arr, fn) {
|
|
102
|
+
return arr.reduce((acc, x) => {
|
|
103
|
+
(acc[fn(x)] ||= []).push(x);
|
|
104
|
+
return acc;
|
|
105
|
+
}, {});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/core/generateTypes.ts
|
|
109
|
+
async function generateTypes({
|
|
110
|
+
config,
|
|
111
|
+
project,
|
|
112
|
+
rootPath,
|
|
113
|
+
apiGroup,
|
|
114
|
+
fileName,
|
|
115
|
+
outputRoot
|
|
116
|
+
}) {
|
|
117
|
+
fs.mkdirSync(outputRoot, { recursive: true });
|
|
118
|
+
const outputPath = path.join(outputRoot, `${fileName}.d.ts`);
|
|
119
|
+
const absInput = path.resolve(rootPath, apiGroup.appTypePath);
|
|
120
|
+
const sourceFile = project.addSourceFileAtPath(absInput);
|
|
121
|
+
const typeAliases = sourceFile.getTypeAliases();
|
|
122
|
+
const interfaces = sourceFile.getInterfaces();
|
|
123
|
+
let result = `// AUTO-GENERATED from ${apiGroup.appTypePath}
|
|
124
|
+
|
|
125
|
+
`;
|
|
126
|
+
typeAliases.forEach((alias) => {
|
|
127
|
+
const raw = alias.getType().getText(alias);
|
|
128
|
+
const clean = normalizeImportPaths(raw);
|
|
129
|
+
result += `export type ${alias.getName()} = ${clean};
|
|
130
|
+
|
|
131
|
+
`;
|
|
132
|
+
});
|
|
133
|
+
interfaces.forEach((intf) => {
|
|
134
|
+
result += intf.getText() + "\n\n";
|
|
135
|
+
});
|
|
136
|
+
const preContent = config.preDefineTypeContent || "";
|
|
137
|
+
fs.writeFileSync(outputPath, `${preContent}
|
|
138
|
+
${result}`, "utf-8");
|
|
139
|
+
console.log(`\u2705 Wrote: ${outputPath}`);
|
|
140
|
+
return { appTypePath: outputPath, name: fileName };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/core/generateOpenApi.ts
|
|
144
|
+
import fs2 from "fs";
|
|
145
|
+
import path2 from "path";
|
|
146
|
+
import {
|
|
147
|
+
SyntaxKind as SyntaxKind2
|
|
148
|
+
} from "ts-morph";
|
|
149
|
+
|
|
150
|
+
// src/utils/buildSchema.ts
|
|
151
|
+
import { SyntaxKind } from "ts-morph";
|
|
152
|
+
function buildSchema(type) {
|
|
153
|
+
var _a;
|
|
154
|
+
if (type.isUnion()) {
|
|
155
|
+
const members = type.getUnionTypes();
|
|
156
|
+
const lits = members.filter((u) => u.isStringLiteral());
|
|
157
|
+
const onlyNull = members.every(
|
|
158
|
+
(u) => u.isStringLiteral() || u.isNull() || u.isUndefined()
|
|
159
|
+
);
|
|
160
|
+
if (lits.length && onlyNull) {
|
|
161
|
+
const schema = {
|
|
162
|
+
type: "string",
|
|
163
|
+
enum: lits.map((u) => u.getLiteralValue())
|
|
164
|
+
};
|
|
165
|
+
if (members.some((u) => u.isNull() || u.isUndefined()))
|
|
166
|
+
schema.nullable = true;
|
|
167
|
+
return schema;
|
|
168
|
+
}
|
|
169
|
+
const nonNull = members.filter((u) => !u.isNull() && !u.isUndefined());
|
|
170
|
+
return { oneOf: nonNull.map(buildSchema) };
|
|
171
|
+
}
|
|
172
|
+
if (type.isString()) return { type: "string" };
|
|
173
|
+
if (type.isNumber()) return { type: "number" };
|
|
174
|
+
if (type.isBoolean()) return { type: "boolean" };
|
|
175
|
+
if (type.isArray()) {
|
|
176
|
+
return {
|
|
177
|
+
type: "array",
|
|
178
|
+
items: buildSchema(type.getArrayElementTypeOrThrow())
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const decls = ((_a = type.getSymbol()) == null ? void 0 : _a.getDeclarations()) || [];
|
|
182
|
+
const isLit = decls.some(
|
|
183
|
+
(d) => d.getKind() === SyntaxKind.TypeLiteral || d.getKind() === SyntaxKind.InterfaceDeclaration
|
|
184
|
+
);
|
|
185
|
+
if (!isLit) return {};
|
|
186
|
+
const props = type.getProperties().filter(
|
|
187
|
+
(p) => {
|
|
188
|
+
var _a2;
|
|
189
|
+
return ((_a2 = p.getValueDeclaration()) == null ? void 0 : _a2.getKind()) === SyntaxKind.PropertySignature;
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
const propsMap = {};
|
|
193
|
+
const req = [];
|
|
194
|
+
for (const p of props) {
|
|
195
|
+
const decl = p.getValueDeclarationOrThrow();
|
|
196
|
+
propsMap[p.getName()] = buildSchema(decl.getType());
|
|
197
|
+
if (!p.isOptional()) req.push(p.getName());
|
|
198
|
+
}
|
|
199
|
+
const res = { type: "object", properties: propsMap };
|
|
200
|
+
if (req.length) res.required = req;
|
|
201
|
+
return res;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/utils/parameters.ts
|
|
205
|
+
function genParameters(type) {
|
|
206
|
+
var _a;
|
|
207
|
+
const input = (_a = type.getProperty("input")) == null ? void 0 : _a.getValueDeclarationOrThrow().getType();
|
|
208
|
+
if (!input) return [];
|
|
209
|
+
const sources = ["query", "param", "header", "cookie"];
|
|
210
|
+
const params = [];
|
|
211
|
+
for (const src of sources) {
|
|
212
|
+
const p = input.getProperty(src);
|
|
213
|
+
if (!p) continue;
|
|
214
|
+
const srcType = p.getValueDeclarationOrThrow().getType();
|
|
215
|
+
for (const f of srcType.getProperties()) {
|
|
216
|
+
const ft = f.getValueDeclarationOrThrow().getType();
|
|
217
|
+
params.push({
|
|
218
|
+
name: f.getName(),
|
|
219
|
+
in: src === "param" ? "path" : src,
|
|
220
|
+
required: !f.isOptional(),
|
|
221
|
+
schema: buildSchema(ft)
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return params;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/utils/requestBody.ts
|
|
229
|
+
function genRequestBody(type) {
|
|
230
|
+
var _a;
|
|
231
|
+
const inp = (_a = type.getProperty("input")) == null ? void 0 : _a.getValueDeclarationOrThrow().getType();
|
|
232
|
+
if (!inp) return null;
|
|
233
|
+
const content = {};
|
|
234
|
+
const j = inp.getProperty("json");
|
|
235
|
+
if (j) {
|
|
236
|
+
content["application/json"] = {
|
|
237
|
+
schema: buildSchema(j.getValueDeclarationOrThrow().getType())
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const f = inp.getProperty("form");
|
|
241
|
+
if (f) {
|
|
242
|
+
content["multipart/form-data"] = {
|
|
243
|
+
schema: buildSchema(f.getValueDeclarationOrThrow().getType())
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return Object.keys(content).length ? { required: true, content } : null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/core/generateOpenApi.ts
|
|
250
|
+
async function generateOpenApi({
|
|
251
|
+
config,
|
|
252
|
+
snapshotPath,
|
|
253
|
+
fileName,
|
|
254
|
+
project,
|
|
255
|
+
rootPath,
|
|
256
|
+
outputRoot
|
|
257
|
+
}) {
|
|
258
|
+
const sf = project.addSourceFileAtPath(
|
|
259
|
+
path2.resolve(rootPath, snapshotPath.appTypePath)
|
|
260
|
+
);
|
|
261
|
+
const aliasDecl = sf.getTypeAliasOrThrow("AppType");
|
|
262
|
+
const topTypeNode = aliasDecl.getTypeNode();
|
|
263
|
+
let typeArgs;
|
|
264
|
+
if (topTypeNode == null ? void 0 : topTypeNode.isKind(SyntaxKind2.TypeReference)) {
|
|
265
|
+
typeArgs = topTypeNode.getTypeArguments();
|
|
266
|
+
} else if (topTypeNode == null ? void 0 : topTypeNode.isKind(SyntaxKind2.ImportType)) {
|
|
267
|
+
typeArgs = topTypeNode.getTypeArguments();
|
|
268
|
+
} else {
|
|
269
|
+
throw new Error("AppType must be an ImportType or a TypeReference");
|
|
270
|
+
}
|
|
271
|
+
if (typeArgs.length < 2) {
|
|
272
|
+
throw new Error("Expected two type arguments on HonoBase");
|
|
273
|
+
}
|
|
274
|
+
const routesNode = typeArgs[1];
|
|
275
|
+
const literals = [];
|
|
276
|
+
if (routesNode.isKind(SyntaxKind2.IntersectionType)) {
|
|
277
|
+
for (const tn of routesNode.asKind(SyntaxKind2.IntersectionType).getTypeNodes()) {
|
|
278
|
+
if (tn.isKind(SyntaxKind2.TypeLiteral))
|
|
279
|
+
literals.push(tn);
|
|
280
|
+
}
|
|
281
|
+
} else if (routesNode.isKind(SyntaxKind2.TypeLiteral)) {
|
|
282
|
+
literals.push(routesNode);
|
|
283
|
+
} else {
|
|
284
|
+
throw new Error("Routes type is not a literal or intersection of literals");
|
|
285
|
+
}
|
|
286
|
+
const paths = {};
|
|
287
|
+
for (const lit of literals) {
|
|
288
|
+
for (const member of lit.getMembers()) {
|
|
289
|
+
if (!member.isKind(SyntaxKind2.PropertySignature)) continue;
|
|
290
|
+
const routeProp = member.asKindOrThrow(SyntaxKind2.PropertySignature);
|
|
291
|
+
const raw = routeProp.getNameNode().getText().replace(/"/g, "");
|
|
292
|
+
const route = raw.replace(/:([^/]+)/g, "{$1}");
|
|
293
|
+
if (!paths[route]) paths[route] = {};
|
|
294
|
+
const tn = routeProp.getTypeNode();
|
|
295
|
+
if (!tn || !tn.isKind(SyntaxKind2.TypeLiteral)) continue;
|
|
296
|
+
const rhs = tn;
|
|
297
|
+
for (const m of rhs.getMembers()) {
|
|
298
|
+
if (!m.isKind(SyntaxKind2.PropertySignature)) continue;
|
|
299
|
+
const methodProp = m.asKindOrThrow(SyntaxKind2.PropertySignature);
|
|
300
|
+
const name = methodProp.getNameNode().getText();
|
|
301
|
+
const http = name.slice(1).toLowerCase();
|
|
302
|
+
const variants = unwrapUnion(methodProp.getType());
|
|
303
|
+
const op = {
|
|
304
|
+
summary: `Auto-generated ${http.toUpperCase()} ${route}`
|
|
305
|
+
};
|
|
306
|
+
const params = genParameters(variants[0]);
|
|
307
|
+
if (params.length) op.parameters = params;
|
|
308
|
+
const rb = genRequestBody(variants[0]);
|
|
309
|
+
if (rb) op.requestBody = rb;
|
|
310
|
+
op.responses = {};
|
|
311
|
+
const byStatus = groupBy(variants, (v) => {
|
|
312
|
+
const s = v.getProperty("status").getValueDeclarationOrThrow().getType().getText();
|
|
313
|
+
return /^\d+$/.test(s) ? s : "default";
|
|
314
|
+
});
|
|
315
|
+
for (const [code, vs] of Object.entries(byStatus)) {
|
|
316
|
+
const schemas = vs.map(
|
|
317
|
+
(v) => buildSchema(
|
|
318
|
+
v.getProperty("output").getValueDeclarationOrThrow().getType()
|
|
319
|
+
)
|
|
320
|
+
);
|
|
321
|
+
const schema = schemas.length > 1 ? { oneOf: schemas } : schemas[0];
|
|
322
|
+
op.responses[code] = {
|
|
323
|
+
description: code === "default" ? `Generic status from ${vs[0].getProperty("status").getValueDeclarationOrThrow().getType().getText()}` : `Status ${code}`,
|
|
324
|
+
content: { "application/json": { schema } }
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
paths[route][http] = op;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const spec = {
|
|
332
|
+
...config.openApi,
|
|
333
|
+
paths
|
|
334
|
+
};
|
|
335
|
+
const outputPath = path2.join(outputRoot, `${fileName}.json`);
|
|
336
|
+
fs2.mkdirSync(path2.dirname(outputPath), { recursive: true });
|
|
337
|
+
fs2.writeFileSync(outputPath, JSON.stringify(spec, null, 2), "utf-8");
|
|
338
|
+
console.log(`\u2705 OpenAPI written to ${outputPath}`);
|
|
339
|
+
return { openApiPath: outputPath };
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/core/runGenerate.ts
|
|
343
|
+
async function runGenerate(configPath) {
|
|
344
|
+
const config = await loadConfig(configPath);
|
|
345
|
+
const rootPath = process.cwd();
|
|
346
|
+
console.log("Initializing ts-morph with tsConfig:", config.tsConfigPath);
|
|
347
|
+
const project = new Project({
|
|
348
|
+
tsConfigFilePath: resolve2(rootPath, config.tsConfigPath)
|
|
349
|
+
});
|
|
350
|
+
const isDevMode = __dirname.includes("/src/") || __dirname.includes("\\src\\");
|
|
351
|
+
const libDir = isDevMode ? path3.resolve(__dirname, "../../") : path3.dirname(__require.resolve("@rcmade/hono-docs/package.json"));
|
|
352
|
+
const apis = config.apis;
|
|
353
|
+
const snapshotOutputRoot = path3.resolve(libDir, "output/types");
|
|
354
|
+
const openAPiOutputRoot = path3.resolve(libDir, "output/openapi");
|
|
355
|
+
const commonParams = {
|
|
356
|
+
config,
|
|
357
|
+
libDir,
|
|
358
|
+
project,
|
|
359
|
+
rootPath
|
|
360
|
+
};
|
|
361
|
+
for (const apiGroup of apis) {
|
|
362
|
+
const sanitizedName = sanitizeApiPrefix(apiGroup.apiPrefix);
|
|
363
|
+
const snapshotPath = await generateTypes({
|
|
364
|
+
...commonParams,
|
|
365
|
+
apiGroup,
|
|
366
|
+
fileName: sanitizedName,
|
|
367
|
+
outputRoot: snapshotOutputRoot
|
|
368
|
+
});
|
|
369
|
+
await generateOpenApi({
|
|
370
|
+
snapshotPath,
|
|
371
|
+
...commonParams,
|
|
372
|
+
fileName: sanitizedName,
|
|
373
|
+
outputRoot: openAPiOutputRoot
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const merged = {
|
|
377
|
+
...config.openApi,
|
|
378
|
+
tags: [],
|
|
379
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
380
|
+
paths: {}
|
|
381
|
+
};
|
|
382
|
+
for (const apiGroup of apis) {
|
|
383
|
+
const name = sanitizeApiPrefix(apiGroup.apiPrefix);
|
|
384
|
+
const openApiFile = path3.join(openAPiOutputRoot, `${name}.json`);
|
|
385
|
+
if (!fs3.existsSync(openApiFile)) {
|
|
386
|
+
console.warn(`\u26A0\uFE0F Missing OpenAPI file: ${openApiFile}`);
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const json = JSON.parse(fs3.readFileSync(openApiFile, "utf-8"));
|
|
390
|
+
merged.tags.push({ name: apiGroup.name });
|
|
391
|
+
const customApiMap = /* @__PURE__ */ new Map();
|
|
392
|
+
if (apiGroup == null ? void 0 : apiGroup.api) {
|
|
393
|
+
for (const customApi of apiGroup.api) {
|
|
394
|
+
const fullPath = path3.posix.join(apiGroup.apiPrefix, customApi.api).replace(/\/+$/, "") || "/";
|
|
395
|
+
customApiMap.set(
|
|
396
|
+
`${customApi.method.toLowerCase()} ${fullPath}`,
|
|
397
|
+
customApi
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
for (const [pathKey, operations] of Object.entries(json.paths)) {
|
|
402
|
+
const prefixedPath = path3.posix.join(apiGroup.apiPrefix, pathKey).replace(/\/+$/, "") || "/";
|
|
403
|
+
if (!merged.paths[prefixedPath]) merged.paths[prefixedPath] = {};
|
|
404
|
+
for (const [method, operation] of Object.entries(operations)) {
|
|
405
|
+
const opKey = `${method.toLowerCase()} ${prefixedPath}`;
|
|
406
|
+
const customApi = customApiMap.get(opKey);
|
|
407
|
+
if (customApi) {
|
|
408
|
+
operation.summary = customApi.summery || operation.summary;
|
|
409
|
+
operation.description = customApi.description || operation.description;
|
|
410
|
+
operation.tags = customApi.tag && customApi.tag.length > 0 ? customApi.tag : [apiGroup.name];
|
|
411
|
+
} else {
|
|
412
|
+
operation.tags = operation.tags || [];
|
|
413
|
+
if (!operation.tags.includes(apiGroup.name)) {
|
|
414
|
+
operation.tags.push(apiGroup.name);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
cleanDefaultResponse(operation, prefixedPath, method);
|
|
418
|
+
merged.paths[prefixedPath][method] = operation;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const outputPath = path3.join(rootPath, config.outputs.openApiJson);
|
|
423
|
+
fs3.mkdirSync(path3.dirname(outputPath), { recursive: true });
|
|
424
|
+
fs3.writeFileSync(outputPath, `${JSON.stringify(merged, null, 2)}
|
|
425
|
+
`);
|
|
426
|
+
console.log(`\u2705 Final merged OpenAPI spec written to: ${outputPath}`);
|
|
427
|
+
}
|
|
428
|
+
export {
|
|
429
|
+
defineConfig,
|
|
430
|
+
runGenerate
|
|
431
|
+
};
|
|
432
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/index.ts","../src/core/runGenerate.ts","../src/config/loadConfig.ts","../src/core/generateTypes.ts","../src/utils/format.ts","../src/core/generateOpenApi.ts","../src/utils/buildSchema.ts","../src/utils/parameters.ts","../src/utils/requestBody.ts"],"sourcesContent":["// src/config/index.ts\nimport type { HonoDocsConfig } from \"../types\";\n\n/**\n * A no‑op helper to get TS inference and IDE support when\n * writing `export default defineConfig({...})` in userland.\n */\nexport function defineConfig(config: HonoDocsConfig): HonoDocsConfig {\n return config;\n}\n","import fs from \"node:fs\";\nimport path, { resolve } from \"node:path\";\nimport { Project } from \"ts-morph\";\nimport { loadConfig } from \"../config/loadConfig\";\nimport { generateTypes } from \"./generateTypes\";\nimport { generateOpenApi } from \"./generateOpenApi\";\nimport { Api } from \"../types\";\nimport { cleanDefaultResponse, sanitizeApiPrefix } from \"../utils/format\";\n\nexport async function runGenerate(configPath: string) {\n const config = await loadConfig(configPath);\n\n const rootPath = process.cwd();\n console.log(\"Initializing ts-morph with tsConfig:\", config.tsConfigPath);\n const project = new Project({\n tsConfigFilePath: resolve(rootPath, config.tsConfigPath),\n });\n\n const isDevMode =\n __dirname.includes(\"/src/\") || __dirname.includes(\"\\\\src\\\\\");\n\n const libDir = isDevMode\n ? path.resolve(__dirname, \"../../\")\n : path.dirname(require.resolve(\"@rcmade/hono-docs/package.json\"));\n\n const apis = config.apis;\n\n const snapshotOutputRoot = path.resolve(libDir, \"output/types\");\n const openAPiOutputRoot = path.resolve(libDir, \"output/openapi\");\n\n const commonParams = {\n config,\n libDir,\n project,\n rootPath,\n };\n for (const apiGroup of apis) {\n const sanitizedName = sanitizeApiPrefix(apiGroup.apiPrefix);\n\n const snapshotPath = await generateTypes({\n ...commonParams,\n apiGroup: apiGroup,\n fileName: sanitizedName,\n outputRoot: snapshotOutputRoot,\n });\n\n await generateOpenApi({\n snapshotPath,\n ...commonParams,\n fileName: sanitizedName,\n outputRoot: openAPiOutputRoot,\n });\n }\n\n const merged = {\n ...config.openApi,\n tags: [] as { name: string }[],\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n paths: {} as Record<string, any>,\n };\n\n for (const apiGroup of apis) {\n const name = sanitizeApiPrefix(apiGroup.apiPrefix);\n const openApiFile = path.join(openAPiOutputRoot, `${name}.json`);\n\n if (!fs.existsSync(openApiFile)) {\n console.warn(`⚠️ Missing OpenAPI file: ${openApiFile}`);\n continue;\n }\n\n const json = JSON.parse(fs.readFileSync(openApiFile, \"utf-8\"));\n merged.tags.push({ name: apiGroup.name });\n\n const customApiMap = new Map<string, Api>();\n\n if (apiGroup?.api) {\n for (const customApi of apiGroup.api) {\n const fullPath =\n path.posix\n .join(apiGroup.apiPrefix, customApi.api)\n .replace(/\\/+$/, \"\") || \"/\";\n customApiMap.set(\n `${customApi.method.toLowerCase()} ${fullPath}`,\n customApi\n );\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n for (const [pathKey, operations] of Object.entries<any>(json.paths)) {\n const prefixedPath =\n path.posix.join(apiGroup.apiPrefix, pathKey).replace(/\\/+$/, \"\") || \"/\";\n if (!merged.paths[prefixedPath]) merged.paths[prefixedPath] = {};\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n for (const [method, operation] of Object.entries<any>(operations)) {\n const opKey = `${method.toLowerCase()} ${prefixedPath}`;\n const customApi = customApiMap.get(opKey);\n\n // Override or enrich metadata if defined\n if (customApi) {\n operation.summary = customApi.summery || operation.summary;\n operation.description =\n customApi.description || operation.description;\n operation.tags =\n customApi.tag && customApi.tag.length > 0\n ? customApi.tag\n : [apiGroup.name];\n } else {\n operation.tags = operation.tags || [];\n if (!operation.tags.includes(apiGroup.name)) {\n operation.tags.push(apiGroup.name);\n }\n }\n\n cleanDefaultResponse(operation, prefixedPath, method);\n merged.paths[prefixedPath][method] = operation;\n }\n }\n }\n\n const outputPath = path.join(rootPath, config.outputs.openApiJson);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n\n fs.writeFileSync(outputPath, `${JSON.stringify(merged, null, 2)}\\n`);\n\n console.log(`✅ Final merged OpenAPI spec written to: ${outputPath}`);\n}\n","import { resolve, extname } from \"path\";\nimport { existsSync } from \"fs\";\nimport { pathToFileURL } from \"url\";\nimport { register } from \"esbuild-register/dist/node\";\nimport type { HonoDocsConfig } from \"../types\";\n\nexport async function loadConfig(configFile: string): Promise<HonoDocsConfig> {\n // 1. Resolve absolute path\n const fullPath = resolve(process.cwd(), configFile);\n\n if (!existsSync(fullPath)) {\n throw new Error(`[hono-docs] Config file not found: ${fullPath}`);\n }\n\n // 2. Detect file extension\n const ext = extname(fullPath);\n let unregister = () => {};\n\n // 3. Register TS transpiler if needed\n if (ext === \".ts\" || ext === \".tsx\" || ext === \".mts\") {\n const reg = register({\n target: \"es2020\",\n jsx: \"automatic\",\n });\n unregister = reg.unregister;\n }\n\n // 4. Dynamically load the config\n let configModule: unknown;\n\n try {\n if (ext === \".mjs\" || ext === \".mts\") {\n // ESM config\n configModule = await import(pathToFileURL(fullPath).href);\n } else {\n // Use require with esbuild-register hook for .ts/.js\n configModule = require(fullPath);\n }\n } catch (err) {\n unregister();\n throw new Error(\n `[hono-docs] Failed to load config: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n } finally {\n unregister();\n }\n\n // 5. Handle default or named export\n const config =\n configModule &&\n typeof configModule === \"object\" &&\n \"default\" in configModule\n ? // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (configModule as any).default\n : configModule;\n\n if (!config || typeof config !== \"object\") {\n throw new Error(\n `[hono-docs] Invalid config file. Expected an object, got: ${typeof config}`\n );\n }\n\n return config as HonoDocsConfig;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { ApiGroup, GenerateParams } from \"../types\";\nimport { normalizeImportPaths } from \"../utils/format\";\n\nexport async function generateTypes({\n config,\n project,\n rootPath,\n apiGroup,\n fileName,\n outputRoot,\n}: GenerateParams & { apiGroup: ApiGroup }) {\n fs.mkdirSync(outputRoot, { recursive: true });\n\n const outputPath = path.join(outputRoot, `${fileName}.d.ts`);\n const absInput = path.resolve(rootPath, apiGroup.appTypePath);\n\n const sourceFile = project.addSourceFileAtPath(absInput);\n const typeAliases = sourceFile.getTypeAliases();\n const interfaces = sourceFile.getInterfaces();\n\n let result = `// AUTO-GENERATED from ${apiGroup.appTypePath}\\n\\n`;\n\n typeAliases.forEach((alias) => {\n const raw = alias.getType().getText(alias);\n const clean = normalizeImportPaths(raw);\n result += `export type ${alias.getName()} = ${clean};\\n\\n`;\n });\n\n interfaces.forEach((intf) => {\n result += intf.getText() + \"\\n\\n\";\n });\n\n const preContent = config.preDefineTypeContent || \"\";\n\n fs.writeFileSync(outputPath, `${preContent}\\n${result}`, \"utf-8\");\n console.log(`✅ Wrote: ${outputPath}`);\n return { appTypePath: outputPath, name: fileName };\n}\n","export function sanitizeApiPrefix(prefix: string): string {\n return prefix\n .replace(/^\\//, \"\")\n .split(/[^a-z0-9]+/i)\n .filter(Boolean)\n .map((seg, i) =>\n i === 0\n ? seg.toLowerCase()\n : seg[0].toUpperCase() + seg.slice(1).toLowerCase()\n )\n .join(\"\");\n}\n\nexport function unwrapUnion(\n type: import(\"ts-morph\").Type\n): import(\"ts-morph\").Type[] {\n return type.isUnion() ? type.getUnionTypes() : [type];\n}\n\nexport function normalizeImportPaths(typeText: string): string {\n return typeText.replace(/from [\"'].*node_modules\\/(.*)[\"']/g, `from \"$1\"`);\n}\n\nexport function cleanDefaultResponse(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n operation: any,\n pathKey: string,\n method: string\n) {\n const defaultResponse = operation.responses?.default;\n if (!defaultResponse) return;\n\n const desc = defaultResponse.description ?? \"\";\n\n if (desc.includes(\"import(\")) {\n const content = defaultResponse.content;\n\n if (content && Object.keys(content).length > 0) {\n defaultResponse.description = \"Default fallback response\";\n console.log(\n `ℹ️ Cleaned 'default' description in ${method.toUpperCase()} ${pathKey}`\n );\n } else {\n delete operation.responses.default;\n console.log(\n `🗑️ Removed empty 'default' in ${method.toUpperCase()} ${pathKey}`\n );\n }\n }\n}\n\n\nexport function groupBy<T>(\n arr: T[],\n fn: (x: T) => string\n): Record<string, T[]> {\n return arr.reduce((acc, x) => {\n (acc[fn(x)] ||= []).push(x);\n return acc;\n }, {} as Record<string, T[]>);\n}\n","// src/core/generateOpenApi.ts\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n SyntaxKind,\n TypeLiteralNode,\n ImportTypeNode,\n TypeReferenceNode,\n TypeNode,\n ts,\n} from \"ts-morph\";\nimport type {\n AppTypeSnapshotPath,\n GenerateParams,\n OpenApiPath,\n} from \"../types\";\nimport { genParameters } from \"../utils/parameters\";\nimport { genRequestBody } from \"../utils/requestBody\";\nimport { buildSchema } from \"../utils/buildSchema\";\nimport { groupBy, unwrapUnion } from \"../utils/format\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype OpenAPI = Record<string, any>;\n\nexport async function generateOpenApi({\n config,\n snapshotPath,\n fileName,\n project,\n rootPath,\n outputRoot,\n}: // {\n// config: HonoDocsConfig;\n// snapshotPath: AppTypeSnapshotPath;\n// }\nGenerateParams & {\n snapshotPath: AppTypeSnapshotPath;\n}): Promise<OpenApiPath> {\n const sf = project.addSourceFileAtPath(\n path.resolve(rootPath, snapshotPath.appTypePath)\n );\n const aliasDecl = sf.getTypeAliasOrThrow(\"AppType\");\n\n const topTypeNode = aliasDecl.getTypeNode();\n\n let typeArgs: readonly TypeNode<ts.TypeNode>[];\n\n if (topTypeNode?.isKind(SyntaxKind.TypeReference)) {\n typeArgs = (topTypeNode as TypeReferenceNode).getTypeArguments();\n } else if (topTypeNode?.isKind(SyntaxKind.ImportType)) {\n typeArgs = (topTypeNode as ImportTypeNode).getTypeArguments();\n } else {\n throw new Error(\"AppType must be an ImportType or a TypeReference\");\n }\n\n if (typeArgs.length < 2) {\n throw new Error(\"Expected two type arguments on HonoBase\");\n }\n\n const routesNode = typeArgs[1];\n\n // Gather all TypeLiteralNodes (handle intersections)\n const literals: TypeLiteralNode[] = [];\n if (routesNode.isKind(SyntaxKind.IntersectionType)) {\n for (const tn of routesNode\n .asKind(SyntaxKind.IntersectionType)!\n .getTypeNodes()) {\n if (tn.isKind(SyntaxKind.TypeLiteral))\n literals.push(tn as TypeLiteralNode);\n }\n } else if (routesNode.isKind(SyntaxKind.TypeLiteral)) {\n literals.push(routesNode as TypeLiteralNode);\n } else {\n throw new Error(\"Routes type is not a literal or intersection of literals\");\n }\n\n const paths: OpenAPI = {};\n\n for (const lit of literals) {\n for (const member of lit.getMembers()) {\n if (!member.isKind(SyntaxKind.PropertySignature)) continue;\n const routeProp = member.asKindOrThrow(SyntaxKind.PropertySignature);\n // Extract route string and normalize to OpenAPI path syntax\n const raw = routeProp.getNameNode().getText().replace(/\"/g, \"\");\n const route = raw.replace(/:([^/]+)/g, \"{$1}\");\n if (!paths[route]) paths[route] = {};\n\n // === NEW: get the RHS TypeLiteralNode properly ===\n const tn = routeProp.getTypeNode();\n if (!tn || !tn.isKind(SyntaxKind.TypeLiteral)) continue;\n const rhs = tn as TypeLiteralNode;\n\n for (const m of rhs.getMembers()) {\n if (!m.isKind(SyntaxKind.PropertySignature)) continue;\n const methodProp = m.asKindOrThrow(SyntaxKind.PropertySignature);\n const name = methodProp.getNameNode().getText(); // e.g. \"$get\"\n const http = name.slice(1).toLowerCase(); // \"get\", \"post\", etc.\n const variants = unwrapUnion(methodProp.getType());\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const op: any = {\n summary: `Auto-generated ${http.toUpperCase()} ${route}`,\n };\n\n // parameters\n const params = genParameters(variants[0]);\n if (params.length) op.parameters = params;\n\n // requestBody\n const rb = genRequestBody(variants[0]);\n if (rb) op.requestBody = rb;\n\n // responses\n op.responses = {};\n const byStatus = groupBy(variants, (v) => {\n const s = v\n .getProperty(\"status\")!\n .getValueDeclarationOrThrow()\n .getType()\n .getText();\n return /^\\d+$/.test(s) ? s : \"default\";\n });\n for (const [code, vs] of Object.entries(byStatus)) {\n const schemas = vs.map((v) =>\n buildSchema(\n v.getProperty(\"output\")!.getValueDeclarationOrThrow().getType()\n )\n );\n const schema = schemas.length > 1 ? { oneOf: schemas } : schemas[0];\n op.responses[code] = {\n description:\n code === \"default\"\n ? `Generic status from ${vs[0]\n .getProperty(\"status\")!\n .getValueDeclarationOrThrow()\n .getType()\n .getText()}`\n : `Status ${code}`,\n content: { \"application/json\": { schema } },\n };\n }\n\n paths[route][http] = op;\n }\n }\n }\n\n const spec = {\n ...config.openApi,\n paths,\n };\n\n // write to disk\n const outputPath = path.join(outputRoot, `${fileName}.json`);\n\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, JSON.stringify(spec, null, 2), \"utf-8\");\n console.log(`✅ OpenAPI written to ${outputPath}`);\n return { openApiPath: outputPath };\n}\n","import { SyntaxKind } from \"ts-morph\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function buildSchema(type: import(\"ts-morph\").Type): any {\n if (type.isUnion()) {\n const members = type.getUnionTypes();\n const lits = members.filter((u) => u.isStringLiteral());\n const onlyNull = members.every(\n (u) => u.isStringLiteral() || u.isNull() || u.isUndefined()\n );\n if (lits.length && onlyNull) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const schema: any = {\n type: \"string\",\n enum: lits.map((u) => u.getLiteralValue()),\n };\n if (members.some((u) => u.isNull() || u.isUndefined()))\n schema.nullable = true;\n return schema;\n }\n const nonNull = members.filter((u) => !u.isNull() && !u.isUndefined());\n return { oneOf: nonNull.map(buildSchema) };\n }\n if (type.isString()) return { type: \"string\" };\n if (type.isNumber()) return { type: \"number\" };\n if (type.isBoolean()) return { type: \"boolean\" };\n if (type.isArray()) {\n return {\n type: \"array\",\n items: buildSchema(type.getArrayElementTypeOrThrow()),\n };\n }\n\n const decls = type.getSymbol()?.getDeclarations() || [];\n const isLit = decls.some(\n (d) =>\n d.getKind() === SyntaxKind.TypeLiteral ||\n d.getKind() === SyntaxKind.InterfaceDeclaration\n );\n if (!isLit) return {};\n\n const props = type\n .getProperties()\n .filter(\n (p) => p.getValueDeclaration()?.getKind() === SyntaxKind.PropertySignature\n );\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const propsMap: Record<string, any> = {};\n const req: string[] = [];\n for (const p of props) {\n const decl = p.getValueDeclarationOrThrow();\n propsMap[p.getName()] = buildSchema(decl.getType());\n if (!p.isOptional()) req.push(p.getName());\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const res: any = { type: \"object\", properties: propsMap };\n if (req.length) res.required = req;\n return res;\n}\n","import { buildSchema } from \"./buildSchema\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function genParameters(type: import(\"ts-morph\").Type): any[] {\n const input = type\n .getProperty(\"input\")\n ?.getValueDeclarationOrThrow()\n .getType();\n if (!input) return [];\n const sources = [\"query\", \"param\", \"header\", \"cookie\"];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const params: any[] = [];\n for (const src of sources) {\n const p = input.getProperty(src);\n if (!p) continue;\n const srcType = p.getValueDeclarationOrThrow().getType();\n for (const f of srcType.getProperties()) {\n const ft = f.getValueDeclarationOrThrow().getType();\n params.push({\n name: f.getName(),\n in: src === \"param\" ? \"path\" : src,\n required: !f.isOptional(),\n schema: buildSchema(ft),\n });\n }\n }\n return params;\n}\n","import { buildSchema } from \"./buildSchema\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function genRequestBody(type: import(\"ts-morph\").Type): any | null {\n const inp = type.getProperty(\"input\")?.getValueDeclarationOrThrow().getType();\n if (!inp) return null;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const content: Record<string, any> = {};\n const j = inp.getProperty(\"json\");\n if (j) {\n content[\"application/json\"] = {\n schema: buildSchema(j.getValueDeclarationOrThrow().getType()),\n };\n }\n const f = inp.getProperty(\"form\");\n if (f) {\n content[\"multipart/form-data\"] = {\n schema: buildSchema(f.getValueDeclarationOrThrow().getType()),\n };\n }\n return Object.keys(content).length ? { required: true, content } : null;\n}\n"],"mappings":";;;;;;;;AAOO,SAAS,aAAa,QAAwC;AACnE,SAAO;AACT;;;ACTA,OAAOA,SAAQ;AACf,OAAOC,SAAQ,WAAAC,gBAAe;AAC9B,SAAS,eAAe;;;ACFxB,SAAS,SAAS,eAAe;AACjC,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AAGzB,eAAsB,WAAW,YAA6C;AAE5E,QAAM,WAAW,QAAQ,QAAQ,IAAI,GAAG,UAAU;AAElD,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,UAAM,IAAI,MAAM,sCAAsC,QAAQ,EAAE;AAAA,EAClE;AAGA,QAAM,MAAM,QAAQ,QAAQ;AAC5B,MAAI,aAAa,MAAM;AAAA,EAAC;AAGxB,MAAI,QAAQ,SAAS,QAAQ,UAAU,QAAQ,QAAQ;AACrD,UAAM,MAAM,SAAS;AAAA,MACnB,QAAQ;AAAA,MACR,KAAK;AAAA,IACP,CAAC;AACD,iBAAa,IAAI;AAAA,EACnB;AAGA,MAAI;AAEJ,MAAI;AACF,QAAI,QAAQ,UAAU,QAAQ,QAAQ;AAEpC,qBAAe,MAAM,OAAO,cAAc,QAAQ,EAAE;AAAA,IACtD,OAAO;AAEL,qBAAe,UAAQ,QAAQ;AAAA,IACjC;AAAA,EACF,SAAS,KAAK;AACZ,eAAW;AACX,UAAM,IAAI;AAAA,MACR,sCACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CACjD;AAAA,IACF;AAAA,EACF,UAAE;AACA,eAAW;AAAA,EACb;AAGA,QAAM,SACJ,gBACA,OAAO,iBAAiB,YACxB,aAAa;AAAA;AAAA,IAER,aAAqB;AAAA,MACtB;AAEN,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI;AAAA,MACR,6DAA6D,OAAO,MAAM;AAAA,IAC5E;AAAA,EACF;AAEA,SAAO;AACT;;;ACjEA,OAAO,QAAQ;AACf,OAAO,UAAU;;;ACDV,SAAS,kBAAkB,QAAwB;AACxD,SAAO,OACJ,QAAQ,OAAO,EAAE,EACjB,MAAM,aAAa,EACnB,OAAO,OAAO,EACd;AAAA,IAAI,CAAC,KAAK,MACT,MAAM,IACF,IAAI,YAAY,IAChB,IAAI,CAAC,EAAE,YAAY,IAAI,IAAI,MAAM,CAAC,EAAE,YAAY;AAAA,EACtD,EACC,KAAK,EAAE;AACZ;AAEO,SAAS,YACd,MAC2B;AAC3B,SAAO,KAAK,QAAQ,IAAI,KAAK,cAAc,IAAI,CAAC,IAAI;AACtD;AAEO,SAAS,qBAAqB,UAA0B;AAC7D,SAAO,SAAS,QAAQ,sCAAsC,WAAW;AAC3E;AAEO,SAAS,qBAEd,WACA,SACA,QACA;AA5BF;AA6BE,QAAM,mBAAkB,eAAU,cAAV,mBAAqB;AAC7C,MAAI,CAAC,gBAAiB;AAEtB,QAAM,OAAO,gBAAgB,eAAe;AAE5C,MAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,UAAM,UAAU,gBAAgB;AAEhC,QAAI,WAAW,OAAO,KAAK,OAAO,EAAE,SAAS,GAAG;AAC9C,sBAAgB,cAAc;AAC9B,cAAQ;AAAA,QACN,iDAAuC,OAAO,YAAY,CAAC,IAAI,OAAO;AAAA,MACxE;AAAA,IACF,OAAO;AACL,aAAO,UAAU,UAAU;AAC3B,cAAQ;AAAA,QACN,8CAAkC,OAAO,YAAY,CAAC,IAAI,OAAO;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AACF;AAGO,SAAS,QACd,KACA,IACqB;AACrB,SAAO,IAAI,OAAO,CAAC,KAAK,MAAM;AAC5B,KAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;AAC1B,WAAO;AAAA,EACT,GAAG,CAAC,CAAwB;AAC9B;;;ADvDA,eAAsB,cAAc;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4C;AAC1C,KAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAE5C,QAAM,aAAa,KAAK,KAAK,YAAY,GAAG,QAAQ,OAAO;AAC3D,QAAM,WAAW,KAAK,QAAQ,UAAU,SAAS,WAAW;AAE5D,QAAM,aAAa,QAAQ,oBAAoB,QAAQ;AACvD,QAAM,cAAc,WAAW,eAAe;AAC9C,QAAM,aAAa,WAAW,cAAc;AAE5C,MAAI,SAAS,0BAA0B,SAAS,WAAW;AAAA;AAAA;AAE3D,cAAY,QAAQ,CAAC,UAAU;AAC7B,UAAM,MAAM,MAAM,QAAQ,EAAE,QAAQ,KAAK;AACzC,UAAM,QAAQ,qBAAqB,GAAG;AACtC,cAAU,eAAe,MAAM,QAAQ,CAAC,MAAM,KAAK;AAAA;AAAA;AAAA,EACrD,CAAC;AAED,aAAW,QAAQ,CAAC,SAAS;AAC3B,cAAU,KAAK,QAAQ,IAAI;AAAA,EAC7B,CAAC;AAED,QAAM,aAAa,OAAO,wBAAwB;AAElD,KAAG,cAAc,YAAY,GAAG,UAAU;AAAA,EAAK,MAAM,IAAI,OAAO;AAChE,UAAQ,IAAI,iBAAY,UAAU,EAAE;AACpC,SAAO,EAAE,aAAa,YAAY,MAAM,SAAS;AACnD;;;AEtCA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB;AAAA,EACE,cAAAC;AAAA,OAMK;;;ACVP,SAAS,kBAAkB;AAGpB,SAAS,YAAY,MAAoC;AAHhE;AAIE,MAAI,KAAK,QAAQ,GAAG;AAClB,UAAM,UAAU,KAAK,cAAc;AACnC,UAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC;AACtD,UAAM,WAAW,QAAQ;AAAA,MACvB,CAAC,MAAM,EAAE,gBAAgB,KAAK,EAAE,OAAO,KAAK,EAAE,YAAY;AAAA,IAC5D;AACA,QAAI,KAAK,UAAU,UAAU;AAE3B,YAAM,SAAc;AAAA,QAClB,MAAM;AAAA,QACN,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC;AAAA,MAC3C;AACA,UAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE,YAAY,CAAC;AACnD,eAAO,WAAW;AACpB,aAAO;AAAA,IACT;AACA,UAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,CAAC,EAAE,YAAY,CAAC;AACrE,WAAO,EAAE,OAAO,QAAQ,IAAI,WAAW,EAAE;AAAA,EAC3C;AACA,MAAI,KAAK,SAAS,EAAG,QAAO,EAAE,MAAM,SAAS;AAC7C,MAAI,KAAK,SAAS,EAAG,QAAO,EAAE,MAAM,SAAS;AAC7C,MAAI,KAAK,UAAU,EAAG,QAAO,EAAE,MAAM,UAAU;AAC/C,MAAI,KAAK,QAAQ,GAAG;AAClB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,YAAY,KAAK,2BAA2B,CAAC;AAAA,IACtD;AAAA,EACF;AAEA,QAAM,UAAQ,UAAK,UAAU,MAAf,mBAAkB,sBAAqB,CAAC;AACtD,QAAM,QAAQ,MAAM;AAAA,IAClB,CAAC,MACC,EAAE,QAAQ,MAAM,WAAW,eAC3B,EAAE,QAAQ,MAAM,WAAW;AAAA,EAC/B;AACA,MAAI,CAAC,MAAO,QAAO,CAAC;AAEpB,QAAM,QAAQ,KACX,cAAc,EACd;AAAA,IACC,CAAC,MAAG;AA5CV,UAAAC;AA4Ca,eAAAA,MAAA,EAAE,oBAAoB,MAAtB,gBAAAA,IAAyB,eAAc,WAAW;AAAA;AAAA,EAC3D;AAEF,QAAM,WAAgC,CAAC;AACvC,QAAM,MAAgB,CAAC;AACvB,aAAW,KAAK,OAAO;AACrB,UAAM,OAAO,EAAE,2BAA2B;AAC1C,aAAS,EAAE,QAAQ,CAAC,IAAI,YAAY,KAAK,QAAQ,CAAC;AAClD,QAAI,CAAC,EAAE,WAAW,EAAG,KAAI,KAAK,EAAE,QAAQ,CAAC;AAAA,EAC3C;AAEA,QAAM,MAAW,EAAE,MAAM,UAAU,YAAY,SAAS;AACxD,MAAI,IAAI,OAAQ,KAAI,WAAW;AAC/B,SAAO;AACT;;;ACvDO,SAAS,cAAc,MAAsC;AAHpE;AAIE,QAAM,SAAQ,UACX,YAAY,OAAO,MADR,mBAEV,6BACD;AACH,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,QAAM,UAAU,CAAC,SAAS,SAAS,UAAU,QAAQ;AAErD,QAAM,SAAgB,CAAC;AACvB,aAAW,OAAO,SAAS;AACzB,UAAM,IAAI,MAAM,YAAY,GAAG;AAC/B,QAAI,CAAC,EAAG;AACR,UAAM,UAAU,EAAE,2BAA2B,EAAE,QAAQ;AACvD,eAAW,KAAK,QAAQ,cAAc,GAAG;AACvC,YAAM,KAAK,EAAE,2BAA2B,EAAE,QAAQ;AAClD,aAAO,KAAK;AAAA,QACV,MAAM,EAAE,QAAQ;AAAA,QAChB,IAAI,QAAQ,UAAU,SAAS;AAAA,QAC/B,UAAU,CAAC,EAAE,WAAW;AAAA,QACxB,QAAQ,YAAY,EAAE;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;;;ACxBO,SAAS,eAAe,MAA2C;AAH1E;AAIE,QAAM,OAAM,UAAK,YAAY,OAAO,MAAxB,mBAA2B,6BAA6B;AACpE,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,UAA+B,CAAC;AACtC,QAAM,IAAI,IAAI,YAAY,MAAM;AAChC,MAAI,GAAG;AACL,YAAQ,kBAAkB,IAAI;AAAA,MAC5B,QAAQ,YAAY,EAAE,2BAA2B,EAAE,QAAQ,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,QAAM,IAAI,IAAI,YAAY,MAAM;AAChC,MAAI,GAAG;AACL,YAAQ,qBAAqB,IAAI;AAAA,MAC/B,QAAQ,YAAY,EAAE,2BAA2B,EAAE,QAAQ,CAAC;AAAA,IAC9D;AAAA,EACF;AACA,SAAO,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,UAAU,MAAM,QAAQ,IAAI;AACrE;;;AHGA,eAAsB,gBAAgB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMyB;AACvB,QAAM,KAAK,QAAQ;AAAA,IACjBC,MAAK,QAAQ,UAAU,aAAa,WAAW;AAAA,EACjD;AACA,QAAM,YAAY,GAAG,oBAAoB,SAAS;AAElD,QAAM,cAAc,UAAU,YAAY;AAE1C,MAAI;AAEJ,MAAI,2CAAa,OAAOC,YAAW,gBAAgB;AACjD,eAAY,YAAkC,iBAAiB;AAAA,EACjE,WAAW,2CAAa,OAAOA,YAAW,aAAa;AACrD,eAAY,YAA+B,iBAAiB;AAAA,EAC9D,OAAO;AACL,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,QAAM,aAAa,SAAS,CAAC;AAG7B,QAAM,WAA8B,CAAC;AACrC,MAAI,WAAW,OAAOA,YAAW,gBAAgB,GAAG;AAClD,eAAW,MAAM,WACd,OAAOA,YAAW,gBAAgB,EAClC,aAAa,GAAG;AACjB,UAAI,GAAG,OAAOA,YAAW,WAAW;AAClC,iBAAS,KAAK,EAAqB;AAAA,IACvC;AAAA,EACF,WAAW,WAAW,OAAOA,YAAW,WAAW,GAAG;AACpD,aAAS,KAAK,UAA6B;AAAA,EAC7C,OAAO;AACL,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AAEA,QAAM,QAAiB,CAAC;AAExB,aAAW,OAAO,UAAU;AAC1B,eAAW,UAAU,IAAI,WAAW,GAAG;AACrC,UAAI,CAAC,OAAO,OAAOA,YAAW,iBAAiB,EAAG;AAClD,YAAM,YAAY,OAAO,cAAcA,YAAW,iBAAiB;AAEnE,YAAM,MAAM,UAAU,YAAY,EAAE,QAAQ,EAAE,QAAQ,MAAM,EAAE;AAC9D,YAAM,QAAQ,IAAI,QAAQ,aAAa,MAAM;AAC7C,UAAI,CAAC,MAAM,KAAK,EAAG,OAAM,KAAK,IAAI,CAAC;AAGnC,YAAM,KAAK,UAAU,YAAY;AACjC,UAAI,CAAC,MAAM,CAAC,GAAG,OAAOA,YAAW,WAAW,EAAG;AAC/C,YAAM,MAAM;AAEZ,iBAAW,KAAK,IAAI,WAAW,GAAG;AAChC,YAAI,CAAC,EAAE,OAAOA,YAAW,iBAAiB,EAAG;AAC7C,cAAM,aAAa,EAAE,cAAcA,YAAW,iBAAiB;AAC/D,cAAM,OAAO,WAAW,YAAY,EAAE,QAAQ;AAC9C,cAAM,OAAO,KAAK,MAAM,CAAC,EAAE,YAAY;AACvC,cAAM,WAAW,YAAY,WAAW,QAAQ,CAAC;AAGjD,cAAM,KAAU;AAAA,UACd,SAAS,kBAAkB,KAAK,YAAY,CAAC,IAAI,KAAK;AAAA,QACxD;AAGA,cAAM,SAAS,cAAc,SAAS,CAAC,CAAC;AACxC,YAAI,OAAO,OAAQ,IAAG,aAAa;AAGnC,cAAM,KAAK,eAAe,SAAS,CAAC,CAAC;AACrC,YAAI,GAAI,IAAG,cAAc;AAGzB,WAAG,YAAY,CAAC;AAChB,cAAM,WAAW,QAAQ,UAAU,CAAC,MAAM;AACxC,gBAAM,IAAI,EACP,YAAY,QAAQ,EACpB,2BAA2B,EAC3B,QAAQ,EACR,QAAQ;AACX,iBAAO,QAAQ,KAAK,CAAC,IAAI,IAAI;AAAA,QAC/B,CAAC;AACD,mBAAW,CAAC,MAAM,EAAE,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACjD,gBAAM,UAAU,GAAG;AAAA,YAAI,CAAC,MACtB;AAAA,cACE,EAAE,YAAY,QAAQ,EAAG,2BAA2B,EAAE,QAAQ;AAAA,YAChE;AAAA,UACF;AACA,gBAAM,SAAS,QAAQ,SAAS,IAAI,EAAE,OAAO,QAAQ,IAAI,QAAQ,CAAC;AAClE,aAAG,UAAU,IAAI,IAAI;AAAA,YACnB,aACE,SAAS,YACL,uBAAuB,GAAG,CAAC,EACxB,YAAY,QAAQ,EACpB,2BAA2B,EAC3B,QAAQ,EACR,QAAQ,CAAC,KACZ,UAAU,IAAI;AAAA,YACpB,SAAS,EAAE,oBAAoB,EAAE,OAAO,EAAE;AAAA,UAC5C;AAAA,QACF;AAEA,cAAM,KAAK,EAAE,IAAI,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,OAAO;AAAA,IACX,GAAG,OAAO;AAAA,IACV;AAAA,EACF;AAGA,QAAM,aAAaD,MAAK,KAAK,YAAY,GAAG,QAAQ,OAAO;AAE3D,EAAAE,IAAG,UAAUF,MAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,EAAAE,IAAG,cAAc,YAAY,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AACnE,UAAQ,IAAI,6BAAwB,UAAU,EAAE;AAChD,SAAO,EAAE,aAAa,WAAW;AACnC;;;AJtJA,eAAsB,YAAY,YAAoB;AACpD,QAAM,SAAS,MAAM,WAAW,UAAU;AAE1C,QAAM,WAAW,QAAQ,IAAI;AAC7B,UAAQ,IAAI,wCAAwC,OAAO,YAAY;AACvE,QAAM,UAAU,IAAI,QAAQ;AAAA,IAC1B,kBAAkBC,SAAQ,UAAU,OAAO,YAAY;AAAA,EACzD,CAAC;AAED,QAAM,YACJ,UAAU,SAAS,OAAO,KAAK,UAAU,SAAS,SAAS;AAE7D,QAAM,SAAS,YACXC,MAAK,QAAQ,WAAW,QAAQ,IAChCA,MAAK,QAAQ,UAAQ,QAAQ,gCAAgC,CAAC;AAElE,QAAM,OAAO,OAAO;AAEpB,QAAM,qBAAqBA,MAAK,QAAQ,QAAQ,cAAc;AAC9D,QAAM,oBAAoBA,MAAK,QAAQ,QAAQ,gBAAgB;AAE/D,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,aAAW,YAAY,MAAM;AAC3B,UAAM,gBAAgB,kBAAkB,SAAS,SAAS;AAE1D,UAAM,eAAe,MAAM,cAAc;AAAA,MACvC,GAAG;AAAA,MACH;AAAA,MACA,UAAU;AAAA,MACV,YAAY;AAAA,IACd,CAAC;AAED,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA,GAAG;AAAA,MACH,UAAU;AAAA,MACV,YAAY;AAAA,IACd,CAAC;AAAA,EACH;AAEA,QAAM,SAAS;AAAA,IACb,GAAG,OAAO;AAAA,IACV,MAAM,CAAC;AAAA;AAAA,IAEP,OAAO,CAAC;AAAA,EACV;AAEA,aAAW,YAAY,MAAM;AAC3B,UAAM,OAAO,kBAAkB,SAAS,SAAS;AACjD,UAAM,cAAcA,MAAK,KAAK,mBAAmB,GAAG,IAAI,OAAO;AAE/D,QAAI,CAACC,IAAG,WAAW,WAAW,GAAG;AAC/B,cAAQ,KAAK,sCAA4B,WAAW,EAAE;AACtD;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,MAAMA,IAAG,aAAa,aAAa,OAAO,CAAC;AAC7D,WAAO,KAAK,KAAK,EAAE,MAAM,SAAS,KAAK,CAAC;AAExC,UAAM,eAAe,oBAAI,IAAiB;AAE1C,QAAI,qCAAU,KAAK;AACjB,iBAAW,aAAa,SAAS,KAAK;AACpC,cAAM,WACJD,MAAK,MACF,KAAK,SAAS,WAAW,UAAU,GAAG,EACtC,QAAQ,QAAQ,EAAE,KAAK;AAC5B,qBAAa;AAAA,UACX,GAAG,UAAU,OAAO,YAAY,CAAC,IAAI,QAAQ;AAAA,UAC7C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,eAAW,CAAC,SAAS,UAAU,KAAK,OAAO,QAAa,KAAK,KAAK,GAAG;AACnE,YAAM,eACJA,MAAK,MAAM,KAAK,SAAS,WAAW,OAAO,EAAE,QAAQ,QAAQ,EAAE,KAAK;AACtE,UAAI,CAAC,OAAO,MAAM,YAAY,EAAG,QAAO,MAAM,YAAY,IAAI,CAAC;AAG/D,iBAAW,CAAC,QAAQ,SAAS,KAAK,OAAO,QAAa,UAAU,GAAG;AACjE,cAAM,QAAQ,GAAG,OAAO,YAAY,CAAC,IAAI,YAAY;AACrD,cAAM,YAAY,aAAa,IAAI,KAAK;AAGxC,YAAI,WAAW;AACb,oBAAU,UAAU,UAAU,WAAW,UAAU;AACnD,oBAAU,cACR,UAAU,eAAe,UAAU;AACrC,oBAAU,OACR,UAAU,OAAO,UAAU,IAAI,SAAS,IACpC,UAAU,MACV,CAAC,SAAS,IAAI;AAAA,QACtB,OAAO;AACL,oBAAU,OAAO,UAAU,QAAQ,CAAC;AACpC,cAAI,CAAC,UAAU,KAAK,SAAS,SAAS,IAAI,GAAG;AAC3C,sBAAU,KAAK,KAAK,SAAS,IAAI;AAAA,UACnC;AAAA,QACF;AAEA,6BAAqB,WAAW,cAAc,MAAM;AACpD,eAAO,MAAM,YAAY,EAAE,MAAM,IAAI;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAaA,MAAK,KAAK,UAAU,OAAO,QAAQ,WAAW;AACjE,EAAAC,IAAG,UAAUD,MAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAE1D,EAAAC,IAAG,cAAc,YAAY,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,CAAI;AAEnE,UAAQ,IAAI,gDAA2C,UAAU,EAAE;AACrE;","names":["fs","path","resolve","fs","path","SyntaxKind","_a","path","SyntaxKind","fs","resolve","path","fs"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rcmade/hono-docs",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Auto-generate OpenAPI 3.0 spec from Hono route types",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/rcmade/hono-docs.git"
|
|
8
|
+
},
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/rcmade/hono-docs/issues"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/rcmade/hono-docs#readme",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"openapi",
|
|
15
|
+
"hono",
|
|
16
|
+
"typescript",
|
|
17
|
+
"documentation",
|
|
18
|
+
"cli",
|
|
19
|
+
"generator"
|
|
20
|
+
],
|
|
21
|
+
"main": "dist/index.js",
|
|
22
|
+
"module": "dist/index.mjs",
|
|
23
|
+
"types": "dist/index.d.ts",
|
|
24
|
+
"bin": {
|
|
25
|
+
"hono-docs": "dist/cli/index.js"
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"import": "./dist/index.mjs",
|
|
30
|
+
"require": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./core": {
|
|
33
|
+
"import": "./dist/core/index.mjs",
|
|
34
|
+
"require": "./dist/core/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./cli": {
|
|
37
|
+
"import": "./dist/cli/index.mjs",
|
|
38
|
+
"require": "./dist/cli/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"dist"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"esbuild-register": "^3.6.0",
|
|
50
|
+
"ts-morph": "^26.0.0",
|
|
51
|
+
"yargs": "^18.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@eslint/js": "^9.29.0",
|
|
55
|
+
"@types/node": "^24.0.3",
|
|
56
|
+
"@types/yargs": "^17.0.33",
|
|
57
|
+
"@typescript-eslint/eslint-plugin": "^8.34.1",
|
|
58
|
+
"@typescript-eslint/parser": "^8.34.1",
|
|
59
|
+
"eslint": "^9.29.0",
|
|
60
|
+
"eslint-config-prettier": "^10.1.5",
|
|
61
|
+
"eslint-plugin-format": "^1.0.1",
|
|
62
|
+
"eslint-plugin-import": "^2.32.0",
|
|
63
|
+
"globals": "^16.2.0",
|
|
64
|
+
"husky": "^9.1.7",
|
|
65
|
+
"openapi-types": "^12.1.3",
|
|
66
|
+
"standard-version": "^9.5.0",
|
|
67
|
+
"tsup": "^8.5.0",
|
|
68
|
+
"typescript": "^5.8.3",
|
|
69
|
+
"typescript-eslint": "^8.33.1"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "tsup",
|
|
73
|
+
"lint": "eslint . --ext .ts,.js",
|
|
74
|
+
"postinstall": "husky install"
|
|
75
|
+
}
|
|
76
|
+
}
|