oas-codegen 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wuchao
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # oas-codegen
2
+
3
+ 基于 OpenAPI 规范生成 TypeScript 客户端代码的**配方化 CLI**——薄封装 [orval](https://orval.dev),把「拉取规范 → 生成 vue-query 客户端」收敛成一个命令。
4
+
5
+ > **设计取舍**:不重写 OpenAPI→TS 编译器,而是把 orval 8 作为版本锁定的引擎,由本包提供规范获取、默认配方(vue-query + 自定义 axios mutator)与清晰配置。生成逻辑全部透传给 orval,天然继承其全部特性。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ pnpm add -D oas-codegen
11
+ ```
12
+
13
+ 要求 Node.js ≥ 18(使用全局 `fetch`)。
14
+
15
+ ## 快速开始
16
+
17
+ ```bash
18
+ oas-codegen init # 生成 oas-codegen.config.ts(含注释)
19
+ oas-codegen fetch # 从后端拉取 OpenAPI 规范 → specs/openapi.json
20
+ oas-codegen generate # 生成 TS 类型 + vue-query hooks → src/api/generated/
21
+ ```
22
+
23
+ **零配置默认值**刻意等价于旧手写流程的行为,什么都不写也能跑:
24
+
25
+ | 默认项 | 值 |
26
+ | --- | --- |
27
+ | 规范来源 | springdoc 后端,`${API_BASE_URL || http://localhost:8082}/v3/api-docs` |
28
+ | 规范落盘 | `specs/openapi.json` |
29
+ | 生成入口 | `src/api/generated/endpoints.ts` |
30
+ | 类型目录 | `src/api/generated/models` |
31
+ | 客户端 | `vue-query` |
32
+ | mutator | `./src/api/customInstance.ts` 的 `customInstance` |
33
+
34
+ ### 命令
35
+
36
+ ```
37
+ oas-codegen <fetch|generate|init> [options]
38
+
39
+ fetch 从后端拉取 OpenAPI 规范并保存为 JSON
40
+ generate 根据规范生成 TypeScript 类型与请求代码
41
+ init 生成默认的 oas-codegen.config.ts
42
+ ```
43
+
44
+ 公共选项:`--config <path>`(配置文件路径,默认自动探测 `oas-codegen.config.ts` → `.mts`/`.cts`/`.js`/`.mjs`/`.cjs`/`.json`)。
45
+ `init` 另支持 `--force` 覆盖已存在配置。
46
+
47
+ ## 配置参考
48
+
49
+ ```ts
50
+ import { defineConfig } from 'oas-codegen'
51
+
52
+ export default defineConfig({
53
+ source: {
54
+ type: 'springdoc', // 'springdoc' | 'url' | 'file'
55
+ baseUrl: 'http://localhost:8082', // 后端地址;环境变量优先
56
+ specPath: '/v3/api-docs', // springdoc 规范接口路径
57
+ url: 'http://x/api-docs', // type='url' 时直接指定规范地址
58
+ envFile: '.env.local', // 先读该文件(dotenv),再取环境变量
59
+ envBaseUrl: 'API_BASE_URL', // 覆盖 baseUrl 的环境变量名
60
+ output: 'specs/openapi.json', // 规范保存路径(相对项目根)
61
+ },
62
+ generate: {
63
+ input: 'specs/openapi.json', // 缺省取 source.output
64
+ target: 'src/api/generated/endpoints.ts',
65
+ schemas: 'src/api/generated/models',
66
+ client: 'vue-query', // 'vue-query' | 'react-query' | 'axios' | 'fetch'
67
+ // queryVersion: 5, // 显式锁定 TanStack Query 大版本;缺省由 orval 探测
68
+ mutator: {
69
+ name: 'customInstance',
70
+ path: './src/api/customInstance.ts',
71
+ },
72
+ },
73
+ })
74
+ ```
75
+
76
+ ## source 类型
77
+
78
+ | 类型 | 语义 | `fetch` 行为 |
79
+ | --- | --- | --- |
80
+ | `springdoc`(默认) | 从后端 `base + specPath` 拉规范 | 网络拉取,写入 `output` |
81
+ | `url` | 直接指定规范地址 | 网络拉取,写入 `output` |
82
+ | `file` | `output` 就是本地规范文件路径 | 读取并校验 JSON,**不重写**文件 |
83
+
84
+ `file` 类型适用于离线 / CI / 私有源场景:把 spec 提交进仓库(有版本历史),`oas-codegen fetch` 只做校验、`oas-codegen generate` 直接读本地文件,全程不联网、无鉴权负担。
85
+
86
+ ## Mutator 契约(重要)
87
+
88
+ orval 8 对自定义 HTTP 客户端采用 **axios 式 `(url, config)` 双参约定**(与 orval 7 的「单对象 config」不同)。你的 `customInstance` 必须长这样:
89
+
90
+ ```ts
91
+ import request from './request'
92
+
93
+ export const customInstance = <T>(
94
+ url: string,
95
+ config: {
96
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
97
+ headers?: Record<string, string>
98
+ body?: string // orval 8 传 JSON.stringify 后的字符串(旧版是对象,注意)
99
+ params?: Record<string, any>
100
+ signal?: AbortSignal
101
+ },
102
+ ): Promise<T> => request({ url, ...config }) as Promise<T>
103
+ ```
104
+
105
+ 生成代码的调用形态:
106
+
107
+ ```ts
108
+ // GET(query 已拼进 URL)
109
+ customInstance<GetTaskResponse>(getGetTaskUrl(id), { ...options, method: 'GET' })
110
+ // POST / PUT / PATCH / DELETE(body 已序列化)
111
+ customInstance<LoginResponse>(getLoginUrl(), { ...options, method: 'POST', headers: { 'Content-Type': 'application/json', ... }, body: JSON.stringify(loginDTO) })
112
+ ```
113
+
114
+ 如果你的底层客户端(如 axios)期望 `data` 而不是 `body`,在 mutator 里自行映射即可。
115
+
116
+ ## 配置解析
117
+
118
+ - 支持 `.ts` / `.js` / `.json` 等,`.ts` 通过 [jiti](https://github.com/unjs/jiti) 加载,无额外构建步骤
119
+ - 找不到配置文件时返回全默认配置(即兼容旧 `export-api.mjs` + `orval.config.ts` 的行为)
120
+ - `--config` 指向不存在的文件会直接报错,不会静默回退
121
+
122
+ ## 开发
123
+
124
+ ```bash
125
+ pnpm dev # tsx 直接跑 CLI
126
+ pnpm typecheck # tsc --noEmit
127
+ pnpm test # vitest
128
+ pnpm build # tsup 构建 dist/
129
+ ```
130
+
131
+ ## 说明
132
+
133
+ - 生成过程中会在项目根目录短暂出现 `oas-codegen.orval.config.<pid>.mjs`(orval 把配置相对路径解析为配置文件所在目录,必须放在项目内),结束后自动删除;若进程被强杀可能残留,忽略或删除即可。
134
+ - 版本号通过 `package.json` 读取,`--version` 无需手动同步。
135
+
136
+ ## 路线图
137
+
138
+ - [x] M0 提取:`fetch` + `generate` 复刻旧流程
139
+ - [x] M1 CLI + 配置 schema(zod 校验、默认值、`defineConfig` 类型提示)
140
+ - [ ] M2 配方/模板系统:`customInstance` 脚手架、`init` 一键接入现有项目
141
+ - [ ] M3 发布:semantic-release、npm provenance、publint / arethetypeswrong 校验
142
+ - [ ] M4 文档站点与更丰富配方(react-query / fetch / 请求 mock)
@@ -0,0 +1,359 @@
1
+ // src/config/schema.ts
2
+ import { z } from "zod";
3
+ var DEFAULT_MUTATOR = {
4
+ name: "customInstance",
5
+ path: "./src/api/customInstance.ts"
6
+ };
7
+ var DEFAULT_GENERATE = {
8
+ target: "src/api/generated/endpoints.ts",
9
+ schemas: "src/api/generated/models",
10
+ client: "vue-query"
11
+ };
12
+ var DEFAULT_SOURCE = {
13
+ type: "springdoc",
14
+ specPath: "/v3/api-docs",
15
+ envBaseUrl: "API_BASE_URL",
16
+ output: "specs/openapi.json"
17
+ };
18
+ var sourceSchema = z.object({
19
+ /** 来源类型:springdoc 后端接口 / 直接 URL / 本地文件 */
20
+ type: z.enum(["springdoc", "url", "file"]).default(DEFAULT_SOURCE.type),
21
+ /** 后端地址(springdoc 类型使用),环境变量优先 */
22
+ baseUrl: z.string().optional(),
23
+ /** springdoc 规范接口路径 */
24
+ specPath: z.string().default(DEFAULT_SOURCE.specPath),
25
+ /** url 类型时直接指定规范地址 */
26
+ url: z.string().optional(),
27
+ /** 读取该文件中的环境变量(如 .env.local),相对项目根目录 */
28
+ envFile: z.string().optional(),
29
+ /** 覆盖 baseUrl 的环境变量名 */
30
+ envBaseUrl: z.string().default(DEFAULT_SOURCE.envBaseUrl),
31
+ /** 规范保存路径,相对项目根目录 */
32
+ output: z.string().default(DEFAULT_SOURCE.output)
33
+ });
34
+ var generateSchema = z.object({
35
+ /** spec 输入路径,默认取 source.output */
36
+ input: z.string().optional(),
37
+ /** 生成的 API 入口文件 */
38
+ target: z.string().default(DEFAULT_GENERATE.target),
39
+ /** 生成的类型定义目录 */
40
+ schemas: z.string().default(DEFAULT_GENERATE.schemas),
41
+ /** 客户端配方 */
42
+ client: z.enum(["vue-query", "react-query", "axios", "fetch"]).default(DEFAULT_GENERATE.client),
43
+ /** 请求 mutator(axios 封装约定) */
44
+ mutator: z.object({
45
+ name: z.string().default(DEFAULT_MUTATOR.name),
46
+ path: z.string().default(DEFAULT_MUTATOR.path)
47
+ }).optional(),
48
+ /** TanStack Query 大版本;缺省时由 orval 从项目依赖探测(探测不到会回退 v4) */
49
+ queryVersion: z.union([z.literal(4), z.literal(5)]).optional()
50
+ });
51
+ var configSchema = z.object({
52
+ source: sourceSchema.default(DEFAULT_SOURCE),
53
+ generate: generateSchema.default(DEFAULT_GENERATE)
54
+ });
55
+
56
+ // src/config/load.ts
57
+ import fs from "fs";
58
+ import path from "path";
59
+ import { createJiti } from "jiti";
60
+ var DEFAULT_CONFIG_FILE = "oas-codegen.config.ts";
61
+ var CONFIG_FILE_EXTS = [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"];
62
+ function resolveConfigFile(cwd, configFile) {
63
+ if (configFile) return path.resolve(cwd, configFile);
64
+ for (const ext of CONFIG_FILE_EXTS) {
65
+ const candidate = path.resolve(cwd, `oas-codegen.config${ext}`);
66
+ if (fs.existsSync(candidate)) return candidate;
67
+ }
68
+ return void 0;
69
+ }
70
+ function firstLine(err) {
71
+ const msg = err instanceof Error ? err.message : String(err);
72
+ return msg.split("\n")[0] ?? msg;
73
+ }
74
+ async function loadConfig(cwd, configFile) {
75
+ const abs = resolveConfigFile(cwd, configFile);
76
+ if (!abs) return configSchema.parse({});
77
+ if (!fs.existsSync(abs)) {
78
+ throw new Error(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${abs}
79
+ \uFF08\u53EF\u7528 "oas-codegen init" \u751F\u6210\u9ED8\u8BA4\u914D\u7F6E\uFF09`);
80
+ }
81
+ let raw;
82
+ if (abs.endsWith(".json")) {
83
+ try {
84
+ raw = JSON.parse(fs.readFileSync(abs, "utf8"));
85
+ } catch (err) {
86
+ throw new Error(`\u914D\u7F6E\u6587\u4EF6\u4E0D\u662F\u5408\u6CD5 JSON: ${abs}
87
+ \u539F\u56E0: ${firstLine(err)}`);
88
+ }
89
+ } else {
90
+ try {
91
+ const jiti = createJiti(import.meta.url, { interopDefault: true });
92
+ const mod = await jiti.import(abs);
93
+ raw = mod?.default ?? mod;
94
+ } catch (err) {
95
+ throw new Error(`\u52A0\u8F7D\u914D\u7F6E\u6587\u4EF6\u5931\u8D25: ${abs}
96
+ \u539F\u56E0: ${firstLine(err)}`);
97
+ }
98
+ }
99
+ return configSchema.parse(raw);
100
+ }
101
+
102
+ // src/spec/fetch.ts
103
+ import fs2 from "fs";
104
+ import path2 from "path";
105
+ async function fetchSpec(url) {
106
+ let res;
107
+ try {
108
+ res = await fetch(url);
109
+ } catch (err) {
110
+ throw new Error(`\u65E0\u6CD5\u8FDE\u63A5 ${url}: ${err.message}`);
111
+ }
112
+ if (!res.ok) {
113
+ throw new Error(`\u62C9\u53D6 OpenAPI \u5931\u8D25: ${res.status} ${res.statusText}
114
+ ${url}
115
+ \u8BF7\u786E\u8BA4\u540E\u7AEF\u5DF2\u542F\u52A8\u3002`);
116
+ }
117
+ return res.json();
118
+ }
119
+ function writeSpec(spec, output) {
120
+ fs2.mkdirSync(path2.dirname(output), { recursive: true });
121
+ fs2.writeFileSync(output, JSON.stringify(spec, null, 2));
122
+ }
123
+ function readLocalSpec(file) {
124
+ let raw;
125
+ try {
126
+ raw = fs2.readFileSync(file, "utf8");
127
+ } catch (err) {
128
+ throw new Error(`\u8BFB\u53D6\u672C\u5730 OpenAPI \u89C4\u8303\u5931\u8D25: ${file}
129
+ \u539F\u56E0: ${err.message}`);
130
+ }
131
+ try {
132
+ return JSON.parse(raw);
133
+ } catch (err) {
134
+ throw new Error(`\u672C\u5730\u89C4\u8303\u4E0D\u662F\u5408\u6CD5 JSON: ${file}
135
+ \u539F\u56E0: ${err.message}`);
136
+ }
137
+ }
138
+
139
+ // src/spec/source.ts
140
+ import fs3 from "fs";
141
+ import path3 from "path";
142
+ import dotenv from "dotenv";
143
+ function loadEnvFile(envFile, cwd) {
144
+ if (!envFile) return;
145
+ const file = path3.resolve(cwd, envFile);
146
+ if (!fs3.existsSync(file)) return;
147
+ dotenv.config({ path: file });
148
+ }
149
+ function resolveSource(source, cwd) {
150
+ const output = path3.resolve(cwd, source.output);
151
+ if (source.type === "url") {
152
+ if (!source.url) throw new Error('source.type = "url" \u65F6\u5FC5\u987B\u63D0\u4F9B source.url');
153
+ return { url: source.url, output };
154
+ }
155
+ if (source.type === "springdoc") {
156
+ const base = process.env[source.envBaseUrl] || source.baseUrl || "http://localhost:8082";
157
+ const url = `${base.replace(/\/+$/, "")}${source.specPath}`;
158
+ return { url, output };
159
+ }
160
+ return { url: void 0, output };
161
+ }
162
+
163
+ // src/commands/fetch.ts
164
+ async function runFetch(opts = {}) {
165
+ const cwd = opts.cwd ?? process.cwd();
166
+ const config = await loadConfig(cwd, opts.config);
167
+ loadEnvFile(config.source.envFile, cwd);
168
+ const { url, output } = resolveSource(config.source, cwd);
169
+ if (url === void 0) {
170
+ if (!opts.silent) console.log(`Reading OpenAPI spec from ${output} ...`);
171
+ const spec2 = readLocalSpec(output);
172
+ if (!opts.silent) console.log(`Validated ${output}`);
173
+ return { url, output, spec: spec2 };
174
+ }
175
+ if (!opts.silent) console.log(`Fetching OpenAPI spec from ${url} ...`);
176
+ const spec = await fetchSpec(url);
177
+ writeSpec(spec, output);
178
+ if (!opts.silent) console.log(`Saved to ${output}`);
179
+ return { url, output, spec };
180
+ }
181
+
182
+ // src/generate/orval.ts
183
+ import fs4 from "fs";
184
+ import path4 from "path";
185
+ import { spawn } from "child_process";
186
+ import { createRequire } from "module";
187
+ var require2 = createRequire(import.meta.url);
188
+ var ORVAL_PROJECT = "generated";
189
+ function resolveMutator(g) {
190
+ return g.mutator ?? (g.client === "vue-query" ? { ...DEFAULT_MUTATOR } : void 0);
191
+ }
192
+ function renderOrvalConfig(config) {
193
+ const g = config.generate;
194
+ const input = toPosix(g.input ?? config.source.output);
195
+ const mutator = resolveMutator(g);
196
+ return {
197
+ [ORVAL_PROJECT]: {
198
+ input,
199
+ output: {
200
+ target: toPosix(g.target),
201
+ schemas: toPosix(g.schemas),
202
+ client: g.client,
203
+ mock: false,
204
+ override: {
205
+ ...mutator ? { mutator: { name: mutator.name, path: toPosix(mutator.path) } } : {},
206
+ query: {
207
+ useQuery: true,
208
+ ...g.queryVersion !== void 0 ? { version: g.queryVersion } : {}
209
+ }
210
+ }
211
+ }
212
+ }
213
+ };
214
+ }
215
+ function toPosix(p) {
216
+ return p.replace(/\\/g, "/");
217
+ }
218
+ function resolveOrvalBin() {
219
+ const entry = require2.resolve("orval");
220
+ let dir = path4.dirname(entry);
221
+ for (; ; ) {
222
+ const pkgJson = path4.join(dir, "package.json");
223
+ if (fs4.existsSync(pkgJson)) {
224
+ const pkg = JSON.parse(fs4.readFileSync(pkgJson, "utf8"));
225
+ if (pkg.name === "orval" && pkg.bin) {
226
+ const bin = typeof pkg.bin === "string" ? pkg.bin : pkg.bin.orval;
227
+ if (bin) return path4.join(dir, bin);
228
+ }
229
+ }
230
+ const parent = path4.dirname(dir);
231
+ if (parent === dir) {
232
+ throw new Error("\u672A\u627E\u5230 orval \u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u8BF7\u786E\u8BA4\u4F9D\u8D56\u5DF2\u5B89\u88C5\uFF08pnpm install\uFF09");
233
+ }
234
+ dir = parent;
235
+ }
236
+ }
237
+ function runOrval(args, cwd) {
238
+ const bin = resolveOrvalBin();
239
+ return new Promise((resolve, reject) => {
240
+ const child = spawn(process.execPath, [bin, ...args], { cwd, stdio: "inherit" });
241
+ let settled = false;
242
+ const finish = (fn) => {
243
+ if (!settled) {
244
+ settled = true;
245
+ fn();
246
+ }
247
+ };
248
+ child.once("error", (err) => finish(() => reject(new Error(`\u65E0\u6CD5\u542F\u52A8 orval: ${err.message}`))));
249
+ child.once(
250
+ "exit",
251
+ (code) => finish(() => {
252
+ if (code === 0) resolve();
253
+ else reject(new Error(`orval \u9000\u51FA\uFF0C\u9000\u51FA\u7801 ${code}`));
254
+ })
255
+ );
256
+ });
257
+ }
258
+
259
+ // src/commands/generate.ts
260
+ import fs5 from "fs";
261
+ import path5 from "path";
262
+ function tempConfigPath(cwd) {
263
+ return path5.join(cwd, `oas-codegen.orval.config.${process.pid}.mjs`);
264
+ }
265
+ async function runGenerate(opts = {}) {
266
+ const cwd = opts.cwd ?? process.cwd();
267
+ const config = await loadConfig(cwd, opts.config);
268
+ const input = path5.resolve(cwd, config.generate.input ?? config.source.output);
269
+ if (!fs5.existsSync(input)) {
270
+ throw new Error(`\u627E\u4E0D\u5230 OpenAPI spec: ${input}
271
+ \u8BF7\u5148\u8FD0\u884C "oas-codegen fetch"\u3002`);
272
+ }
273
+ const mutator = resolveMutator(config.generate);
274
+ if (mutator) {
275
+ const mutatorFile = path5.resolve(cwd, mutator.path);
276
+ if (!fs5.existsSync(mutatorFile)) {
277
+ throw new Error(
278
+ `\u627E\u4E0D\u5230 mutator \u6587\u4EF6: ${mutatorFile}
279
+ \u81EA\u5B9A\u4E49 HTTP \u5BA2\u6237\u7AEF\u9700\u8981\u5148\u521B\u5EFA\u8BE5\u6587\u4EF6\uFF08\u7B7E\u540D\u89C1 README\u300CMutator \u5951\u7EA6\u300D\uFF09\uFF0C\u6216\u8C03\u6574 generate.mutator \u6307\u5411\u5DF2\u6709\u6587\u4EF6\u3002`
280
+ );
281
+ }
282
+ }
283
+ const tmpFile = tempConfigPath(cwd);
284
+ fs5.writeFileSync(tmpFile, `export default ${JSON.stringify(renderOrvalConfig(config), null, 2)}
285
+ `);
286
+ console.log(`Generating TypeScript from ${input} ...`);
287
+ try {
288
+ await runOrval(["--config", tmpFile], cwd);
289
+ console.log("Done.");
290
+ } finally {
291
+ fs5.rmSync(tmpFile, { force: true });
292
+ }
293
+ }
294
+
295
+ // src/commands/init.ts
296
+ import fs6 from "fs";
297
+ import path6 from "path";
298
+ var CONFIG_TEMPLATE = `import { defineConfig } from 'oas-codegen'
299
+
300
+ export default defineConfig({
301
+ source: {
302
+ type: 'springdoc',
303
+ // \u540E\u7AEF\u5730\u5740\uFF1A\u4F18\u5148\u8BFB\u73AF\u5883\u53D8\u91CF API_BASE_URL\uFF0C\u672A\u8BBE\u7F6E\u65F6\u7528 baseUrl
304
+ baseUrl: 'http://localhost:8082',
305
+ specPath: '/v3/api-docs',
306
+ envFile: '.env.local',
307
+ output: 'specs/openapi.json',
308
+ },
309
+ generate: {
310
+ target: 'src/api/generated/endpoints.ts',
311
+ schemas: 'src/api/generated/models',
312
+ client: 'vue-query',
313
+ // \u663E\u5F0F\u9501\u5B9A TanStack Query \u5927\u7248\u672C\uFF084 \u6216 5\uFF09\uFF1B\u4E0D\u5199\u5219\u6309\u9879\u76EE\u4F9D\u8D56\u81EA\u52A8\u63A2\u6D4B
314
+ // queryVersion: 5,
315
+ // mutator \u9700\u4E3A (url, config) \u53CC\u53C2\u7EA6\u5B9A\uFF08orval 8 \u6807\u51C6\uFF09\uFF0C\u5B57\u6BB5\u89C1 README
316
+ mutator: {
317
+ name: 'customInstance',
318
+ path: './src/api/customInstance.ts',
319
+ },
320
+ },
321
+ })
322
+ `;
323
+ function runInit(opts = {}) {
324
+ const cwd = opts.cwd ?? process.cwd();
325
+ const file = path6.join(cwd, DEFAULT_CONFIG_FILE);
326
+ if (fs6.existsSync(file) && !opts.force) {
327
+ console.log(`\u5DF2\u5B58\u5728 ${DEFAULT_CONFIG_FILE}\uFF0C\u8DF3\u8FC7\uFF08\u52A0 --force \u8986\u76D6\uFF09`);
328
+ return;
329
+ }
330
+ fs6.writeFileSync(file, CONFIG_TEMPLATE);
331
+ console.log(`\u5DF2\u521B\u5EFA ${DEFAULT_CONFIG_FILE}`);
332
+ console.log("\u63A5\u4E0B\u6765\uFF1Aoas-codegen fetch && oas-codegen generate");
333
+ }
334
+
335
+ export {
336
+ DEFAULT_MUTATOR,
337
+ DEFAULT_GENERATE,
338
+ DEFAULT_SOURCE,
339
+ sourceSchema,
340
+ generateSchema,
341
+ configSchema,
342
+ DEFAULT_CONFIG_FILE,
343
+ loadConfig,
344
+ fetchSpec,
345
+ writeSpec,
346
+ readLocalSpec,
347
+ loadEnvFile,
348
+ resolveSource,
349
+ runFetch,
350
+ ORVAL_PROJECT,
351
+ resolveMutator,
352
+ renderOrvalConfig,
353
+ resolveOrvalBin,
354
+ runOrval,
355
+ runGenerate,
356
+ CONFIG_TEMPLATE,
357
+ runInit
358
+ };
359
+ //# sourceMappingURL=chunk-AWDG5OSK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/schema.ts","../src/config/load.ts","../src/spec/fetch.ts","../src/spec/source.ts","../src/commands/fetch.ts","../src/generate/orval.ts","../src/commands/generate.ts","../src/commands/init.ts"],"sourcesContent":["import { z } from 'zod'\n\n/** 默认 mutator:生成的调用统一走项目自己的 axios 封装。 */\nexport const DEFAULT_MUTATOR = {\n name: 'customInstance',\n path: './src/api/customInstance.ts',\n} as const\n\n/** 未配置 generate 时的默认配方(daily-task 的 orval 约定)。 */\nexport const DEFAULT_GENERATE = {\n target: 'src/api/generated/endpoints.ts',\n schemas: 'src/api/generated/models',\n client: 'vue-query',\n} as const\n\n/**\n * 未配置 source 时的默认值。刻意等价于 daily-task 旧 export-api.mjs 的行为,\n * 这样「什么都不写」就是历史行为,迁移零成本。\n */\nexport const DEFAULT_SOURCE = {\n type: 'springdoc',\n specPath: '/v3/api-docs',\n envBaseUrl: 'API_BASE_URL',\n output: 'specs/openapi.json',\n} as const\n\n/** spec 来源配置:从哪拿 OpenAPI 规范。 */\nexport const sourceSchema = z.object({\n /** 来源类型:springdoc 后端接口 / 直接 URL / 本地文件 */\n type: z.enum(['springdoc', 'url', 'file']).default(DEFAULT_SOURCE.type),\n /** 后端地址(springdoc 类型使用),环境变量优先 */\n baseUrl: z.string().optional(),\n /** springdoc 规范接口路径 */\n specPath: z.string().default(DEFAULT_SOURCE.specPath),\n /** url 类型时直接指定规范地址 */\n url: z.string().optional(),\n /** 读取该文件中的环境变量(如 .env.local),相对项目根目录 */\n envFile: z.string().optional(),\n /** 覆盖 baseUrl 的环境变量名 */\n envBaseUrl: z.string().default(DEFAULT_SOURCE.envBaseUrl),\n /** 规范保存路径,相对项目根目录 */\n output: z.string().default(DEFAULT_SOURCE.output),\n})\n\n/** 生成配置:spec → TypeScript 客户端代码的产出约定。 */\nexport const generateSchema = z.object({\n /** spec 输入路径,默认取 source.output */\n input: z.string().optional(),\n /** 生成的 API 入口文件 */\n target: z.string().default(DEFAULT_GENERATE.target),\n /** 生成的类型定义目录 */\n schemas: z.string().default(DEFAULT_GENERATE.schemas),\n /** 客户端配方 */\n client: z\n .enum(['vue-query', 'react-query', 'axios', 'fetch'])\n .default(DEFAULT_GENERATE.client),\n /** 请求 mutator(axios 封装约定) */\n mutator: z\n .object({\n name: z.string().default(DEFAULT_MUTATOR.name),\n path: z.string().default(DEFAULT_MUTATOR.path),\n })\n .optional(),\n /** TanStack Query 大版本;缺省时由 orval 从项目依赖探测(探测不到会回退 v4) */\n queryVersion: z.union([z.literal(4), z.literal(5)]).optional(),\n})\n\nexport type SourceConfig = z.infer<typeof sourceSchema>\nexport type SourceConfigInput = z.input<typeof sourceSchema>\nexport type GenerateConfig = z.infer<typeof generateSchema>\nexport type GenerateConfigInput = z.input<typeof generateSchema>\n\nexport const configSchema = z.object({\n source: sourceSchema.default(DEFAULT_SOURCE),\n generate: generateSchema.default(DEFAULT_GENERATE),\n})\n\nexport type OasConfig = z.infer<typeof configSchema>\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { createJiti } from 'jiti'\nimport { configSchema, type OasConfig } from './schema.js'\n\nexport const DEFAULT_CONFIG_FILE = 'oas-codegen.config.ts'\n\n/** 未显式指定 --config 时按顺序探测的配置文件后缀(对齐 vite/tsup 的习惯)。 */\nconst CONFIG_FILE_EXTS = ['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs', '.json']\n\n/** 解析配置文件绝对路径;显式指定但不存在时返回该路径,由调用方报错。 */\nfunction resolveConfigFile(cwd: string, configFile?: string): string | undefined {\n if (configFile) return path.resolve(cwd, configFile)\n for (const ext of CONFIG_FILE_EXTS) {\n const candidate = path.resolve(cwd, `oas-codegen.config${ext}`)\n if (fs.existsSync(candidate)) return candidate\n }\n return undefined\n}\n\nfunction firstLine(err: unknown): string {\n const msg = err instanceof Error ? err.message : String(err)\n return msg.split('\\n')[0] ?? msg\n}\n\n/**\n * 加载 oas-codegen 配置文件,返回「生效配置」(默认值已填充)。\n * 未找到配置文件时返回全默认配置,即兼容旧 export-api.mjs + orval.config.ts 的行为。\n * 支持 .ts / .mts / .cts / .js / .mjs / .cjs / .json。\n */\nexport async function loadConfig(cwd: string, configFile?: string): Promise<OasConfig> {\n const abs = resolveConfigFile(cwd, configFile)\n\n if (!abs) return configSchema.parse({})\n if (!fs.existsSync(abs)) {\n throw new Error(`配置文件不存在: ${abs}\\n(可用 \"oas-codegen init\" 生成默认配置)`)\n }\n\n let raw: unknown\n if (abs.endsWith('.json')) {\n try {\n raw = JSON.parse(fs.readFileSync(abs, 'utf8'))\n } catch (err) {\n throw new Error(`配置文件不是合法 JSON: ${abs}\\n原因: ${firstLine(err)}`)\n }\n } else {\n try {\n const jiti = createJiti(import.meta.url, { interopDefault: true })\n const mod = (await jiti.import(abs)) as Record<string, unknown> | undefined\n raw = mod?.default ?? mod\n } catch (err) {\n throw new Error(`加载配置文件失败: ${abs}\\n原因: ${firstLine(err)}`)\n }\n }\n\n return configSchema.parse(raw)\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\n\n/** 从指定 URL 拉取 OpenAPI 规范并解析为 JSON。 */\nexport async function fetchSpec(url: string): Promise<unknown> {\n let res: Response\n try {\n res = await fetch(url)\n } catch (err) {\n throw new Error(`无法连接 ${url}: ${(err as Error).message}`)\n }\n if (!res.ok) {\n throw new Error(`拉取 OpenAPI 失败: ${res.status} ${res.statusText}\\n${url}\\n请确认后端已启动。`)\n }\n return res.json() as Promise<unknown>\n}\n\n/** 将规范以 2 空格缩进的 JSON 写入磁盘(自动创建目录)。 */\nexport function writeSpec(spec: unknown, output: string): void {\n fs.mkdirSync(path.dirname(output), { recursive: true })\n fs.writeFileSync(output, JSON.stringify(spec, null, 2))\n}\n\n/** 读取本地 OpenAPI 规范文件并解析为 JSON(file 类型使用,不重写文件)。 */\nexport function readLocalSpec(file: string): unknown {\n let raw: string\n try {\n raw = fs.readFileSync(file, 'utf8')\n } catch (err) {\n throw new Error(`读取本地 OpenAPI 规范失败: ${file}\\n原因: ${(err as Error).message}`)\n }\n try {\n return JSON.parse(raw)\n } catch (err) {\n throw new Error(`本地规范不是合法 JSON: ${file}\\n原因: ${(err as Error).message}`)\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport dotenv from 'dotenv'\nimport type { SourceConfig } from '../config/schema.js'\n\nexport interface ResolvedSource {\n /** 最终请求的 spec 地址;file 类型为 undefined */\n url: string | undefined\n /** 保存 spec 的绝对路径 */\n output: string\n}\n\n/** 加载 envFile(如 .env.local)中的变量到 process.env,缺省静默跳过。 */\nexport function loadEnvFile(envFile: string | undefined, cwd: string): void {\n if (!envFile) return\n const file = path.resolve(cwd, envFile)\n if (!fs.existsSync(file)) return\n dotenv.config({ path: file })\n}\n\n/** 将配置的 source 解析成具体的请求地址与输出路径(处理环境变量优先级与默认值)。 */\nexport function resolveSource(source: SourceConfig, cwd: string): ResolvedSource {\n const output = path.resolve(cwd, source.output)\n\n if (source.type === 'url') {\n if (!source.url) throw new Error('source.type = \"url\" 时必须提供 source.url')\n return { url: source.url, output }\n }\n\n if (source.type === 'springdoc') {\n const base = process.env[source.envBaseUrl] || source.baseUrl || 'http://localhost:8082'\n const url = `${base.replace(/\\/+$/, '')}${source.specPath}`\n return { url, output }\n }\n\n // file 类型:output 就是本地规范文件路径,无网络请求\n return { url: undefined, output }\n}\n","import { loadConfig } from '../config/load.js'\nimport { fetchSpec, readLocalSpec, writeSpec } from '../spec/fetch.js'\nimport { loadEnvFile, resolveSource } from '../spec/source.js'\n\nexport interface FetchOptions {\n config?: string\n cwd?: string\n /** 静默模式(供编程调用时使用),默认打印过程日志 */\n silent?: boolean\n}\n\nexport interface FetchResult {\n url: string | undefined\n output: string\n spec: unknown\n}\n\n/** 获取 OpenAPI 规范:springdoc/url 从网络拉取并落盘,file 类型读取本地文件校验。 */\nexport async function runFetch(opts: FetchOptions = {}): Promise<FetchResult> {\n const cwd = opts.cwd ?? process.cwd()\n const config = await loadConfig(cwd, opts.config)\n loadEnvFile(config.source.envFile, cwd)\n const { url, output } = resolveSource(config.source, cwd)\n\n if (url === undefined) {\n // file 类型:规范已在本地,读取校验即可(不重写,避免污染提交的文件)\n if (!opts.silent) console.log(`Reading OpenAPI spec from ${output} ...`)\n const spec = readLocalSpec(output)\n if (!opts.silent) console.log(`Validated ${output}`)\n return { url, output, spec }\n }\n\n if (!opts.silent) console.log(`Fetching OpenAPI spec from ${url} ...`)\n const spec = await fetchSpec(url)\n writeSpec(spec, output)\n if (!opts.silent) console.log(`Saved to ${output}`)\n\n return { url, output, spec }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { spawn } from 'node:child_process'\nimport { createRequire } from 'node:module'\nimport { DEFAULT_MUTATOR, type GenerateConfig, type OasConfig } from '../config/schema.js'\n\nconst require = createRequire(import.meta.url)\n\n/** orval 配置里的统一项目名。 */\nexport const ORVAL_PROJECT = 'generated'\n\n/**\n * 解析生效的 mutator:显式配置优先;缺省时仅默认配方(vue-query)注入 customInstance。\n * react-query / axios / fetch 不自动绑 mutator,避免凭空要求 customInstance 文件存在。\n */\nexport function resolveMutator(g: GenerateConfig): { name: string; path: string } | undefined {\n return g.mutator ?? (g.client === 'vue-query' ? { ...DEFAULT_MUTATOR } : undefined)\n}\n\n/** 将 oas-codegen 配置渲染成 orval 可执行的配置对象。 */\nexport function renderOrvalConfig(config: OasConfig): Record<string, unknown> {\n const g = config.generate\n const input = toPosix(g.input ?? config.source.output)\n const mutator = resolveMutator(g)\n\n return {\n [ORVAL_PROJECT]: {\n input,\n output: {\n target: toPosix(g.target),\n schemas: toPosix(g.schemas),\n client: g.client,\n mock: false,\n override: {\n ...(mutator ? { mutator: { name: mutator.name, path: toPosix(mutator.path) } } : {}),\n query: {\n useQuery: true,\n ...(g.queryVersion !== undefined ? { version: g.queryVersion } : {}),\n },\n },\n },\n },\n }\n}\n\nfunction toPosix(p: string): string {\n return p.replace(/\\\\/g, '/')\n}\n\n/**\n * 解析包内置 orval 的可执行入口(读取依赖包 package.json 的 bin 字段)。\n * 不依赖 exports map,向上遍历目录定位 orval 包根。\n */\nexport function resolveOrvalBin(): string {\n const entry = require.resolve('orval')\n let dir = path.dirname(entry)\n for (;;) {\n const pkgJson = path.join(dir, 'package.json')\n if (fs.existsSync(pkgJson)) {\n const pkg = JSON.parse(fs.readFileSync(pkgJson, 'utf8'))\n if (pkg.name === 'orval' && pkg.bin) {\n const bin = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin.orval\n if (bin) return path.join(dir, bin)\n }\n }\n const parent = path.dirname(dir)\n if (parent === dir) {\n throw new Error('未找到 orval 可执行文件,请确认依赖已安装(pnpm install)')\n }\n dir = parent\n }\n}\n\n/** 以指定 cwd 运行 orval,stdio 直接透传,失败时 reject。 */\nexport function runOrval(args: string[], cwd: string): Promise<void> {\n const bin = resolveOrvalBin()\n return new Promise((resolve, reject) => {\n const child = spawn(process.execPath, [bin, ...args], { cwd, stdio: 'inherit' })\n let settled = false\n const finish = (fn: () => void) => {\n if (!settled) {\n settled = true\n fn()\n }\n }\n child.once('error', (err) => finish(() => reject(new Error(`无法启动 orval: ${err.message}`))))\n child.once('exit', (code) =>\n finish(() => {\n if (code === 0) resolve()\n else reject(new Error(`orval 退出,退出码 ${code}`))\n }),\n )\n })\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { loadConfig } from '../config/load.js'\nimport { renderOrvalConfig, resolveMutator, runOrval } from '../generate/orval.js'\n\nexport interface GenerateOptions {\n config?: string\n cwd?: string\n}\n\n/**\n * 临时 orval 配置写在项目根目录,而不是系统临时目录:\n * orval 把配置里的相对路径解析为「相对于配置文件所在目录」,\n * 写到 tmpdir 会让 spec / 输出路径全部指向错误的目录。\n */\nfunction tempConfigPath(cwd: string): string {\n return path.join(cwd, `oas-codegen.orval.config.${process.pid}.mjs`)\n}\n\n/** 根据本地 spec 生成 TypeScript 客户端代码。 */\nexport async function runGenerate(opts: GenerateOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd()\n const config = await loadConfig(cwd, opts.config)\n\n const input = path.resolve(cwd, config.generate.input ?? config.source.output)\n if (!fs.existsSync(input)) {\n throw new Error(`找不到 OpenAPI spec: ${input}\\n请先运行 \"oas-codegen fetch\"。`)\n }\n\n // orval 会读取 mutator 文件来确定生成代码的签名;缺失时给它一个可读错误而不是 ENOENT。\n const mutator = resolveMutator(config.generate)\n if (mutator) {\n const mutatorFile = path.resolve(cwd, mutator.path)\n if (!fs.existsSync(mutatorFile)) {\n throw new Error(\n `找不到 mutator 文件: ${mutatorFile}\\n` +\n `自定义 HTTP 客户端需要先创建该文件(签名见 README「Mutator 契约」),或调整 generate.mutator 指向已有文件。`,\n )\n }\n }\n\n const tmpFile = tempConfigPath(cwd)\n fs.writeFileSync(tmpFile, `export default ${JSON.stringify(renderOrvalConfig(config), null, 2)}\\n`)\n\n console.log(`Generating TypeScript from ${input} ...`)\n try {\n await runOrval(['--config', tmpFile], cwd)\n console.log('Done.')\n } finally {\n fs.rmSync(tmpFile, { force: true })\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { DEFAULT_CONFIG_FILE } from '../config/load.js'\n\nexport interface InitOptions {\n cwd?: string\n force?: boolean\n}\n\n/** 默认的 oas-codegen.config.ts 模板(与 daily-task 现行约定一致)。 */\nexport const CONFIG_TEMPLATE = `import { defineConfig } from 'oas-codegen'\n\nexport default defineConfig({\n source: {\n type: 'springdoc',\n // 后端地址:优先读环境变量 API_BASE_URL,未设置时用 baseUrl\n baseUrl: 'http://localhost:8082',\n specPath: '/v3/api-docs',\n envFile: '.env.local',\n output: 'specs/openapi.json',\n },\n generate: {\n target: 'src/api/generated/endpoints.ts',\n schemas: 'src/api/generated/models',\n client: 'vue-query',\n // 显式锁定 TanStack Query 大版本(4 或 5);不写则按项目依赖自动探测\n // queryVersion: 5,\n // mutator 需为 (url, config) 双参约定(orval 8 标准),字段见 README\n mutator: {\n name: 'customInstance',\n path: './src/api/customInstance.ts',\n },\n },\n})\n`\n\n/** 在目标项目根目录生成配置文件。 */\nexport function runInit(opts: InitOptions = {}): void {\n const cwd = opts.cwd ?? process.cwd()\n const file = path.join(cwd, DEFAULT_CONFIG_FILE)\n\n if (fs.existsSync(file) && !opts.force) {\n console.log(`已存在 ${DEFAULT_CONFIG_FILE},跳过(加 --force 覆盖)`)\n return\n }\n\n fs.writeFileSync(file, CONFIG_TEMPLATE)\n console.log(`已创建 ${DEFAULT_CONFIG_FILE}`)\n console.log('接下来:oas-codegen fetch && oas-codegen generate')\n}\n"],"mappings":"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","names":["fs","path","fs","path","spec","fs","path","require","path","fs","fs","path","path","fs","fs","path","path","fs"]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ runFetch,
4
+ runGenerate,
5
+ runInit
6
+ } from "./chunk-AWDG5OSK.js";
7
+
8
+ // src/cli.ts
9
+ import { readFileSync } from "fs";
10
+ import { defineCommand, runMain, showUsage } from "citty";
11
+ function readVersion() {
12
+ try {
13
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
14
+ return typeof pkg.version === "string" ? pkg.version : void 0;
15
+ } catch {
16
+ return void 0;
17
+ }
18
+ }
19
+ var configArg = {
20
+ type: "string",
21
+ description: "\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4 ./oas-codegen.config.ts\uFF09",
22
+ valueHint: "path"
23
+ };
24
+ var subCommands = {
25
+ fetch: defineCommand({
26
+ meta: { name: "fetch", description: "\u4ECE\u540E\u7AEF\u62C9\u53D6 OpenAPI \u89C4\u8303\u5E76\u4FDD\u5B58\u4E3A JSON" },
27
+ args: { config: configArg },
28
+ async run({ args }) {
29
+ await runFetch({ config: args.config });
30
+ }
31
+ }),
32
+ generate: defineCommand({
33
+ meta: { name: "generate", description: "\u6839\u636E\u89C4\u8303\u751F\u6210 TypeScript \u7C7B\u578B\u4E0E\u8BF7\u6C42\u4EE3\u7801" },
34
+ args: { config: configArg },
35
+ async run({ args }) {
36
+ await runGenerate({ config: args.config });
37
+ }
38
+ }),
39
+ init: defineCommand({
40
+ meta: { name: "init", description: "\u751F\u6210\u9ED8\u8BA4\u7684 oas-codegen.config.ts" },
41
+ args: {
42
+ force: { type: "boolean", description: "\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u914D\u7F6E\u6587\u4EF6" }
43
+ },
44
+ run({ args }) {
45
+ runInit({ force: args.force });
46
+ }
47
+ })
48
+ };
49
+ var main = defineCommand({
50
+ meta: {
51
+ name: "oas-codegen",
52
+ version: readVersion(),
53
+ description: "OpenAPI \u2192 TypeScript \u5BA2\u6237\u7AEF\u4EE3\u7801\u751F\u6210\uFF08\u5C01\u88C5 orval \u7684\u914D\u65B9\u5316 CLI\uFF09"
54
+ },
55
+ subCommands,
56
+ async run(ctx) {
57
+ const first = ctx.rawArgs.find((arg) => !arg.startsWith("-"));
58
+ if (first && first in subCommands) return;
59
+ await showUsage(ctx.cmd);
60
+ process.exit(1);
61
+ }
62
+ });
63
+ runMain(main);
64
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync } from 'node:fs'\nimport { defineCommand, runMain, showUsage } from 'citty'\nimport { runFetch } from './commands/fetch.js'\nimport { runGenerate } from './commands/generate.js'\nimport { runInit } from './commands/init.js'\n\n/** 读取包根 package.json 的版本号(src/ 与 dist/ 上移一级都是包根)。 */\nfunction readVersion(): string | undefined {\n try {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))\n return typeof pkg.version === 'string' ? pkg.version : undefined\n } catch {\n return undefined\n }\n}\n\nconst configArg = {\n type: 'string' as const,\n description: '配置文件路径(默认 ./oas-codegen.config.ts)',\n valueHint: 'path',\n}\n\nconst subCommands = {\n fetch: defineCommand({\n meta: { name: 'fetch', description: '从后端拉取 OpenAPI 规范并保存为 JSON' },\n args: { config: configArg },\n async run({ args }) {\n await runFetch({ config: args.config })\n },\n }),\n generate: defineCommand({\n meta: { name: 'generate', description: '根据规范生成 TypeScript 类型与请求代码' },\n args: { config: configArg },\n async run({ args }) {\n await runGenerate({ config: args.config })\n },\n }),\n init: defineCommand({\n meta: { name: 'init', description: '生成默认的 oas-codegen.config.ts' },\n args: {\n force: { type: 'boolean' as const, description: '覆盖已存在的配置文件' },\n },\n run({ args }) {\n runInit({ force: args.force })\n },\n }),\n}\n\nconst main = defineCommand({\n meta: {\n name: 'oas-codegen',\n version: readVersion(),\n description: 'OpenAPI → TypeScript 客户端代码生成(封装 orval 的配方化 CLI)',\n },\n subCommands,\n async run(ctx) {\n // citty 在子命令执行完毕后仍会调用主命令的 run,所以这里只在「没跑任何子命令」时打印帮助\n const first = ctx.rawArgs.find((arg) => !arg.startsWith('-'))\n if (first && first in subCommands) return\n await showUsage(ctx.cmd)\n process.exit(1)\n },\n})\n\nrunMain(main)\n"],"mappings":";;;;;;;;AACA,SAAS,oBAAoB;AAC7B,SAAS,eAAe,SAAS,iBAAiB;AAMlD,SAAS,cAAkC;AACzC,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,aAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM,CAAC;AACxF,WAAO,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;AAAA,EACzD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAM,YAAY;AAAA,EAChB,MAAM;AAAA,EACN,aAAa;AAAA,EACb,WAAW;AACb;AAEA,IAAM,cAAc;AAAA,EAClB,OAAO,cAAc;AAAA,IACnB,MAAM,EAAE,MAAM,SAAS,aAAa,mFAA4B;AAAA,IAChE,MAAM,EAAE,QAAQ,UAAU;AAAA,IAC1B,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,YAAM,SAAS,EAAE,QAAQ,KAAK,OAAO,CAAC;AAAA,IACxC;AAAA,EACF,CAAC;AAAA,EACD,UAAU,cAAc;AAAA,IACtB,MAAM,EAAE,MAAM,YAAY,aAAa,6FAA4B;AAAA,IACnE,MAAM,EAAE,QAAQ,UAAU;AAAA,IAC1B,MAAM,IAAI,EAAE,KAAK,GAAG;AAClB,YAAM,YAAY,EAAE,QAAQ,KAAK,OAAO,CAAC;AAAA,IAC3C;AAAA,EACF,CAAC;AAAA,EACD,MAAM,cAAc;AAAA,IAClB,MAAM,EAAE,MAAM,QAAQ,aAAa,uDAA8B;AAAA,IACjE,MAAM;AAAA,MACJ,OAAO,EAAE,MAAM,WAAoB,aAAa,+DAAa;AAAA,IAC/D;AAAA,IACA,IAAI,EAAE,KAAK,GAAG;AACZ,cAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;AAAA,IAC/B;AAAA,EACF,CAAC;AACH;AAEA,IAAM,OAAO,cAAc;AAAA,EACzB,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS,YAAY;AAAA,IACrB,aAAa;AAAA,EACf;AAAA,EACA;AAAA,EACA,MAAM,IAAI,KAAK;AAEb,UAAM,QAAQ,IAAI,QAAQ,KAAK,CAAC,QAAQ,CAAC,IAAI,WAAW,GAAG,CAAC;AAC5D,QAAI,SAAS,SAAS,YAAa;AACnC,UAAM,UAAU,IAAI,GAAG;AACvB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAED,QAAQ,IAAI;","names":[]}
@@ -0,0 +1,174 @@
1
+ import { z } from 'zod';
2
+
3
+ /** 默认 mutator:生成的调用统一走项目自己的 axios 封装。 */
4
+ declare const DEFAULT_MUTATOR: {
5
+ readonly name: "customInstance";
6
+ readonly path: "./src/api/customInstance.ts";
7
+ };
8
+ /** 未配置 generate 时的默认配方(daily-task 的 orval 约定)。 */
9
+ declare const DEFAULT_GENERATE: {
10
+ readonly target: "src/api/generated/endpoints.ts";
11
+ readonly schemas: "src/api/generated/models";
12
+ readonly client: "vue-query";
13
+ };
14
+ /**
15
+ * 未配置 source 时的默认值。刻意等价于 daily-task 旧 export-api.mjs 的行为,
16
+ * 这样「什么都不写」就是历史行为,迁移零成本。
17
+ */
18
+ declare const DEFAULT_SOURCE: {
19
+ readonly type: "springdoc";
20
+ readonly specPath: "/v3/api-docs";
21
+ readonly envBaseUrl: "API_BASE_URL";
22
+ readonly output: "specs/openapi.json";
23
+ };
24
+ /** spec 来源配置:从哪拿 OpenAPI 规范。 */
25
+ declare const sourceSchema: z.ZodObject<{
26
+ type: z.ZodDefault<z.ZodEnum<{
27
+ springdoc: "springdoc";
28
+ url: "url";
29
+ file: "file";
30
+ }>>;
31
+ baseUrl: z.ZodOptional<z.ZodString>;
32
+ specPath: z.ZodDefault<z.ZodString>;
33
+ url: z.ZodOptional<z.ZodString>;
34
+ envFile: z.ZodOptional<z.ZodString>;
35
+ envBaseUrl: z.ZodDefault<z.ZodString>;
36
+ output: z.ZodDefault<z.ZodString>;
37
+ }, z.core.$strip>;
38
+ /** 生成配置:spec → TypeScript 客户端代码的产出约定。 */
39
+ declare const generateSchema: z.ZodObject<{
40
+ input: z.ZodOptional<z.ZodString>;
41
+ target: z.ZodDefault<z.ZodString>;
42
+ schemas: z.ZodDefault<z.ZodString>;
43
+ client: z.ZodDefault<z.ZodEnum<{
44
+ fetch: "fetch";
45
+ "vue-query": "vue-query";
46
+ "react-query": "react-query";
47
+ axios: "axios";
48
+ }>>;
49
+ mutator: z.ZodOptional<z.ZodObject<{
50
+ name: z.ZodDefault<z.ZodString>;
51
+ path: z.ZodDefault<z.ZodString>;
52
+ }, z.core.$strip>>;
53
+ queryVersion: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<4>, z.ZodLiteral<5>]>>;
54
+ }, z.core.$strip>;
55
+ type SourceConfig = z.infer<typeof sourceSchema>;
56
+ type SourceConfigInput = z.input<typeof sourceSchema>;
57
+ type GenerateConfig = z.infer<typeof generateSchema>;
58
+ type GenerateConfigInput = z.input<typeof generateSchema>;
59
+ declare const configSchema: z.ZodObject<{
60
+ source: z.ZodDefault<z.ZodObject<{
61
+ type: z.ZodDefault<z.ZodEnum<{
62
+ springdoc: "springdoc";
63
+ url: "url";
64
+ file: "file";
65
+ }>>;
66
+ baseUrl: z.ZodOptional<z.ZodString>;
67
+ specPath: z.ZodDefault<z.ZodString>;
68
+ url: z.ZodOptional<z.ZodString>;
69
+ envFile: z.ZodOptional<z.ZodString>;
70
+ envBaseUrl: z.ZodDefault<z.ZodString>;
71
+ output: z.ZodDefault<z.ZodString>;
72
+ }, z.core.$strip>>;
73
+ generate: z.ZodDefault<z.ZodObject<{
74
+ input: z.ZodOptional<z.ZodString>;
75
+ target: z.ZodDefault<z.ZodString>;
76
+ schemas: z.ZodDefault<z.ZodString>;
77
+ client: z.ZodDefault<z.ZodEnum<{
78
+ fetch: "fetch";
79
+ "vue-query": "vue-query";
80
+ "react-query": "react-query";
81
+ axios: "axios";
82
+ }>>;
83
+ mutator: z.ZodOptional<z.ZodObject<{
84
+ name: z.ZodDefault<z.ZodString>;
85
+ path: z.ZodDefault<z.ZodString>;
86
+ }, z.core.$strip>>;
87
+ queryVersion: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<4>, z.ZodLiteral<5>]>>;
88
+ }, z.core.$strip>>;
89
+ }, z.core.$strip>;
90
+ type OasConfig = z.infer<typeof configSchema>;
91
+
92
+ /**
93
+ * 类型安全的配置助手,与 orval 的 defineConfig 用法一致。
94
+ * 支持直接传对象,或传一个返回配置的函数(便于按环境派生)。
95
+ */
96
+ declare function defineConfig(config: OasConfig | (() => OasConfig)): OasConfig | (() => OasConfig);
97
+
98
+ declare const DEFAULT_CONFIG_FILE = "oas-codegen.config.ts";
99
+ /**
100
+ * 加载 oas-codegen 配置文件,返回「生效配置」(默认值已填充)。
101
+ * 未找到配置文件时返回全默认配置,即兼容旧 export-api.mjs + orval.config.ts 的行为。
102
+ * 支持 .ts / .mts / .cts / .js / .mjs / .cjs / .json。
103
+ */
104
+ declare function loadConfig(cwd: string, configFile?: string): Promise<OasConfig>;
105
+
106
+ /** 从指定 URL 拉取 OpenAPI 规范并解析为 JSON。 */
107
+ declare function fetchSpec(url: string): Promise<unknown>;
108
+ /** 将规范以 2 空格缩进的 JSON 写入磁盘(自动创建目录)。 */
109
+ declare function writeSpec(spec: unknown, output: string): void;
110
+ /** 读取本地 OpenAPI 规范文件并解析为 JSON(file 类型使用,不重写文件)。 */
111
+ declare function readLocalSpec(file: string): unknown;
112
+
113
+ interface ResolvedSource {
114
+ /** 最终请求的 spec 地址;file 类型为 undefined */
115
+ url: string | undefined;
116
+ /** 保存 spec 的绝对路径 */
117
+ output: string;
118
+ }
119
+ /** 加载 envFile(如 .env.local)中的变量到 process.env,缺省静默跳过。 */
120
+ declare function loadEnvFile(envFile: string | undefined, cwd: string): void;
121
+ /** 将配置的 source 解析成具体的请求地址与输出路径(处理环境变量优先级与默认值)。 */
122
+ declare function resolveSource(source: SourceConfig, cwd: string): ResolvedSource;
123
+
124
+ /** orval 配置里的统一项目名。 */
125
+ declare const ORVAL_PROJECT = "generated";
126
+ /**
127
+ * 解析生效的 mutator:显式配置优先;缺省时仅默认配方(vue-query)注入 customInstance。
128
+ * react-query / axios / fetch 不自动绑 mutator,避免凭空要求 customInstance 文件存在。
129
+ */
130
+ declare function resolveMutator(g: GenerateConfig): {
131
+ name: string;
132
+ path: string;
133
+ } | undefined;
134
+ /** 将 oas-codegen 配置渲染成 orval 可执行的配置对象。 */
135
+ declare function renderOrvalConfig(config: OasConfig): Record<string, unknown>;
136
+ /**
137
+ * 解析包内置 orval 的可执行入口(读取依赖包 package.json 的 bin 字段)。
138
+ * 不依赖 exports map,向上遍历目录定位 orval 包根。
139
+ */
140
+ declare function resolveOrvalBin(): string;
141
+ /** 以指定 cwd 运行 orval,stdio 直接透传,失败时 reject。 */
142
+ declare function runOrval(args: string[], cwd: string): Promise<void>;
143
+
144
+ interface FetchOptions {
145
+ config?: string;
146
+ cwd?: string;
147
+ /** 静默模式(供编程调用时使用),默认打印过程日志 */
148
+ silent?: boolean;
149
+ }
150
+ interface FetchResult {
151
+ url: string | undefined;
152
+ output: string;
153
+ spec: unknown;
154
+ }
155
+ /** 获取 OpenAPI 规范:springdoc/url 从网络拉取并落盘,file 类型读取本地文件校验。 */
156
+ declare function runFetch(opts?: FetchOptions): Promise<FetchResult>;
157
+
158
+ interface GenerateOptions {
159
+ config?: string;
160
+ cwd?: string;
161
+ }
162
+ /** 根据本地 spec 生成 TypeScript 客户端代码。 */
163
+ declare function runGenerate(opts?: GenerateOptions): Promise<void>;
164
+
165
+ interface InitOptions {
166
+ cwd?: string;
167
+ force?: boolean;
168
+ }
169
+ /** 默认的 oas-codegen.config.ts 模板(与 daily-task 现行约定一致)。 */
170
+ declare const CONFIG_TEMPLATE = "import { defineConfig } from 'oas-codegen'\n\nexport default defineConfig({\n source: {\n type: 'springdoc',\n // \u540E\u7AEF\u5730\u5740\uFF1A\u4F18\u5148\u8BFB\u73AF\u5883\u53D8\u91CF API_BASE_URL\uFF0C\u672A\u8BBE\u7F6E\u65F6\u7528 baseUrl\n baseUrl: 'http://localhost:8082',\n specPath: '/v3/api-docs',\n envFile: '.env.local',\n output: 'specs/openapi.json',\n },\n generate: {\n target: 'src/api/generated/endpoints.ts',\n schemas: 'src/api/generated/models',\n client: 'vue-query',\n // \u663E\u5F0F\u9501\u5B9A TanStack Query \u5927\u7248\u672C\uFF084 \u6216 5\uFF09\uFF1B\u4E0D\u5199\u5219\u6309\u9879\u76EE\u4F9D\u8D56\u81EA\u52A8\u63A2\u6D4B\n // queryVersion: 5,\n // mutator \u9700\u4E3A (url, config) \u53CC\u53C2\u7EA6\u5B9A\uFF08orval 8 \u6807\u51C6\uFF09\uFF0C\u5B57\u6BB5\u89C1 README\n mutator: {\n name: 'customInstance',\n path: './src/api/customInstance.ts',\n },\n },\n})\n";
171
+ /** 在目标项目根目录生成配置文件。 */
172
+ declare function runInit(opts?: InitOptions): void;
173
+
174
+ export { CONFIG_TEMPLATE, DEFAULT_CONFIG_FILE, DEFAULT_GENERATE, DEFAULT_MUTATOR, DEFAULT_SOURCE, type GenerateConfig, type GenerateConfigInput, ORVAL_PROJECT, type OasConfig, type SourceConfig, type SourceConfigInput, configSchema, defineConfig, fetchSpec, generateSchema, loadConfig, loadEnvFile, readLocalSpec, renderOrvalConfig, resolveMutator, resolveOrvalBin, resolveSource, runFetch, runGenerate, runInit, runOrval, sourceSchema, writeSpec };
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ import {
2
+ CONFIG_TEMPLATE,
3
+ DEFAULT_CONFIG_FILE,
4
+ DEFAULT_GENERATE,
5
+ DEFAULT_MUTATOR,
6
+ DEFAULT_SOURCE,
7
+ ORVAL_PROJECT,
8
+ configSchema,
9
+ fetchSpec,
10
+ generateSchema,
11
+ loadConfig,
12
+ loadEnvFile,
13
+ readLocalSpec,
14
+ renderOrvalConfig,
15
+ resolveMutator,
16
+ resolveOrvalBin,
17
+ resolveSource,
18
+ runFetch,
19
+ runGenerate,
20
+ runInit,
21
+ runOrval,
22
+ sourceSchema,
23
+ writeSpec
24
+ } from "./chunk-AWDG5OSK.js";
25
+
26
+ // src/config/define.ts
27
+ function defineConfig(config) {
28
+ return config;
29
+ }
30
+ export {
31
+ CONFIG_TEMPLATE,
32
+ DEFAULT_CONFIG_FILE,
33
+ DEFAULT_GENERATE,
34
+ DEFAULT_MUTATOR,
35
+ DEFAULT_SOURCE,
36
+ ORVAL_PROJECT,
37
+ configSchema,
38
+ defineConfig,
39
+ fetchSpec,
40
+ generateSchema,
41
+ loadConfig,
42
+ loadEnvFile,
43
+ readLocalSpec,
44
+ renderOrvalConfig,
45
+ resolveMutator,
46
+ resolveOrvalBin,
47
+ resolveSource,
48
+ runFetch,
49
+ runGenerate,
50
+ runInit,
51
+ runOrval,
52
+ sourceSchema,
53
+ writeSpec
54
+ };
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/define.ts"],"sourcesContent":["import type { OasConfig } from './schema.js'\n\n/**\n * 类型安全的配置助手,与 orval 的 defineConfig 用法一致。\n * 支持直接传对象,或传一个返回配置的函数(便于按环境派生)。\n */\nexport function defineConfig(config: OasConfig | (() => OasConfig)): OasConfig | (() => OasConfig) {\n return config\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAMO,SAAS,aAAa,QAAsE;AACjG,SAAO;AACT;","names":[]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "oas-codegen",
3
+ "version": "0.1.0",
4
+ "description": "Opinionated OpenAPI → TypeScript + TanStack Query codegen, built on top of orval. Spec fetching + recipe-based generate pipeline.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "wuchao",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://gitee.com/big-bear-frontend/oas-codegen.git"
11
+ },
12
+ "engines": {
13
+ "node": ">=18.0.0"
14
+ },
15
+ "bin": {
16
+ "oas-codegen": "./dist/cli.js"
17
+ },
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "scripts": {
32
+ "dev": "tsx src/cli.ts",
33
+ "build": "tsup",
34
+ "test": "vitest run",
35
+ "typecheck": "tsc --noEmit",
36
+ "prepublishOnly": "pnpm typecheck && pnpm test && pnpm build"
37
+ },
38
+ "dependencies": {
39
+ "citty": "^0.2.2",
40
+ "dotenv": "^17.4.2",
41
+ "jiti": "^2.7.0",
42
+ "orval": "^8.33.0",
43
+ "zod": "^4.6.5"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^26.5.1",
47
+ "tsup": "^8.5.1",
48
+ "tsx": "^4.23.13",
49
+ "typescript": "^5.9.3",
50
+ "vitest": "^5.0.0"
51
+ }
52
+ }