@kubb/plugin-msw 6.2.0-canary.20261111T115959 → 6.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/dist/index.cjs +357 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +358 -213
- package/dist/index.js.map +1 -1
- package/docs/options.md +1 -1
- package/kubb-extension.json +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,238 @@ let _kubb_plugin_faker = require("@kubb/plugin-faker");
|
|
|
7
7
|
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
8
8
|
let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
|
|
9
9
|
let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
|
|
10
|
+
// kubb-extension.json
|
|
11
|
+
var compatibility = {
|
|
12
|
+
"kubb": ">=5.18.4",
|
|
13
|
+
"node": ">=22.22"
|
|
14
|
+
};
|
|
15
|
+
// package.json
|
|
16
|
+
var version = "6.2.0";
|
|
17
|
+
// src/generators/handlersGenerator.ts
|
|
18
|
+
/**
|
|
19
|
+
* Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
|
|
20
|
+
* `handlers.ts` file that re-exports every generated handler in operation
|
|
21
|
+
* order, ready to spread into `setupServer(...handlers)` or
|
|
22
|
+
* `setupWorker(...handlers)`.
|
|
23
|
+
*/
|
|
24
|
+
const handlersGenerator = (0, _kubb_kit.defineGenerator)({
|
|
25
|
+
name: "plugin-msw",
|
|
26
|
+
operations(nodes, ctx) {
|
|
27
|
+
const { resolver, config, root } = ctx;
|
|
28
|
+
const { output, group } = ctx.options;
|
|
29
|
+
const handlersName = resolver.handler.listName();
|
|
30
|
+
const createHandlersName = resolver.resolveName({
|
|
31
|
+
key: ["handlers", "create"],
|
|
32
|
+
name: _kubb_kit.casing.camel(handlersName, { prefix: "create" })
|
|
33
|
+
});
|
|
34
|
+
const file = resolver.file({
|
|
35
|
+
name: handlersName,
|
|
36
|
+
extname: ".ts",
|
|
37
|
+
root,
|
|
38
|
+
output,
|
|
39
|
+
group: group ?? void 0
|
|
40
|
+
});
|
|
41
|
+
const handlerNames = nodes.map((node) => resolver.handler.name(node));
|
|
42
|
+
const imports = nodes.map((node, index) => {
|
|
43
|
+
const operationName = handlerNames[index];
|
|
44
|
+
const operationFile = resolver.file({
|
|
45
|
+
name: resolver.name(node.operationId),
|
|
46
|
+
extname: ".ts",
|
|
47
|
+
tag: node.tags[0] ?? "default",
|
|
48
|
+
path: node.path,
|
|
49
|
+
root,
|
|
50
|
+
output,
|
|
51
|
+
group: group ?? void 0
|
|
52
|
+
});
|
|
53
|
+
return _kubb_kit.ast.factory.createImport({
|
|
54
|
+
name: [operationName],
|
|
55
|
+
root: file.path,
|
|
56
|
+
path: operationFile.path
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
const handlers = handlerNames.map((name) => `${name}(undefined, options)`);
|
|
60
|
+
return [_kubb_kit.ast.factory.createFile({
|
|
61
|
+
baseName: file.baseName,
|
|
62
|
+
path: file.path,
|
|
63
|
+
meta: file.meta,
|
|
64
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
65
|
+
output,
|
|
66
|
+
config,
|
|
67
|
+
file: {
|
|
68
|
+
path: file.path,
|
|
69
|
+
baseName: file.baseName
|
|
70
|
+
}
|
|
71
|
+
}),
|
|
72
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
73
|
+
output,
|
|
74
|
+
config,
|
|
75
|
+
file: {
|
|
76
|
+
path: file.path,
|
|
77
|
+
baseName: file.baseName
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
imports,
|
|
81
|
+
sources: [_kubb_kit.ast.factory.createSource({
|
|
82
|
+
name: createHandlersName,
|
|
83
|
+
isIndexable: true,
|
|
84
|
+
isExportable: true,
|
|
85
|
+
nodes: [_kubb_kit.ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {
|
|
86
|
+
return [${handlers.join(", ")}] as const
|
|
87
|
+
}`)]
|
|
88
|
+
}), _kubb_kit.ast.factory.createSource({
|
|
89
|
+
name: handlersName,
|
|
90
|
+
isIndexable: true,
|
|
91
|
+
isExportable: true,
|
|
92
|
+
nodes: [_kubb_kit.ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)]
|
|
93
|
+
})]
|
|
94
|
+
})];
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
// ../../internals/utils/src/reserved.ts
|
|
98
|
+
/**
|
|
99
|
+
* JavaScript and Java reserved words.
|
|
100
|
+
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
101
|
+
*/
|
|
102
|
+
const reservedWords = /* @__PURE__ */ new Set([
|
|
103
|
+
"abstract",
|
|
104
|
+
"arguments",
|
|
105
|
+
"await",
|
|
106
|
+
"boolean",
|
|
107
|
+
"break",
|
|
108
|
+
"byte",
|
|
109
|
+
"case",
|
|
110
|
+
"catch",
|
|
111
|
+
"char",
|
|
112
|
+
"class",
|
|
113
|
+
"const",
|
|
114
|
+
"continue",
|
|
115
|
+
"debugger",
|
|
116
|
+
"default",
|
|
117
|
+
"delete",
|
|
118
|
+
"do",
|
|
119
|
+
"double",
|
|
120
|
+
"else",
|
|
121
|
+
"enum",
|
|
122
|
+
"eval",
|
|
123
|
+
"export",
|
|
124
|
+
"extends",
|
|
125
|
+
"false",
|
|
126
|
+
"final",
|
|
127
|
+
"finally",
|
|
128
|
+
"float",
|
|
129
|
+
"for",
|
|
130
|
+
"function",
|
|
131
|
+
"goto",
|
|
132
|
+
"if",
|
|
133
|
+
"implements",
|
|
134
|
+
"import",
|
|
135
|
+
"in",
|
|
136
|
+
"instanceof",
|
|
137
|
+
"int",
|
|
138
|
+
"interface",
|
|
139
|
+
"let",
|
|
140
|
+
"long",
|
|
141
|
+
"native",
|
|
142
|
+
"new",
|
|
143
|
+
"null",
|
|
144
|
+
"package",
|
|
145
|
+
"private",
|
|
146
|
+
"protected",
|
|
147
|
+
"public",
|
|
148
|
+
"return",
|
|
149
|
+
"short",
|
|
150
|
+
"static",
|
|
151
|
+
"super",
|
|
152
|
+
"switch",
|
|
153
|
+
"synchronized",
|
|
154
|
+
"this",
|
|
155
|
+
"throw",
|
|
156
|
+
"throws",
|
|
157
|
+
"transient",
|
|
158
|
+
"true",
|
|
159
|
+
"try",
|
|
160
|
+
"typeof",
|
|
161
|
+
"var",
|
|
162
|
+
"void",
|
|
163
|
+
"volatile",
|
|
164
|
+
"while",
|
|
165
|
+
"with",
|
|
166
|
+
"yield",
|
|
167
|
+
"Array",
|
|
168
|
+
"Date",
|
|
169
|
+
"hasOwnProperty",
|
|
170
|
+
"Infinity",
|
|
171
|
+
"isFinite",
|
|
172
|
+
"isNaN",
|
|
173
|
+
"isPrototypeOf",
|
|
174
|
+
"length",
|
|
175
|
+
"Math",
|
|
176
|
+
"name",
|
|
177
|
+
"NaN",
|
|
178
|
+
"Number",
|
|
179
|
+
"Object",
|
|
180
|
+
"prototype",
|
|
181
|
+
"String",
|
|
182
|
+
"toString",
|
|
183
|
+
"undefined",
|
|
184
|
+
"valueOf"
|
|
185
|
+
]);
|
|
186
|
+
/**
|
|
187
|
+
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* isValidVarName('status') // true
|
|
192
|
+
* isValidVarName('class') // false (reserved word)
|
|
193
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
function isValidVarName(name) {
|
|
197
|
+
if (!name || reservedWords.has(name)) return false;
|
|
198
|
+
return isIdentifier(name);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
202
|
+
*
|
|
203
|
+
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
204
|
+
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
205
|
+
* deciding whether an object key needs quoting.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```ts
|
|
209
|
+
* isIdentifier('name') // true
|
|
210
|
+
* isIdentifier('x-total')// false
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
function isIdentifier(name) {
|
|
214
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
215
|
+
}
|
|
216
|
+
// ../../internals/utils/src/codegen.ts
|
|
217
|
+
/**
|
|
218
|
+
* An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* objectKey('x-total') // "'x-total'"
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
function objectKey(name) {
|
|
226
|
+
return isIdentifier(name) ? name : _kubb_kit.text.quote(name);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* A JSON value as a JavaScript literal, with single-quoted strings and identifier keys left unquoted.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```ts
|
|
233
|
+
* toLiteral({ id: 1, 'x-tag': ['a'] }) // "{ id: 1, 'x-tag': ['a'] }"
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
function toLiteral(value) {
|
|
237
|
+
if (typeof value === "string") return _kubb_kit.text.quote(value);
|
|
238
|
+
if (Array.isArray(value)) return `[${value.map(toLiteral).join(", ")}]`;
|
|
239
|
+
if (value && typeof value === "object") return `{ ${Object.entries(value).map(([key, item]) => `${objectKey(key)}: ${toLiteral(item)}`).join(", ")} }`;
|
|
240
|
+
return String(value);
|
|
241
|
+
}
|
|
10
242
|
// ../../internals/shared/src/operation.ts
|
|
11
243
|
/**
|
|
12
244
|
* Builds the `ResolverFileParams` every operation generator passes to
|
|
@@ -72,19 +304,12 @@ function resolveOperationFile(options) {
|
|
|
72
304
|
return file;
|
|
73
305
|
});
|
|
74
306
|
}
|
|
75
|
-
function baseContentType(contentType) {
|
|
76
|
-
return contentType.split(";")[0].trim().toLowerCase();
|
|
77
|
-
}
|
|
78
307
|
/**
|
|
79
|
-
*
|
|
80
|
-
* `application/x-ndjson`, `application/json-seq`, or any media type with an OpenAPI 3.2 `itemSchema`.
|
|
308
|
+
* How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.
|
|
81
309
|
*/
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
function isJsonContentType(baseType) {
|
|
87
|
-
return baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json";
|
|
310
|
+
function getStreamFraming(entry) {
|
|
311
|
+
if (_kubb_kit.ast.mediaType.getBase(entry) === "text/event-stream") return "events";
|
|
312
|
+
return entry.stream && _kubb_kit.ast.mediaType.isStream(entry) ? "lines" : void 0;
|
|
88
313
|
}
|
|
89
314
|
/**
|
|
90
315
|
* Parser for one response content entry.
|
|
@@ -95,10 +320,10 @@ function isJsonContentType(baseType) {
|
|
|
95
320
|
* `image/*`, `audio/*`, and `video/*` are blobs.
|
|
96
321
|
*/
|
|
97
322
|
function responseTypeForContent(entry) {
|
|
98
|
-
const baseType =
|
|
323
|
+
const baseType = _kubb_kit.ast.mediaType.getBase(entry);
|
|
99
324
|
if (!baseType) return void 0;
|
|
100
|
-
if (
|
|
101
|
-
if (
|
|
325
|
+
if (getStreamFraming(entry)) return "stream";
|
|
326
|
+
if (_kubb_kit.ast.mediaType.isJson(baseType)) return void 0;
|
|
102
327
|
if (entry.schema?.type === "blob") return "blob";
|
|
103
328
|
if (baseType.startsWith("text/")) return "text";
|
|
104
329
|
if (isBinaryContentType(baseType)) return "blob";
|
|
@@ -115,14 +340,14 @@ function isBinaryContentType(baseType) {
|
|
|
115
340
|
function toResponseContent(entry, entries = [entry]) {
|
|
116
341
|
const { contentType, schema } = entry;
|
|
117
342
|
if (!schema || entry.stream) return entry;
|
|
118
|
-
const baseType =
|
|
119
|
-
const resolved = _kubb_kit.ast.
|
|
343
|
+
const baseType = _kubb_kit.ast.mediaType.getBase(contentType);
|
|
344
|
+
const resolved = _kubb_kit.ast.ref.getSchema(schema);
|
|
120
345
|
if (isBinaryContentType(baseType)) return resolved.type === "blob" ? entry : {
|
|
121
346
|
...entry,
|
|
122
347
|
schema: _kubb_kit.ast.factory.createSchema({ type: "blob" }),
|
|
123
348
|
keysToOmit: null
|
|
124
349
|
};
|
|
125
|
-
const isPlainMediaType = Boolean(baseType) && !
|
|
350
|
+
const isPlainMediaType = Boolean(baseType) && !_kubb_kit.ast.mediaType.isJson(baseType) && !baseType.includes("*");
|
|
126
351
|
if (resolved.type === "blob") return isPlainMediaType && !entries.every((item) => item.schema?.type === "blob") ? {
|
|
127
352
|
...entry,
|
|
128
353
|
schema: _kubb_kit.ast.factory.createSchema({ type: "string" }),
|
|
@@ -138,42 +363,15 @@ function toResponseContent(entry, entries = [entry]) {
|
|
|
138
363
|
keysToOmit: null
|
|
139
364
|
} : entry;
|
|
140
365
|
}
|
|
141
|
-
function getStatusCodeNumber(statusCode) {
|
|
142
|
-
const code = Number(statusCode);
|
|
143
|
-
return Number.isNaN(code) ? null : code;
|
|
144
|
-
}
|
|
145
|
-
function isSuccessStatusCode(statusCode) {
|
|
146
|
-
const code = getStatusCodeNumber(statusCode);
|
|
147
|
-
return code !== null && code >= 200 && code < 300;
|
|
148
|
-
}
|
|
149
|
-
function getSuccessResponses(responses) {
|
|
150
|
-
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
151
|
-
}
|
|
152
|
-
function getOperationSuccessResponses(node) {
|
|
153
|
-
return getSuccessResponses(node.responses);
|
|
154
|
-
}
|
|
155
|
-
function getPrimarySuccessResponse(node) {
|
|
156
|
-
return getOperationSuccessResponses(node)[0] ?? null;
|
|
157
|
-
}
|
|
158
|
-
function resolveResponseTypes(node, resolver) {
|
|
159
|
-
const types = [];
|
|
160
|
-
for (const response of node.responses) {
|
|
161
|
-
if (response.statusCode === "default") {
|
|
162
|
-
types.push(["default", resolver.response.response(node)]);
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
const code = getStatusCodeNumber(response.statusCode);
|
|
166
|
-
if (code === null) continue;
|
|
167
|
-
types.push([code, isSuccessStatusCode(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)]);
|
|
168
|
-
}
|
|
169
|
-
return types;
|
|
170
|
-
}
|
|
171
366
|
// ../../internals/shared/src/resolver.ts
|
|
172
367
|
/**
|
|
173
368
|
* Builds the allocation key for a declaration an operation owns. `scope` names which declaration
|
|
174
369
|
* it is, so two of them never share an allocation even when a custom resolver gives both the same
|
|
175
370
|
* name. Leave `scope` empty for the operation's own declaration.
|
|
176
371
|
*
|
|
372
|
+
* The key starts from the operation's input `pointer`, which macros keep, so a plugin whose macro
|
|
373
|
+
* rewrites the method or path still resolves another plugin's names for the same operation.
|
|
374
|
+
*
|
|
177
375
|
* @example
|
|
178
376
|
* ```ts
|
|
179
377
|
* resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
|
|
@@ -182,8 +380,7 @@ function resolveResponseTypes(node, resolver) {
|
|
|
182
380
|
function operationKey(node, ...scope) {
|
|
183
381
|
return [
|
|
184
382
|
"operation",
|
|
185
|
-
node.method,
|
|
186
|
-
node.path,
|
|
383
|
+
...node.pointer ? [node.pointer] : [node.method, node.path],
|
|
187
384
|
...scope.map(String)
|
|
188
385
|
];
|
|
189
386
|
}
|
|
@@ -193,124 +390,25 @@ function operationKey(node, ...scope) {
|
|
|
193
390
|
function getEnumNames(ctx) {
|
|
194
391
|
return ctx.memo("enumNames:set", () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => schema.type === "enum" && schema.name ? [schema.name] : [])));
|
|
195
392
|
}
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
202
|
-
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
203
|
-
*
|
|
204
|
-
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
205
|
-
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
206
|
-
* per-plugin convention.
|
|
207
|
-
*
|
|
208
|
-
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
209
|
-
*
|
|
210
|
-
* @example
|
|
211
|
-
* ```ts
|
|
212
|
-
* createGroupConfig(group) // shared across every plugin
|
|
213
|
-
* ```
|
|
214
|
-
*/
|
|
215
|
-
function createGroupConfig(group) {
|
|
216
|
-
if (!group) return null;
|
|
217
|
-
const defaultName = (ctx) => {
|
|
218
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
219
|
-
return (0, _kubb_kit.camelCase)(ctx.group);
|
|
220
|
-
};
|
|
221
|
-
return {
|
|
222
|
-
...group,
|
|
223
|
-
name: group.name ? group.name : defaultName
|
|
224
|
-
};
|
|
393
|
+
// src/utils.ts
|
|
394
|
+
function toSimpleStyle(value) {
|
|
395
|
+
if (Array.isArray(value)) return value.join(",");
|
|
396
|
+
if (typeof value === "object" && value !== null) return Object.entries(value).flat().join(",");
|
|
397
|
+
return String(value);
|
|
225
398
|
}
|
|
226
|
-
// kubb-extension.json
|
|
227
|
-
var compatibility = {
|
|
228
|
-
"kubb": ">=5.16.0",
|
|
229
|
-
"node": ">=22.22"
|
|
230
|
-
};
|
|
231
|
-
// package.json
|
|
232
|
-
var version = "6.2.0-canary.20261111T115959";
|
|
233
|
-
// src/generators/handlersGenerator.ts
|
|
234
399
|
/**
|
|
235
|
-
*
|
|
236
|
-
* `handlers.ts` file that re-exports every generated handler in operation
|
|
237
|
-
* order, ready to spread into `setupServer(...handlers)` or
|
|
238
|
-
* `setupWorker(...handlers)`.
|
|
400
|
+
* The declared headers a mock sends, each with its example's `serializedValue` or `value`, else its schema's `default` or first example.
|
|
239
401
|
*/
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const file = resolver.file({
|
|
251
|
-
name: handlersName,
|
|
252
|
-
extname: ".ts",
|
|
253
|
-
root,
|
|
254
|
-
output,
|
|
255
|
-
group: group ?? void 0
|
|
256
|
-
});
|
|
257
|
-
const handlerNames = nodes.map((node) => resolver.handler.name(node));
|
|
258
|
-
const imports = nodes.map((node, index) => {
|
|
259
|
-
const operationName = handlerNames[index];
|
|
260
|
-
const operationFile = resolver.file({
|
|
261
|
-
name: resolver.name(node.operationId),
|
|
262
|
-
extname: ".ts",
|
|
263
|
-
tag: node.tags[0] ?? "default",
|
|
264
|
-
path: node.path,
|
|
265
|
-
root,
|
|
266
|
-
output,
|
|
267
|
-
group: group ?? void 0
|
|
268
|
-
});
|
|
269
|
-
return _kubb_kit.ast.factory.createImport({
|
|
270
|
-
name: [operationName],
|
|
271
|
-
root: file.path,
|
|
272
|
-
path: operationFile.path
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
const handlers = handlerNames.map((name) => `${name}(undefined, options)`);
|
|
276
|
-
return [_kubb_kit.ast.factory.createFile({
|
|
277
|
-
baseName: file.baseName,
|
|
278
|
-
path: file.path,
|
|
279
|
-
meta: file.meta,
|
|
280
|
-
banner: resolver.default.banner(ctx.meta, {
|
|
281
|
-
output,
|
|
282
|
-
config,
|
|
283
|
-
file: {
|
|
284
|
-
path: file.path,
|
|
285
|
-
baseName: file.baseName
|
|
286
|
-
}
|
|
287
|
-
}),
|
|
288
|
-
footer: resolver.default.footer(ctx.meta, {
|
|
289
|
-
output,
|
|
290
|
-
config,
|
|
291
|
-
file: {
|
|
292
|
-
path: file.path,
|
|
293
|
-
baseName: file.baseName
|
|
294
|
-
}
|
|
295
|
-
}),
|
|
296
|
-
imports,
|
|
297
|
-
sources: [_kubb_kit.ast.factory.createSource({
|
|
298
|
-
name: createHandlersName,
|
|
299
|
-
isIndexable: true,
|
|
300
|
-
isExportable: true,
|
|
301
|
-
nodes: [_kubb_kit.ast.factory.createText(`export function ${createHandlersName}(options?: { baseURL?: string }) {
|
|
302
|
-
return [${handlers.join(", ")}] as const
|
|
303
|
-
}`)]
|
|
304
|
-
}), _kubb_kit.ast.factory.createSource({
|
|
305
|
-
name: handlersName,
|
|
306
|
-
isIndexable: true,
|
|
307
|
-
isExportable: true,
|
|
308
|
-
nodes: [_kubb_kit.ast.factory.createText(`export const ${handlersName} = ${createHandlersName}()`)]
|
|
309
|
-
})]
|
|
310
|
-
})];
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
// src/utils.ts
|
|
402
|
+
function getMockHeaders(response) {
|
|
403
|
+
return (response?.headers ?? []).flatMap((header) => {
|
|
404
|
+
if (header.name.toLowerCase() === "content-type") return [];
|
|
405
|
+
const example = header.examples?.find((entry) => entry.serializedValue !== void 0 || entry.value !== void 0);
|
|
406
|
+
const headerValue = example?.serializedValue ?? example?.value ?? header.schema.default ?? header.schema.examples?.[0];
|
|
407
|
+
if (headerValue === void 0 || headerValue === null) return [];
|
|
408
|
+
const headerText = toSimpleStyle(headerValue);
|
|
409
|
+
return /^[\t\x20-\x7E\x80-\xFF]*$/.test(headerText) ? [[header.name, headerText]] : [];
|
|
410
|
+
});
|
|
411
|
+
}
|
|
314
412
|
/**
|
|
315
413
|
* Returns the base URL as source code. A `${...}` value stays a template literal.
|
|
316
414
|
*/
|
|
@@ -338,13 +436,12 @@ function hasResponseSchema(response) {
|
|
|
338
436
|
* content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.
|
|
339
437
|
*/
|
|
340
438
|
function getResponseContentType(response) {
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
return typeof value === "string" && value.length > 0 ? value : null;
|
|
439
|
+
const contentType = getResponseEntry(response)?.contentType;
|
|
440
|
+
return typeof contentType === "string" && contentType.length > 0 ? contentType : null;
|
|
344
441
|
}
|
|
345
|
-
function
|
|
346
|
-
const
|
|
347
|
-
return
|
|
442
|
+
function getResponseEntry(response) {
|
|
443
|
+
const contents = response?.content ?? [];
|
|
444
|
+
return contents.find((entry) => _kubb_kit.ast.mediaType.isJson(entry)) ?? contents[0];
|
|
348
445
|
}
|
|
349
446
|
/**
|
|
350
447
|
* The first content type of a response that the client reads as text, such as `application/xml`, or `null` when none is.
|
|
@@ -352,7 +449,7 @@ function isJson(contentType) {
|
|
|
352
449
|
function getTextContentType(response) {
|
|
353
450
|
return (response?.content ?? []).find((entry) => {
|
|
354
451
|
const schema = toResponseContent(entry, response?.content).schema;
|
|
355
|
-
return !isJson(entry
|
|
452
|
+
return !_kubb_kit.ast.mediaType.isJson(entry) && schema && _kubb_kit.ast.ref.getSchema(schema).primitive === "string";
|
|
356
453
|
})?.contentType ?? null;
|
|
357
454
|
}
|
|
358
455
|
/**
|
|
@@ -361,40 +458,88 @@ function getTextContentType(response) {
|
|
|
361
458
|
*/
|
|
362
459
|
function responseReturn(body, status, response) {
|
|
363
460
|
const contentType = getContentType(response);
|
|
461
|
+
const headers = getMockHeaders(response);
|
|
462
|
+
const name = isValidVarName(body) ? body : "body";
|
|
463
|
+
const prelude = name === body ? "" : `const body = ${body}\n `;
|
|
464
|
+
if (contentType && _kubb_kit.ast.mediaType.getBase(contentType) === "text/event-stream") {
|
|
465
|
+
const entry = getResponseEntry(response);
|
|
466
|
+
const serialized = entry?.examples?.find((example) => example.serializedValue !== void 0)?.serializedValue;
|
|
467
|
+
const fallback = serialized === void 0 ? "''" : toLiteral(serialized);
|
|
468
|
+
if (entry?.stream?.shape !== "event") return `${prelude}return ${responseLiteral(`${name} === undefined ? ${fallback} : \`\${${eventDataLines(name)}}\\n\\n\``, status, contentType, headers)}`;
|
|
469
|
+
return `${prelude}const event = ${name} as { event?: unknown; id?: unknown; retry?: unknown; data?: unknown } | undefined
|
|
470
|
+
const lines = [
|
|
471
|
+
...(['event', 'id', 'retry'] as const).filter((field) => event?.[field] !== undefined).map((field) => \`\${field}: \${event?.[field]}\`),
|
|
472
|
+
// A client dispatches an event only once it has a \`data\` line, so an event without data still sends an empty one.
|
|
473
|
+
event?.data === undefined ? 'data:' : ${eventDataLines("event.data")},
|
|
474
|
+
]
|
|
475
|
+
|
|
476
|
+
return ${responseLiteral(`event === undefined ? ${fallback} : \`\${lines.join('\\n')}\\n\\n\``, status, contentType, headers)}`;
|
|
477
|
+
}
|
|
364
478
|
const textContentType = hasResponseSchema(response) ? getTextContentType(response) : null;
|
|
365
|
-
if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType)}`;
|
|
366
|
-
|
|
367
|
-
return `${name === body ? "" : `const body = ${body}\n `}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType)}
|
|
479
|
+
if (!textContentType) return `return ${responseLiteral(`JSON.stringify(${body})`, status, contentType, headers)}`;
|
|
480
|
+
return `${prelude}if (typeof ${name} === 'string') return ${responseLiteral(name, status, textContentType, headers)}
|
|
368
481
|
|
|
369
|
-
return ${responseLiteral(`JSON.stringify(${name})`, status, contentType)}`;
|
|
482
|
+
return ${responseLiteral(`JSON.stringify(${name})`, status, contentType, headers)}`;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* The `data:` lines of one Server-Sent Event. A string goes out as it is, since a client keeps string `data` as text.
|
|
486
|
+
*/
|
|
487
|
+
function eventDataLines(body) {
|
|
488
|
+
return `(typeof ${body} === 'string' ? ${body} : JSON.stringify(${body})).split('\\n').map((line) => \`data: \${line}\`).join('\\n')`;
|
|
370
489
|
}
|
|
371
490
|
/**
|
|
372
|
-
*
|
|
373
|
-
|
|
491
|
+
* The spec example of the response entry the mock sends, as source code, or `null` when that entry has none.
|
|
492
|
+
*/
|
|
493
|
+
function getExampleBody(response) {
|
|
494
|
+
const entry = getResponseEntry(response);
|
|
495
|
+
const example = entry?.examples?.find((item) => item.value !== void 0 || item.serializedValue !== void 0);
|
|
496
|
+
if (!entry || !example) return null;
|
|
497
|
+
if (example.value !== void 0) return toLiteral(example.value);
|
|
498
|
+
if (!_kubb_kit.ast.mediaType.isJson(entry)) return _kubb_kit.ast.mediaType.getBase(entry) !== "text/event-stream" && getTextContentType(response) === entry.contentType ? toLiteral(example.serializedValue) : null;
|
|
499
|
+
try {
|
|
500
|
+
return toLiteral(JSON.parse(example.serializedValue));
|
|
501
|
+
} catch {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const mswMethods = /* @__PURE__ */ new Set([
|
|
506
|
+
"GET",
|
|
507
|
+
"POST",
|
|
508
|
+
"PUT",
|
|
509
|
+
"PATCH",
|
|
510
|
+
"DELETE",
|
|
511
|
+
"HEAD",
|
|
512
|
+
"OPTIONS"
|
|
513
|
+
]);
|
|
514
|
+
/**
|
|
515
|
+
* Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post'). MSW has no `http.query`, `http.trace` or
|
|
516
|
+
* handler for a custom `additionalOperations` method, so such an operation maps to `http.all` and its handler checks the
|
|
517
|
+
* method itself.
|
|
374
518
|
*/
|
|
375
519
|
function getMswMethod(node) {
|
|
376
|
-
return node.method
|
|
520
|
+
return mswMethods.has(node.method) ? node.method.toLowerCase() : "all";
|
|
377
521
|
}
|
|
378
522
|
/**
|
|
379
|
-
* The `headers` entry of a mocked `Response` init, or an empty string when there is no
|
|
523
|
+
* The `headers` entry of a mocked `Response` init, or an empty string when there is no header to send.
|
|
380
524
|
*/
|
|
381
|
-
function headersBlock(contentType) {
|
|
382
|
-
|
|
525
|
+
function headersBlock(contentType, headers) {
|
|
526
|
+
const entries = [...contentType ? [`'Content-Type': ${_kubb_kit.text.quote(contentType)}`] : [], ...headers.map(([name, value]) => `${_kubb_kit.text.quote(name)}: ${_kubb_kit.text.quote(value)}`)];
|
|
527
|
+
return entries.length ? `\n headers: {\n ${entries.join(",\n ")}\n },` : "";
|
|
383
528
|
}
|
|
384
529
|
/**
|
|
385
530
|
* The `new HttpResponse<string>(...)` expression a mocked handler returns.
|
|
386
531
|
*/
|
|
387
|
-
function responseLiteral(body, status, contentType) {
|
|
532
|
+
function responseLiteral(body, status, contentType, headers) {
|
|
388
533
|
return `new HttpResponse<string>(${body}, {
|
|
389
|
-
status: ${status},${headersBlock(contentType)}
|
|
534
|
+
status: ${status},${headersBlock(contentType, headers)}
|
|
390
535
|
})`;
|
|
391
536
|
}
|
|
392
537
|
// src/components/Mock.tsx
|
|
393
|
-
function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }) {
|
|
538
|
+
function Mock({ baseURL = "", name, fakerName, responseName, typeName, requestTypeName, node }) {
|
|
394
539
|
const method = getMswMethod(node);
|
|
395
|
-
const methodGuard =
|
|
396
|
-
const successResponse =
|
|
397
|
-
const statusCode = successResponse ?
|
|
540
|
+
const methodGuard = method === "all" ? `if (info.request.method !== ${_kubb_kit.text.quote(node.method)}) return\n\n ` : "";
|
|
541
|
+
const successResponse = _kubb_kit.ast.http.responses.findPrimary(node);
|
|
542
|
+
const statusCode = successResponse ? _kubb_kit.ast.http.status.toCode(successResponse) : 200;
|
|
398
543
|
const url = _kubb_kit.Url.toPath(node.path);
|
|
399
544
|
const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
|
|
400
545
|
const paramType = fakerName ? typeName : dataType;
|
|
@@ -402,7 +547,10 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
|
|
|
402
547
|
const params = `data?: ${paramType} | ${requestBodyType ? `HttpResponseResolver<Record<string, string>, ${requestBodyType}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`}, options?: { baseURL?: string }`;
|
|
403
548
|
const httpCall = requestBodyType ? `http.${method}<Record<string, string>, ${requestBodyType}>` : `http.${method}`;
|
|
404
549
|
const urlLiteral = `\`\${options?.baseURL ?? ${resolveDefaultBaseURL(baseURL)}}${url.replace(/([^/]):/g, "$1\\\\:")}\``;
|
|
405
|
-
const
|
|
550
|
+
const example = fakerName ? null : getExampleBody(successResponse);
|
|
551
|
+
const exampleBody = example ? `data === undefined ? ${example} : data` : "data";
|
|
552
|
+
const responseBody = fakerName ? `data ?? ${fakerName}()` : exampleBody;
|
|
553
|
+
const responseStatements = responseName && !example ? `return ${responseName}(${responseBody})` : responseReturn(responseBody, statusCode, successResponse);
|
|
406
554
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
407
555
|
name,
|
|
408
556
|
isIndexable: true,
|
|
@@ -414,16 +562,15 @@ function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }
|
|
|
414
562
|
children: `return ${httpCall}(${urlLiteral}, function handler(info) {
|
|
415
563
|
${methodGuard}if(typeof data === 'function') return data(info)
|
|
416
564
|
|
|
417
|
-
${
|
|
565
|
+
${responseStatements}
|
|
418
566
|
})`
|
|
419
567
|
})
|
|
420
568
|
});
|
|
421
569
|
}
|
|
422
570
|
// src/components/Response.tsx
|
|
423
571
|
function Response({ name, typeName, response }) {
|
|
424
|
-
const
|
|
425
|
-
const
|
|
426
|
-
const responseName = `${name}Response${statusCode}`;
|
|
572
|
+
const params = `data?: ${typeName}`;
|
|
573
|
+
const responseName = `${name}Response${response.statusCode}`;
|
|
427
574
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
|
|
428
575
|
name: responseName,
|
|
429
576
|
isIndexable: true,
|
|
@@ -433,7 +580,7 @@ function Response({ name, typeName, response }) {
|
|
|
433
580
|
export: true,
|
|
434
581
|
params,
|
|
435
582
|
children: `
|
|
436
|
-
${responseReturn("data",
|
|
583
|
+
${responseReturn("data", _kubb_kit.ast.http.status.toCode(response), response)}`
|
|
437
584
|
})
|
|
438
585
|
});
|
|
439
586
|
}
|
|
@@ -448,7 +595,7 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
448
595
|
name: "msw",
|
|
449
596
|
renderer: _kubb_renderer_jsx.jsxRenderer,
|
|
450
597
|
operation(node, ctx) {
|
|
451
|
-
if (!_kubb_kit.ast.
|
|
598
|
+
if (!_kubb_kit.ast.http.isOperation(node)) return null;
|
|
452
599
|
const { driver, resolver, config, root } = ctx;
|
|
453
600
|
const { output, parser, baseURL, group } = ctx.options;
|
|
454
601
|
const fileName = resolver.name(node.operationId);
|
|
@@ -490,8 +637,8 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
490
637
|
}),
|
|
491
638
|
responseName: tsResolver.response.response(node)
|
|
492
639
|
};
|
|
493
|
-
const successResponses =
|
|
494
|
-
const referencedNames =
|
|
640
|
+
const successResponses = _kubb_kit.ast.http.responses.filterSuccess(node);
|
|
641
|
+
const referencedNames = _kubb_kit.ast.http.responses.getImportedNames(node.responses);
|
|
495
642
|
const enumOptions = pluginTs.options?.enum;
|
|
496
643
|
const enumNames = getEnumNames(ctx);
|
|
497
644
|
const hasResponseNameCollision = referencedNames.some((name) => {
|
|
@@ -502,14 +649,15 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
502
649
|
isEnum: enumNames.has(name)
|
|
503
650
|
}) === type.responseName;
|
|
504
651
|
});
|
|
505
|
-
const types =
|
|
506
|
-
|
|
507
|
-
return [code, hasResponseNameCollision && successResponse ? tsResolver.response.status(node, successResponse.statusCode) : typeName];
|
|
652
|
+
const types = node.responses.filter((response) => response.statusCode !== "default" && _kubb_kit.ast.http.status.getClass(response) !== 1).map((response) => {
|
|
653
|
+
return [response, _kubb_kit.ast.http.status.isSuccess(response) && !hasResponseNameCollision ? type.responseName : tsResolver.response.status(node, response.statusCode)];
|
|
508
654
|
});
|
|
509
|
-
const responseTypeNames = types.map(([, typeName]) => typeName)
|
|
655
|
+
const responseTypeNames = types.map(([, typeName]) => typeName);
|
|
510
656
|
const mockResponseName = hasResponseNameCollision && successResponses[0] ? tsResolver.response.status(node, successResponses[0].statusCode) : type.responseName;
|
|
511
657
|
const hasSuccessSchema = successResponses.some((response) => hasResponseSchema(response));
|
|
512
|
-
const
|
|
658
|
+
const primaryResponse = _kubb_kit.ast.http.responses.findPrimary(node);
|
|
659
|
+
const responseHelperName = primaryResponse && hasResponseSchema(primaryResponse) && types.some(([response, typeName]) => response.statusCode === primaryResponse.statusCode && typeName === mockResponseName) ? `${mock.name}Response${primaryResponse.statusCode}` : void 0;
|
|
660
|
+
const requestName = _kubb_kit.ast.http.requestBody.getSchema(node) ? tsResolver.response.body(node) : null;
|
|
513
661
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx.File, {
|
|
514
662
|
baseName: mock.file.baseName,
|
|
515
663
|
path: mock.file.path,
|
|
@@ -560,20 +708,17 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
560
708
|
root: mock.file.path,
|
|
561
709
|
path: faker.file.path
|
|
562
710
|
}),
|
|
563
|
-
types.
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
response,
|
|
569
|
-
name: mock.name
|
|
570
|
-
}, typeName);
|
|
571
|
-
}),
|
|
711
|
+
types.map(([response, typeName]) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Response, {
|
|
712
|
+
typeName,
|
|
713
|
+
response,
|
|
714
|
+
name: mock.name
|
|
715
|
+
}, response.statusCode)),
|
|
572
716
|
/* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Mock, {
|
|
573
717
|
name: mock.name,
|
|
574
718
|
typeName: mockResponseName,
|
|
575
719
|
requestTypeName: requestName,
|
|
576
720
|
fakerName: faker && hasSuccessSchema ? faker.name : void 0,
|
|
721
|
+
responseName: responseHelperName,
|
|
577
722
|
node,
|
|
578
723
|
baseURL
|
|
579
724
|
})
|
|
@@ -600,7 +745,7 @@ const mswGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
600
745
|
const resolverMsw = (0, _kubb_kit.createResolver)({
|
|
601
746
|
pluginName: "plugin-msw",
|
|
602
747
|
name(name) {
|
|
603
|
-
return this.memo(`name:${name}`, () =>
|
|
748
|
+
return this.memo(`name:${name}`, () => _kubb_kit.casing.camel(name, { suffix: "handler" }));
|
|
604
749
|
},
|
|
605
750
|
handler: {
|
|
606
751
|
name(node) {
|
|
@@ -675,7 +820,7 @@ const pluginMsw = (0, _kubb_kit.defineExtension)({
|
|
|
675
820
|
output,
|
|
676
821
|
parser,
|
|
677
822
|
baseURL,
|
|
678
|
-
group:
|
|
823
|
+
group: group ?? null,
|
|
679
824
|
exclude,
|
|
680
825
|
include,
|
|
681
826
|
override,
|