+5
.changeset/short-phones-count.md
+5
.changeset/short-phones-count.md
+4
-4
.github/copilot-instructions.md
+4
-4
.github/copilot-instructions.md
···
49
49
# Server starts on http://localhost:5173/
50
50
51
51
# Run CLI tool directly
52
-
node packages/openapi-ts/bin/index.cjs --help
52
+
node packages/openapi-ts/dist/run.js --help
53
53
# or after building
54
54
npx @hey-api/openapi-ts --help
55
55
```
···
85
85
86
86
```bash
87
87
# Test CLI help
88
-
node packages/openapi-ts/bin/index.cjs --help
88
+
node packages/openapi-ts/dist/run.js --help
89
89
90
90
# Test CLI version
91
-
node packages/openapi-ts/bin/index.cjs --version
91
+
node packages/openapi-ts/dist/run.js --version
92
92
93
93
# Test basic code generation with a simple OpenAPI spec
94
94
# Create a minimal test spec and generate client code
95
-
node packages/openapi-ts/bin/index.cjs -i path/to/spec.json -o ./test-output --plugins "@hey-api/client-fetch" "@hey-api/typescript"
95
+
node packages/openapi-ts/dist/run.js -i path/to/spec.json -o ./test-output --plugins "@hey-api/client-fetch" "@hey-api/typescript"
96
96
```
97
97
98
98
2. **Example Application Test**:
+4
-4
.github/workflows/ci.yml
+4
-4
.github/workflows/ci.yml
···
37
37
- name: Build packages
38
38
run: pnpm build --filter="@hey-api/**"
39
39
40
-
- name: Build examples
41
-
if: matrix.node-version == '24.10.0' && matrix.os == 'ubuntu-latest'
42
-
run: pnpm build --filter="@example/**"
43
-
44
40
- name: Check examples generated code
45
41
if: matrix.node-version == '24.10.0' && matrix.os == 'ubuntu-latest'
46
42
run: pnpm examples:check
43
+
44
+
- name: Build examples
45
+
if: matrix.node-version == '24.10.0' && matrix.os == 'ubuntu-latest'
46
+
run: pnpm build --filter="@example/**"
47
47
48
48
- name: Run linter
49
49
run: pnpm lint
+1
.gitignore
+1
.gitignore
+3
-6
.vscode/launch.json
+3
-6
.vscode/launch.json
···
13
13
"request": "launch",
14
14
"name": "openapi-ts",
15
15
"skipFiles": ["<node_internals>/**"],
16
+
"cwd": "${workspaceFolder}/dev",
16
17
"runtimeExecutable": "node",
17
-
"runtimeArgs": [],
18
-
"program": "${workspaceFolder}/packages/openapi-ts/bin/index.cjs",
19
-
"args": [
20
-
"-f",
21
-
"${workspaceFolder}/packages/openapi-ts-tests/main/test/openapi-ts.config.ts"
22
-
]
18
+
"runtimeArgs": ["-r", "ts-node/register/transpile-only"],
19
+
"program": "${workspaceFolder}/packages/openapi-ts/src/cli.ts"
23
20
}
24
21
]
25
22
}
debug-helpers/graph-hotspots.js
dev/graph-hotspots.js
debug-helpers/graph-hotspots.js
dev/graph-hotspots.js
debug-helpers/json-to-dot.js
dev/json-to-dot.js
debug-helpers/json-to-dot.js
dev/json-to-dot.js
+2
package.json
+2
package.json
···
48
48
"@config/vite-base": "workspace:*",
49
49
"@eslint/js": "9.32.0",
50
50
"@hey-api/custom-client": "workspace:*",
51
+
"@hey-api/openapi-ts": "workspace:*",
51
52
"@types/node": "22.10.5",
52
53
"@typescript-eslint/eslint-plugin": "8.29.1",
53
54
"@vitest/coverage-v8": "3.1.1",
···
65
66
"prettier": "3.4.2",
66
67
"rollup": "4.31.0",
67
68
"rollup-plugin-dts": "6.1.1",
69
+
"ts-node": "10.9.2",
68
70
"tsdown": "0.15.8",
69
71
"turbo": "2.5.8",
70
72
"typescript": "5.9.3",
+1
packages/openapi-ts-tests/main/.gitignore
+1
packages/openapi-ts-tests/main/.gitignore
-368
packages/openapi-ts-tests/main/test/bin.test.ts
-368
packages/openapi-ts-tests/main/test/bin.test.ts
···
1
-
import path from 'node:path';
2
-
3
-
import { sync } from 'cross-spawn';
4
-
import { describe, expect, it } from 'vitest';
5
-
6
-
import { getSpecsPath } from '../../utils';
7
-
8
-
describe('bin', () => {
9
-
it('supports required parameters', () => {
10
-
const result = sync('node', [
11
-
path.resolve(
12
-
__dirname,
13
-
'..',
14
-
'..',
15
-
'..',
16
-
'openapi-ts',
17
-
'bin',
18
-
'index.cjs',
19
-
),
20
-
'--input',
21
-
path.resolve(getSpecsPath(), 'v3.json'),
22
-
'--output',
23
-
path.resolve(__dirname, 'generated', 'bin'),
24
-
'--client',
25
-
'@hey-api/client-fetch',
26
-
'--dry-run',
27
-
'true',
28
-
]);
29
-
expect(result.error).toBeFalsy();
30
-
expect(result.status).toBe(0);
31
-
});
32
-
33
-
it('generates angular client', () => {
34
-
const result = sync('node', [
35
-
path.resolve(
36
-
__dirname,
37
-
'..',
38
-
'..',
39
-
'..',
40
-
'openapi-ts',
41
-
'bin',
42
-
'index.cjs',
43
-
),
44
-
'--input',
45
-
path.resolve(getSpecsPath(), 'v3.json'),
46
-
'--output',
47
-
path.resolve(__dirname, 'generated', 'bin'),
48
-
'--client',
49
-
'legacy/angular',
50
-
'--dry-run',
51
-
'true',
52
-
]);
53
-
expect(result.error).toBeFalsy();
54
-
expect(result.status).toBe(0);
55
-
});
56
-
57
-
it('generates axios client', () => {
58
-
const result = sync('node', [
59
-
path.resolve(
60
-
__dirname,
61
-
'..',
62
-
'..',
63
-
'..',
64
-
'openapi-ts',
65
-
'bin',
66
-
'index.cjs',
67
-
),
68
-
'--input',
69
-
path.resolve(getSpecsPath(), 'v3.json'),
70
-
'--output',
71
-
path.resolve(__dirname, 'generated', 'bin'),
72
-
'--client',
73
-
'legacy/axios',
74
-
'--dry-run',
75
-
'true',
76
-
]);
77
-
expect(result.error).toBeFalsy();
78
-
expect(result.status).toBe(0);
79
-
});
80
-
81
-
it('generates fetch client', () => {
82
-
const result = sync('node', [
83
-
path.resolve(
84
-
__dirname,
85
-
'..',
86
-
'..',
87
-
'..',
88
-
'openapi-ts',
89
-
'bin',
90
-
'index.cjs',
91
-
),
92
-
'--input',
93
-
path.resolve(getSpecsPath(), 'v3.json'),
94
-
'--output',
95
-
path.resolve(__dirname, 'generated', 'bin'),
96
-
'--client',
97
-
'legacy/fetch',
98
-
'--dry-run',
99
-
'true',
100
-
]);
101
-
expect(result.error).toBeFalsy();
102
-
expect(result.status).toBe(0);
103
-
});
104
-
105
-
it('generates node client', () => {
106
-
const result = sync('node', [
107
-
path.resolve(
108
-
__dirname,
109
-
'..',
110
-
'..',
111
-
'..',
112
-
'openapi-ts',
113
-
'bin',
114
-
'index.cjs',
115
-
),
116
-
'--input',
117
-
path.resolve(getSpecsPath(), 'v3.json'),
118
-
'--output',
119
-
path.resolve(__dirname, 'generated', 'bin'),
120
-
'--client',
121
-
'legacy/node',
122
-
'--dry-run',
123
-
'true',
124
-
]);
125
-
expect(result.error).toBeFalsy();
126
-
expect(result.status).toBe(0);
127
-
});
128
-
129
-
it('generates xhr client', () => {
130
-
const result = sync('node', [
131
-
path.resolve(
132
-
__dirname,
133
-
'..',
134
-
'..',
135
-
'..',
136
-
'openapi-ts',
137
-
'bin',
138
-
'index.cjs',
139
-
),
140
-
'--input',
141
-
path.resolve(getSpecsPath(), 'v3.json'),
142
-
'--output',
143
-
path.resolve(__dirname, 'generated', 'bin'),
144
-
'--client',
145
-
'legacy/xhr',
146
-
'--dry-run',
147
-
'true',
148
-
]);
149
-
expect(result.error).toBeFalsy();
150
-
expect(result.status).toBe(0);
151
-
});
152
-
153
-
it('supports all parameters', () => {
154
-
const result = sync('node', [
155
-
path.resolve(
156
-
__dirname,
157
-
'..',
158
-
'..',
159
-
'..',
160
-
'openapi-ts',
161
-
'bin',
162
-
'index.cjs',
163
-
),
164
-
'--input',
165
-
path.resolve(getSpecsPath(), 'v3.json'),
166
-
'--output',
167
-
path.resolve(__dirname, 'generated', 'bin'),
168
-
'--client',
169
-
'legacy/fetch',
170
-
'--useOptions',
171
-
'--exportCore',
172
-
'true',
173
-
'--plugins',
174
-
'@hey-api/schemas',
175
-
'@hey-api/sdk',
176
-
'@hey-api/typescript',
177
-
'--dry-run',
178
-
'true',
179
-
]);
180
-
expect(result.error).toBeFalsy();
181
-
expect(result.status).toBe(0);
182
-
});
183
-
184
-
it('throws error without input', () => {
185
-
const result = sync('node', [
186
-
path.resolve(
187
-
__dirname,
188
-
'..',
189
-
'..',
190
-
'..',
191
-
'openapi-ts',
192
-
'bin',
193
-
'index.cjs',
194
-
),
195
-
'--dry-run',
196
-
'true',
197
-
]);
198
-
expect(result.stderr.toString()).toContain('missing input');
199
-
});
200
-
201
-
it('throws error without output', () => {
202
-
const result = sync('node', [
203
-
path.resolve(
204
-
__dirname,
205
-
'..',
206
-
'..',
207
-
'..',
208
-
'openapi-ts',
209
-
'bin',
210
-
'index.cjs',
211
-
),
212
-
'--input',
213
-
path.resolve(getSpecsPath(), 'v3.json'),
214
-
'--dry-run',
215
-
'true',
216
-
]);
217
-
expect(result.stderr.toString()).toContain('missing output');
218
-
});
219
-
220
-
it('throws error with wrong parameters', () => {
221
-
const result = sync('node', [
222
-
path.resolve(
223
-
__dirname,
224
-
'..',
225
-
'..',
226
-
'..',
227
-
'openapi-ts',
228
-
'bin',
229
-
'index.cjs',
230
-
),
231
-
'--input',
232
-
path.resolve(getSpecsPath(), 'v3.json'),
233
-
'--output',
234
-
path.resolve(__dirname, 'generated', 'bin'),
235
-
'--unknown',
236
-
'--dry-run',
237
-
'true',
238
-
]);
239
-
expect(result.stderr.toString()).toContain(
240
-
`error: unknown option '--unknown'`,
241
-
);
242
-
});
243
-
244
-
it('displays help', () => {
245
-
const result = sync('node', [
246
-
path.resolve(
247
-
__dirname,
248
-
'..',
249
-
'..',
250
-
'..',
251
-
'openapi-ts',
252
-
'bin',
253
-
'index.cjs',
254
-
),
255
-
'--help',
256
-
'--dry-run',
257
-
'true',
258
-
]);
259
-
expect(result.stdout.toString()).toContain(`Usage: openapi-ts [options]`);
260
-
expect(result.stdout.toString()).toContain(`-i, --input <value>`);
261
-
expect(result.stdout.toString()).toContain(`-o, --output <value>`);
262
-
expect(result.stderr.toString()).toBe('');
263
-
});
264
-
});
265
-
266
-
describe('cli', () => {
267
-
it('handles false booleans', () => {
268
-
const result = sync('node', [
269
-
path.resolve(
270
-
__dirname,
271
-
'..',
272
-
'..',
273
-
'..',
274
-
'openapi-ts',
275
-
'bin',
276
-
'index.cjs',
277
-
),
278
-
'--input',
279
-
path.resolve(getSpecsPath(), 'v3.json'),
280
-
'--output',
281
-
path.resolve(__dirname, 'generated', 'bin'),
282
-
'--debug',
283
-
'--plugins',
284
-
'--useOptions',
285
-
'false',
286
-
'--dry-run',
287
-
'true',
288
-
]);
289
-
const stderr = result.stderr.toString();
290
-
expect(stderr).toMatch(/level: 'debug'/);
291
-
expect(stderr).toMatch(/dryRun: true/);
292
-
expect(stderr).not.toMatch(/@hey-api\/schemas/);
293
-
expect(stderr).not.toMatch(/@hey-api\/sdk/);
294
-
expect(stderr).not.toMatch(/@hey-api\/typescript/);
295
-
expect(stderr).toMatch(/useOptions: false/);
296
-
});
297
-
298
-
it('handles true booleans', () => {
299
-
const result = sync('node', [
300
-
path.resolve(
301
-
__dirname,
302
-
'..',
303
-
'..',
304
-
'..',
305
-
'openapi-ts',
306
-
'bin',
307
-
'index.cjs',
308
-
),
309
-
'--input',
310
-
path.resolve(getSpecsPath(), 'v3.json'),
311
-
'--output',
312
-
path.resolve(__dirname, 'generated', 'bin'),
313
-
'--client',
314
-
'@hey-api/client-fetch',
315
-
'--debug',
316
-
'--plugins',
317
-
'@hey-api/schemas',
318
-
'@hey-api/sdk',
319
-
'@hey-api/typescript',
320
-
'--useOptions',
321
-
'true',
322
-
'--dry-run',
323
-
'true',
324
-
]);
325
-
const stderr = result.stderr.toString();
326
-
expect(stderr).toMatch(/level: 'debug'/);
327
-
expect(stderr).toMatch(/dryRun: true/);
328
-
expect(stderr).toMatch(/@hey-api\/schemas/);
329
-
expect(stderr).toMatch(/@hey-api\/sdk/);
330
-
expect(stderr).toMatch(/@hey-api\/typescript/);
331
-
expect(stderr).toMatch(/useOptions: true/);
332
-
});
333
-
334
-
it('handles optional booleans', () => {
335
-
const result = sync('node', [
336
-
path.resolve(
337
-
__dirname,
338
-
'..',
339
-
'..',
340
-
'..',
341
-
'openapi-ts',
342
-
'bin',
343
-
'index.cjs',
344
-
),
345
-
'--input',
346
-
path.resolve(getSpecsPath(), 'v3.json'),
347
-
'--output',
348
-
path.resolve(__dirname, 'generated', 'bin'),
349
-
'--client',
350
-
'@hey-api/client-fetch',
351
-
'--debug',
352
-
'--plugins',
353
-
'@hey-api/schemas',
354
-
'@hey-api/sdk',
355
-
'@hey-api/typescript',
356
-
'--useOptions',
357
-
'--dry-run',
358
-
'true',
359
-
]);
360
-
const stderr = result.stderr.toString();
361
-
expect(stderr).toMatch(/level: 'debug'/);
362
-
expect(stderr).toMatch(/dryRun: true/);
363
-
expect(stderr).toMatch(/@hey-api\/schemas/);
364
-
expect(stderr).toMatch(/@hey-api\/sdk/);
365
-
expect(stderr).toMatch(/@hey-api\/typescript/);
366
-
expect(stderr).toMatch(/useOptions: true/);
367
-
});
368
-
});
+23
packages/openapi-ts-tests/main/test/cli.test.ts
+23
packages/openapi-ts-tests/main/test/cli.test.ts
···
1
+
import path from 'node:path';
2
+
3
+
import { sync } from 'cross-spawn';
4
+
import { describe, expect, it } from 'vitest';
5
+
6
+
import { getSpecsPath } from '../../utils';
7
+
8
+
const specs = getSpecsPath();
9
+
10
+
describe('bin', () => {
11
+
it('openapi-ts works', () => {
12
+
const result = sync('openapi-ts', [
13
+
'--input',
14
+
path.resolve(specs, 'v3.json'),
15
+
'--output',
16
+
path.resolve(__dirname, '.gen'),
17
+
'--dry-run',
18
+
'true',
19
+
]);
20
+
expect(result.error).toBeFalsy();
21
+
expect(result.status).toBe(0);
22
+
});
23
+
});
packages/openapi-ts-tests/main/test/hey-api.ts
dev/hey-api.ts
packages/openapi-ts-tests/main/test/hey-api.ts
dev/hey-api.ts
+5
-5
packages/openapi-ts-tests/main/test/openapi-ts.config.ts
dev/openapi-ts.config.ts
+5
-5
packages/openapi-ts-tests/main/test/openapi-ts.config.ts
dev/openapi-ts.config.ts
···
6
6
// @ts-ignore
7
7
import { defineConfig, utils } from '@hey-api/openapi-ts';
8
8
9
-
import { getSpecsPath } from '../../utils';
10
9
// @ts-ignore
11
-
import { myClientPlugin } from './custom/client/plugin';
10
+
import { myClientPlugin } from '../packages/openapi-ts-tests/main/test/custom/client/plugin';
11
+
import { getSpecsPath } from '../packages/openapi-ts-tests/utils';
12
12
13
13
// @ts-ignore
14
14
// eslint-disable-next-line arrow-body-style
···
96
96
// importFileExtension: '.ts',
97
97
// // indexFile: false,
98
98
// // lint: 'eslint',
99
-
// path: path.resolve(__dirname, 'generated', 'sample'),
99
+
// path: path.resolve(__dirname, '.gen'),
100
100
// tsConfigPath: path.resolve(
101
101
// __dirname,
102
102
// 'tsconfig',
103
103
// 'tsconfig.nodenext.json',
104
104
// ),
105
105
// },
106
-
path.resolve(__dirname, 'generated', 'sample'),
106
+
'.gen',
107
107
],
108
108
parser: {
109
109
filters: {
···
478
478
// // level: 'debug',
479
479
// path: './logs',
480
480
// },
481
-
// output: path.resolve(__dirname, 'generated', 'sample'),
481
+
// output: '.gen',
482
482
// },
483
483
];
484
484
});
-148
packages/openapi-ts/bin/index.cjs
-148
packages/openapi-ts/bin/index.cjs
···
1
-
#!/usr/bin/env node
2
-
3
-
'use strict';
4
-
5
-
const path = require('path');
6
-
7
-
const { program } = require('commander');
8
-
const pkg = require('../package.json');
9
-
10
-
const params = program
11
-
.name(Object.keys(pkg.bin)[0])
12
-
.usage('[options]')
13
-
.version(pkg.version)
14
-
.option(
15
-
'-c, --client <value>',
16
-
'HTTP client to generate [@hey-api/client-axios, @hey-api/client-fetch, @hey-api/client-next, @hey-api/client-nuxt, legacy/angular, legacy/axios, legacy/fetch, legacy/node, legacy/xhr]',
17
-
)
18
-
.option('-d, --debug', 'Set log level to debug')
19
-
.option('--dry-run [value]', 'Skip writing files to disk?')
20
-
.option(
21
-
'-e, --experimental-parser [value]',
22
-
'Opt-in to the experimental parser?',
23
-
)
24
-
.option('-f, --file [value]', 'Path to the config file')
25
-
.option(
26
-
'-i, --input <value>',
27
-
'OpenAPI specification (path, url, or string content)',
28
-
)
29
-
.option('-l, --logs [value]', 'Logs folder')
30
-
.option('-o, --output <value>', 'Output folder')
31
-
.option('-p, --plugins [value...]', "List of plugins you'd like to use")
32
-
.option(
33
-
'--base [value]',
34
-
'DEPRECATED. Manually set base in OpenAPI config instead of inferring from server value',
35
-
)
36
-
.option('-s, --silent', 'Set log level to silent')
37
-
.option(
38
-
'--no-log-file',
39
-
'Disable writing a log file. Works like --silent but without suppressing console output',
40
-
)
41
-
.option(
42
-
'-w, --watch [value]',
43
-
'Regenerate the client when the input file changes?',
44
-
)
45
-
.option('--exportCore [value]', 'DEPRECATED. Write core files to disk')
46
-
.option('--name <value>', 'DEPRECATED. Custom client class name')
47
-
.option('--request <value>', 'DEPRECATED. Path to custom request file')
48
-
.option(
49
-
'--useOptions [value]',
50
-
'DEPRECATED. Use options instead of arguments?',
51
-
)
52
-
.parse(process.argv)
53
-
.opts();
54
-
55
-
const stringToBoolean = (value) => {
56
-
if (value === 'true') {
57
-
return true;
58
-
}
59
-
if (value === 'false') {
60
-
return false;
61
-
}
62
-
return value;
63
-
};
64
-
65
-
const processParams = (obj, booleanKeys) => {
66
-
for (const key of booleanKeys) {
67
-
const value = obj[key];
68
-
if (typeof value === 'string') {
69
-
const parsedValue = stringToBoolean(value);
70
-
delete obj[key];
71
-
obj[key] = parsedValue;
72
-
}
73
-
}
74
-
if (obj.file) {
75
-
obj.configFile = obj.file;
76
-
}
77
-
return obj;
78
-
};
79
-
80
-
async function start() {
81
-
let userConfig;
82
-
83
-
try {
84
-
const { createClient } = require(
85
-
path.resolve(__dirname, '../dist/index.cjs'),
86
-
);
87
-
88
-
userConfig = processParams(params, [
89
-
'dryRun',
90
-
'logFile',
91
-
'experimentalParser',
92
-
'exportCore',
93
-
'useOptions',
94
-
]);
95
-
96
-
if (params.plugins === true) {
97
-
userConfig.plugins = [];
98
-
} else if (params.plugins) {
99
-
userConfig.plugins = params.plugins;
100
-
} else if (userConfig.client) {
101
-
userConfig.plugins = ['@hey-api/typescript', '@hey-api/sdk'];
102
-
}
103
-
104
-
if (userConfig.client) {
105
-
userConfig.plugins.push(userConfig.client);
106
-
delete userConfig.client;
107
-
}
108
-
109
-
userConfig.logs = userConfig.logs
110
-
? {
111
-
path: userConfig.logs,
112
-
}
113
-
: {};
114
-
115
-
if (userConfig.debug || stringToBoolean(process.env.DEBUG)) {
116
-
userConfig.logs.level = 'debug';
117
-
} else if (userConfig.silent) {
118
-
userConfig.logs.level = 'silent';
119
-
}
120
-
121
-
userConfig.logs.file = userConfig.logFile;
122
-
delete userConfig.logFile;
123
-
124
-
if (typeof params.watch === 'string') {
125
-
userConfig.watch = Number.parseInt(params.watch, 10);
126
-
}
127
-
128
-
if (!Object.keys(userConfig.logs).length) {
129
-
delete userConfig.logs;
130
-
}
131
-
132
-
const context = await createClient(userConfig);
133
-
if (
134
-
!context[0] ||
135
-
!context[0].config ||
136
-
!context[0].config.input ||
137
-
!context[0].config.input.some(
138
-
(input) => input.watch && input.watch.enabled,
139
-
)
140
-
) {
141
-
process.exit(0);
142
-
}
143
-
} catch {
144
-
process.exit(1);
145
-
}
146
-
}
147
-
148
-
start();
+18
packages/openapi-ts/bin/run.js
+18
packages/openapi-ts/bin/run.js
···
1
+
#!/usr/bin/env node
2
+
import { spawnSync } from 'node:child_process';
3
+
import fs from 'node:fs';
4
+
import path from 'node:path';
5
+
import { fileURLToPath } from 'node:url';
6
+
7
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
const target = path.join(__dirname, '..', 'dist', 'run.js');
9
+
10
+
if (!fs.existsSync(target)) {
11
+
console.error('openapi-ts not built (expect dist/run.js)');
12
+
process.exit(1);
13
+
}
14
+
15
+
const res = spawnSync(process.execPath, [target, ...process.argv.slice(2)], {
16
+
stdio: 'inherit',
17
+
});
18
+
process.exit(res.status ?? 0);
+1
-1
packages/openapi-ts/package.json
+1
-1
packages/openapi-ts/package.json
+292
packages/openapi-ts/src/__tests__/cli.test.ts
+292
packages/openapi-ts/src/__tests__/cli.test.ts
···
1
+
import type { Mock } from 'vitest';
2
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+
import { runCli } from '~/cli';
5
+
import { createClient } from '~/index';
6
+
7
+
vi.mock('~/index', () => {
8
+
const result: Awaited<ReturnType<typeof createClient>> = [];
9
+
return {
10
+
createClient: vi.fn().mockResolvedValue(result),
11
+
};
12
+
});
13
+
const spyExit = vi
14
+
.spyOn(process, 'exit')
15
+
.mockImplementation(() => ({}) as never);
16
+
17
+
const spy = createClient as Mock;
18
+
19
+
describe('cli', () => {
20
+
beforeEach(() => {
21
+
spy.mockClear();
22
+
spyExit.mockClear();
23
+
});
24
+
25
+
it('with default options', async () => {
26
+
const originalArgv = process.argv.slice();
27
+
try {
28
+
process.argv = [String(process.argv[0]), String(process.argv[1])];
29
+
await runCli();
30
+
} finally {
31
+
process.argv = originalArgv;
32
+
}
33
+
expect(spy).toHaveBeenCalledWith({
34
+
logs: {
35
+
file: true,
36
+
},
37
+
});
38
+
});
39
+
40
+
it('with minimal options', async () => {
41
+
const originalArgv = process.argv.slice();
42
+
try {
43
+
process.argv = [
44
+
String(process.argv[0]),
45
+
String(process.argv[1]),
46
+
'--input',
47
+
'foo.json',
48
+
'--output',
49
+
'bar',
50
+
];
51
+
await runCli();
52
+
} finally {
53
+
process.argv = originalArgv;
54
+
}
55
+
expect(spy).toHaveBeenCalledWith({
56
+
input: 'foo.json',
57
+
logs: {
58
+
file: true,
59
+
},
60
+
output: 'bar',
61
+
});
62
+
});
63
+
64
+
it('with no plugins', async () => {
65
+
const originalArgv = process.argv.slice();
66
+
try {
67
+
process.argv = [
68
+
String(process.argv[0]),
69
+
String(process.argv[1]),
70
+
'--plugins',
71
+
];
72
+
await runCli();
73
+
} finally {
74
+
process.argv = originalArgv;
75
+
}
76
+
expect(spy).toHaveBeenCalledWith({
77
+
logs: {
78
+
file: true,
79
+
},
80
+
plugins: [],
81
+
});
82
+
});
83
+
84
+
it('with plugins', async () => {
85
+
const originalArgv = process.argv.slice();
86
+
try {
87
+
process.argv = [
88
+
String(process.argv[0]),
89
+
String(process.argv[1]),
90
+
'--plugins',
91
+
'foo',
92
+
];
93
+
await runCli();
94
+
} finally {
95
+
process.argv = originalArgv;
96
+
}
97
+
expect(spy).toHaveBeenCalledWith({
98
+
logs: {
99
+
file: true,
100
+
},
101
+
plugins: ['foo'],
102
+
});
103
+
});
104
+
105
+
it('with default plugins', async () => {
106
+
const originalArgv = process.argv.slice();
107
+
try {
108
+
process.argv = [
109
+
String(process.argv[0]),
110
+
String(process.argv[1]),
111
+
'--client',
112
+
'foo',
113
+
];
114
+
await runCli();
115
+
} finally {
116
+
process.argv = originalArgv;
117
+
}
118
+
expect(spy).toHaveBeenCalledWith({
119
+
logs: {
120
+
file: true,
121
+
},
122
+
plugins: ['@hey-api/typescript', '@hey-api/sdk', 'foo'],
123
+
});
124
+
});
125
+
126
+
describe('logs', () => {
127
+
it('debug', async () => {
128
+
const originalArgv = process.argv.slice();
129
+
try {
130
+
process.argv = [
131
+
String(process.argv[0]),
132
+
String(process.argv[1]),
133
+
'--debug',
134
+
];
135
+
await runCli();
136
+
} finally {
137
+
process.argv = originalArgv;
138
+
}
139
+
expect(spy).toHaveBeenCalledWith({
140
+
logs: {
141
+
file: true,
142
+
level: 'debug',
143
+
},
144
+
});
145
+
});
146
+
147
+
it('silent', async () => {
148
+
const originalArgv = process.argv.slice();
149
+
try {
150
+
process.argv = [
151
+
String(process.argv[0]),
152
+
String(process.argv[1]),
153
+
'--silent',
154
+
];
155
+
await runCli();
156
+
} finally {
157
+
process.argv = originalArgv;
158
+
}
159
+
expect(spy).toHaveBeenCalledWith({
160
+
logs: {
161
+
file: true,
162
+
level: 'silent',
163
+
},
164
+
});
165
+
});
166
+
167
+
it('no log file', async () => {
168
+
const originalArgv = process.argv.slice();
169
+
try {
170
+
process.argv = [
171
+
String(process.argv[0]),
172
+
String(process.argv[1]),
173
+
'--no-log-file',
174
+
];
175
+
await runCli();
176
+
} finally {
177
+
process.argv = originalArgv;
178
+
}
179
+
expect(spy).toHaveBeenCalledWith({
180
+
logs: {
181
+
file: false,
182
+
},
183
+
});
184
+
});
185
+
});
186
+
187
+
it('with all options', async () => {
188
+
const originalArgv = process.argv.slice();
189
+
try {
190
+
process.argv = [
191
+
String(process.argv[0]),
192
+
String(process.argv[1]),
193
+
'--client',
194
+
'foo',
195
+
'--dry-run',
196
+
'true',
197
+
'--experimental-parser',
198
+
'true',
199
+
'--file',
200
+
'bar',
201
+
'--input',
202
+
'baz',
203
+
'--logs',
204
+
'qux',
205
+
'--output',
206
+
'quux',
207
+
'--plugins',
208
+
'--watch',
209
+
'--useOptions',
210
+
'--exportCore',
211
+
];
212
+
await runCli();
213
+
} finally {
214
+
process.argv = originalArgv;
215
+
}
216
+
expect(spy).toHaveBeenCalledWith({
217
+
configFile: 'bar',
218
+
dryRun: true,
219
+
experimentalParser: true,
220
+
exportCore: true,
221
+
input: 'baz',
222
+
logs: {
223
+
file: true,
224
+
path: 'qux',
225
+
},
226
+
output: 'quux',
227
+
plugins: ['foo'],
228
+
useOptions: true,
229
+
watch: true,
230
+
});
231
+
});
232
+
233
+
it('with false booleans', async () => {
234
+
const originalArgv = process.argv.slice();
235
+
try {
236
+
process.argv = [
237
+
String(process.argv[0]),
238
+
String(process.argv[1]),
239
+
'--useOptions',
240
+
'false',
241
+
];
242
+
await runCli();
243
+
} finally {
244
+
process.argv = originalArgv;
245
+
}
246
+
expect(spy).toHaveBeenCalledWith({
247
+
logs: {
248
+
file: true,
249
+
},
250
+
useOptions: false,
251
+
});
252
+
});
253
+
254
+
it('exits when not in watch mode', async () => {
255
+
const originalArgv = process.argv.slice();
256
+
try {
257
+
process.argv = [String(process.argv[0]), String(process.argv[1])];
258
+
await runCli();
259
+
} finally {
260
+
process.argv = originalArgv;
261
+
}
262
+
expect(spyExit).toHaveBeenCalledWith(0);
263
+
});
264
+
265
+
it('does not exit in watch mode', async () => {
266
+
spy.mockResolvedValueOnce([
267
+
{
268
+
config: { input: [{ watch: { enabled: true } }] },
269
+
},
270
+
]);
271
+
const originalArgv = process.argv.slice();
272
+
try {
273
+
process.argv = [String(process.argv[0]), String(process.argv[1])];
274
+
await runCli();
275
+
} finally {
276
+
process.argv = originalArgv;
277
+
}
278
+
expect(spyExit).not.toHaveBeenCalled();
279
+
});
280
+
281
+
it('exits with error code on error', async () => {
282
+
spy.mockRejectedValueOnce('Some error');
283
+
const originalArgv = process.argv.slice();
284
+
try {
285
+
process.argv = [String(process.argv[0]), String(process.argv[1])];
286
+
await runCli();
287
+
} finally {
288
+
process.argv = originalArgv;
289
+
}
290
+
expect(spyExit).toHaveBeenCalledWith(1);
291
+
});
292
+
});
+144
packages/openapi-ts/src/cli.ts
+144
packages/openapi-ts/src/cli.ts
···
1
+
import type { OptionValues } from 'commander';
2
+
import { Command } from 'commander';
3
+
4
+
import { createClient } from '~/index';
5
+
6
+
import pkg from '../package.json' assert { type: 'json' };
7
+
8
+
const stringToBoolean = (
9
+
value: string | undefined,
10
+
): boolean | string | undefined => {
11
+
if (value === 'true') return true;
12
+
if (value === 'false') return false;
13
+
return value;
14
+
};
15
+
16
+
const processParams = (
17
+
obj: OptionValues,
18
+
booleanKeys: ReadonlyArray<string>,
19
+
): OptionValues => {
20
+
for (const key of booleanKeys) {
21
+
const value = obj[key];
22
+
if (typeof value === 'string') {
23
+
const parsedValue = stringToBoolean(value);
24
+
delete obj[key];
25
+
obj[key] = parsedValue;
26
+
}
27
+
}
28
+
return obj;
29
+
};
30
+
31
+
export const runCli = async (): Promise<void> => {
32
+
const params = new Command()
33
+
.name(Object.keys(pkg.bin)[0]!)
34
+
.usage('[options]')
35
+
.version(pkg.version)
36
+
.option(
37
+
'-c, --client <value>',
38
+
'HTTP client to generate [@hey-api/client-axios, @hey-api/client-fetch, @hey-api/client-next, @hey-api/client-nuxt, legacy/angular, legacy/axios, legacy/fetch, legacy/node, legacy/xhr]',
39
+
)
40
+
.option('-d, --debug', 'Set log level to debug')
41
+
.option('--dry-run [value]', 'Skip writing files to disk?')
42
+
.option(
43
+
'-e, --experimental-parser [value]',
44
+
'Opt-in to the experimental parser?',
45
+
)
46
+
.option('-f, --file [value]', 'Path to the config file')
47
+
.option(
48
+
'-i, --input <value>',
49
+
'OpenAPI specification (path, url, or string content)',
50
+
)
51
+
.option('-l, --logs [value]', 'Logs folder')
52
+
.option('-o, --output <value>', 'Output folder')
53
+
.option('-p, --plugins [value...]', "List of plugins you'd like to use")
54
+
.option(
55
+
'--base [value]',
56
+
'DEPRECATED. Manually set base in OpenAPI config instead of inferring from server value',
57
+
)
58
+
.option('-s, --silent', 'Set log level to silent')
59
+
.option(
60
+
'--no-log-file',
61
+
'Disable writing a log file. Works like --silent but without suppressing console output',
62
+
)
63
+
.option(
64
+
'-w, --watch [value]',
65
+
'Regenerate the client when the input file changes?',
66
+
)
67
+
.option('--exportCore [value]', 'DEPRECATED. Write core files to disk')
68
+
.option('--name <value>', 'DEPRECATED. Custom client class name')
69
+
.option('--request <value>', 'DEPRECATED. Path to custom request file')
70
+
.option(
71
+
'--useOptions [value]',
72
+
'DEPRECATED. Use options instead of arguments?',
73
+
)
74
+
.parse(process.argv)
75
+
.opts();
76
+
77
+
let userConfig: Record<string, unknown>;
78
+
79
+
try {
80
+
userConfig = processParams(params, [
81
+
'dryRun',
82
+
'experimentalParser',
83
+
'exportCore',
84
+
'logFile',
85
+
'useOptions',
86
+
]);
87
+
88
+
if (userConfig.file) {
89
+
userConfig.configFile = userConfig.file;
90
+
delete userConfig.file;
91
+
}
92
+
93
+
if (params.plugins === true) {
94
+
userConfig.plugins = [];
95
+
} else if (params.plugins) {
96
+
userConfig.plugins = params.plugins;
97
+
} else if (userConfig.client) {
98
+
userConfig.plugins = ['@hey-api/typescript', '@hey-api/sdk'];
99
+
}
100
+
101
+
if (userConfig.client) {
102
+
(userConfig.plugins as Array<string>).push(userConfig.client as string);
103
+
delete userConfig.client;
104
+
}
105
+
106
+
userConfig.logs = userConfig.logs
107
+
? {
108
+
path: userConfig.logs,
109
+
}
110
+
: {};
111
+
112
+
if (userConfig.debug || stringToBoolean(process.env.DEBUG)) {
113
+
(userConfig.logs as Record<string, unknown>).level = 'debug';
114
+
delete userConfig.debug;
115
+
} else if (userConfig.silent) {
116
+
(userConfig.logs as Record<string, unknown>).level = 'silent';
117
+
delete userConfig.silent;
118
+
}
119
+
120
+
(userConfig.logs as Record<string, unknown>).file = userConfig.logFile;
121
+
delete userConfig.logFile;
122
+
123
+
if (typeof params.watch === 'string') {
124
+
userConfig.watch = Number.parseInt(params.watch, 10);
125
+
}
126
+
127
+
if (!Object.keys(userConfig.logs as Record<string, unknown>).length) {
128
+
delete userConfig.logs;
129
+
}
130
+
131
+
const context = await createClient(
132
+
userConfig as unknown as Required<Parameters<typeof createClient>>[0],
133
+
);
134
+
if (
135
+
!context[0]?.config.input.some(
136
+
(input) => input.watch && input.watch.enabled,
137
+
)
138
+
) {
139
+
process.exit(0);
140
+
}
141
+
} catch {
142
+
process.exit(1);
143
+
}
144
+
};
+133
packages/openapi-ts/src/generate.ts
+133
packages/openapi-ts/src/generate.ts
···
1
+
import { checkNodeVersion } from '~/config/engine';
2
+
import type { Configs } from '~/config/init';
3
+
import { initConfigs } from '~/config/init';
4
+
import { getLogs } from '~/config/logs';
5
+
import { createClient as pCreateClient } from '~/createClient';
6
+
import {
7
+
ConfigValidationError,
8
+
JobError,
9
+
logCrashReport,
10
+
openGitHubIssueWithCrashReport,
11
+
printCrashReport,
12
+
shouldReportCrash,
13
+
} from '~/error';
14
+
import type { IR } from '~/ir/types';
15
+
import type { Client } from '~/types/client';
16
+
import type { UserConfig } from '~/types/config';
17
+
import type { LazyOrAsync, MaybeArray } from '~/types/utils';
18
+
import { printCliIntro } from '~/utils/cli';
19
+
import { registerHandlebarTemplates } from '~/utils/handlebars';
20
+
import { Logger } from '~/utils/logger';
21
+
22
+
/**
23
+
* Generate a client from the provided configuration.
24
+
*
25
+
* @param userConfig User provided {@link UserConfig} configuration(s).
26
+
*/
27
+
export const createClient = async (
28
+
userConfig?: LazyOrAsync<MaybeArray<UserConfig>>,
29
+
logger = new Logger(),
30
+
): Promise<ReadonlyArray<Client | IR.Context>> => {
31
+
const resolvedConfig =
32
+
typeof userConfig === 'function' ? await userConfig() : userConfig;
33
+
const userConfigs = resolvedConfig
34
+
? resolvedConfig instanceof Array
35
+
? resolvedConfig
36
+
: [resolvedConfig]
37
+
: [];
38
+
39
+
let rawLogs = userConfigs.find(
40
+
(config) => getLogs(config).level !== 'silent',
41
+
)?.logs;
42
+
if (typeof rawLogs === 'string') {
43
+
rawLogs = getLogs({ logs: rawLogs });
44
+
}
45
+
46
+
let configs: Configs | undefined;
47
+
48
+
try {
49
+
checkNodeVersion();
50
+
51
+
const eventCreateClient = logger.timeEvent('createClient');
52
+
53
+
const eventConfig = logger.timeEvent('config');
54
+
configs = await initConfigs({ logger, userConfigs });
55
+
const printIntro = configs.results.some(
56
+
(result) => result.config.logs.level !== 'silent',
57
+
);
58
+
if (printIntro) {
59
+
printCliIntro();
60
+
}
61
+
eventConfig.timeEnd();
62
+
63
+
const allConfigErrors = configs.results.flatMap((result) =>
64
+
result.errors.map((error) => ({ error, jobIndex: result.jobIndex })),
65
+
);
66
+
if (allConfigErrors.length) {
67
+
throw new ConfigValidationError(allConfigErrors);
68
+
}
69
+
70
+
const eventHandlebars = logger.timeEvent('handlebars');
71
+
const templates = registerHandlebarTemplates();
72
+
eventHandlebars.timeEnd();
73
+
74
+
const clients = await Promise.all(
75
+
configs.results.map(async (result) => {
76
+
try {
77
+
return await pCreateClient({
78
+
config: result.config,
79
+
dependencies: configs!.dependencies,
80
+
jobIndex: result.jobIndex,
81
+
logger,
82
+
templates,
83
+
});
84
+
} catch (error) {
85
+
throw new JobError('', {
86
+
error,
87
+
jobIndex: result.jobIndex,
88
+
});
89
+
}
90
+
}),
91
+
);
92
+
const result = clients.filter((client) => Boolean(client)) as ReadonlyArray<
93
+
Client | IR.Context
94
+
>;
95
+
96
+
eventCreateClient.timeEnd();
97
+
98
+
const printLogs = configs.results.some(
99
+
(result) => result.config.logs.level === 'debug',
100
+
);
101
+
logger.report(printLogs);
102
+
103
+
return result;
104
+
} catch (error) {
105
+
const results = configs?.results ?? [];
106
+
107
+
const logs =
108
+
results.find((result) => result.config.logs.level !== 'silent')?.config
109
+
.logs ??
110
+
results[0]?.config.logs ??
111
+
rawLogs;
112
+
const dryRun =
113
+
results.some((result) => result.config.dryRun) ??
114
+
userConfigs.some((config) => config.dryRun) ??
115
+
false;
116
+
const logPath =
117
+
logs?.file && !dryRun
118
+
? logCrashReport(error, logs.path ?? '')
119
+
: undefined;
120
+
if (!logs || logs.level !== 'silent') {
121
+
printCrashReport({ error, logPath });
122
+
const isInteractive =
123
+
results.some((result) => result.config.interactive) ??
124
+
userConfigs.some((config) => config.interactive) ??
125
+
false;
126
+
if (await shouldReportCrash({ error, isInteractive })) {
127
+
await openGitHubIssueWithCrashReport(error);
128
+
}
129
+
}
130
+
131
+
throw error;
132
+
}
133
+
};
+2
-1
packages/openapi-ts/src/generate/legacy/output.ts
+2
-1
packages/openapi-ts/src/generate/legacy/output.ts
+5
-1
packages/openapi-ts/src/generate/output.ts
+5
-1
packages/openapi-ts/src/generate/output.ts
···
21
21
};
22
22
23
23
const client = getClientPlugin(context.config);
24
-
if ('bundle' in client.config && client.config.bundle) {
24
+
if (
25
+
'bundle' in client.config &&
26
+
client.config.bundle &&
27
+
!context.config.dryRun
28
+
) {
25
29
// not proud of this one
26
30
// @ts-expect-error
27
31
context.config._FRAGILE_CLIENT_BUNDLE_RENAMED = generateClientBundle({
+1
-130
packages/openapi-ts/src/index.ts
+1
-130
packages/openapi-ts/src/index.ts
···
35
35
// @ts-expect-error
36
36
import colorSupport from 'color-support';
37
37
38
-
import { checkNodeVersion } from '~/config/engine';
39
-
import type { Configs } from '~/config/init';
40
-
import { initConfigs } from '~/config/init';
41
-
import { getLogs } from '~/config/logs';
42
-
import { createClient as pCreateClient } from '~/createClient';
43
-
import {
44
-
ConfigValidationError,
45
-
JobError,
46
-
logCrashReport,
47
-
openGitHubIssueWithCrashReport,
48
-
printCrashReport,
49
-
shouldReportCrash,
50
-
} from '~/error';
51
-
import type { IR } from '~/ir/types';
52
-
import type { Client } from '~/types/client';
53
38
import type { UserConfig } from '~/types/config';
54
39
import type { LazyOrAsync, MaybeArray } from '~/types/utils';
55
-
import { printCliIntro } from '~/utils/cli';
56
-
import { registerHandlebarTemplates } from '~/utils/handlebars';
57
-
import { Logger } from '~/utils/logger';
58
40
59
41
colors.enabled = colorSupport().hasBasic;
60
42
61
-
/**
62
-
* Generate a client from the provided configuration.
63
-
*
64
-
* @param userConfig User provided {@link UserConfig} configuration(s).
65
-
*/
66
-
export const createClient = async (
67
-
userConfig?: LazyOrAsync<MaybeArray<UserConfig>>,
68
-
logger = new Logger(),
69
-
): Promise<ReadonlyArray<Client | IR.Context>> => {
70
-
const resolvedConfig =
71
-
typeof userConfig === 'function' ? await userConfig() : userConfig;
72
-
const userConfigs = resolvedConfig
73
-
? resolvedConfig instanceof Array
74
-
? resolvedConfig
75
-
: [resolvedConfig]
76
-
: [];
77
-
78
-
let rawLogs = userConfigs.find(
79
-
(config) => getLogs(config).level !== 'silent',
80
-
)?.logs;
81
-
if (typeof rawLogs === 'string') {
82
-
rawLogs = getLogs({ logs: rawLogs });
83
-
}
84
-
85
-
let configs: Configs | undefined;
86
-
87
-
try {
88
-
checkNodeVersion();
89
-
90
-
const eventCreateClient = logger.timeEvent('createClient');
91
-
92
-
const eventConfig = logger.timeEvent('config');
93
-
configs = await initConfigs({ logger, userConfigs });
94
-
const printIntro = configs.results.some(
95
-
(result) => result.config.logs.level !== 'silent',
96
-
);
97
-
if (printIntro) {
98
-
printCliIntro();
99
-
}
100
-
eventConfig.timeEnd();
101
-
102
-
const allConfigErrors = configs.results.flatMap((result) =>
103
-
result.errors.map((error) => ({ error, jobIndex: result.jobIndex })),
104
-
);
105
-
if (allConfigErrors.length) {
106
-
throw new ConfigValidationError(allConfigErrors);
107
-
}
108
-
109
-
const eventHandlebars = logger.timeEvent('handlebars');
110
-
const templates = registerHandlebarTemplates();
111
-
eventHandlebars.timeEnd();
112
-
113
-
const clients = await Promise.all(
114
-
configs.results.map(async (result) => {
115
-
try {
116
-
return await pCreateClient({
117
-
config: result.config,
118
-
dependencies: configs!.dependencies,
119
-
jobIndex: result.jobIndex,
120
-
logger,
121
-
templates,
122
-
});
123
-
} catch (error) {
124
-
throw new JobError('', {
125
-
error,
126
-
jobIndex: result.jobIndex,
127
-
});
128
-
}
129
-
}),
130
-
);
131
-
const result = clients.filter((client) => Boolean(client)) as ReadonlyArray<
132
-
Client | IR.Context
133
-
>;
134
-
135
-
eventCreateClient.timeEnd();
136
-
137
-
const printLogs = configs.results.some(
138
-
(result) => result.config.logs.level === 'debug',
139
-
);
140
-
logger.report(printLogs);
141
-
142
-
return result;
143
-
} catch (error) {
144
-
const results = configs?.results ?? [];
145
-
146
-
const logs =
147
-
results.find((result) => result.config.logs.level !== 'silent')?.config
148
-
.logs ??
149
-
results[0]?.config.logs ??
150
-
rawLogs;
151
-
const dryRun =
152
-
results.some((result) => result.config.dryRun) ??
153
-
userConfigs.some((config) => config.dryRun) ??
154
-
false;
155
-
const logPath =
156
-
logs?.file && !dryRun
157
-
? logCrashReport(error, logs.path ?? '')
158
-
: undefined;
159
-
if (!logs || logs.level !== 'silent') {
160
-
printCrashReport({ error, logPath });
161
-
const isInteractive =
162
-
results.some((result) => result.config.interactive) ??
163
-
userConfigs.some((config) => config.interactive) ??
164
-
false;
165
-
if (await shouldReportCrash({ error, isInteractive })) {
166
-
await openGitHubIssueWithCrashReport(error);
167
-
}
168
-
}
169
-
170
-
throw error;
171
-
}
172
-
};
43
+
export { createClient } from './generate';
173
44
174
45
/**
175
46
* Type helper for openapi-ts.config.ts, returns {@link MaybeArray<UserConfig>} object(s)
+1
-1
packages/openapi-ts/src/ir/context.ts
+1
-1
packages/openapi-ts/src/ir/context.ts
···
14
14
15
15
import type { IR } from './types';
16
16
17
-
export class IRContext<Spec extends Record<string, any> = any> {
17
+
export class Context<Spec extends Record<string, any> = any> {
18
18
/**
19
19
* Configuration for parsing and generating the output. This
20
20
* is a mix of user-provided and default values.
+1
-1
packages/openapi-ts/src/ir/types.d.ts
+1
-1
packages/openapi-ts/src/ir/types.d.ts
+2
-2
packages/openapi-ts/src/openApi/index.ts
+2
-2
packages/openapi-ts/src/openApi/index.ts
···
1
1
import { satisfies } from '~/config/utils/package';
2
-
import { IRContext } from '~/ir/context';
2
+
import { Context } from '~/ir/context';
3
3
import type { IR } from '~/ir/types';
4
4
import { parseV2_0_X } from '~/openApi/2.0.x';
5
5
import { parseV3_0_X } from '~/openApi/3.0.x';
···
74
74
logger: Logger;
75
75
spec: unknown;
76
76
}): IR.Context | undefined => {
77
-
const context = new IRContext({
77
+
const context = new Context({
78
78
config,
79
79
dependencies,
80
80
logger,
+5
packages/openapi-ts/src/run.ts
+5
packages/openapi-ts/src/run.ts
+1
-1
packages/openapi-ts/tsdown.config.ts
+1
-1
packages/openapi-ts/tsdown.config.ts
+11
-5
pnpm-lock.yaml
+11
-5
pnpm-lock.yaml
···
37
37
'@hey-api/custom-client':
38
38
specifier: workspace:*
39
39
version: link:packages/custom-client
40
+
'@hey-api/openapi-ts':
41
+
specifier: workspace:*
42
+
version: link:packages/openapi-ts
40
43
'@types/node':
41
44
specifier: 22.10.5
42
45
version: 22.10.5
···
88
91
rollup-plugin-dts:
89
92
specifier: 6.1.1
90
93
version: 6.1.1(rollup@4.31.0)(typescript@5.9.3)
94
+
ts-node:
95
+
specifier: 10.9.2
96
+
version: 10.9.2(@types/node@22.10.5)(typescript@5.9.3)
91
97
tsdown:
92
98
specifier: 0.15.8
93
99
version: 0.15.8(@arethetypeswrong/core@0.18.2)(typescript@5.9.3)
···
170
176
devDependencies:
171
177
'@angular-devkit/build-angular':
172
178
specifier: 19.2.0
173
-
version: 19.2.0(3d70bfaaf57777265a4e229c41aeb4c6)
179
+
version: 19.2.0(a969acb6c0970aa981df1cb7672d7c2e)
174
180
'@angular/cli':
175
181
specifier: 19.2.0
176
182
version: 19.2.0(@types/node@22.10.5)(chokidar@4.0.3)
···
264
270
devDependencies:
265
271
'@angular-devkit/build-angular':
266
272
specifier: 19.2.0
267
-
version: 19.2.0(a969acb6c0970aa981df1cb7672d7c2e)
273
+
version: 19.2.0(3d70bfaaf57777265a4e229c41aeb4c6)
268
274
'@angular/cli':
269
275
specifier: 19.2.0
270
276
version: 19.2.0(@types/node@22.10.5)(chokidar@4.0.3)
···
23183
23189
eslint: 9.17.0(jiti@2.6.1)
23184
23190
eslint-import-resolver-node: 0.3.9
23185
23191
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1))
23186
-
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1))
23192
+
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1))
23187
23193
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.17.0(jiti@2.6.1))
23188
23194
eslint-plugin-react: 7.37.5(eslint@9.17.0(jiti@2.6.1))
23189
23195
eslint-plugin-react-hooks: 5.2.0(eslint@9.17.0(jiti@2.6.1))
···
23221
23227
tinyglobby: 0.2.14
23222
23228
unrs-resolver: 1.11.1
23223
23229
optionalDependencies:
23224
-
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1))
23230
+
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1))
23225
23231
transitivePeerDependencies:
23226
23232
- supports-color
23227
23233
···
23236
23242
transitivePeerDependencies:
23237
23243
- supports-color
23238
23244
23239
-
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.17.0(jiti@2.6.1)):
23245
+
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.29.1(eslint@9.17.0(jiti@2.6.1))(typescript@5.8.3))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)))(eslint@9.17.0(jiti@2.6.1)):
23240
23246
dependencies:
23241
23247
'@rtsao/scc': 1.1.0
23242
23248
array-includes: 3.1.9
-7
pnpm-workspace.yaml
-7
pnpm-workspace.yaml
+13
-4
scripts/examples-generate.sh
+13
-4
scripts/examples-generate.sh
···
10
10
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
11
11
12
12
echo "⏳ Generating client code for all examples..."
13
+
13
14
# Find all examples with openapi-ts script and generate code in parallel
14
15
# Concurrency control: adjust this number depending on CI machine resources
15
16
CONCURRENCY=${CONCURRENCY:-4}
···
44
45
echo "Generating: $example_name"
45
46
set -e
46
47
cd "$dir"
47
-
echo "-> Running pnpm openapi-ts"
48
-
pnpm openapi-ts
48
+
echo "-> Running openapi-ts"
49
+
pnpm run openapi-ts
49
50
50
51
# Format generated files in this example only to keep the step fast
51
52
if command -v pnpm >/dev/null 2>&1 && pnpm -w -s --version >/dev/null 2>&1; then
···
76
77
echo "✅ $name succeeded"
77
78
else
78
79
name=$(cat "$tmpdir/$pid.name" 2>/dev/null || echo "$pid")
79
-
log=$(cat "$tmpdir/$pid.log" 2>/dev/null || echo "(no log)")
80
-
echo "❌ $name failed — see log: $tmpdir/$pid.log"
80
+
# Read the metadata file which contains the path to the real log
81
+
logpath=$(cat "$tmpdir/$pid.log" 2>/dev/null || echo "")
82
+
if [ -n "$logpath" ] && [ -f "$logpath" ]; then
83
+
echo "❌ $name failed — showing full log ($logpath):"
84
+
echo "---- full log start ----"
85
+
cat "$logpath" || true
86
+
echo "---- full log end ----"
87
+
else
88
+
echo "❌ $name failed — no log found (metadata: $tmpdir/$pid.log)"
89
+
fi
81
90
failed=1
82
91
fi
83
92
done