@rexeus/typeweaver-hono 0.8.0 → 0.9.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/README.md +6 -2
- package/dist/index.cjs +39 -73
- package/dist/index.mjs +34 -62
- package/dist/index.mjs.map +1 -1
- package/dist/lib/TypeweaverHono.ts +10 -17
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ npm install @rexeus/typeweaver-core
|
|
|
29
29
|
## 💡 How to use
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
npx typeweaver generate --input ./api/
|
|
32
|
+
npx typeweaver generate --input ./api/spec/index.ts --output ./api/generated --plugins hono
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
More on the CLI in
|
|
@@ -66,7 +66,11 @@ export class UserHandlers implements HonoUserApiHandler {
|
|
|
66
66
|
|
|
67
67
|
return createGetUserSuccessResponse({
|
|
68
68
|
header: { "Content-Type": "application/json" },
|
|
69
|
-
body: {
|
|
69
|
+
body: {
|
|
70
|
+
id: request.param.userId,
|
|
71
|
+
name: "Jane",
|
|
72
|
+
email: "jane@example.com",
|
|
73
|
+
},
|
|
70
74
|
});
|
|
71
75
|
}
|
|
72
76
|
// Implement other operation handlers: handleCreateUserRequest, ...
|
package/dist/index.cjs
CHANGED
|
@@ -6,16 +6,12 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
}
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
19
15
|
}
|
|
20
16
|
return to;
|
|
21
17
|
};
|
|
@@ -23,7 +19,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
19
|
value: mod,
|
|
24
20
|
enumerable: true
|
|
25
21
|
}) : target, mod));
|
|
26
|
-
|
|
27
22
|
//#endregion
|
|
28
23
|
let node_path = require("node:path");
|
|
29
24
|
node_path = __toESM(node_path);
|
|
@@ -32,65 +27,37 @@ let _rexeus_typeweaver_gen = require("@rexeus/typeweaver-gen");
|
|
|
32
27
|
let _rexeus_typeweaver_core = require("@rexeus/typeweaver-core");
|
|
33
28
|
let case$1 = require("case");
|
|
34
29
|
case$1 = __toESM(case$1);
|
|
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
|
-
static compareRoutes(a, b) {
|
|
69
|
-
const aSegments = a.path.split("/").filter((s) => s);
|
|
70
|
-
const bSegments = b.path.split("/").filter((s) => s);
|
|
71
|
-
if (aSegments.length !== bSegments.length) return aSegments.length - bSegments.length;
|
|
72
|
-
for (let i = 0; i < aSegments.length; i++) {
|
|
73
|
-
const aSegment = aSegments[i];
|
|
74
|
-
const bSegment = bSegments[i];
|
|
75
|
-
const aIsParam = aSegment.startsWith(":");
|
|
76
|
-
if (aIsParam !== bSegment.startsWith(":")) return aIsParam ? 1 : -1;
|
|
77
|
-
if (aSegment !== bSegment) return aSegment.localeCompare(bSegment);
|
|
78
|
-
}
|
|
79
|
-
return this.getMethodPriority(a.method) - this.getMethodPriority(b.method);
|
|
80
|
-
}
|
|
81
|
-
static getMethodPriority(method) {
|
|
82
|
-
return {
|
|
83
|
-
GET: 1,
|
|
84
|
-
POST: 2,
|
|
85
|
-
PUT: 3,
|
|
86
|
-
PATCH: 4,
|
|
87
|
-
DELETE: 5,
|
|
88
|
-
OPTIONS: 6,
|
|
89
|
-
HEAD: 7
|
|
90
|
-
}[method] ?? 999;
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
30
|
+
//#region src/honoRouterGenerator.ts
|
|
31
|
+
function generate(context) {
|
|
32
|
+
const moduleDir = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
33
|
+
const templateFile = node_path.default.join(moduleDir, "templates", "HonoRouter.ejs");
|
|
34
|
+
for (const resource of context.normalizedSpec.resources) writeHonoRouter(resource, templateFile, context);
|
|
35
|
+
}
|
|
36
|
+
function writeHonoRouter(resource, templateFile, context) {
|
|
37
|
+
const pascalCaseEntityName = case$1.default.pascal(resource.name);
|
|
38
|
+
const outputDir = context.getResourceOutputDir(resource.name);
|
|
39
|
+
const outputPath = node_path.default.join(outputDir, `${pascalCaseEntityName}Hono.ts`);
|
|
40
|
+
const operations = resource.operations.filter((operation) => operation.method !== _rexeus_typeweaver_core.HttpMethod.HEAD).map((operation) => createOperationData(operation)).sort((a, b) => (0, _rexeus_typeweaver_gen.compareRoutes)(a, b));
|
|
41
|
+
const content = context.renderTemplate(templateFile, {
|
|
42
|
+
coreDir: node_path.default.relative(outputDir, context.outputDir),
|
|
43
|
+
entityName: resource.name,
|
|
44
|
+
pascalCaseEntityName,
|
|
45
|
+
operations
|
|
46
|
+
});
|
|
47
|
+
const relativePath = node_path.default.relative(context.outputDir, outputPath);
|
|
48
|
+
context.writeFile(relativePath, content);
|
|
49
|
+
}
|
|
50
|
+
function createOperationData(operation) {
|
|
51
|
+
const operationId = operation.operationId;
|
|
52
|
+
const className = case$1.default.pascal(operationId);
|
|
53
|
+
return {
|
|
54
|
+
operationId,
|
|
55
|
+
className,
|
|
56
|
+
handlerName: `handle${className}Request`,
|
|
57
|
+
method: operation.method,
|
|
58
|
+
path: operation.path
|
|
59
|
+
};
|
|
60
|
+
}
|
|
94
61
|
//#endregion
|
|
95
62
|
//#region src/index.ts
|
|
96
63
|
const moduleDir = node_path.default.dirname((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
@@ -99,9 +66,8 @@ var HonoPlugin = class extends _rexeus_typeweaver_gen.BasePlugin {
|
|
|
99
66
|
generate(context) {
|
|
100
67
|
const libSourceDir = node_path.default.join(moduleDir, "lib");
|
|
101
68
|
this.copyLibFiles(context, libSourceDir, this.name);
|
|
102
|
-
|
|
69
|
+
generate(context);
|
|
103
70
|
}
|
|
104
71
|
};
|
|
105
|
-
|
|
106
72
|
//#endregion
|
|
107
|
-
module.exports = HonoPlugin;
|
|
73
|
+
module.exports = HonoPlugin;
|
package/dist/index.mjs
CHANGED
|
@@ -1,67 +1,39 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { BasePlugin } from "@rexeus/typeweaver-gen";
|
|
3
|
+
import { BasePlugin, compareRoutes } from "@rexeus/typeweaver-gen";
|
|
4
4
|
import { HttpMethod } from "@rexeus/typeweaver-core";
|
|
5
5
|
import Case from "case";
|
|
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
|
-
static compareRoutes(a, b) {
|
|
40
|
-
const aSegments = a.path.split("/").filter((s) => s);
|
|
41
|
-
const bSegments = b.path.split("/").filter((s) => s);
|
|
42
|
-
if (aSegments.length !== bSegments.length) return aSegments.length - bSegments.length;
|
|
43
|
-
for (let i = 0; i < aSegments.length; i++) {
|
|
44
|
-
const aSegment = aSegments[i];
|
|
45
|
-
const bSegment = bSegments[i];
|
|
46
|
-
const aIsParam = aSegment.startsWith(":");
|
|
47
|
-
if (aIsParam !== bSegment.startsWith(":")) return aIsParam ? 1 : -1;
|
|
48
|
-
if (aSegment !== bSegment) return aSegment.localeCompare(bSegment);
|
|
49
|
-
}
|
|
50
|
-
return this.getMethodPriority(a.method) - this.getMethodPriority(b.method);
|
|
51
|
-
}
|
|
52
|
-
static getMethodPriority(method) {
|
|
53
|
-
return {
|
|
54
|
-
GET: 1,
|
|
55
|
-
POST: 2,
|
|
56
|
-
PUT: 3,
|
|
57
|
-
PATCH: 4,
|
|
58
|
-
DELETE: 5,
|
|
59
|
-
OPTIONS: 6,
|
|
60
|
-
HEAD: 7
|
|
61
|
-
}[method] ?? 999;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
6
|
+
//#region src/honoRouterGenerator.ts
|
|
7
|
+
function generate(context) {
|
|
8
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const templateFile = path.join(moduleDir, "templates", "HonoRouter.ejs");
|
|
10
|
+
for (const resource of context.normalizedSpec.resources) writeHonoRouter(resource, templateFile, context);
|
|
11
|
+
}
|
|
12
|
+
function writeHonoRouter(resource, templateFile, context) {
|
|
13
|
+
const pascalCaseEntityName = Case.pascal(resource.name);
|
|
14
|
+
const outputDir = context.getResourceOutputDir(resource.name);
|
|
15
|
+
const outputPath = path.join(outputDir, `${pascalCaseEntityName}Hono.ts`);
|
|
16
|
+
const operations = resource.operations.filter((operation) => operation.method !== HttpMethod.HEAD).map((operation) => createOperationData(operation)).sort((a, b) => compareRoutes(a, b));
|
|
17
|
+
const content = context.renderTemplate(templateFile, {
|
|
18
|
+
coreDir: path.relative(outputDir, context.outputDir),
|
|
19
|
+
entityName: resource.name,
|
|
20
|
+
pascalCaseEntityName,
|
|
21
|
+
operations
|
|
22
|
+
});
|
|
23
|
+
const relativePath = path.relative(context.outputDir, outputPath);
|
|
24
|
+
context.writeFile(relativePath, content);
|
|
25
|
+
}
|
|
26
|
+
function createOperationData(operation) {
|
|
27
|
+
const operationId = operation.operationId;
|
|
28
|
+
const className = Case.pascal(operationId);
|
|
29
|
+
return {
|
|
30
|
+
operationId,
|
|
31
|
+
className,
|
|
32
|
+
handlerName: `handle${className}Request`,
|
|
33
|
+
method: operation.method,
|
|
34
|
+
path: operation.path
|
|
35
|
+
};
|
|
36
|
+
}
|
|
65
37
|
//#endregion
|
|
66
38
|
//#region src/index.ts
|
|
67
39
|
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -70,10 +42,10 @@ var HonoPlugin = class extends BasePlugin {
|
|
|
70
42
|
generate(context) {
|
|
71
43
|
const libSourceDir = path.join(moduleDir, "lib");
|
|
72
44
|
this.copyLibFiles(context, libSourceDir, this.name);
|
|
73
|
-
|
|
45
|
+
generate(context);
|
|
74
46
|
}
|
|
75
47
|
};
|
|
76
|
-
|
|
77
48
|
//#endregion
|
|
78
49
|
export { HonoPlugin as default };
|
|
50
|
+
|
|
79
51
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/
|
|
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 { compareRoutes } from \"@rexeus/typeweaver-gen\";\nimport type {\n GeneratorContext,\n NormalizedOperation,\n NormalizedResource,\n} from \"@rexeus/typeweaver-gen\";\nimport Case from \"case\";\n\nexport function 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 resource of context.normalizedSpec.resources) {\n writeHonoRouter(resource, templateFile, context);\n }\n}\n\nfunction writeHonoRouter(\n resource: NormalizedResource,\n templateFile: string,\n context: GeneratorContext\n): void {\n const pascalCaseEntityName = Case.pascal(resource.name);\n const outputDir = context.getResourceOutputDir(resource.name);\n const outputPath = path.join(outputDir, `${pascalCaseEntityName}Hono.ts`);\n\n const operations = resource.operations\n // Hono handles HEAD requests automatically, so we skip them\n .filter(operation => operation.method !== HttpMethod.HEAD)\n .map(operation => createOperationData(operation))\n .sort((a, b) => compareRoutes(a, b));\n\n const content = context.renderTemplate(templateFile, {\n coreDir: path.relative(outputDir, context.outputDir),\n entityName: resource.name,\n pascalCaseEntityName,\n operations,\n });\n\n const relativePath = path.relative(context.outputDir, outputPath);\n context.writeFile(relativePath, content);\n}\n\nfunction createOperationData(operation: NormalizedOperation) {\n const operationId = operation.operationId;\n const className = Case.pascal(operationId);\n const handlerName = `handle${className}Request`;\n\n return {\n operationId,\n className,\n handlerName,\n method: operation.method,\n path: operation.path,\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 { generate as generateHonoRouters } 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 generateHonoRouters(context);\n }\n}\n"],"mappings":";;;;;;AAWA,SAAgB,SAAS,SAAiC;CACxD,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;CAC9D,MAAM,eAAe,KAAK,KAAK,WAAW,aAAa,iBAAiB;AAExE,MAAK,MAAM,YAAY,QAAQ,eAAe,UAC5C,iBAAgB,UAAU,cAAc,QAAQ;;AAIpD,SAAS,gBACP,UACA,cACA,SACM;CACN,MAAM,uBAAuB,KAAK,OAAO,SAAS,KAAK;CACvD,MAAM,YAAY,QAAQ,qBAAqB,SAAS,KAAK;CAC7D,MAAM,aAAa,KAAK,KAAK,WAAW,GAAG,qBAAqB,SAAS;CAEzE,MAAM,aAAa,SAAS,WAEzB,QAAO,cAAa,UAAU,WAAW,WAAW,KAAK,CACzD,KAAI,cAAa,oBAAoB,UAAU,CAAC,CAChD,MAAM,GAAG,MAAM,cAAc,GAAG,EAAE,CAAC;CAEtC,MAAM,UAAU,QAAQ,eAAe,cAAc;EACnD,SAAS,KAAK,SAAS,WAAW,QAAQ,UAAU;EACpD,YAAY,SAAS;EACrB;EACA;EACD,CAAC;CAEF,MAAM,eAAe,KAAK,SAAS,QAAQ,WAAW,WAAW;AACjE,SAAQ,UAAU,cAAc,QAAQ;;AAG1C,SAAS,oBAAoB,WAAgC;CAC3D,MAAM,cAAc,UAAU;CAC9B,MAAM,YAAY,KAAK,OAAO,YAAY;AAG1C,QAAO;EACL;EACA;EACA,aALkB,SAAS,UAAU;EAMrC,QAAQ,UAAU;EAClB,MAAM,UAAU;EACjB;;;;ACnDH,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE9D,IAAqB,aAArB,cAAwC,WAAW;CACjD,OAAc;CAEd,SAAyB,SAAiC;EACxD,MAAM,eAAe,KAAK,KAAK,WAAW,MAAM;AAChD,OAAK,aAAa,SAAS,cAAc,KAAK,KAAK;AAEnD,WAAoB,QAAQ"}
|
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
+
createDefaultErrorBody,
|
|
10
|
+
createDefaultErrorResponse,
|
|
11
|
+
internalServerErrorDefaultError,
|
|
9
12
|
isTypedHttpResponse,
|
|
10
13
|
RequestValidationError,
|
|
14
|
+
validationDefaultError,
|
|
11
15
|
} from "@rexeus/typeweaver-core";
|
|
12
16
|
import type {
|
|
13
17
|
IHttpRequest,
|
|
@@ -191,10 +195,9 @@ export abstract class TypeweaverHono<
|
|
|
191
195
|
*/
|
|
192
196
|
private readonly defaultHandlers = {
|
|
193
197
|
requestValidation: (error: RequestValidationError): IHttpResponse => ({
|
|
194
|
-
statusCode:
|
|
198
|
+
statusCode: validationDefaultError.statusCode,
|
|
195
199
|
body: {
|
|
196
|
-
|
|
197
|
-
message: error.message,
|
|
200
|
+
...createDefaultErrorBody(validationDefaultError),
|
|
198
201
|
issues: {
|
|
199
202
|
header: error.headerIssues,
|
|
200
203
|
body: error.bodyIssues,
|
|
@@ -204,23 +207,13 @@ export abstract class TypeweaverHono<
|
|
|
204
207
|
},
|
|
205
208
|
}),
|
|
206
209
|
|
|
207
|
-
responseValidation: (): IHttpResponse =>
|
|
208
|
-
|
|
209
|
-
body: {
|
|
210
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
211
|
-
message: "An unexpected error occurred",
|
|
212
|
-
},
|
|
213
|
-
}),
|
|
210
|
+
responseValidation: (): IHttpResponse =>
|
|
211
|
+
createDefaultErrorResponse(internalServerErrorDefaultError),
|
|
214
212
|
|
|
215
213
|
httpResponse: (error: ITypedHttpResponse): IHttpResponse => error,
|
|
216
214
|
|
|
217
|
-
unknown: (): IHttpResponse =>
|
|
218
|
-
|
|
219
|
-
body: {
|
|
220
|
-
code: "INTERNAL_SERVER_ERROR",
|
|
221
|
-
message: "An unexpected error occurred",
|
|
222
|
-
},
|
|
223
|
-
}),
|
|
215
|
+
unknown: (): IHttpResponse =>
|
|
216
|
+
createDefaultErrorResponse(internalServerErrorDefaultError),
|
|
224
217
|
};
|
|
225
218
|
|
|
226
219
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rexeus/typeweaver-hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Generates Hono routers and handlers straight from your API definitions. Powered by Typeweaver 🧵✨",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -47,14 +47,14 @@
|
|
|
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.9.1",
|
|
51
|
+
"@rexeus/typeweaver-gen": "^0.9.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"hono": "^4.
|
|
54
|
+
"hono": "^4.12.9",
|
|
55
55
|
"test-utils": "file:../test-utils",
|
|
56
|
-
"@rexeus/typeweaver-core": "^0.
|
|
57
|
-
"@rexeus/typeweaver-gen": "^0.
|
|
56
|
+
"@rexeus/typeweaver-core": "^0.9.1",
|
|
57
|
+
"@rexeus/typeweaver-gen": "^0.9.1"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"case": "^1.6.3"
|