ts-proto-client 1.0.3 → 1.0.5
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 +232 -217
- package/dist/index.js +4 -4
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -1,217 +1,232 @@
|
|
|
1
|
-
# ts-proto-client
|
|
2
|
-
|
|
3
|
-
基于 Proto 文件自动生成 TypeScript
|
|
4
|
-
|
|
5
|
-
## 功能
|
|
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
|
-
|
|
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
|
-
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
protoc
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
--
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
src
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
1
|
+
# ts-proto-client
|
|
2
|
+
|
|
3
|
+
基于 Proto 文件自动生成 TypeScript 类型声明和 API 请求函数的 CLI 工具。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- **类型生成** — 调用 `protoc` + `ts-proto` 插件,将 `.proto` 文件编译为 TypeScript 类型声明
|
|
8
|
+
- **API 生成** — 解析 Proto 中的 `service` 定义及 HTTP 注解(`google.api.http`),自动生成类型安全的 API 请求函数
|
|
9
|
+
- **自动识别 Proto 根目录** — 从文件路径中自动定位 `protos` 目录,无需手动配置
|
|
10
|
+
- **灵活的输出路径** — 支持自定义 TypeScript 类型和 API 代码的输出目录
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 原理
|
|
15
|
+
|
|
16
|
+
工具将 `.proto` 文件拆分为两个产物:
|
|
17
|
+
|
|
18
|
+
| 产物 | 生成方式 | 说明 |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| **TypeScript 类型** (`.ts`) | 调用 `protoc` + `ts-proto` 插件 | 将 proto message 编译为 TS interface/type |
|
|
21
|
+
| **API 请求函数** (`.ts`) | 使用 `protobufjs` 解析 proto,提取 `service` + HTTP 注解 | 生成调用 `service()` 的函数,入参与返回值均有类型约束 |
|
|
22
|
+
|
|
23
|
+
生成的 API 函数依赖项目中的 `service` 工具函数(需由使用者自行实现),函数签名示例:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { service } from "../..";
|
|
27
|
+
|
|
28
|
+
import type {
|
|
29
|
+
HelloRequest,
|
|
30
|
+
HelloResponse,
|
|
31
|
+
} from "@/protos/type/hello";
|
|
32
|
+
|
|
33
|
+
// 登录
|
|
34
|
+
export function Login(data: HelloRequest) {
|
|
35
|
+
return service<HelloResponse["data"]>({
|
|
36
|
+
url: "/hello",
|
|
37
|
+
method: "post",
|
|
38
|
+
data
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 环境要求
|
|
46
|
+
|
|
47
|
+
- **Node.js** >= 18
|
|
48
|
+
- **protoc** >= 3.15(需支持 `--ts_proto_out`)
|
|
49
|
+
- **pnpm**(推荐,也可使用 npm / yarn)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 安装
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 安装本工具
|
|
57
|
+
pnpm add -D ts-proto-client
|
|
58
|
+
|
|
59
|
+
# 安装 ts-proto 依赖(本工具通过 protoc 调用 ts-proto 插件)
|
|
60
|
+
pnpm add -D ts-proto
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## protoc 安装
|
|
66
|
+
|
|
67
|
+
本工具依赖 `protoc`(Protocol Buffers 编译器),请先安装。
|
|
68
|
+
|
|
69
|
+
### macOS
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
brew install protobuf
|
|
73
|
+
protoc --version # 验证安装
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Windows
|
|
77
|
+
|
|
78
|
+
1. 从 [Protocol Buffers Releases](https://github.com/protocolbuffers/protobuf/releases) 下载 `protoc-<version>-win64.zip`
|
|
79
|
+
2. 解压到目标目录(如 `D:\protoc`)
|
|
80
|
+
3. 将 `D:\protoc\bin` 添加到系统环境变量 `PATH`
|
|
81
|
+
4. 验证:`protoc --version`
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 使用方式
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# 基本用法
|
|
89
|
+
npx proto-gen generate --file <proto 文件路径>
|
|
90
|
+
|
|
91
|
+
# 自定义输出路径
|
|
92
|
+
npx proto-gen generate \
|
|
93
|
+
--file <proto 文件路径> \
|
|
94
|
+
--type-path <类型输出目录> \
|
|
95
|
+
--api-path <API 输出目录>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 示例
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npx proto-gen generate \
|
|
102
|
+
--file ./src/data/protos/xishan-guiyuan/ruralVisualization.proto
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 参数说明
|
|
108
|
+
|
|
109
|
+
| 参数 | 必填 | 默认值 | 说明 |
|
|
110
|
+
| --- | --- | --- | --- |
|
|
111
|
+
| `--file` | 是 | — | Proto 文件的完整路径 |
|
|
112
|
+
| `--type-path` | 否 | `src/protos/type` | TypeScript 类型声明的输出目录 |
|
|
113
|
+
| `--api-path` | 否 | `src/protos/api` | API 请求函数的输出目录 |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 目录约定
|
|
118
|
+
|
|
119
|
+
工具会在 `--file` 的文件路径中自动查找 **`protos`** 目录,并将其作为 Proto 根目录。
|
|
120
|
+
|
|
121
|
+
### 示例目录结构
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
src
|
|
125
|
+
└─ data
|
|
126
|
+
└─ protos ← 工具自动识别此目录为 Proto Root
|
|
127
|
+
├─ hospital
|
|
128
|
+
│ └─ account.proto
|
|
129
|
+
└─ xishan-guiyuan
|
|
130
|
+
└─ ruralVisualization.proto
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 生成结果
|
|
134
|
+
|
|
135
|
+
执行:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx proto-gen generate \
|
|
139
|
+
--file ./src/data/protos/xishan-guiyuan/ruralVisualization.proto
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
输出:
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
src
|
|
146
|
+
└─ protos
|
|
147
|
+
├─ type ← TypeScript 类型(默认)
|
|
148
|
+
│ └─ xishan-guiyuan
|
|
149
|
+
│ └─ ruralVisualization.ts
|
|
150
|
+
└─ api ← API 请求函数(默认)
|
|
151
|
+
└─ ruralVisualization.ts
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
生成的目录结构会保持 proto 文件在 `protos` 下的相对路径。
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Proto 文件要求
|
|
159
|
+
|
|
160
|
+
工具依赖 Proto 文件中的 **HTTP 注解**来生成 API 函数,需要你的 `.proto` 文件包含:
|
|
161
|
+
|
|
162
|
+
1. **`google.api.http` 选项** — 定义 HTTP 方法和路径
|
|
163
|
+
2. **`openapiv2_operation.summary` 选项** — 作为生成函数的注释(可选)
|
|
164
|
+
|
|
165
|
+
示例:
|
|
166
|
+
|
|
167
|
+
```protobuf
|
|
168
|
+
import "google/api/annotations.proto";
|
|
169
|
+
|
|
170
|
+
service UserService {
|
|
171
|
+
rpc GetUser(GetUserRequest) returns (GetUserResponse) {
|
|
172
|
+
option (google.api.http) = {
|
|
173
|
+
get: "/api/user/{id}"
|
|
174
|
+
};
|
|
175
|
+
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
|
176
|
+
summary: "获取用户信息"
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
> 需要第三方 proto 文件(`google/api/annotations.proto` 等),请将它们放置在 `protos/third_party/googleapis/` 目录下。
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 本地开发
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# 开发模式(直接运行 TS 源码)
|
|
190
|
+
pnpm dev generate --file <proto 文件路径>
|
|
191
|
+
|
|
192
|
+
# 构建
|
|
193
|
+
pnpm build
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 常见问题
|
|
199
|
+
|
|
200
|
+
### protoc: command not found
|
|
201
|
+
|
|
202
|
+
`protoc` 未安装或未添加到 PATH,请参考上方 [protoc 安装](#protoc-安装) 章节。
|
|
203
|
+
|
|
204
|
+
### protoc-gen-ts_proto: command not found
|
|
205
|
+
|
|
206
|
+
`ts-proto` 未安装,执行:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pnpm add -D ts-proto
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 未找到 protos 根目录
|
|
213
|
+
|
|
214
|
+
工具通过查找路径中的 `protos` 目录来确定 Proto 根目录。如果你的目录名不是 `protos`(例如 `protobuf`),将无法自动识别。
|
|
215
|
+
|
|
216
|
+
正确示例:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
--file ./src/data/protos/hospital/account.proto ✅
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
无法识别:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
--file ./src/data/protobuf/hospital/account.proto ❌
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
3
|
-
`))}let
|
|
4
|
-
`)}`,`} from "${o}";`,"",
|
|
2
|
+
import{Command as M}from"commander";import k from"fs";import R from"path";import E from"fs";import l from"fs";import j from"path";import{Root as D,Service as H}from"protobufjs";import a from"path";import z from"fs";var g=["get","post","put","delete","patch"],h="(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation).summary",d="(google.api.http)";function T(t){let r=a.normalize(t).split(a.sep).indexOf("protos");if(r===-1)throw new Error("\u672A\u627E\u5230 protos \u6839\u76EE\u5F55");return r}function I(t){let o=[t,a.join(t,"third_party/googleapis")],e=new D;return e.resolvePath=(r,p)=>{for(let n of o){let s=a.join(n,p);if(z.existsSync(s))return s}return p},e}function v(t,o=[]){if(!t.nested)return o;for(let e of Object.values(t.nested))e instanceof H&&o.push(e),v(e,o);return o}function b(t){let e=t.parsedOptions?.find(n=>n[d])?.[d];if(!e)return null;let r=g.find(n=>e[n]);if(!r)return null;let p=e[r];return p?{method:r,url:p,summary:t.options?.[h]??""}:null}function O(t){let o=I(t.root);o.loadSync(t.file);let e=v(o),r={};for(let p of e){let n=[];for(let s of Object.values(p.methods)){let f=b(s);f&&n.push({name:s.name,requestType:s.requestType,responseType:s.responseType,hasData:!!s.resolvedResponseType?.fields.data,...f})}n.length>0&&(r[p.name]=n)}return r}function x(t,o){let e={};for(let[r,p]of Object.entries(t)){let n=new Set,s=[];for(let i of p){n.add(i.requestType),i.hasData&&n.add(i.responseType);let u=[],w=i.method==="get"?"params: data":"data";i.summary&&u.push(`// ${i.summary}`);let A=i.hasData?`<${i.responseType}["data"]>`:"";u.push(`export function ${i.name}(data: ${i.requestType}) {`,` return service${A}({`,` url: "${i.url.replace("/api","")}",`,` method: "${i.method}",`,` ${w}`," })","}"),s.push(u.join(`
|
|
3
|
+
`))}if(s.length===0)continue;let f=[...n].sort();e[r]=['import { service } from "../..";',"","import type {",` ${f.join(`,
|
|
4
|
+
`)}`,`} from "${o}";`,"",s.join(`
|
|
5
5
|
|
|
6
6
|
`)].join(`
|
|
7
|
-
`)}
|
|
7
|
+
`)}return e}function P(t){let e=a.normalize(t).split(a.sep),r=T(t);return e.slice(0,r+1).join(a.sep)}function S(t){let e=a.normalize(t.file).split(a.sep),r=T(t.file),p=e.slice(r+1).join(a.sep),n=a.join(t.typeOutput,p.replace(/\.proto$/,""));return a.relative(t.apiOutput,n).replace(/\\/g,"/")}function _(t){let o=j.resolve(process.cwd(),t.apiOutput);l.existsSync(o)||l.mkdirSync(o,{recursive:!0});let e=O(t),r=S(t),p=x(e,r);for(let[n,s]of Object.entries(p))l.writeFileSync(j.join(o,`${n}.ts`),s,"utf8");console.log("\u2705 Generate Success")}import{execSync as q}from"child_process";import m from"fs";import c from"path";function $(t){let o=c.resolve(process.cwd(),"node_modules/ts-proto/package.json");if(!m.existsSync(o))throw new Error("\u672A\u5B89\u88C5 ts-proto\uFF0C\u8BF7\u6267\u884C\uFF1Apnpm add -D ts-proto");let e=c.resolve(process.cwd(),t.typeOutput);m.existsSync(e)||m.mkdirSync(e,{recursive:!0});let r=["protoc",`--ts_proto_out=${e}`,"--ts_proto_opt=onlyTypes=true","--ts_proto_opt=useOptionals=optional","--ts_proto_opt=esModuleInterop=true",`--proto_path=${t.root}`,`--proto_path=${c.join(t.root,"third_party/googleapis")}`,c.resolve(t.file)];q(r.join(" "),{stdio:"inherit"}),m.rmSync(c.join(e,"google"),{recursive:!0,force:!0}),m.rmSync(c.join(e,"common.ts"),{force:!0})}function C(t){if(!E.existsSync(t.root)){console.log(`proto \u9879\u76EE\u4E0D\u5B58\u5728(${t.root})`);return}$(t),_(t)}var y=new M;y.name("proto-gen").description("proto code generator").version("1.0.4");y.command("generate").requiredOption("--file <path>","proto \u6587\u4EF6\u8DEF\u5F84").option("--api-path <path>","API \u4EE3\u7801\u8F93\u51FA\u76EE\u5F55").option("--type-path <path>","TypeScript \u7C7B\u578B\u8F93\u51FA\u76EE\u5F55").action(t=>{if(!k.existsSync(t.file)){console.log(`\u274C ${t.file} \u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u8DEF\u5F84`);return}let o=R.parse(t.file).name,e=P(t.file),r={file:t.file,root:R.resolve(process.cwd(),e),typeOutput:t.typePath||"src/protos/type",apiOutput:t.apiPath?`${t.apiPath}${o?"/"+o:""}`:`src/protos/api${o?"/"+o:""}`};C(r)});y.parse();
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-proto-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "tsx src/index.ts",
|
|
8
|
-
"build": "tsup src/index.ts --format esm --clean"
|
|
9
|
-
"release": "pnpm build && npm publish"
|
|
8
|
+
"build": "tsup src/index.ts --format esm --clean"
|
|
10
9
|
},
|
|
11
10
|
"bin": {
|
|
12
11
|
"proto-gen": "./dist/index.js"
|
|
@@ -20,11 +19,11 @@
|
|
|
20
19
|
"packageManager": "pnpm@10.14.0",
|
|
21
20
|
"dependencies": {
|
|
22
21
|
"commander": "^15.0.0",
|
|
23
|
-
"protobufjs": "^8.6.3"
|
|
24
|
-
"ts-proto": "^2.11.8"
|
|
22
|
+
"protobufjs": "^8.6.3"
|
|
25
23
|
},
|
|
26
24
|
"devDependencies": {
|
|
27
25
|
"@types/node": "^25.9.3",
|
|
26
|
+
"ts-proto": "^2.11.8",
|
|
28
27
|
"tsup": "^8.5.1",
|
|
29
28
|
"tsx": "^4.22.4",
|
|
30
29
|
"typescript": "^6.0.3"
|