@rexeus/typeweaver-hono 0.5.0 → 0.6.0
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/README.md +11 -9
- package/dist/LICENSE +1 -1
- package/dist/NOTICE +1 -1
- package/dist/index.cjs +99 -95
- package/dist/index.d.cts +6 -5
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +78 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/TypeweaverHono.ts +1 -1
- package/dist/templates/HonoRouter.ejs +3 -3
- package/package.json +11 -11
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -93
- package/dist/metafile-cjs.json +0 -1
- package/dist/metafile-esm.json +0 -1
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ import { HttpStatusCode } from "@rexeus/typeweaver-core";
|
|
|
54
54
|
import type { IGetUserRequest, GetUserResponse, UserNotFoundErrorResponse } from "./generated";
|
|
55
55
|
import { GetUserSuccessResponse } from "./generated";
|
|
56
56
|
|
|
57
|
-
export class UserHandlers implements
|
|
57
|
+
export class UserHandlers implements HonoUserApiHandler {
|
|
58
58
|
async handleGetUserRequest(request: IGetUserRequest, context: Context): Promise<GetUserResponse> {
|
|
59
59
|
// Symbolic database fetch
|
|
60
60
|
const databaseResult = {} as any;
|
|
@@ -108,21 +108,23 @@ serve({ fetch: app.fetch, port: 3000 }, () => {
|
|
|
108
108
|
|
|
109
109
|
`TypeweaverHonoOptions<RequestHandlers>`
|
|
110
110
|
|
|
111
|
-
- `requestHandlers`: object implementing the generated
|
|
111
|
+
- `requestHandlers`: object implementing the generated `Hono<ResourceName>ApiHandler` type
|
|
112
112
|
- `validateRequests` (default: `true`): enable/disable request validation
|
|
113
|
-
- `handleValidationErrors`: `true` | `false` | `(err, c) => IHttpResponse
|
|
113
|
+
- `handleValidationErrors`: `true` | `false` | `(err, c) => IHttpResponse | Promise<IHttpResponse>`,
|
|
114
114
|
- If `true` (default), returns `400 Bad Request` with validation issues in the body
|
|
115
|
-
- If `false`,
|
|
115
|
+
- If `false`, disables this handler (errors fall through to the unknown error handler)
|
|
116
116
|
- If function, calls the function with the error and context, expects an `IHttpResponse` to
|
|
117
117
|
return, so you can customize the response in the way you want
|
|
118
|
-
- `handleHttpResponseErrors`: `true` | `false` |
|
|
118
|
+
- `handleHttpResponseErrors`: `true` | `false` |
|
|
119
|
+
`(err, c) => IHttpResponse | Promise<IHttpResponse>`
|
|
119
120
|
- If `true` (default), returns thrown `HttpResponse` as-is, they will be sent as the response
|
|
120
|
-
- If `false`,
|
|
121
|
+
- If `false`, disables this handler (errors fall through to the unknown error handler)
|
|
121
122
|
- If function, calls the function with the error and context, expects an `IHttpResponse` to
|
|
122
123
|
return, so you can customize the response in the way you want
|
|
123
|
-
- `handleUnknownErrors`: `true` | `false` | `(err, c) => IHttpResponse
|
|
124
|
+
- `handleUnknownErrors`: `true` | `false` | `(err, c) => IHttpResponse | Promise<IHttpResponse>`
|
|
124
125
|
- If `true` (default), returns `500 Internal Server Error` with a generic message
|
|
125
|
-
- If `false`,
|
|
126
|
+
- If `false`, disables this handler (errors propagate to Hono's error handling, e.g. via
|
|
127
|
+
`app.onError`)
|
|
126
128
|
- If function, calls the function with the error and context, expects an `IHttpResponse` to
|
|
127
129
|
return, so you can customize the response in the way you want
|
|
128
130
|
|
|
@@ -131,4 +133,4 @@ object.
|
|
|
131
133
|
|
|
132
134
|
## 📄 License
|
|
133
135
|
|
|
134
|
-
Apache 2.0 © Dennis Wentzien
|
|
136
|
+
Apache 2.0 © Dennis Wentzien 2026
|
package/dist/LICENSE
CHANGED
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
same "printed page" as the copyright notice for easier
|
|
188
188
|
identification within third-party archives.
|
|
189
189
|
|
|
190
|
-
Copyright
|
|
190
|
+
Copyright 2026 Dennis Wentzien
|
|
191
191
|
|
|
192
192
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
193
|
you may not use this file except in compliance with the License.
|
package/dist/NOTICE
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,102 +1,106 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
10
26
|
|
|
11
|
-
|
|
12
|
-
|
|
27
|
+
//#endregion
|
|
28
|
+
let node_path = require("node:path");
|
|
29
|
+
node_path = __toESM(node_path);
|
|
30
|
+
let node_url = require("node:url");
|
|
31
|
+
let _rexeus_typeweaver_gen = require("@rexeus/typeweaver-gen");
|
|
32
|
+
let _rexeus_typeweaver_core = require("@rexeus/typeweaver-core");
|
|
33
|
+
let case$1 = require("case");
|
|
34
|
+
case$1 = __toESM(case$1);
|
|
13
35
|
|
|
14
|
-
|
|
15
|
-
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
16
|
-
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
36
|
+
//#region src/HonoRouterGenerator.ts
|
|
17
37
|
var HonoRouterGenerator = class {
|
|
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
|
-
if (aSegment !== bSegment) {
|
|
72
|
-
return aSegment.localeCompare(bSegment);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return this.getMethodPriority(a.method) - this.getMethodPriority(b.method);
|
|
76
|
-
}
|
|
77
|
-
static getMethodPriority(method) {
|
|
78
|
-
const priorities = {
|
|
79
|
-
GET: 1,
|
|
80
|
-
POST: 2,
|
|
81
|
-
PUT: 3,
|
|
82
|
-
PATCH: 4,
|
|
83
|
-
DELETE: 5,
|
|
84
|
-
OPTIONS: 6,
|
|
85
|
-
HEAD: 7
|
|
86
|
-
};
|
|
87
|
-
return priorities[method] ?? 999;
|
|
88
|
-
}
|
|
38
|
+
static generate(context) {
|
|
39
|
+
const moduleDir = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
40
|
+
const templateFile = node_path.default.join(moduleDir, "templates", "HonoRouter.ejs");
|
|
41
|
+
for (const [entityName, entityResource] of Object.entries(context.resources.entityResources)) this.writeHonoRouter(entityName, templateFile, entityResource.operations, context);
|
|
42
|
+
}
|
|
43
|
+
static writeHonoRouter(entityName, templateFile, operationResources, context) {
|
|
44
|
+
const pascalCaseEntityName = case$1.default.pascal(entityName);
|
|
45
|
+
const outputDir = node_path.default.join(context.outputDir, entityName);
|
|
46
|
+
const outputPath = node_path.default.join(outputDir, `${pascalCaseEntityName}Hono.ts`);
|
|
47
|
+
const operations = operationResources.filter((resource) => resource.definition.method !== _rexeus_typeweaver_core.HttpMethod.HEAD).map((resource) => this.createOperationData(resource)).sort((a, b) => this.compareRoutes(a, b));
|
|
48
|
+
const content = context.renderTemplate(templateFile, {
|
|
49
|
+
coreDir: node_path.default.relative(outputDir, context.outputDir),
|
|
50
|
+
entityName,
|
|
51
|
+
pascalCaseEntityName,
|
|
52
|
+
operations
|
|
53
|
+
});
|
|
54
|
+
const relativePath = node_path.default.relative(context.outputDir, outputPath);
|
|
55
|
+
context.writeFile(relativePath, content);
|
|
56
|
+
}
|
|
57
|
+
static createOperationData(resource) {
|
|
58
|
+
const operationId = resource.definition.operationId;
|
|
59
|
+
const className = case$1.default.pascal(operationId);
|
|
60
|
+
return {
|
|
61
|
+
className,
|
|
62
|
+
handlerName: `handle${className}Request`,
|
|
63
|
+
method: resource.definition.method,
|
|
64
|
+
path: resource.definition.path
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
static compareRoutes(a, b) {
|
|
68
|
+
const aSegments = a.path.split("/").filter((s) => s);
|
|
69
|
+
const bSegments = b.path.split("/").filter((s) => s);
|
|
70
|
+
if (aSegments.length !== bSegments.length) return aSegments.length - bSegments.length;
|
|
71
|
+
for (let i = 0; i < aSegments.length; i++) {
|
|
72
|
+
const aSegment = aSegments[i];
|
|
73
|
+
const bSegment = bSegments[i];
|
|
74
|
+
const aIsParam = aSegment.startsWith(":");
|
|
75
|
+
if (aIsParam !== bSegment.startsWith(":")) return aIsParam ? 1 : -1;
|
|
76
|
+
if (aSegment !== bSegment) return aSegment.localeCompare(bSegment);
|
|
77
|
+
}
|
|
78
|
+
return this.getMethodPriority(a.method) - this.getMethodPriority(b.method);
|
|
79
|
+
}
|
|
80
|
+
static getMethodPriority(method) {
|
|
81
|
+
return {
|
|
82
|
+
GET: 1,
|
|
83
|
+
POST: 2,
|
|
84
|
+
PUT: 3,
|
|
85
|
+
PATCH: 4,
|
|
86
|
+
DELETE: 5,
|
|
87
|
+
OPTIONS: 6,
|
|
88
|
+
HEAD: 7
|
|
89
|
+
}[method] ?? 999;
|
|
90
|
+
}
|
|
89
91
|
};
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/index.ts
|
|
95
|
+
const moduleDir = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
96
|
+
var HonoPlugin = class extends _rexeus_typeweaver_gen.BasePlugin {
|
|
97
|
+
name = "hono";
|
|
98
|
+
generate(context) {
|
|
99
|
+
const libSourceDir = node_path.default.join(moduleDir, "lib");
|
|
100
|
+
this.copyLibFiles(context, libSourceDir, this.name);
|
|
101
|
+
HonoRouterGenerator.generate(context);
|
|
102
|
+
}
|
|
100
103
|
};
|
|
101
104
|
|
|
102
|
-
|
|
105
|
+
//#endregion
|
|
106
|
+
module.exports = HonoPlugin;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { BasePlugin, GeneratorContext } from
|
|
1
|
+
import { BasePlugin, GeneratorContext } from "@rexeus/typeweaver-gen";
|
|
2
2
|
|
|
3
|
+
//#region src/index.d.ts
|
|
3
4
|
declare class HonoPlugin extends BasePlugin {
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
name: string;
|
|
6
|
+
generate(context: GeneratorContext): void;
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
export = HonoPlugin;
|
|
9
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;cAQqB,UAAA,SAAmB,UAAA;EAC/B,IAAA;EAES,QAAA,CAAS,OAAA,EAAS,gBAAA;AAAA;AAAA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BasePlugin, GeneratorContext } from "@rexeus/typeweaver-gen";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
declare class HonoPlugin extends BasePlugin {
|
|
5
|
+
name: string;
|
|
6
|
+
generate(context: GeneratorContext): void;
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { HonoPlugin as default };
|
|
10
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;cAQqB,UAAA,SAAmB,UAAA;EAC/B,IAAA;EAES,QAAA,CAAS,OAAA,EAAS,gBAAA;AAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { BasePlugin } from "@rexeus/typeweaver-gen";
|
|
4
|
+
import { HttpMethod } from "@rexeus/typeweaver-core";
|
|
5
|
+
import Case from "case";
|
|
6
|
+
|
|
7
|
+
//#region src/HonoRouterGenerator.ts
|
|
8
|
+
var HonoRouterGenerator = class {
|
|
9
|
+
static generate(context) {
|
|
10
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const templateFile = path.join(moduleDir, "templates", "HonoRouter.ejs");
|
|
12
|
+
for (const [entityName, entityResource] of Object.entries(context.resources.entityResources)) this.writeHonoRouter(entityName, templateFile, entityResource.operations, context);
|
|
13
|
+
}
|
|
14
|
+
static writeHonoRouter(entityName, templateFile, operationResources, context) {
|
|
15
|
+
const pascalCaseEntityName = Case.pascal(entityName);
|
|
16
|
+
const outputDir = path.join(context.outputDir, entityName);
|
|
17
|
+
const outputPath = path.join(outputDir, `${pascalCaseEntityName}Hono.ts`);
|
|
18
|
+
const operations = operationResources.filter((resource) => resource.definition.method !== HttpMethod.HEAD).map((resource) => this.createOperationData(resource)).sort((a, b) => this.compareRoutes(a, b));
|
|
19
|
+
const content = context.renderTemplate(templateFile, {
|
|
20
|
+
coreDir: path.relative(outputDir, context.outputDir),
|
|
21
|
+
entityName,
|
|
22
|
+
pascalCaseEntityName,
|
|
23
|
+
operations
|
|
24
|
+
});
|
|
25
|
+
const relativePath = path.relative(context.outputDir, outputPath);
|
|
26
|
+
context.writeFile(relativePath, content);
|
|
27
|
+
}
|
|
28
|
+
static createOperationData(resource) {
|
|
29
|
+
const operationId = resource.definition.operationId;
|
|
30
|
+
const className = Case.pascal(operationId);
|
|
31
|
+
return {
|
|
32
|
+
className,
|
|
33
|
+
handlerName: `handle${className}Request`,
|
|
34
|
+
method: resource.definition.method,
|
|
35
|
+
path: resource.definition.path
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
static compareRoutes(a, b) {
|
|
39
|
+
const aSegments = a.path.split("/").filter((s) => s);
|
|
40
|
+
const bSegments = b.path.split("/").filter((s) => s);
|
|
41
|
+
if (aSegments.length !== bSegments.length) return aSegments.length - bSegments.length;
|
|
42
|
+
for (let i = 0; i < aSegments.length; i++) {
|
|
43
|
+
const aSegment = aSegments[i];
|
|
44
|
+
const bSegment = bSegments[i];
|
|
45
|
+
const aIsParam = aSegment.startsWith(":");
|
|
46
|
+
if (aIsParam !== bSegment.startsWith(":")) return aIsParam ? 1 : -1;
|
|
47
|
+
if (aSegment !== bSegment) return aSegment.localeCompare(bSegment);
|
|
48
|
+
}
|
|
49
|
+
return this.getMethodPriority(a.method) - this.getMethodPriority(b.method);
|
|
50
|
+
}
|
|
51
|
+
static getMethodPriority(method) {
|
|
52
|
+
return {
|
|
53
|
+
GET: 1,
|
|
54
|
+
POST: 2,
|
|
55
|
+
PUT: 3,
|
|
56
|
+
PATCH: 4,
|
|
57
|
+
DELETE: 5,
|
|
58
|
+
OPTIONS: 6,
|
|
59
|
+
HEAD: 7
|
|
60
|
+
}[method] ?? 999;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/index.ts
|
|
66
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
67
|
+
var HonoPlugin = class extends BasePlugin {
|
|
68
|
+
name = "hono";
|
|
69
|
+
generate(context) {
|
|
70
|
+
const libSourceDir = path.join(moduleDir, "lib");
|
|
71
|
+
this.copyLibFiles(context, libSourceDir, this.name);
|
|
72
|
+
HonoRouterGenerator.generate(context);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
export { HonoPlugin as default };
|
|
78
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/HonoRouterGenerator.ts","../src/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { HttpMethod } from \"@rexeus/typeweaver-core\";\nimport type {\n GeneratorContext,\n OperationResource,\n} from \"@rexeus/typeweaver-gen\";\nimport Case from \"case\";\n\nexport class HonoRouterGenerator {\n public static generate(context: GeneratorContext): void {\n const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n const templateFile = path.join(moduleDir, \"templates\", \"HonoRouter.ejs\");\n\n for (const [entityName, entityResource] of Object.entries(\n context.resources.entityResources\n )) {\n this.writeHonoRouter(\n entityName,\n templateFile,\n entityResource.operations,\n context\n );\n }\n }\n\n private static writeHonoRouter(\n entityName: string,\n templateFile: string,\n operationResources: OperationResource[],\n context: GeneratorContext\n ): void {\n const pascalCaseEntityName = Case.pascal(entityName);\n const outputDir = path.join(context.outputDir, entityName);\n const outputPath = path.join(outputDir, `${pascalCaseEntityName}Hono.ts`);\n\n const operations = operationResources\n // Hono handles HEAD requests automatically, so we skip them\n .filter(resource => resource.definition.method !== HttpMethod.HEAD)\n .map(resource => this.createOperationData(resource))\n .sort((a, b) => this.compareRoutes(a, b));\n\n const content = context.renderTemplate(templateFile, {\n coreDir: path.relative(outputDir, context.outputDir),\n entityName,\n pascalCaseEntityName,\n operations,\n });\n\n const relativePath = path.relative(context.outputDir, outputPath);\n context.writeFile(relativePath, content);\n }\n\n private static createOperationData(resource: OperationResource) {\n const operationId = resource.definition.operationId;\n const className = Case.pascal(operationId);\n const handlerName = `handle${className}Request`;\n\n return {\n className,\n handlerName,\n method: resource.definition.method,\n path: resource.definition.path,\n };\n }\n\n private static compareRoutes(\n a: ReturnType<typeof HonoRouterGenerator.createOperationData>,\n b: ReturnType<typeof HonoRouterGenerator.createOperationData>\n ): number {\n const aSegments = a.path.split(\"/\").filter(s => s);\n const bSegments = b.path.split(\"/\").filter(s => s);\n\n // 1. Compare by depth first (shallow to deep)\n if (aSegments.length !== bSegments.length) {\n return aSegments.length - bSegments.length;\n }\n\n // 2. Compare segment by segment\n for (let i = 0; i < aSegments.length; i++) {\n const aSegment = aSegments[i]!;\n const bSegment = bSegments[i]!;\n\n const aIsParam = aSegment.startsWith(\":\");\n const bIsParam = bSegment.startsWith(\":\");\n\n // Static segments before parameters\n if (aIsParam !== bIsParam) {\n return aIsParam ? 1 : -1;\n }\n\n // Within same type, alphabetical order\n if (aSegment !== bSegment) {\n return aSegment.localeCompare(bSegment);\n }\n }\n\n // 3. Same path = sort by HTTP method priority\n return this.getMethodPriority(a.method) - this.getMethodPriority(b.method);\n }\n\n private static getMethodPriority(method: string): number {\n const priorities: Record<string, number> = {\n GET: 1,\n POST: 2,\n PUT: 3,\n PATCH: 4,\n DELETE: 5,\n OPTIONS: 6,\n HEAD: 7,\n };\n return priorities[method] ?? 999;\n }\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { BasePlugin } from \"@rexeus/typeweaver-gen\";\nimport type { GeneratorContext } from \"@rexeus/typeweaver-gen\";\nimport { HonoRouterGenerator } from \"./HonoRouterGenerator\";\n\nconst moduleDir = path.dirname(fileURLToPath(import.meta.url));\n\nexport default class HonoPlugin extends BasePlugin {\n public name = \"hono\";\n\n public override generate(context: GeneratorContext): void {\n const libSourceDir = path.join(moduleDir, \"lib\");\n this.copyLibFiles(context, libSourceDir, this.name);\n\n HonoRouterGenerator.generate(context);\n }\n}\n"],"mappings":";;;;;;;AASA,IAAa,sBAAb,MAAiC;CAC/B,OAAc,SAAS,SAAiC;EACtD,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;EAC9D,MAAM,eAAe,KAAK,KAAK,WAAW,aAAa,iBAAiB;AAExE,OAAK,MAAM,CAAC,YAAY,mBAAmB,OAAO,QAChD,QAAQ,UAAU,gBACnB,CACC,MAAK,gBACH,YACA,cACA,eAAe,YACf,QACD;;CAIL,OAAe,gBACb,YACA,cACA,oBACA,SACM;EACN,MAAM,uBAAuB,KAAK,OAAO,WAAW;EACpD,MAAM,YAAY,KAAK,KAAK,QAAQ,WAAW,WAAW;EAC1D,MAAM,aAAa,KAAK,KAAK,WAAW,GAAG,qBAAqB,SAAS;EAEzE,MAAM,aAAa,mBAEhB,QAAO,aAAY,SAAS,WAAW,WAAW,WAAW,KAAK,CAClE,KAAI,aAAY,KAAK,oBAAoB,SAAS,CAAC,CACnD,MAAM,GAAG,MAAM,KAAK,cAAc,GAAG,EAAE,CAAC;EAE3C,MAAM,UAAU,QAAQ,eAAe,cAAc;GACnD,SAAS,KAAK,SAAS,WAAW,QAAQ,UAAU;GACpD;GACA;GACA;GACD,CAAC;EAEF,MAAM,eAAe,KAAK,SAAS,QAAQ,WAAW,WAAW;AACjE,UAAQ,UAAU,cAAc,QAAQ;;CAG1C,OAAe,oBAAoB,UAA6B;EAC9D,MAAM,cAAc,SAAS,WAAW;EACxC,MAAM,YAAY,KAAK,OAAO,YAAY;AAG1C,SAAO;GACL;GACA,aAJkB,SAAS,UAAU;GAKrC,QAAQ,SAAS,WAAW;GAC5B,MAAM,SAAS,WAAW;GAC3B;;CAGH,OAAe,cACb,GACA,GACQ;EACR,MAAM,YAAY,EAAE,KAAK,MAAM,IAAI,CAAC,QAAO,MAAK,EAAE;EAClD,MAAM,YAAY,EAAE,KAAK,MAAM,IAAI,CAAC,QAAO,MAAK,EAAE;AAGlD,MAAI,UAAU,WAAW,UAAU,OACjC,QAAO,UAAU,SAAS,UAAU;AAItC,OAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;GACzC,MAAM,WAAW,UAAU;GAC3B,MAAM,WAAW,UAAU;GAE3B,MAAM,WAAW,SAAS,WAAW,IAAI;AAIzC,OAAI,aAHa,SAAS,WAAW,IAAI,CAIvC,QAAO,WAAW,IAAI;AAIxB,OAAI,aAAa,SACf,QAAO,SAAS,cAAc,SAAS;;AAK3C,SAAO,KAAK,kBAAkB,EAAE,OAAO,GAAG,KAAK,kBAAkB,EAAE,OAAO;;CAG5E,OAAe,kBAAkB,QAAwB;AAUvD,SAT2C;GACzC,KAAK;GACL,MAAM;GACN,KAAK;GACL,OAAO;GACP,QAAQ;GACR,SAAS;GACT,MAAM;GACP,CACiB,WAAW;;;;;;ACzGjC,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE9D,IAAqB,aAArB,cAAwC,WAAW;CACjD,AAAO,OAAO;CAEd,AAAgB,SAAS,SAAiC;EACxD,MAAM,eAAe,KAAK,KAAK,WAAW,MAAM;AAChD,OAAK,aAAa,SAAS,cAAc,KAAK,KAAK;AAEnD,sBAAoB,SAAS,QAAQ"}
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { HttpResponse, RequestValidationError } from "@rexeus/typeweaver-core";
|
|
9
|
-
import { Hono } from "hono";
|
|
10
9
|
import type {
|
|
11
10
|
IHttpRequest,
|
|
12
11
|
IHttpResponse,
|
|
13
12
|
IRequestValidator,
|
|
14
13
|
} from "@rexeus/typeweaver-core";
|
|
14
|
+
import { Hono } from "hono";
|
|
15
15
|
import { HonoAdapter } from "./HonoAdapter";
|
|
16
16
|
import type { HonoRequestHandler } from "./HonoRequestHandler";
|
|
17
17
|
import type { Context } from "hono";
|
|
@@ -14,14 +14,14 @@ import { <%- operation.className %>RequestValidator } from "./<%- operation.clas
|
|
|
14
14
|
import type { <%- operation.className %>Response } from "./<%- operation.className %>Response";
|
|
15
15
|
<% } %>
|
|
16
16
|
|
|
17
|
-
export type <%- pascalCaseEntityName %>ApiHandler = {
|
|
17
|
+
export type Hono<%- pascalCaseEntityName %>ApiHandler = {
|
|
18
18
|
<% for (const operation of operations) { %>
|
|
19
19
|
<%- operation.handlerName %>: HonoRequestHandler<I<%- operation.className %>Request, <%- operation.className %>Response>;
|
|
20
20
|
<% } %>
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
export class <%- pascalCaseEntityName %>Hono extends TypeweaverHono
|
|
24
|
-
public constructor(options: TypeweaverHonoOptions
|
|
23
|
+
export class <%- pascalCaseEntityName %>Hono extends TypeweaverHono<Hono<%- pascalCaseEntityName %>ApiHandler> {
|
|
24
|
+
public constructor(options: TypeweaverHonoOptions<Hono<%- pascalCaseEntityName %>ApiHandler>) {
|
|
25
25
|
super(options);
|
|
26
26
|
this.setupRoutes();
|
|
27
27
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rexeus/typeweaver-hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Generates Hono routers and handlers straight from your API definitions. Powered by Typeweaver 🧵✨",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"main": "dist/index.cjs",
|
|
8
|
-
"module": "dist/index.
|
|
9
|
-
"types": "dist/index.d.
|
|
8
|
+
"module": "dist/index.mjs",
|
|
9
|
+
"types": "dist/index.d.mts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"import": {
|
|
13
|
-
"types": "./dist/index.d.
|
|
14
|
-
"default": "./dist/index.
|
|
13
|
+
"types": "./dist/index.d.mts",
|
|
14
|
+
"default": "./dist/index.mjs"
|
|
15
15
|
},
|
|
16
16
|
"require": {
|
|
17
17
|
"types": "./dist/index.d.cts",
|
|
@@ -47,22 +47,22 @@
|
|
|
47
47
|
"homepage": "https://github.com/rexeus/typeweaver#readme",
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"hono": "^4.11.0",
|
|
50
|
-
"@rexeus/typeweaver-core": "^0.
|
|
51
|
-
"@rexeus/typeweaver-gen": "^0.
|
|
50
|
+
"@rexeus/typeweaver-core": "^0.6.0",
|
|
51
|
+
"@rexeus/typeweaver-gen": "^0.6.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"hono": "^4.11.3",
|
|
55
55
|
"test-utils": "file:../test-utils",
|
|
56
|
-
"@rexeus/typeweaver-core": "^0.
|
|
57
|
-
"@rexeus/typeweaver-gen": "^0.
|
|
56
|
+
"@rexeus/typeweaver-core": "^0.6.0",
|
|
57
|
+
"@rexeus/typeweaver-gen": "^0.6.0"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"case": "^1.6.3"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"typecheck": "tsc --noEmit",
|
|
64
|
-
"format": "
|
|
65
|
-
"build": "
|
|
64
|
+
"format": "oxfmt",
|
|
65
|
+
"build": "tsdown && mkdir -p ./dist/templates ./dist/lib && cp -r ./src/templates/* ./dist/templates/ && cp -r ./src/lib/* ./dist/lib/ && cp ../../LICENSE ../../NOTICE ./dist/",
|
|
66
66
|
"test": "vitest --run",
|
|
67
67
|
"preversion": "npm run build"
|
|
68
68
|
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import { BasePlugin } from '@rexeus/typeweaver-gen';
|
|
4
|
-
import { HttpMethod } from '@rexeus/typeweaver-core';
|
|
5
|
-
import Case from 'case';
|
|
6
|
-
|
|
7
|
-
// src/index.ts
|
|
8
|
-
var HonoRouterGenerator = class {
|
|
9
|
-
static generate(context) {
|
|
10
|
-
const moduleDir2 = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
const templateFile = path.join(moduleDir2, "templates", "HonoRouter.ejs");
|
|
12
|
-
for (const [entityName, entityResource] of Object.entries(
|
|
13
|
-
context.resources.entityResources
|
|
14
|
-
)) {
|
|
15
|
-
this.writeHonoRouter(
|
|
16
|
-
entityName,
|
|
17
|
-
templateFile,
|
|
18
|
-
entityResource.operations,
|
|
19
|
-
context
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
static writeHonoRouter(entityName, templateFile, operationResources, context) {
|
|
24
|
-
const pascalCaseEntityName = Case.pascal(entityName);
|
|
25
|
-
const outputDir = path.join(context.outputDir, entityName);
|
|
26
|
-
const outputPath = path.join(outputDir, `${pascalCaseEntityName}Hono.ts`);
|
|
27
|
-
const operations = operationResources.filter((resource) => resource.definition.method !== HttpMethod.HEAD).map((resource) => this.createOperationData(resource)).sort((a, b) => this.compareRoutes(a, b));
|
|
28
|
-
const content = context.renderTemplate(templateFile, {
|
|
29
|
-
coreDir: path.relative(outputDir, context.outputDir),
|
|
30
|
-
entityName,
|
|
31
|
-
pascalCaseEntityName,
|
|
32
|
-
operations
|
|
33
|
-
});
|
|
34
|
-
const relativePath = path.relative(context.outputDir, outputPath);
|
|
35
|
-
context.writeFile(relativePath, content);
|
|
36
|
-
}
|
|
37
|
-
static createOperationData(resource) {
|
|
38
|
-
const operationId = resource.definition.operationId;
|
|
39
|
-
const className = Case.pascal(operationId);
|
|
40
|
-
const handlerName = `handle${className}Request`;
|
|
41
|
-
return {
|
|
42
|
-
className,
|
|
43
|
-
handlerName,
|
|
44
|
-
method: resource.definition.method,
|
|
45
|
-
path: resource.definition.path
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
static compareRoutes(a, b) {
|
|
49
|
-
const aSegments = a.path.split("/").filter((s) => s);
|
|
50
|
-
const bSegments = b.path.split("/").filter((s) => s);
|
|
51
|
-
if (aSegments.length !== bSegments.length) {
|
|
52
|
-
return aSegments.length - bSegments.length;
|
|
53
|
-
}
|
|
54
|
-
for (let i = 0; i < aSegments.length; i++) {
|
|
55
|
-
const aSegment = aSegments[i];
|
|
56
|
-
const bSegment = bSegments[i];
|
|
57
|
-
const aIsParam = aSegment.startsWith(":");
|
|
58
|
-
const bIsParam = bSegment.startsWith(":");
|
|
59
|
-
if (aIsParam !== bIsParam) {
|
|
60
|
-
return aIsParam ? 1 : -1;
|
|
61
|
-
}
|
|
62
|
-
if (aSegment !== bSegment) {
|
|
63
|
-
return aSegment.localeCompare(bSegment);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return this.getMethodPriority(a.method) - this.getMethodPriority(b.method);
|
|
67
|
-
}
|
|
68
|
-
static getMethodPriority(method) {
|
|
69
|
-
const priorities = {
|
|
70
|
-
GET: 1,
|
|
71
|
-
POST: 2,
|
|
72
|
-
PUT: 3,
|
|
73
|
-
PATCH: 4,
|
|
74
|
-
DELETE: 5,
|
|
75
|
-
OPTIONS: 6,
|
|
76
|
-
HEAD: 7
|
|
77
|
-
};
|
|
78
|
-
return priorities[method] ?? 999;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// src/index.ts
|
|
83
|
-
var moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
84
|
-
var HonoPlugin = class extends BasePlugin {
|
|
85
|
-
name = "hono";
|
|
86
|
-
generate(context) {
|
|
87
|
-
const libSourceDir = path.join(moduleDir, "lib");
|
|
88
|
-
this.copyLibFiles(context, libSourceDir, this.name);
|
|
89
|
-
HonoRouterGenerator.generate(context);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export { HonoPlugin as default };
|
package/dist/metafile-cjs.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/HonoRouterGenerator.ts":{"bytes":3465,"imports":[{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true},{"path":"@rexeus/typeweaver-core","kind":"import-statement","external":true},{"path":"case","kind":"import-statement","external":true},{"path":"/home/runner/work/typeweaver/typeweaver/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":617,"imports":[{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true},{"path":"@rexeus/typeweaver-gen","kind":"import-statement","external":true},{"path":"src/HonoRouterGenerator.ts","kind":"import-statement","original":"./HonoRouterGenerator"},{"path":"/home/runner/work/typeweaver/typeweaver/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/index.cjs":{"imports":[{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true},{"path":"@rexeus/typeweaver-gen","kind":"import-statement","external":true},{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true},{"path":"@rexeus/typeweaver-core","kind":"import-statement","external":true},{"path":"case","kind":"import-statement","external":true}],"exports":["default"],"entryPoint":"src/index.ts","inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytesInOutput":314},"src/index.ts":{"bytesInOutput":451},"src/HonoRouterGenerator.ts":{"bytesInOutput":2705}},"bytes":3701}}}
|
package/dist/metafile-esm.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js":{"bytes":322,"imports":[{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true}],"format":"esm"},"src/HonoRouterGenerator.ts":{"bytes":3465,"imports":[{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true},{"path":"@rexeus/typeweaver-core","kind":"import-statement","external":true},{"path":"case","kind":"import-statement","external":true},{"path":"/home/runner/work/typeweaver/typeweaver/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":617,"imports":[{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true},{"path":"@rexeus/typeweaver-gen","kind":"import-statement","external":true},{"path":"src/HonoRouterGenerator.ts","kind":"import-statement","original":"./HonoRouterGenerator"},{"path":"/home/runner/work/typeweaver/typeweaver/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/index.js":{"imports":[{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true},{"path":"@rexeus/typeweaver-gen","kind":"import-statement","external":true},{"path":"node:path","kind":"import-statement","external":true},{"path":"node:url","kind":"import-statement","external":true},{"path":"@rexeus/typeweaver-core","kind":"import-statement","external":true},{"path":"case","kind":"import-statement","external":true}],"exports":["default"],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":453},"src/HonoRouterGenerator.ts":{"bytesInOutput":2707}},"bytes":3260}}}
|