@erest/gen 0.1.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 +69 -0
- package/dist/handler.d.ts +18 -0
- package/dist/handler.js +65 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +53 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @erest/gen
|
|
2
|
+
|
|
3
|
+
erest codegen CLI:从 Zod schema 生成 handler 骨架等。
|
|
4
|
+
|
|
5
|
+
> npm 包名为 `@erest/gen`,安装后通过 `erest-gen` 命令调用。
|
|
6
|
+
|
|
7
|
+
## 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add -D @erest/gen
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 用法
|
|
14
|
+
|
|
15
|
+
### handler 命令
|
|
16
|
+
|
|
17
|
+
从 Zod schema 文件生成 `registerTyped` handler 骨架。
|
|
18
|
+
|
|
19
|
+
**约定**:schema 文件中以 `export const XxxSchema = z.object({...})` 形式导出的 schema 会被识别,每个生成一个 handler 骨架。
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 输出到 stdout
|
|
23
|
+
npx erest-gen handler --from ./schemas/user.ts --group user
|
|
24
|
+
|
|
25
|
+
# 输出到文件
|
|
26
|
+
npx erest-gen handler --from ./schemas/user.ts --group user --out ./handlers/user.ts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**示例输入** `schemas/user.ts`:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { z } from "zod";
|
|
33
|
+
|
|
34
|
+
export const UserCreateSchema = z.object({
|
|
35
|
+
name: z.string(),
|
|
36
|
+
age: z.number().int(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export const UserUpdateSchema = z.object({
|
|
40
|
+
name: z.string().optional(),
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**生成输出**:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import type ERest from "erest";
|
|
48
|
+
import { UserCreateSchema } from "./schemas/user.js";
|
|
49
|
+
import { UserUpdateSchema } from "./schemas/user.js";
|
|
50
|
+
|
|
51
|
+
export function registerUserHandlers(api: ERest["api"]) {
|
|
52
|
+
api
|
|
53
|
+
.post("/user-create")
|
|
54
|
+
.group("user")
|
|
55
|
+
.registerTyped(
|
|
56
|
+
{ body: UserCreateSchema },
|
|
57
|
+
(req, reply) => {
|
|
58
|
+
// TODO: 实现 user-create 处理逻辑
|
|
59
|
+
return reply.json({ ok: true });
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
// ...
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 后续计划
|
|
67
|
+
|
|
68
|
+
- `test` 命令:为已注册 API 生成测试样板
|
|
69
|
+
- `docs` 命令:从 ERest 实例导出 markdown
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* handler codegen:从 Zod schema 文件生成 registerTyped handler 骨架。
|
|
3
|
+
*
|
|
4
|
+
* 解析策略(轻量,不引入 TS AST 依赖):
|
|
5
|
+
* 用正则识别 `export const XxxSchema = z.object({...})` 形式的导出。
|
|
6
|
+
* 对每个识别到的 schema,生成一个 handler 骨架。
|
|
7
|
+
*/
|
|
8
|
+
export interface GenerateHandlerOptions {
|
|
9
|
+
/** schema 文件路径(相对 CWD) */
|
|
10
|
+
schemaFile: string;
|
|
11
|
+
/** API 分组 */
|
|
12
|
+
group: string;
|
|
13
|
+
}
|
|
14
|
+
/** 从源码中提取 export const XxxSchema 名称列表 */
|
|
15
|
+
export declare function extractSchemaNames(source: string): string[];
|
|
16
|
+
/** 从 SchemaName 推导资源名(UserCreateSchema -> user-create) */
|
|
17
|
+
export declare function schemaNameToResource(name: string): string;
|
|
18
|
+
export declare function generateHandler(options: GenerateHandlerOptions): string;
|
package/dist/handler.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* handler codegen:从 Zod schema 文件生成 registerTyped handler 骨架。
|
|
3
|
+
*
|
|
4
|
+
* 解析策略(轻量,不引入 TS AST 依赖):
|
|
5
|
+
* 用正则识别 `export const XxxSchema = z.object({...})` 形式的导出。
|
|
6
|
+
* 对每个识别到的 schema,生成一个 handler 骨架。
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
/** 从源码中提取 export const XxxSchema 名称列表 */
|
|
10
|
+
export function extractSchemaNames(source) {
|
|
11
|
+
const names = [];
|
|
12
|
+
// 匹配 export const XxxSchema = z.object(不要求完整 object 体,只要赋值为 z.object 调用)
|
|
13
|
+
const re = /export\s+const\s+([A-Z]\w*Schema)\s*=\s*z\.object/g;
|
|
14
|
+
let m;
|
|
15
|
+
while ((m = re.exec(source)) !== null) {
|
|
16
|
+
names.push(m[1]);
|
|
17
|
+
}
|
|
18
|
+
return names;
|
|
19
|
+
}
|
|
20
|
+
/** 从 SchemaName 推导资源名(UserCreateSchema -> user-create) */
|
|
21
|
+
export function schemaNameToResource(name) {
|
|
22
|
+
return name
|
|
23
|
+
.replace(/Schema$/, "")
|
|
24
|
+
.replace(/([A-Z])/g, "-$1")
|
|
25
|
+
.replace(/^-/, "")
|
|
26
|
+
.toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
export function generateHandler(options) {
|
|
29
|
+
const { schemaFile, group } = options;
|
|
30
|
+
const source = readFileSync(schemaFile, "utf-8");
|
|
31
|
+
const schemaNames = extractSchemaNames(source);
|
|
32
|
+
if (schemaNames.length === 0) {
|
|
33
|
+
throw new Error(`erest-gen: 未在 ${schemaFile} 中找到 \`export const XxxSchema = z.object(...)\` 形式的导出`);
|
|
34
|
+
}
|
|
35
|
+
const handlers = schemaNames
|
|
36
|
+
.map((name) => {
|
|
37
|
+
const resource = schemaNameToResource(name);
|
|
38
|
+
return ` api
|
|
39
|
+
.post("/${resource}")
|
|
40
|
+
.group("${group}")
|
|
41
|
+
.registerTyped(
|
|
42
|
+
{ body: ${name} },
|
|
43
|
+
(req, reply) => {
|
|
44
|
+
// TODO: 实现 ${resource} 处理逻辑
|
|
45
|
+
// req.body 类型由 ${name} 推导
|
|
46
|
+
return reply.json({ ok: true });
|
|
47
|
+
}
|
|
48
|
+
);`;
|
|
49
|
+
})
|
|
50
|
+
.join("\n\n");
|
|
51
|
+
const importPath = schemaFile.replace(/\.ts$/, ".js");
|
|
52
|
+
const baseName = schemaFile.split("/").pop().replace(/\.ts$/, "");
|
|
53
|
+
const capitalizedBase = capitalize(baseName);
|
|
54
|
+
return `// 由 erest-gen 生成,请按需修改
|
|
55
|
+
import type ERest from "erest";
|
|
56
|
+
${schemaNames.map((n) => `import { ${n} } from "./${importPath}";`).join("\n")}
|
|
57
|
+
|
|
58
|
+
export function register${capitalizedBase}Handlers(api: ERest["api"]) {
|
|
59
|
+
${handlers}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
function capitalize(s) {
|
|
64
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
65
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* erest-gen: erest codegen CLI
|
|
4
|
+
*
|
|
5
|
+
* 首版命令:
|
|
6
|
+
* handler --from <schema-file> --group <group> [--out <output>]
|
|
7
|
+
* 从 Zod schema 文件读取导出的 *Schema 常量,生成 registerTyped handler 骨架。
|
|
8
|
+
*
|
|
9
|
+
* 约定:schema 文件中 `export const XxxSchema = z.object({...})` 会被识别。
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* erest-gen: erest codegen CLI
|
|
4
|
+
*
|
|
5
|
+
* 首版命令:
|
|
6
|
+
* handler --from <schema-file> --group <group> [--out <output>]
|
|
7
|
+
* 从 Zod schema 文件读取导出的 *Schema 常量,生成 registerTyped handler 骨架。
|
|
8
|
+
*
|
|
9
|
+
* 约定:schema 文件中 `export const XxxSchema = z.object({...})` 会被识别。
|
|
10
|
+
*/
|
|
11
|
+
import { generateHandler } from "./handler.js";
|
|
12
|
+
import { writeFileSync } from "node:fs";
|
|
13
|
+
function parseArgs(argv) {
|
|
14
|
+
const [, , command, ...rest] = argv;
|
|
15
|
+
if (!command)
|
|
16
|
+
return null;
|
|
17
|
+
const args = { command };
|
|
18
|
+
for (let i = 0; i < rest.length; i++) {
|
|
19
|
+
if (rest[i] === "--from")
|
|
20
|
+
args.from = rest[++i];
|
|
21
|
+
else if (rest[i] === "--group")
|
|
22
|
+
args.group = rest[++i];
|
|
23
|
+
else if (rest[i] === "--out")
|
|
24
|
+
args.out = rest[++i];
|
|
25
|
+
}
|
|
26
|
+
return args;
|
|
27
|
+
}
|
|
28
|
+
function main() {
|
|
29
|
+
const args = parseArgs(process.argv);
|
|
30
|
+
if (!args || !args.from) {
|
|
31
|
+
console.error(`erest-gen: 用法见 README
|
|
32
|
+
erest-gen handler --from ./schemas/user.ts --group user [--out ./handlers/user.ts]`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
if (args.command === "handler") {
|
|
36
|
+
const code = generateHandler({
|
|
37
|
+
schemaFile: args.from,
|
|
38
|
+
group: args.group || "Index",
|
|
39
|
+
});
|
|
40
|
+
if (args.out) {
|
|
41
|
+
writeFileSync(args.out, code);
|
|
42
|
+
console.error(`erest-gen: 已生成 ${args.out}`);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
process.stdout.write(code);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.error(`erest-gen: 未知命令 ${args.command}(首版仅支持 handler)`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@erest/gen",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "erest codegen CLI:从 Zod schema 生成 handler 骨架等(实验性,独立于 erest 主版本演进)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"erest-gen": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"dev": "tsc --watch"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "^5.8.0"
|
|
24
|
+
}
|
|
25
|
+
}
|