@kubb/plugin-msw 5.0.0-beta.10 → 5.0.0-beta.101
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 +22 -26
- package/dist/index.cjs +478 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +139 -6
- package/dist/index.js +475 -39
- package/dist/index.js.map +1 -1
- package/package.json +9 -30
- package/dist/components-BYvgvrY7.cjs +0 -569
- package/dist/components-BYvgvrY7.cjs.map +0 -1
- package/dist/components-Cm17DMTE.js +0 -510
- package/dist/components-Cm17DMTE.js.map +0 -1
- package/dist/components.cjs +0 -6
- package/dist/components.d.ts +0 -66
- package/dist/components.js +0 -2
- package/dist/generators-D9gvdP7Z.js +0 -177
- package/dist/generators-D9gvdP7Z.js.map +0 -1
- package/dist/generators-rZ99WaWQ.cjs +0 -187
- package/dist/generators-rZ99WaWQ.cjs.map +0 -1
- package/dist/generators.cjs +0 -4
- package/dist/generators.d.ts +0 -12
- package/dist/generators.js +0 -2
- package/dist/types-ItsHsMvC.d.ts +0 -94
- package/extension.yaml +0 -233
- package/src/components/Handlers.tsx +0 -19
- package/src/components/Mock.tsx +0 -71
- package/src/components/MockWithFaker.tsx +0 -70
- package/src/components/Response.tsx +0 -52
- package/src/components/index.ts +0 -4
- package/src/generators/handlersGenerator.tsx +0 -41
- package/src/generators/index.ts +0 -2
- package/src/generators/mswGenerator.tsx +0 -86
- package/src/index.ts +0 -2
- package/src/plugin.ts +0 -76
- package/src/resolvers/resolverMsw.ts +0 -28
- package/src/types.ts +0 -93
- package/src/utils.ts +0 -58
- /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/README.md
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<h1>@kubb/plugin-msw</h1>
|
|
3
2
|
<a href="https://kubb.dev" target="_blank" rel="noopener noreferrer">
|
|
4
|
-
<img
|
|
3
|
+
<img src="https://kubb.dev/og.png" alt="Kubb banner">
|
|
5
4
|
</a>
|
|
6
5
|
|
|
7
6
|
[![npm version][npm-version-src]][npm-version-href]
|
|
8
7
|
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
9
|
-
[![
|
|
8
|
+
[![Stars][stars-src]][stars-href]
|
|
10
9
|
[![License][license-src]][license-href]
|
|
11
|
-
[![
|
|
10
|
+
[![Node][node-src]][node-href]
|
|
12
11
|
|
|
13
12
|
<h4>
|
|
14
|
-
<a href="https://codesandbox.io/s/github/kubb-labs/plugins/tree/main/examples/msw" target="_blank">View Demo</a>
|
|
15
|
-
<span> · </span>
|
|
16
13
|
<a href="https://kubb.dev/plugins/msw" target="_blank">Documentation</a>
|
|
17
14
|
<span> · </span>
|
|
18
15
|
<a href="https://github.com/kubb-labs/kubb/issues/" target="_blank">Report Bug</a>
|
|
@@ -21,14 +18,13 @@
|
|
|
21
18
|
</h4>
|
|
22
19
|
</div>
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
<br />
|
|
22
|
+
|
|
23
|
+
# @kubb/plugin-msw
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
### Generate MSW request handlers with Kubb
|
|
27
26
|
|
|
28
|
-
-
|
|
29
|
-
- Infers request and response types from the spec
|
|
30
|
-
- Works in both browser (via service worker) and Node.js test environments
|
|
31
|
-
- Works with `@kubb/plugin-faker` to serve realistic response data
|
|
27
|
+
`@kubb/plugin-msw` generates Mock Service Worker (MSW) request handlers with Kubb. The handlers work in both browser and Node.js environments via MSW v2.
|
|
32
28
|
|
|
33
29
|
## Installation
|
|
34
30
|
|
|
@@ -46,7 +42,7 @@ See the [full documentation](https://kubb.dev/plugins/msw) for configuration opt
|
|
|
46
42
|
|
|
47
43
|
## Supporting Kubb
|
|
48
44
|
|
|
49
|
-
Kubb is an
|
|
45
|
+
Kubb is an open source project, and its development is funded entirely by sponsors. If you would like to become a sponsor, please consider:
|
|
50
46
|
|
|
51
47
|
- [Become a Sponsor on GitHub](https://github.com/sponsors/stijnvanhulle)
|
|
52
48
|
|
|
@@ -56,19 +52,19 @@ Kubb is an MIT-licensed open source project with its ongoing development made po
|
|
|
56
52
|
</a>
|
|
57
53
|
</p>
|
|
58
54
|
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
[MIT](https://github.com/kubb-labs/plugins/blob/main/LICENSE)
|
|
58
|
+
|
|
59
59
|
<!-- Badges -->
|
|
60
60
|
|
|
61
|
-
[npm-version-src]: https://
|
|
62
|
-
[npm-version-href]: https://
|
|
63
|
-
[npm-downloads-src]: https://
|
|
64
|
-
[npm-downloads-href]: https://
|
|
65
|
-
[
|
|
61
|
+
[npm-version-src]: https://shieldcn.dev/npm/v/@kubb/plugin-msw.svg?variant=secondary&size=xs&theme=zinc&mode=dark
|
|
62
|
+
[npm-version-href]: https://npmx.dev/package/@kubb/plugin-msw
|
|
63
|
+
[npm-downloads-src]: https://shieldcn.dev/npm/dm/@kubb/plugin-msw.svg?variant=secondary&size=xs&theme=zinc&mode=dark
|
|
64
|
+
[npm-downloads-href]: https://npmx.dev/package/@kubb/plugin-msw
|
|
65
|
+
[stars-src]: https://shieldcn.dev/github/stars/kubb-labs/kubb.svg?variant=secondary&size=xs&theme=zinc&mode=dark
|
|
66
|
+
[stars-href]: https://github.com/kubb-labs/kubb
|
|
67
|
+
[license-src]: https://shieldcn.dev/npm/license/@kubb/plugin-msw.svg?variant=secondary&size=xs&theme=zinc
|
|
66
68
|
[license-href]: https://github.com/kubb-labs/kubb/blob/main/LICENSE
|
|
67
|
-
[
|
|
68
|
-
[
|
|
69
|
-
[minified-src]: https://img.shields.io/bundlephobia/min/@kubb/plugin-msw?style=flat&colorA=18181B&colorB=f58517
|
|
70
|
-
[minified-href]: https://www.npmjs.com/package/@kubb/plugin-msw
|
|
71
|
-
[coverage-src]: https://img.shields.io/codecov/c/github/kubb-labs/kubb?style=flat&colorA=18181B&colorB=f58517
|
|
72
|
-
[coverage-href]: https://www.npmjs.com/package/@kubb/plugin-msw
|
|
73
|
-
[sponsors-src]: https://img.shields.io/github/sponsors/stijnvanhulle?style=flat&colorA=18181B&colorB=f58517
|
|
74
|
-
[sponsors-href]: https://github.com/sponsors/stijnvanhulle/
|
|
69
|
+
[node-src]: https://shieldcn.dev/npm/node/@kubb/plugin-msw.svg?variant=secondary&size=xs&theme=zinc&mode=dark
|
|
70
|
+
[node-href]: https://npmx.dev/package/@kubb/plugin-msw
|
package/dist/index.cjs
CHANGED
|
@@ -2,60 +2,498 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
let _kubb_core = require("@kubb/core");
|
|
5
|
+
//#endregion
|
|
6
|
+
let kubb_kit = require("kubb/kit");
|
|
8
7
|
let _kubb_plugin_faker = require("@kubb/plugin-faker");
|
|
9
8
|
let _kubb_plugin_ts = require("@kubb/plugin-ts");
|
|
9
|
+
let kubb_jsx = require("kubb/jsx");
|
|
10
|
+
let kubb_jsx_jsx_runtime = require("kubb/jsx/jsx-runtime");
|
|
11
|
+
//#region ../../internals/shared/src/operation.ts
|
|
12
|
+
function getStatusCodeNumber(statusCode) {
|
|
13
|
+
const code = Number(statusCode);
|
|
14
|
+
return Number.isNaN(code) ? null : code;
|
|
15
|
+
}
|
|
16
|
+
function isSuccessStatusCode(statusCode) {
|
|
17
|
+
const code = getStatusCodeNumber(statusCode);
|
|
18
|
+
return code !== null && code >= 200 && code < 300;
|
|
19
|
+
}
|
|
20
|
+
function getSuccessResponses(responses) {
|
|
21
|
+
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
22
|
+
}
|
|
23
|
+
function getOperationSuccessResponses(node) {
|
|
24
|
+
return getSuccessResponses(node.responses);
|
|
25
|
+
}
|
|
26
|
+
function getPrimarySuccessResponse(node) {
|
|
27
|
+
return getOperationSuccessResponses(node)[0] ?? null;
|
|
28
|
+
}
|
|
29
|
+
function resolveResponseTypes(node, resolver) {
|
|
30
|
+
const types = [];
|
|
31
|
+
for (const response of node.responses) {
|
|
32
|
+
if (response.statusCode === "default") {
|
|
33
|
+
types.push(["default", resolver.response.response(node)]);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const code = getStatusCodeNumber(response.statusCode);
|
|
37
|
+
if (code === null) continue;
|
|
38
|
+
types.push([code, isSuccessStatusCode(code) ? resolver.response.response(node) : resolver.response.status(node, response.statusCode)]);
|
|
39
|
+
}
|
|
40
|
+
return types;
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region ../../internals/utils/src/casing.ts
|
|
44
|
+
/**
|
|
45
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
46
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
47
|
+
* and capitalizes each word according to `pascal`.
|
|
48
|
+
*
|
|
49
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
50
|
+
*/
|
|
51
|
+
function toCamelOrPascal(text, pascal) {
|
|
52
|
+
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
53
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
54
|
+
return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
|
|
55
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Converts `text` to camelCase.
|
|
59
|
+
*
|
|
60
|
+
* @example Word boundaries
|
|
61
|
+
* `camelCase('hello-world') // 'helloWorld'`
|
|
62
|
+
*
|
|
63
|
+
* @example With a prefix
|
|
64
|
+
* `camelCase('tag', { prefix: 'create' }) // 'createTag'`
|
|
65
|
+
*/
|
|
66
|
+
function camelCase(text, { prefix = "", suffix = "" } = {}) {
|
|
67
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region ../../internals/shared/src/group.ts
|
|
71
|
+
/**
|
|
72
|
+
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
73
|
+
* shared default naming so every plugin groups output consistently:
|
|
74
|
+
*
|
|
75
|
+
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
76
|
+
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
77
|
+
*
|
|
78
|
+
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
79
|
+
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
80
|
+
* per-plugin convention.
|
|
81
|
+
*
|
|
82
|
+
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* createGroupConfig(group) // shared across every plugin
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
function createGroupConfig(group) {
|
|
90
|
+
if (!group) return null;
|
|
91
|
+
const defaultName = (ctx) => {
|
|
92
|
+
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
93
|
+
return camelCase(ctx.group);
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
...group,
|
|
97
|
+
name: group.name ? group.name : defaultName
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/generators/handlersGenerator.ts
|
|
102
|
+
/**
|
|
103
|
+
* Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
|
|
104
|
+
* `handlers.ts` file that re-exports every generated handler in operation
|
|
105
|
+
* order, ready to spread into `setupServer(...handlers)` or
|
|
106
|
+
* `setupWorker(...handlers)`.
|
|
107
|
+
*/
|
|
108
|
+
const handlersGenerator = (0, kubb_kit.defineGenerator)({
|
|
109
|
+
name: "plugin-msw",
|
|
110
|
+
operations(nodes, ctx) {
|
|
111
|
+
const { resolver, config, root } = ctx;
|
|
112
|
+
const { output, group } = ctx.options;
|
|
113
|
+
const handlersName = resolver.handler.listName();
|
|
114
|
+
const file = resolver.file({
|
|
115
|
+
name: handlersName,
|
|
116
|
+
extname: ".ts",
|
|
117
|
+
root,
|
|
118
|
+
output,
|
|
119
|
+
group: group ?? void 0
|
|
120
|
+
});
|
|
121
|
+
const imports = nodes.map((node) => {
|
|
122
|
+
const operationName = resolver.handler.name(node);
|
|
123
|
+
const operationFile = resolver.file({
|
|
124
|
+
name: resolver.name(node.operationId),
|
|
125
|
+
extname: ".ts",
|
|
126
|
+
tag: node.tags[0] ?? "default",
|
|
127
|
+
path: node.path,
|
|
128
|
+
root,
|
|
129
|
+
output,
|
|
130
|
+
group: group ?? void 0
|
|
131
|
+
});
|
|
132
|
+
return kubb_kit.ast.factory.createImport({
|
|
133
|
+
name: [operationName],
|
|
134
|
+
root: file.path,
|
|
135
|
+
path: operationFile.path
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
const handlers = nodes.map((node) => `${resolver.handler.name(node)}()`);
|
|
139
|
+
return [kubb_kit.ast.factory.createFile({
|
|
140
|
+
baseName: file.baseName,
|
|
141
|
+
path: file.path,
|
|
142
|
+
meta: file.meta,
|
|
143
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
144
|
+
output,
|
|
145
|
+
config,
|
|
146
|
+
file: {
|
|
147
|
+
path: file.path,
|
|
148
|
+
baseName: file.baseName
|
|
149
|
+
}
|
|
150
|
+
}),
|
|
151
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
152
|
+
output,
|
|
153
|
+
config,
|
|
154
|
+
file: {
|
|
155
|
+
path: file.path,
|
|
156
|
+
baseName: file.baseName
|
|
157
|
+
}
|
|
158
|
+
}),
|
|
159
|
+
imports,
|
|
160
|
+
sources: [kubb_kit.ast.factory.createSource({
|
|
161
|
+
name: handlersName,
|
|
162
|
+
isIndexable: true,
|
|
163
|
+
isExportable: true,
|
|
164
|
+
nodes: [kubb_kit.ast.factory.createText(`export const ${handlersName} = ${JSON.stringify(handlers).replaceAll("\"", "")} as const`)]
|
|
165
|
+
})]
|
|
166
|
+
})];
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/utils.ts
|
|
171
|
+
/**
|
|
172
|
+
* Gets the content type from a response, defaulting to 'application/json' if a schema exists.
|
|
173
|
+
*/
|
|
174
|
+
function getContentType(response) {
|
|
175
|
+
if (!hasResponseSchema(response)) return null;
|
|
176
|
+
return getResponseContentType(response) ?? "application/json";
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Determines if a response has a schema that is not void or any.
|
|
180
|
+
*/
|
|
181
|
+
function hasResponseSchema(response) {
|
|
182
|
+
const schema = response?.content?.find((entry) => entry.schema)?.schema;
|
|
183
|
+
return !!schema && schema.type !== "void" && schema.type !== "any";
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Picks the content type used for the mocked response header. When a response declares multiple
|
|
187
|
+
* content types, JSON is preferred (the faker mock body is JSON), otherwise the first declared type.
|
|
188
|
+
*/
|
|
189
|
+
function getResponseContentType(response) {
|
|
190
|
+
const contents = response?.content ?? [];
|
|
191
|
+
const value = (contents.find((entry) => {
|
|
192
|
+
const baseType = entry.contentType?.split(";")[0]?.trim().toLowerCase();
|
|
193
|
+
return baseType === "application/json" || baseType?.endsWith("+json");
|
|
194
|
+
}) ?? contents[0])?.contentType;
|
|
195
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post').
|
|
199
|
+
*/
|
|
200
|
+
function getMswMethod(node) {
|
|
201
|
+
return kubb_kit.ast.isHttpOperationNode(node) ? node.method.toLowerCase() : "";
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Resolves faker metadata for an MSW operation, including response name and file path.
|
|
205
|
+
*/
|
|
206
|
+
function resolveFakerMeta(node, options) {
|
|
207
|
+
const { root, fakerResolver, fakerOutput, fakerGroup } = options;
|
|
208
|
+
const tag = node.tags[0] ?? "default";
|
|
209
|
+
return {
|
|
210
|
+
name: fakerResolver.response.response(node),
|
|
211
|
+
file: fakerResolver.file({
|
|
212
|
+
name: node.operationId,
|
|
213
|
+
extname: ".ts",
|
|
214
|
+
tag,
|
|
215
|
+
path: node.path,
|
|
216
|
+
root,
|
|
217
|
+
output: fakerOutput,
|
|
218
|
+
group: fakerGroup ?? void 0
|
|
219
|
+
})
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/components/Mock.tsx
|
|
224
|
+
const declarationPrinter$1 = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
225
|
+
function Mock({ baseURL = "", name, fakerName, typeName, requestTypeName, node }) {
|
|
226
|
+
const method = getMswMethod(node);
|
|
227
|
+
const successResponse = getPrimarySuccessResponse(node);
|
|
228
|
+
const statusCode = successResponse ? Number(successResponse.statusCode) : 200;
|
|
229
|
+
const contentType = getContentType(successResponse);
|
|
230
|
+
const url = kubb_kit.ast.isHttpOperationNode(node) ? kubb_kit.Url.toPath(node.path) : "";
|
|
231
|
+
const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean);
|
|
232
|
+
const dataType = hasResponseSchema(successResponse) ? typeName : "string | number | boolean | null | object";
|
|
233
|
+
const paramType = fakerName ? typeName : dataType;
|
|
234
|
+
const callbackType = requestTypeName ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}>` : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`;
|
|
235
|
+
const params = declarationPrinter$1.print((0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
|
|
236
|
+
name: "data",
|
|
237
|
+
type: `${paramType} | ${callbackType}`,
|
|
238
|
+
optional: true
|
|
239
|
+
})] }));
|
|
240
|
+
const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}>` : `http.${method}`;
|
|
241
|
+
const requestUrl = `${baseURL}${url.replace(/([^/]):/g, "$1\\\\:")}`;
|
|
242
|
+
const urlLiteral = fakerName ? `'${requestUrl}'` : `\`${requestUrl}\``;
|
|
243
|
+
const responseBody = fakerName ? `JSON.stringify(data || ${fakerName}(data))` : "JSON.stringify(data)";
|
|
244
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
|
|
245
|
+
name,
|
|
246
|
+
isIndexable: true,
|
|
247
|
+
isExportable: true,
|
|
248
|
+
children: /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.Function, {
|
|
249
|
+
name,
|
|
250
|
+
export: true,
|
|
251
|
+
params: params ?? "",
|
|
252
|
+
children: `return ${httpCall}(${urlLiteral}, function handler(info) {
|
|
253
|
+
if(typeof data === 'function') return data(info)
|
|
254
|
+
|
|
255
|
+
return new Response(${responseBody}, {
|
|
256
|
+
status: ${statusCode},
|
|
257
|
+
${headers.length ? ` headers: {
|
|
258
|
+
${headers.join(", \n")}
|
|
259
|
+
},` : ""}
|
|
260
|
+
})
|
|
261
|
+
})`
|
|
262
|
+
})
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/components/Response.tsx
|
|
267
|
+
const declarationPrinter = (0, _kubb_plugin_ts.functionPrinter)({ mode: "declaration" });
|
|
268
|
+
function Response({ name, typeName, response }) {
|
|
269
|
+
const statusCode = Number(response.statusCode);
|
|
270
|
+
const contentType = getContentType(response);
|
|
271
|
+
const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean);
|
|
272
|
+
const params = declarationPrinter.print((0, _kubb_plugin_ts.createFunctionParameters)({ params: [(0, _kubb_plugin_ts.createFunctionParameter)({
|
|
273
|
+
name: "data",
|
|
274
|
+
type: typeName,
|
|
275
|
+
optional: !hasResponseSchema(response)
|
|
276
|
+
})] }));
|
|
277
|
+
const responseName = `${name}Response${statusCode}`;
|
|
278
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Source, {
|
|
279
|
+
name: responseName,
|
|
280
|
+
isIndexable: true,
|
|
281
|
+
isExportable: true,
|
|
282
|
+
children: /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.Function, {
|
|
283
|
+
name: responseName,
|
|
284
|
+
export: true,
|
|
285
|
+
params: params ?? "",
|
|
286
|
+
children: `
|
|
287
|
+
return new Response(JSON.stringify(data), {
|
|
288
|
+
status: ${statusCode},
|
|
289
|
+
${headers.length ? ` headers: {
|
|
290
|
+
${headers.join(", \n")}
|
|
291
|
+
},` : ""}
|
|
292
|
+
})`
|
|
293
|
+
})
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/generators/mswGenerator.tsx
|
|
298
|
+
/**
|
|
299
|
+
* Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler
|
|
300
|
+
* per OpenAPI operation. With `parser: 'faker'` the handler returns a value
|
|
301
|
+
* from `@kubb/plugin-faker`; with `parser: 'data'` it returns a typed empty
|
|
302
|
+
* payload for tests to fill in.
|
|
303
|
+
*/
|
|
304
|
+
const mswGenerator = (0, kubb_kit.defineGenerator)({
|
|
305
|
+
name: "msw",
|
|
306
|
+
renderer: kubb_jsx.jsxRenderer,
|
|
307
|
+
operation(node, ctx) {
|
|
308
|
+
if (!kubb_kit.ast.isHttpOperationNode(node)) return null;
|
|
309
|
+
const { driver, resolver, config, root } = ctx;
|
|
310
|
+
const { output, parser, baseURL, group } = ctx.options;
|
|
311
|
+
const fileName = resolver.name(node.operationId);
|
|
312
|
+
const mock = {
|
|
313
|
+
name: resolver.handler.name(node),
|
|
314
|
+
file: resolver.file({
|
|
315
|
+
name: fileName,
|
|
316
|
+
extname: ".ts",
|
|
317
|
+
tag: node.tags[0] ?? "default",
|
|
318
|
+
path: node.path,
|
|
319
|
+
root,
|
|
320
|
+
output,
|
|
321
|
+
group: group ?? void 0
|
|
322
|
+
})
|
|
323
|
+
};
|
|
324
|
+
const fakerPlugin = parser === "faker" ? driver.getPlugin(_kubb_plugin_faker.pluginFakerName) : null;
|
|
325
|
+
const faker = parser === "faker" && fakerPlugin ? resolveFakerMeta(node, {
|
|
326
|
+
root,
|
|
327
|
+
fakerResolver: driver.getResolver(_kubb_plugin_faker.pluginFakerName),
|
|
328
|
+
fakerOutput: fakerPlugin.options?.output ?? output,
|
|
329
|
+
fakerGroup: fakerPlugin.options?.group ?? null
|
|
330
|
+
}) : null;
|
|
331
|
+
const pluginTs = driver.getPlugin(_kubb_plugin_ts.pluginTsName);
|
|
332
|
+
if (!pluginTs) return null;
|
|
333
|
+
const tsResolver = driver.getResolver(_kubb_plugin_ts.pluginTsName);
|
|
334
|
+
const type = {
|
|
335
|
+
file: tsResolver.file({
|
|
336
|
+
name: node.operationId,
|
|
337
|
+
extname: ".ts",
|
|
338
|
+
tag: node.tags[0] ?? "default",
|
|
339
|
+
path: node.path,
|
|
340
|
+
root,
|
|
341
|
+
output: pluginTs.options?.output ?? output,
|
|
342
|
+
group: pluginTs.options?.group ?? void 0
|
|
343
|
+
}),
|
|
344
|
+
responseName: tsResolver.response.response(node)
|
|
345
|
+
};
|
|
346
|
+
const types = resolveResponseTypes(node, tsResolver);
|
|
347
|
+
const hasSuccessSchema = getOperationSuccessResponses(node).some((response) => !!response.content?.[0]?.schema);
|
|
348
|
+
const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.response.body(node) : null;
|
|
349
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsxs)(kubb_jsx.File, {
|
|
350
|
+
baseName: mock.file.baseName,
|
|
351
|
+
path: mock.file.path,
|
|
352
|
+
meta: mock.file.meta,
|
|
353
|
+
banner: resolver.default.banner(ctx.meta, {
|
|
354
|
+
output,
|
|
355
|
+
config,
|
|
356
|
+
file: {
|
|
357
|
+
path: mock.file.path,
|
|
358
|
+
baseName: mock.file.baseName
|
|
359
|
+
}
|
|
360
|
+
}),
|
|
361
|
+
footer: resolver.default.footer(ctx.meta, {
|
|
362
|
+
output,
|
|
363
|
+
config,
|
|
364
|
+
file: {
|
|
365
|
+
path: mock.file.path,
|
|
366
|
+
baseName: mock.file.baseName
|
|
367
|
+
}
|
|
368
|
+
}),
|
|
369
|
+
children: [
|
|
370
|
+
/* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
371
|
+
name: ["http"],
|
|
372
|
+
path: "msw"
|
|
373
|
+
}),
|
|
374
|
+
/* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
375
|
+
name: ["HttpResponseResolver"],
|
|
376
|
+
isTypeOnly: true,
|
|
377
|
+
path: "msw"
|
|
378
|
+
}),
|
|
379
|
+
/* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
380
|
+
name: Array.from(/* @__PURE__ */ new Set([
|
|
381
|
+
type.responseName,
|
|
382
|
+
...types.map((t) => t[1]),
|
|
383
|
+
...requestName ? [requestName] : []
|
|
384
|
+
])),
|
|
385
|
+
path: type.file.path,
|
|
386
|
+
root: mock.file.path,
|
|
387
|
+
isTypeOnly: true
|
|
388
|
+
}),
|
|
389
|
+
parser === "faker" && faker && /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(kubb_jsx.File.Import, {
|
|
390
|
+
name: [faker.name],
|
|
391
|
+
root: mock.file.path,
|
|
392
|
+
path: faker.file.path
|
|
393
|
+
}),
|
|
394
|
+
types.filter(([code]) => code !== "default").map(([code, typeName]) => {
|
|
395
|
+
const response = node.responses.find((item) => item.statusCode === String(code));
|
|
396
|
+
if (!response) return null;
|
|
397
|
+
return /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Response, {
|
|
398
|
+
typeName,
|
|
399
|
+
response,
|
|
400
|
+
name: mock.name
|
|
401
|
+
}, typeName);
|
|
402
|
+
}),
|
|
403
|
+
parser === "faker" && faker && hasSuccessSchema ? /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Mock, {
|
|
404
|
+
name: mock.name,
|
|
405
|
+
typeName: type.responseName,
|
|
406
|
+
requestTypeName: requestName,
|
|
407
|
+
fakerName: faker.name,
|
|
408
|
+
node,
|
|
409
|
+
baseURL
|
|
410
|
+
}) : /* @__PURE__ */ (0, kubb_jsx_jsx_runtime.jsx)(Mock, {
|
|
411
|
+
name: mock.name,
|
|
412
|
+
typeName: type.responseName,
|
|
413
|
+
requestTypeName: requestName,
|
|
414
|
+
node,
|
|
415
|
+
baseURL
|
|
416
|
+
})
|
|
417
|
+
]
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
//#endregion
|
|
10
422
|
//#region src/resolvers/resolverMsw.ts
|
|
11
423
|
/**
|
|
12
|
-
*
|
|
424
|
+
* Default resolver used by `@kubb/plugin-msw`. Decides the names and file
|
|
425
|
+
* paths for every generated MSW handler. Function names get a `Handler`
|
|
426
|
+
* suffix; the aggregate export is always `handlers`.
|
|
427
|
+
*
|
|
428
|
+
* The top-level `name` applies the `handler` suffix, and `file` falls back to the built-in
|
|
429
|
+
* `toFilePath` casing. Operation-specific naming is grouped under the `handler` namespace.
|
|
430
|
+
*
|
|
431
|
+
* @example Resolve a handler name
|
|
432
|
+
* ```ts
|
|
433
|
+
* import { resolverMsw } from '@kubb/plugin-msw'
|
|
13
434
|
*
|
|
14
|
-
*
|
|
435
|
+
* resolverMsw.name('addPet') // 'addPetHandler'
|
|
436
|
+
* ```
|
|
15
437
|
*/
|
|
16
|
-
const resolverMsw = (0,
|
|
17
|
-
name: "default",
|
|
438
|
+
const resolverMsw = (0, kubb_kit.createResolver)({
|
|
18
439
|
pluginName: "plugin-msw",
|
|
19
|
-
|
|
20
|
-
return
|
|
440
|
+
name(name) {
|
|
441
|
+
return camelCase(name, { suffix: "handler" });
|
|
21
442
|
},
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return this.resolveName(node.operationId);
|
|
30
|
-
},
|
|
31
|
-
resolveHandlersName() {
|
|
32
|
-
return "handlers";
|
|
443
|
+
handler: {
|
|
444
|
+
name(node) {
|
|
445
|
+
return this.name(node.operationId);
|
|
446
|
+
},
|
|
447
|
+
listName() {
|
|
448
|
+
return "handlers";
|
|
449
|
+
}
|
|
33
450
|
}
|
|
34
|
-
})
|
|
451
|
+
});
|
|
35
452
|
//#endregion
|
|
36
453
|
//#region src/plugin.ts
|
|
454
|
+
/**
|
|
455
|
+
* Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and
|
|
456
|
+
* cross-plugin dependency references.
|
|
457
|
+
*/
|
|
37
458
|
const pluginMswName = "plugin-msw";
|
|
38
|
-
|
|
459
|
+
/**
|
|
460
|
+
* Generates MSW request handlers from an OpenAPI spec. Drop them into your
|
|
461
|
+
* test setup or service worker to mock the API end-to-end. Request path,
|
|
462
|
+
* method, status, and response body all stay in sync with the spec. Combine
|
|
463
|
+
* with `@kubb/plugin-faker` (via `parser: 'faker'`) to seed handlers with
|
|
464
|
+
* realistic data.
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* ```ts
|
|
468
|
+
* import { defineConfig } from 'kubb/config'
|
|
469
|
+
* import { pluginTs } from '@kubb/plugin-ts'
|
|
470
|
+
* import { pluginMsw } from '@kubb/plugin-msw'
|
|
471
|
+
*
|
|
472
|
+
* export default defineConfig({
|
|
473
|
+
* input: './petStore.yaml',
|
|
474
|
+
* output: { path: './src/gen' },
|
|
475
|
+
* plugins: [
|
|
476
|
+
* pluginTs(),
|
|
477
|
+
* pluginMsw({
|
|
478
|
+
* output: { path: './handlers' },
|
|
479
|
+
* handlers: true,
|
|
480
|
+
* }),
|
|
481
|
+
* ],
|
|
482
|
+
* })
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
485
|
+
const pluginMsw = (0, kubb_kit.definePlugin)((options) => {
|
|
39
486
|
const { output = {
|
|
40
487
|
path: "handlers",
|
|
41
|
-
|
|
42
|
-
}, group, exclude = [], include, override = [], handlers = false, parser = "data", baseURL, resolver: userResolver,
|
|
43
|
-
const groupConfig = group
|
|
44
|
-
...group,
|
|
45
|
-
name: group.name ? group.name : (ctx) => {
|
|
46
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
47
|
-
return `${require_components.camelCase(ctx.group)}Controller`;
|
|
48
|
-
}
|
|
49
|
-
} : void 0;
|
|
488
|
+
barrel: { type: "named" }
|
|
489
|
+
}, group, exclude = [], include, override = [], handlers = false, parser = "data", baseURL, resolver: userResolver, macros: userMacros } = options;
|
|
490
|
+
const groupConfig = createGroupConfig(group);
|
|
50
491
|
return {
|
|
51
492
|
name: pluginMswName,
|
|
52
493
|
options,
|
|
53
|
-
dependencies: [_kubb_plugin_ts.pluginTsName, parser === "faker" ? _kubb_plugin_faker.pluginFakerName :
|
|
494
|
+
dependencies: [_kubb_plugin_ts.pluginTsName, parser === "faker" ? _kubb_plugin_faker.pluginFakerName : null].filter((dependency) => Boolean(dependency)),
|
|
54
495
|
hooks: { "kubb:plugin:setup"(ctx) {
|
|
55
|
-
const resolver = userResolver ?
|
|
56
|
-
...resolverMsw,
|
|
57
|
-
...userResolver
|
|
58
|
-
} : resolverMsw;
|
|
496
|
+
const resolver = userResolver ? kubb_kit.Resolver.merge(resolverMsw, userResolver) : resolverMsw;
|
|
59
497
|
ctx.setOptions({
|
|
60
498
|
output,
|
|
61
499
|
parser,
|
|
@@ -68,10 +506,9 @@ const pluginMsw = (0, _kubb_core.definePlugin)((options) => {
|
|
|
68
506
|
resolver
|
|
69
507
|
});
|
|
70
508
|
ctx.setResolver(resolver);
|
|
71
|
-
if (
|
|
72
|
-
ctx.addGenerator(
|
|
73
|
-
if (handlers) ctx.addGenerator(
|
|
74
|
-
for (const gen of userGenerators) ctx.addGenerator(gen);
|
|
509
|
+
if (userMacros?.length) ctx.setMacros(userMacros);
|
|
510
|
+
ctx.addGenerator(mswGenerator);
|
|
511
|
+
if (handlers) ctx.addGenerator(handlersGenerator);
|
|
75
512
|
} }
|
|
76
513
|
};
|
|
77
514
|
});
|
|
@@ -79,5 +516,6 @@ const pluginMsw = (0, _kubb_core.definePlugin)((options) => {
|
|
|
79
516
|
exports.default = pluginMsw;
|
|
80
517
|
exports.pluginMsw = pluginMsw;
|
|
81
518
|
exports.pluginMswName = pluginMswName;
|
|
519
|
+
exports.resolverMsw = resolverMsw;
|
|
82
520
|
|
|
83
521
|
//# sourceMappingURL=index.cjs.map
|