swaggie 2.1.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +12 -6
- package/dist/gen/genMocks.js +14 -9
- package/dist/index.js +144 -20
- package/dist/types.d.ts +5 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -543,4 +543,5 @@ function error(e) {
|
|
|
543
543
|
<a href="https://www.britishcouncil.org"><img alt="British Council" src="./docs/public/used-in/bc-logo.png" /></a>
|
|
544
544
|
<a href="https://kpmg.com/"><img alt="KPMG" src="./docs/public/used-in/kpmg-logo.png" /></a>
|
|
545
545
|
<a href="https://klarna.com/"><img alt="Klarna" src="./docs/public/used-in/klarna-logo.png" /></a>
|
|
546
|
+
<a href="https://cribl.io/"><img alt="Cribl" src="./docs/public/used-in/cribl-logo.png" /></a>
|
|
546
547
|
</div>
|
package/dist/cli.js
CHANGED
|
@@ -131,12 +131,18 @@ const options = program.opts();
|
|
|
131
131
|
|
|
132
132
|
_index.runCodeGenerator.call(void 0, options ).then(complete, error);
|
|
133
133
|
|
|
134
|
-
function complete(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
function complete(result) {
|
|
135
|
+
const results = Array.isArray(result[0])
|
|
136
|
+
? (result )
|
|
137
|
+
: [result ];
|
|
138
|
+
|
|
139
|
+
for (const [code, opts] of results) {
|
|
140
|
+
if (opts.out) {
|
|
141
|
+
const from = typeof opts.src === 'string' ? `from ${_picocolors.bold.call(void 0, opts.src)} ` : '';
|
|
142
|
+
console.info(_picocolors.cyan.call(void 0, `Api ${from}code generated into ${_picocolors.bold.call(void 0, opts.out)}`));
|
|
143
|
+
} else {
|
|
144
|
+
console.log(code);
|
|
145
|
+
}
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
process.exit(0);
|
package/dist/gen/genMocks.js
CHANGED
|
@@ -67,8 +67,13 @@ const MOCK_FILE_HEADER = `\
|
|
|
67
67
|
.map((name) => {
|
|
68
68
|
const fullName = servicePrefix + name;
|
|
69
69
|
const camelName = _case.camel.call(void 0, fullName);
|
|
70
|
+
// `default` is a JS reserved word and cannot be used as a standalone
|
|
71
|
+
// `export const` identifier. The hooks templates rename it to `main`
|
|
72
|
+
// (see genOperations.ts — hooksCamelCaseName). Mock code that references
|
|
73
|
+
// the hooks namespace must use the same safe name.
|
|
74
|
+
const hooksCamelName = camelName === 'default' ? 'main' : camelName;
|
|
70
75
|
const ops = _genOperations.prepareOperations.call(void 0, groups[name], options, spec.components);
|
|
71
|
-
return { name, camelName, ops };
|
|
76
|
+
return { name, camelName, hooksCamelName, ops };
|
|
72
77
|
})
|
|
73
78
|
.filter((g) => g.ops.length > 0);
|
|
74
79
|
|
|
@@ -399,16 +404,16 @@ function buildCreateSwrHookMocks(
|
|
|
399
404
|
const spy = spyFn(fw);
|
|
400
405
|
const lines = ['export function createApiHookMocks() {', ' return {'];
|
|
401
406
|
|
|
402
|
-
for (const {
|
|
407
|
+
for (const { hooksCamelName, ops } of groups) {
|
|
403
408
|
const getOps = ops.filter((o) => o.method === 'GET');
|
|
404
409
|
const mutOps = ops.filter((o) => o.method !== 'GET');
|
|
405
410
|
|
|
406
|
-
lines.push(` ${
|
|
411
|
+
lines.push(` ${hooksCamelName}: {`);
|
|
407
412
|
lines.push(' queries: {');
|
|
408
413
|
for (const op of getOps) {
|
|
409
414
|
const hookName = toHookName(op.name, 'use');
|
|
410
415
|
lines.push(
|
|
411
|
-
` ${hookName}: withMockSWR(${spy}(${hooksAlias}.${
|
|
416
|
+
` ${hookName}: withMockSWR(${spy}(${hooksAlias}.${hooksCamelName}.queries, '${hookName}').mockReturnValue(defaultSWRReturn)),`
|
|
412
417
|
);
|
|
413
418
|
}
|
|
414
419
|
lines.push(' },');
|
|
@@ -416,7 +421,7 @@ function buildCreateSwrHookMocks(
|
|
|
416
421
|
for (const op of mutOps) {
|
|
417
422
|
const hookName = 'use' + _case.pascal.call(void 0, op.name);
|
|
418
423
|
lines.push(
|
|
419
|
-
` ${hookName}: withMockSWRMutation(${spy}(${hooksAlias}.${
|
|
424
|
+
` ${hookName}: withMockSWRMutation(${spy}(${hooksAlias}.${hooksCamelName}.mutations, '${hookName}').mockReturnValue(defaultSWRMutationReturn as any)),`
|
|
420
425
|
);
|
|
421
426
|
}
|
|
422
427
|
lines.push(' },');
|
|
@@ -436,16 +441,16 @@ function buildCreateTsqHookMocks(
|
|
|
436
441
|
const spy = spyFn(fw);
|
|
437
442
|
const lines = ['export function createApiHookMocks() {', ' return {'];
|
|
438
443
|
|
|
439
|
-
for (const {
|
|
444
|
+
for (const { hooksCamelName, ops } of groups) {
|
|
440
445
|
const getOps = ops.filter((o) => o.method === 'GET');
|
|
441
446
|
const mutOps = ops.filter((o) => o.method !== 'GET');
|
|
442
447
|
|
|
443
|
-
lines.push(` ${
|
|
448
|
+
lines.push(` ${hooksCamelName}: {`);
|
|
444
449
|
lines.push(' queries: {');
|
|
445
450
|
for (const op of getOps) {
|
|
446
451
|
const hookName = toHookName(op.name, 'use');
|
|
447
452
|
lines.push(
|
|
448
|
-
` ${hookName}: withMockQuery(${spy}(${hooksAlias}.${
|
|
453
|
+
` ${hookName}: withMockQuery(${spy}(${hooksAlias}.${hooksCamelName}.queries, '${hookName}').mockReturnValue(defaultQueryReturn as any)),`
|
|
449
454
|
);
|
|
450
455
|
}
|
|
451
456
|
lines.push(' },');
|
|
@@ -453,7 +458,7 @@ function buildCreateTsqHookMocks(
|
|
|
453
458
|
for (const op of mutOps) {
|
|
454
459
|
const hookName = 'use' + _case.pascal.call(void 0, op.name);
|
|
455
460
|
lines.push(
|
|
456
|
-
` ${hookName}: withMockMutation(${spy}(${hooksAlias}.${
|
|
461
|
+
` ${hookName}: withMockMutation(${spy}(${hooksAlias}.${hooksCamelName}.mutations, '${hookName}').mockReturnValue(defaultMutationReturn as any)),`
|
|
457
462
|
);
|
|
458
463
|
}
|
|
459
464
|
lines.push(' },');
|
package/dist/index.js
CHANGED
|
@@ -3,23 +3,47 @@
|
|
|
3
3
|
|
|
4
4
|
var _gen = require('./gen'); var _gen2 = _interopRequireDefault(_gen);
|
|
5
5
|
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
var _utils = require('./utils');
|
|
7
14
|
var _templateValidator = require('./utils/templateValidator');
|
|
8
15
|
var _swagger = require('./swagger');
|
|
9
16
|
|
|
10
17
|
/**
|
|
11
18
|
* Runs the whole code generation process.
|
|
12
|
-
*
|
|
19
|
+
* When the config file contains an array of configs, returns `BatchCodeGenResult`
|
|
20
|
+
* (an array of results, one per entry, fail-fast on first error).
|
|
21
|
+
* Otherwise returns a single `CodeGenResult`.
|
|
13
22
|
**/
|
|
14
|
-
async function runCodeGenerator(
|
|
23
|
+
async function runCodeGenerator(
|
|
24
|
+
options
|
|
25
|
+
) {
|
|
15
26
|
try {
|
|
16
27
|
verifyOptions(options);
|
|
17
|
-
const
|
|
18
|
-
|
|
28
|
+
const optsOrArray = await applyConfigFile(options);
|
|
29
|
+
|
|
30
|
+
if (Array.isArray(optsOrArray)) {
|
|
31
|
+
const results = [];
|
|
32
|
+
for (const opts of optsOrArray) {
|
|
33
|
+
verifyEntryOptions(opts);
|
|
34
|
+
const spec = await _utils.loadSpecDocument.call(void 0, opts.src);
|
|
35
|
+
const verifiedSpec = _utils.verifyDocumentSpec.call(void 0, spec);
|
|
36
|
+
const code = await gen(verifiedSpec, opts);
|
|
37
|
+
results.push([code, opts]);
|
|
38
|
+
}
|
|
39
|
+
return results;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const spec = await _utils.loadSpecDocument.call(void 0, optsOrArray.src);
|
|
19
43
|
const verifiedSpec = _utils.verifyDocumentSpec.call(void 0, spec);
|
|
20
|
-
const code = await gen(verifiedSpec,
|
|
44
|
+
const code = await gen(verifiedSpec, optsOrArray);
|
|
21
45
|
|
|
22
|
-
return [code,
|
|
46
|
+
return [code, optsOrArray];
|
|
23
47
|
} catch (e) {
|
|
24
48
|
return Promise.reject(e);
|
|
25
49
|
}
|
|
@@ -64,6 +88,82 @@ function verifyOptions(options) {
|
|
|
64
88
|
}
|
|
65
89
|
}
|
|
66
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Validates options that apply to each individual entry in an array config.
|
|
93
|
+
* Covers the same constraints as `verifyOptions` but scoped to a single
|
|
94
|
+
* fully-resolved `AppOptions` entry (after config merging and defaults).
|
|
95
|
+
*/
|
|
96
|
+
function verifyEntryOptions(opts) {
|
|
97
|
+
if (!opts.src) {
|
|
98
|
+
throw new Error('Each config entry must have "src" set');
|
|
99
|
+
}
|
|
100
|
+
if (!!opts.mocks !== !!opts.testingFramework) {
|
|
101
|
+
throw new Error('--mocks and --testingFramework must be used together');
|
|
102
|
+
}
|
|
103
|
+
if (opts.mocks && !opts.out) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
'"mocks" requires "out" to be set, since the mock file needs to import the generated client'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if (opts.hooksOut && !opts.out) {
|
|
109
|
+
throw new Error('"hooksOut" requires "out" to be set');
|
|
110
|
+
}
|
|
111
|
+
if (opts.clientSetup && !opts.out) {
|
|
112
|
+
throw new Error('"clientSetup" requires "out" to be set');
|
|
113
|
+
}
|
|
114
|
+
if (opts.forceSetup && !opts.clientSetup) {
|
|
115
|
+
throw new Error('"forceSetup" requires "clientSetup" to be set');
|
|
116
|
+
}
|
|
117
|
+
if (opts.hooksOut) {
|
|
118
|
+
const tpl = opts.template;
|
|
119
|
+
const isL2 =
|
|
120
|
+
(typeof tpl === 'string' && _templateValidator.isL2Template.call(void 0, tpl)) ||
|
|
121
|
+
(Array.isArray(tpl) && typeof tpl[0] === 'string' && _templateValidator.isL2Template.call(void 0, tpl[0]));
|
|
122
|
+
if (!isL2) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'"hooksOut" requires an L2 template (swr, tsq). ' +
|
|
125
|
+
'Reactive hooks are only generated for L2 template pairs.'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Throws if any per-entry-only keys are present as top-level defaults in a
|
|
133
|
+
* multi-config file. These keys must live inside each entry under "configs".
|
|
134
|
+
*/
|
|
135
|
+
function verifyTopLevelDefaults(configUrl, defaults) {
|
|
136
|
+
const blocked = ['src', 'out', 'hooksOut', 'mocks', 'clientSetup'];
|
|
137
|
+
for (const key of blocked) {
|
|
138
|
+
if (key in defaults) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`"${key}" cannot be a top-level default in multi-config file "${configUrl}". ` +
|
|
141
|
+
`Set it inside each entry under "configs" instead.`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Throws if any file-path CLI flags are combined with a multi-config file.
|
|
149
|
+
* Each entry in "configs" must define its own output paths.
|
|
150
|
+
*/
|
|
151
|
+
function verifyOptionsForArrayConfig(options) {
|
|
152
|
+
const blocked = [
|
|
153
|
+
['out', '--out'],
|
|
154
|
+
['hooksOut', '--hooksOut'],
|
|
155
|
+
['mocks', '--mocks'],
|
|
156
|
+
['clientSetup', '--clientSetup'],
|
|
157
|
+
];
|
|
158
|
+
for (const [key, flag] of blocked) {
|
|
159
|
+
if (options[key]) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
`${flag} cannot be used with a multi-config file. Set "${key}" in each entry under "configs" instead.`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
67
167
|
function gen(spec, options) {
|
|
68
168
|
if (options.generationMode === 'full') {
|
|
69
169
|
_templateValidator.validateTemplate.call(void 0, options.template);
|
|
@@ -76,25 +176,49 @@ function gen(spec, options) {
|
|
|
76
176
|
async function applyConfigFile(
|
|
77
177
|
options
|
|
78
178
|
) {
|
|
179
|
+
if (!options.config) {
|
|
180
|
+
return prepareAppOptions(options );
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const configUrl = options.config;
|
|
184
|
+
let configContents;
|
|
185
|
+
let parsedConfig;
|
|
186
|
+
|
|
79
187
|
try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
188
|
+
configContents = await readFile(configUrl);
|
|
189
|
+
parsedConfig = JSON.parse(configContents);
|
|
190
|
+
} catch (_e) {
|
|
191
|
+
return Promise.reject(
|
|
192
|
+
new Error('Could not correctly load config file. It does not exist or you cannot access it')
|
|
193
|
+
);
|
|
194
|
+
}
|
|
83
195
|
|
|
84
|
-
|
|
85
|
-
const configContents = await readFile(configUrl);
|
|
86
|
-
const parsedConfig = JSON.parse(configContents);
|
|
87
|
-
if (!parsedConfig || parsedConfig.length < 1) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
`Could not correctly parse config file from "${configUrl}". Is it a valid JSON file?`
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
return prepareAppOptions({ ...parsedConfig, ...options });
|
|
93
|
-
} catch (e) {
|
|
196
|
+
if (!parsedConfig || typeof parsedConfig !== 'object') {
|
|
94
197
|
return Promise.reject(
|
|
95
198
|
new Error('Could not correctly load config file. It does not exist or you cannot access it')
|
|
96
199
|
);
|
|
97
200
|
}
|
|
201
|
+
|
|
202
|
+
if ('configs' in (parsedConfig )) {
|
|
203
|
+
const { configs: entries, ...topLevelDefaults } = parsedConfig
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
;
|
|
207
|
+
if (!Array.isArray(entries) || entries.length < 1) {
|
|
208
|
+
return Promise.reject(
|
|
209
|
+
new Error(
|
|
210
|
+
`Config file "${configUrl}" has a "configs" key but it is not a non-empty array. Provide at least one config entry.`
|
|
211
|
+
)
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
verifyTopLevelDefaults(configUrl, topLevelDefaults);
|
|
215
|
+
verifyOptionsForArrayConfig(options);
|
|
216
|
+
return entries.map((entry) =>
|
|
217
|
+
prepareAppOptions({ ...topLevelDefaults, ...entry, ...options })
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return prepareAppOptions({ ...parsedConfig, ...options } );
|
|
98
222
|
} exports.applyConfigFile = applyConfigFile;
|
|
99
223
|
|
|
100
224
|
function readFile(filePath) {
|
|
@@ -103,7 +227,7 @@ function readFile(filePath) {
|
|
|
103
227
|
});
|
|
104
228
|
}
|
|
105
229
|
|
|
106
|
-
|
|
230
|
+
;
|
|
107
231
|
|
|
108
232
|
/**
|
|
109
233
|
* CLI options are flat, but within the app we use nested objects.
|
package/dist/types.d.ts
CHANGED
|
@@ -171,6 +171,11 @@ export interface AppOptions extends ClientOptions {
|
|
|
171
171
|
mocks?: string;
|
|
172
172
|
testingFramework?: TestingFramework;
|
|
173
173
|
}
|
|
174
|
+
export type CodeGenResult = [string, AppOptions];
|
|
175
|
+
/**
|
|
176
|
+
* Result of a batch run — one entry per config in the array.
|
|
177
|
+
*/
|
|
178
|
+
export type BatchCodeGenResult = CodeGenResult[];
|
|
174
179
|
/**
|
|
175
180
|
* Local type that represent Operation as understood by Swaggie
|
|
176
181
|
**/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swaggie",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Generate a fully typed TypeScript API client from your OpenAPI 3 spec",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Piotr Dabrowski",
|
|
@@ -73,11 +73,11 @@
|
|
|
73
73
|
"case": "^1.6.3",
|
|
74
74
|
"commander": "^14.0.3",
|
|
75
75
|
"eta": "^4.6.0",
|
|
76
|
-
"yaml": "^2.
|
|
76
|
+
"yaml": "^2.9.0",
|
|
77
77
|
"picocolors": "^1.1.1"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"bun-types": "1.3.
|
|
80
|
+
"bun-types": "1.3.14",
|
|
81
81
|
"openapi-types": "^12.1.3",
|
|
82
82
|
"sucrase": "3.35.1",
|
|
83
83
|
"typescript": "6.0.3",
|