ts-proto-client 1.0.0 → 1.0.2
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 +87 -66
- package/dist/index.js +48 -87
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# ts-proto-client
|
|
2
2
|
|
|
3
|
-
基于 Proto 文件自动生成 TypeScript
|
|
3
|
+
基于 Proto 文件自动生成 TypeScript 类型和 API 请求代码的 CLI 工具。
|
|
4
4
|
|
|
5
5
|
## 功能
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
* 根据 `.proto` 文件生成 TypeScript 类型
|
|
8
|
+
* 根据 Service 定义生成 API 请求方法
|
|
9
|
+
* 支持自定义 Proto 根目录
|
|
10
|
+
* 支持自定义类型输出目录
|
|
11
|
+
* 支持自定义 API 输出目录
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -40,11 +40,11 @@ libprotoc 35.1
|
|
|
40
40
|
|
|
41
41
|
### Windows
|
|
42
42
|
|
|
43
|
-
1. 打开 Protocol Buffers Releases
|
|
43
|
+
1. 打开 Protocol Buffers Releases 页面
|
|
44
44
|
|
|
45
45
|
https://github.com/protocolbuffers/protobuf/releases
|
|
46
46
|
|
|
47
|
-
2.
|
|
47
|
+
2. 下载对应版本
|
|
48
48
|
|
|
49
49
|
```text
|
|
50
50
|
protoc-35.1-win64.zip
|
|
@@ -66,7 +66,7 @@ D:\protoc
|
|
|
66
66
|
└─ readme.txt
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
4.
|
|
69
|
+
4. 将以下目录添加到系统环境变量:
|
|
70
70
|
|
|
71
71
|
```text
|
|
72
72
|
D:\protoc\bin
|
|
@@ -88,109 +88,130 @@ libprotoc 35.1
|
|
|
88
88
|
|
|
89
89
|
## 使用方式
|
|
90
90
|
|
|
91
|
-
###
|
|
92
|
-
|
|
93
|
-
在项目根目录执行:
|
|
91
|
+
### 生成代码
|
|
94
92
|
|
|
95
93
|
```bash
|
|
96
|
-
npx proto-gen
|
|
94
|
+
npx proto-gen generate \
|
|
95
|
+
--file ./account.proto \
|
|
96
|
+
--root ../protos \
|
|
97
|
+
--type-output src/protos/type \
|
|
98
|
+
--api-output src/protos/api/account
|
|
97
99
|
```
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
### 参数说明
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
| 参数 | 必填 | 说明 |
|
|
104
|
+
| --------------- | -- | ------------------------- |
|
|
105
|
+
| `--file` | 是 | Proto 文件路径(相对于 Proto 根目录) |
|
|
106
|
+
| `--root` | 是 | Proto 根目录 |
|
|
107
|
+
| `--type-output` | 是 | TypeScript 类型输出目录 |
|
|
108
|
+
| `--api-output` | 是 | API 文件输出目录 |
|
|
104
109
|
|
|
105
|
-
|
|
110
|
+
---
|
|
106
111
|
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
"ProtoRoot": "../protos",
|
|
110
|
-
"ProtoInput": "hospital/hospital.proto",
|
|
111
|
-
"ProtoTypeOutputPath": "./src/proto-type",
|
|
112
|
-
"ApiOutputPath": "./src/api"
|
|
113
|
-
}
|
|
114
|
-
```
|
|
112
|
+
## 示例
|
|
115
113
|
|
|
116
|
-
|
|
114
|
+
目录结构:
|
|
117
115
|
|
|
118
|
-
|
|
116
|
+
```text
|
|
117
|
+
project
|
|
118
|
+
├─ src
|
|
119
|
+
├─ protos
|
|
120
|
+
│ ├─ hospital
|
|
121
|
+
│ │ └─ account.proto
|
|
122
|
+
│ └─ third_party
|
|
123
|
+
└─ package.json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
执行:
|
|
119
127
|
|
|
120
128
|
```bash
|
|
121
|
-
npx proto-gen generate
|
|
129
|
+
npx proto-gen generate \
|
|
130
|
+
--file hospital/account.proto \
|
|
131
|
+
--root ./protos \
|
|
132
|
+
--type-output src/protos/type \
|
|
133
|
+
--api-output src/protos/api/account
|
|
122
134
|
```
|
|
123
135
|
|
|
124
|
-
|
|
136
|
+
生成:
|
|
125
137
|
|
|
126
138
|
```text
|
|
127
139
|
src
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
└─
|
|
131
|
-
└─
|
|
140
|
+
└─ protos
|
|
141
|
+
├─ type
|
|
142
|
+
│ └─ account.ts
|
|
143
|
+
└─ api
|
|
144
|
+
└─ account.ts
|
|
132
145
|
```
|
|
133
146
|
|
|
134
147
|
---
|
|
135
148
|
|
|
136
|
-
##
|
|
149
|
+
## 本地开发
|
|
137
150
|
|
|
138
|
-
|
|
139
|
-
|--------|--------|
|
|
140
|
-
| ProtoRoot | Proto 根目录 |
|
|
141
|
-
| ProtoInput | 入口 Proto 文件 |
|
|
142
|
-
| ProtoTypeOutputPath | TypeScript 类型输出目录 |
|
|
143
|
-
| ApiOutputPath | API 文件输出目录 |
|
|
151
|
+
启动开发模式:
|
|
144
152
|
|
|
145
|
-
|
|
153
|
+
```bash
|
|
154
|
+
pnpm dev generate \
|
|
155
|
+
--file hospital/account.proto \
|
|
156
|
+
--root ../protos \
|
|
157
|
+
--type-output src/protos/type \
|
|
158
|
+
--api-output src/protos/api/account
|
|
159
|
+
```
|
|
146
160
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 常见问题
|
|
164
|
+
|
|
165
|
+
### protoc: command not found
|
|
166
|
+
|
|
167
|
+
请确认已经正确安装 protoc,并配置到环境变量:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
protoc --version
|
|
154
171
|
```
|
|
155
172
|
|
|
173
|
+
能够正常输出版本号。
|
|
174
|
+
|
|
156
175
|
---
|
|
157
176
|
|
|
158
|
-
|
|
177
|
+
### File does not reside within any path specified using --proto_path
|
|
159
178
|
|
|
160
|
-
|
|
179
|
+
请检查:
|
|
161
180
|
|
|
162
181
|
```bash
|
|
163
|
-
|
|
182
|
+
--file
|
|
164
183
|
```
|
|
165
184
|
|
|
166
|
-
|
|
185
|
+
指定的文件是否位于:
|
|
167
186
|
|
|
168
187
|
```bash
|
|
169
|
-
|
|
188
|
+
--root
|
|
170
189
|
```
|
|
171
190
|
|
|
172
|
-
|
|
191
|
+
指定的目录之下。
|
|
192
|
+
|
|
193
|
+
例如:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
--root ./protos
|
|
197
|
+
--file hospital/account.proto
|
|
198
|
+
```
|
|
173
199
|
|
|
174
|
-
|
|
200
|
+
则实际文件应存在:
|
|
175
201
|
|
|
176
202
|
```text
|
|
177
|
-
|
|
178
|
-
├─ proto.config.json
|
|
179
|
-
├─ src
|
|
180
|
-
│ ├─ api
|
|
181
|
-
│ └─ proto-type
|
|
182
|
-
└─ protos
|
|
203
|
+
./protos/hospital/account.proto
|
|
183
204
|
```
|
|
184
205
|
|
|
185
206
|
---
|
|
186
207
|
|
|
187
|
-
##
|
|
208
|
+
## 环境要求
|
|
188
209
|
|
|
189
|
-
|
|
190
|
-
|
|
210
|
+
* Node.js >= 18
|
|
211
|
+
* protoc >= 35
|
|
191
212
|
|
|
192
213
|
---
|
|
193
214
|
|
|
194
215
|
## License
|
|
195
216
|
|
|
196
|
-
MIT
|
|
217
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -3,67 +3,18 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
-
// src/commands/init.ts
|
|
7
|
-
import prompts from "prompts";
|
|
8
|
-
import fs from "fs";
|
|
9
|
-
var ConfigFilePath = "proto.config.json";
|
|
10
|
-
async function init() {
|
|
11
|
-
const answers = await prompts([
|
|
12
|
-
{
|
|
13
|
-
type: "text",
|
|
14
|
-
name: "ProtoRoot",
|
|
15
|
-
message: "proto \u8F93\u5165\u6587\u4EF6",
|
|
16
|
-
initial: "../protos"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
type: "text",
|
|
20
|
-
name: "ProtoInputPath",
|
|
21
|
-
message: "proto \u8F93\u5165\u6587\u4EF6",
|
|
22
|
-
initial: "hospital/hospital.proto"
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
type: "text",
|
|
26
|
-
name: "ProtoTypeOutputPath",
|
|
27
|
-
message: "\u7C7B\u578B\u8F93\u51FA\u8DEF\u5F84",
|
|
28
|
-
initial: "dist/proto-type"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
type: "text",
|
|
32
|
-
name: "ProtoApiOutputPath",
|
|
33
|
-
message: "\u63A5\u53E3\u8F93\u51FA\u8DEF\u5F84",
|
|
34
|
-
initial: "dist/proto-api"
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
type: "text",
|
|
38
|
-
name: "ImportTypePath",
|
|
39
|
-
message: "\u7C7B\u578B\u6587\u4EF6\u5BFC\u5165\u63A5\u53E3\u7684\u8DEF\u5F84",
|
|
40
|
-
initial: "../proto-type/hospital/hospital"
|
|
41
|
-
}
|
|
42
|
-
]);
|
|
43
|
-
const content = [
|
|
44
|
-
"{",
|
|
45
|
-
` "ProtoRoot": "${answers.ProtoRoot}",`,
|
|
46
|
-
` "ProtoInput": "${answers.ProtoInputPath}",`,
|
|
47
|
-
` "ProtoTypeOutputPath": "${answers.ProtoTypeOutputPath}",`,
|
|
48
|
-
` "ProtoApiOutputPath": "${answers.ProtoApiOutputPath}",`,
|
|
49
|
-
` "ImportTypePath": "${answers.ImportTypePath}"`,
|
|
50
|
-
"}"
|
|
51
|
-
].join("\n");
|
|
52
|
-
fs.writeFileSync(ConfigFilePath, content);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
6
|
// src/commands/generate.ts
|
|
56
|
-
import
|
|
7
|
+
import fs4 from "fs";
|
|
57
8
|
import path4 from "path";
|
|
58
9
|
|
|
59
10
|
// src/generators/api.ts
|
|
60
|
-
import
|
|
11
|
+
import fs2 from "fs";
|
|
61
12
|
import path2 from "path";
|
|
62
13
|
|
|
63
14
|
// src/utils/index.ts
|
|
64
15
|
import { Root, Service } from "protobufjs";
|
|
65
16
|
import path from "path";
|
|
66
|
-
import
|
|
17
|
+
import fs from "fs";
|
|
67
18
|
|
|
68
19
|
// src/constants.ts
|
|
69
20
|
var HTTP_METHODS = [
|
|
@@ -87,7 +38,7 @@ function createRoot(rootPath) {
|
|
|
87
38
|
includePath,
|
|
88
39
|
target
|
|
89
40
|
);
|
|
90
|
-
if (
|
|
41
|
+
if (fs.existsSync(filePath)) {
|
|
91
42
|
return filePath;
|
|
92
43
|
}
|
|
93
44
|
}
|
|
@@ -181,19 +132,24 @@ function generateApiCode(apis, importTypePath) {
|
|
|
181
132
|
}
|
|
182
133
|
return modules;
|
|
183
134
|
}
|
|
135
|
+
function getOutputPath(baseDir, protoFile) {
|
|
136
|
+
const normalized = protoFile.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\.proto$/, "");
|
|
137
|
+
return path.join(baseDir, normalized);
|
|
138
|
+
}
|
|
184
139
|
|
|
185
140
|
// src/generators/api.ts
|
|
186
|
-
function generateApi() {
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
fs3.mkdirSync(outputPath, { recursive: true });
|
|
141
|
+
function generateApi(options) {
|
|
142
|
+
const outputPath = path2.resolve(process.cwd(), options.apiOutput);
|
|
143
|
+
if (!fs2.existsSync(outputPath)) {
|
|
144
|
+
fs2.mkdirSync(outputPath, { recursive: true });
|
|
191
145
|
}
|
|
192
|
-
const apis = parseProto(
|
|
193
|
-
const
|
|
146
|
+
const apis = parseProto(options.root, options.file);
|
|
147
|
+
const result = getOutputPath(options.typeOutput, options.file);
|
|
148
|
+
const relativePath = path2.relative(options.apiOutput, result).replace(/\\/g, "/");
|
|
149
|
+
const codes = generateApiCode(apis, relativePath);
|
|
194
150
|
for (const code of codes) {
|
|
195
151
|
for (const [name, content] of Object.entries(code)) {
|
|
196
|
-
|
|
152
|
+
fs2.writeFileSync(`${outputPath}/${name}.ts`, content, "utf8");
|
|
197
153
|
}
|
|
198
154
|
}
|
|
199
155
|
console.log("\u2705 Generate Success");
|
|
@@ -202,20 +158,19 @@ function generateApi() {
|
|
|
202
158
|
// src/generators/type.ts
|
|
203
159
|
import { execSync } from "child_process";
|
|
204
160
|
import { createRequire } from "module";
|
|
205
|
-
import
|
|
161
|
+
import fs3 from "fs";
|
|
206
162
|
import path3 from "path";
|
|
207
|
-
function generateTypes() {
|
|
163
|
+
function generateTypes(options) {
|
|
208
164
|
const require2 = createRequire(import.meta.url);
|
|
209
165
|
const tsProtoPkg = require2.resolve("ts-proto/package.json");
|
|
210
166
|
const tsProtoRoot = path3.dirname(tsProtoPkg);
|
|
211
167
|
const pluginName = process.platform === "win32" ? "protoc-gen-ts_proto.cmd" : "protoc-gen-ts_proto";
|
|
212
168
|
const pluginPath = path3.resolve(tsProtoRoot, `./node_modules/.bin/${pluginName}`);
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
fs4.mkdirSync(outputPath, { recursive: true });
|
|
169
|
+
const outputPath = path3.resolve(process.cwd(), options.typeOutput);
|
|
170
|
+
if (!fs3.existsSync(outputPath)) {
|
|
171
|
+
fs3.mkdirSync(outputPath, { recursive: true });
|
|
217
172
|
}
|
|
218
|
-
const rootPath = path3.resolve(process.cwd(),
|
|
173
|
+
const rootPath = path3.resolve(process.cwd(), options.root);
|
|
219
174
|
const configs = [
|
|
220
175
|
"protoc",
|
|
221
176
|
`--plugin=protoc-gen-ts_proto=${pluginPath}`,
|
|
@@ -226,44 +181,50 @@ function generateTypes() {
|
|
|
226
181
|
// message 字段生成可选属性
|
|
227
182
|
"--ts_proto_opt=esModuleInterop=true",
|
|
228
183
|
// 使用 ES Module 风格导入
|
|
229
|
-
"--ts_proto_opt=comments=false",
|
|
230
|
-
// 去掉注释
|
|
231
184
|
`--proto_path=${rootPath}`,
|
|
232
185
|
`--proto_path=${path3.join(rootPath, "/third_party/googleapis")}`,
|
|
233
186
|
// protos 中的 google 依赖
|
|
234
|
-
path3.join(rootPath,
|
|
187
|
+
path3.join(rootPath, options.file)
|
|
235
188
|
];
|
|
236
189
|
execSync(configs.join(" "), { stdio: "inherit" });
|
|
237
|
-
|
|
190
|
+
fs3.rmSync(
|
|
238
191
|
path3.join(outputPath, "google"),
|
|
239
192
|
{ recursive: true, force: true }
|
|
240
193
|
);
|
|
241
|
-
|
|
194
|
+
fs3.rmSync(
|
|
242
195
|
path3.join(outputPath, "common.ts"),
|
|
243
196
|
{ force: true }
|
|
244
197
|
);
|
|
245
198
|
}
|
|
246
199
|
|
|
247
200
|
// src/commands/generate.ts
|
|
248
|
-
async function generate() {
|
|
249
|
-
if (!
|
|
250
|
-
console.log(
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
const config = JSON.parse(
|
|
254
|
-
fs5.readFileSync("proto.config.json", "utf8")
|
|
255
|
-
);
|
|
256
|
-
if (!fs5.existsSync(path4.resolve(process.cwd(), config.ProtoRoot))) {
|
|
257
|
-
console.log(`proto \u9879\u76EE\u4E0D\u5B58\u5728(${config.ProtoRoot})`);
|
|
201
|
+
async function generate(options) {
|
|
202
|
+
if (!fs4.existsSync(path4.resolve(process.cwd(), options.root))) {
|
|
203
|
+
console.log(`proto \u9879\u76EE\u4E0D\u5B58\u5728(${options.root})`);
|
|
258
204
|
return;
|
|
259
205
|
}
|
|
260
|
-
generateTypes();
|
|
261
|
-
generateApi();
|
|
206
|
+
generateTypes(options);
|
|
207
|
+
generateApi(options);
|
|
262
208
|
}
|
|
263
209
|
|
|
264
210
|
// src/index.ts
|
|
265
211
|
var program = new Command();
|
|
266
212
|
program.name("proto-gen").description("proto code generator").version("1.0.0");
|
|
267
|
-
program.command("
|
|
268
|
-
|
|
213
|
+
program.command("generate").option("--file, --file <path>").option("--root, --root <path>").option("--type-output, --typeOutput <path>").option("--api-output, --apiOutput <path>").action((options) => {
|
|
214
|
+
if (options.file === void 0) {
|
|
215
|
+
console.log("proto file \u4E0D\u80FD\u4E3A\u7A7A, \u8BF7\u8F93\u5165 --file ./account.proto");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (options.root === void 0) {
|
|
219
|
+
console.log("proto \u6587\u4EF6\u7684\u6839\u8DEF\u5F84\u4E0D\u80FD\u4E3A\u7A7A, \u8BF7\u8F93\u5165 --root ../protos");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (options.typeOutput === void 0) {
|
|
223
|
+
options.typeOutput = "src/protos/type";
|
|
224
|
+
}
|
|
225
|
+
if (options.apiOutput === void 0) {
|
|
226
|
+
options.apiOutput = "src/protos/api";
|
|
227
|
+
}
|
|
228
|
+
generate(options);
|
|
229
|
+
});
|
|
269
230
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-proto-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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"
|
|
8
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
9
|
+
"release": "pnpm build && npm publish"
|
|
9
10
|
},
|
|
10
11
|
"bin": {
|
|
11
12
|
"proto-gen": "./dist/index.js"
|