@kubb/plugin-msw 5.0.0-beta.3 → 5.0.0-beta.30

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.
Files changed (38) hide show
  1. package/README.md +26 -5
  2. package/dist/{components-vO0FIb2i.js → components--7xwlHZU.js} +52 -59
  3. package/dist/components--7xwlHZU.js.map +1 -0
  4. package/dist/{components-CLQ77DVn.cjs → components-CHyTtokd.cjs} +55 -68
  5. package/dist/components-CHyTtokd.cjs.map +1 -0
  6. package/dist/components.cjs +1 -1
  7. package/dist/components.d.ts +5 -5
  8. package/dist/components.js +1 -1
  9. package/dist/{generators-BPJCs1x1.js → generators-BcGCyGBt.js} +61 -32
  10. package/dist/generators-BcGCyGBt.js.map +1 -0
  11. package/dist/{generators-CrmMwWE4.cjs → generators-DzDKzDFe.cjs} +60 -31
  12. package/dist/generators-DzDKzDFe.cjs.map +1 -0
  13. package/dist/generators.cjs +1 -1
  14. package/dist/generators.d.ts +13 -1
  15. package/dist/generators.js +1 -1
  16. package/dist/index.cjs +54 -9
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.ts +31 -1
  19. package/dist/index.js +54 -9
  20. package/dist/index.js.map +1 -1
  21. package/dist/types-CLAiv8qc.d.ts +103 -0
  22. package/extension.yaml +680 -0
  23. package/package.json +11 -14
  24. package/src/components/Handlers.tsx +1 -1
  25. package/src/components/Mock.tsx +5 -4
  26. package/src/components/MockWithFaker.tsx +5 -4
  27. package/src/components/Response.tsx +1 -1
  28. package/src/generators/handlersGenerator.tsx +18 -12
  29. package/src/generators/mswGenerator.tsx +27 -17
  30. package/src/plugin.ts +32 -4
  31. package/src/resolvers/resolverMsw.ts +19 -3
  32. package/src/types.ts +35 -21
  33. package/src/utils.ts +16 -60
  34. package/dist/components-CLQ77DVn.cjs.map +0 -1
  35. package/dist/components-vO0FIb2i.js.map +0 -1
  36. package/dist/generators-BPJCs1x1.js.map +0 -1
  37. package/dist/generators-CrmMwWE4.cjs.map +0 -1
  38. package/dist/types-Dxu0KMQ4.d.ts +0 -89
package/package.json CHANGED
@@ -1,23 +1,19 @@
1
1
  {
2
2
  "name": "@kubb/plugin-msw",
3
- "version": "5.0.0-beta.3",
4
- "description": "Mock Service Worker (MSW) handlers generator plugin for Kubb, creating API mocks from OpenAPI specifications for frontend development and testing.",
3
+ "version": "5.0.0-beta.30",
4
+ "description": "Generate Mock Service Worker (MSW) request handlers from your OpenAPI specification. Intercept HTTP requests in the browser or Node.js for seamless frontend development and testing.",
5
5
  "keywords": [
6
6
  "api-mocking",
7
- "code-generator",
7
+ "code-generation",
8
8
  "codegen",
9
- "development",
10
- "interceptor",
11
9
  "kubb",
12
10
  "mock-service-worker",
13
11
  "mocking",
14
12
  "mocks",
15
13
  "msw",
16
- "oas",
17
14
  "openapi",
18
- "plugins",
15
+ "service-worker",
19
16
  "swagger",
20
- "testing",
21
17
  "typescript"
22
18
  ],
23
19
  "license": "MIT",
@@ -30,7 +26,7 @@
30
26
  "files": [
31
27
  "src",
32
28
  "dist",
33
- "plugin.json",
29
+ "extension.yaml",
34
30
  "!/**/**.test.**",
35
31
  "!/**/__tests__/**",
36
32
  "!/**/__snapshots__/**"
@@ -76,16 +72,17 @@
76
72
  "registry": "https://registry.npmjs.org/"
77
73
  },
78
74
  "dependencies": {
79
- "@kubb/core": "5.0.0-beta.3",
80
- "@kubb/renderer-jsx": "5.0.0-beta.3",
81
- "@kubb/plugin-faker": "5.0.0-beta.3",
82
- "@kubb/plugin-ts": "5.0.0-beta.3"
75
+ "@kubb/core": "5.0.0-beta.29",
76
+ "@kubb/renderer-jsx": "5.0.0-beta.29",
77
+ "@kubb/plugin-faker": "5.0.0-beta.30",
78
+ "@kubb/plugin-ts": "5.0.0-beta.30"
83
79
  },
84
80
  "devDependencies": {
81
+ "@internals/shared": "0.0.0",
85
82
  "@internals/utils": "0.0.0"
86
83
  },
87
84
  "peerDependencies": {
88
- "@kubb/renderer-jsx": "5.0.0-beta.3"
85
+ "@kubb/renderer-jsx": "5.0.0-beta.29"
89
86
  },
90
87
  "size-limit": [
91
88
  {
@@ -7,7 +7,7 @@ type HandlersProps = {
7
7
  */
8
8
  name: string
9
9
  // custom
10
- handlers: string[]
10
+ handlers: Array<string>
11
11
  }
12
12
 
13
13
  export function Handlers({ name, handlers }: HandlersProps): KubbReactNode {
@@ -1,15 +1,16 @@
1
+ import { getPrimarySuccessResponse } from '@internals/shared'
1
2
  import { URLPath } from '@internals/utils'
2
3
  import { ast } from '@kubb/core'
3
4
  import { functionPrinter } from '@kubb/plugin-ts'
4
5
  import { File, Function } from '@kubb/renderer-jsx'
5
6
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
6
- import { getContentType, getMswMethod, getMswUrl, getPrimarySuccessResponse, hasResponseSchema } from '../utils.ts'
7
+ import { getContentType, getMswMethod, getMswUrl, hasResponseSchema } from '../utils.ts'
7
8
 
8
9
  type Props = {
9
10
  name: string
10
11
  typeName: string
11
- requestTypeName?: string
12
- baseURL: string | undefined
12
+ requestTypeName?: string | null
13
+ baseURL: string | null | undefined
13
14
  node: ast.OperationNode
14
15
  }
15
16
 
@@ -22,7 +23,7 @@ export function Mock({ baseURL = '', name, typeName, requestTypeName, node }: Pr
22
23
  const contentType = getContentType(successResponse)
23
24
  const url = new URLPath(getMswUrl(node)).toURLPath()
24
25
 
25
- const headers = [contentType ? `'Content-Type': '${contentType}'` : undefined].filter(Boolean)
26
+ const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean)
26
27
  const responseHasSchema = hasResponseSchema(successResponse)
27
28
  const dataType = responseHasSchema ? typeName : 'string | number | boolean | null | object'
28
29
 
@@ -1,16 +1,17 @@
1
+ import { getPrimarySuccessResponse } from '@internals/shared'
1
2
  import { URLPath } from '@internals/utils'
2
3
  import { ast } from '@kubb/core'
3
4
  import { functionPrinter } from '@kubb/plugin-ts'
4
5
  import { File, Function } from '@kubb/renderer-jsx'
5
6
  import type { KubbReactNode } from '@kubb/renderer-jsx/types'
6
- import { getContentType, getMswMethod, getMswUrl, getPrimarySuccessResponse } from '../utils.ts'
7
+ import { getContentType, getMswMethod, getMswUrl } from '../utils.ts'
7
8
 
8
9
  type Props = {
9
10
  name: string
10
11
  typeName: string
11
- requestTypeName?: string
12
+ requestTypeName?: string | null
12
13
  fakerName: string
13
- baseURL: string | undefined
14
+ baseURL: string | null | undefined
14
15
  node: ast.OperationNode
15
16
  }
16
17
 
@@ -23,7 +24,7 @@ export function MockWithFaker({ baseURL = '', name, fakerName, typeName, request
23
24
  const contentType = getContentType(successResponse)
24
25
  const url = new URLPath(getMswUrl(node)).toURLPath()
25
26
 
26
- const headers = [contentType ? `'Content-Type': '${contentType}'` : undefined].filter(Boolean)
27
+ const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean)
27
28
 
28
29
  const callbackType = requestTypeName
29
30
  ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}, any>`
@@ -16,7 +16,7 @@ const declarationPrinter = functionPrinter({ mode: 'declaration' })
16
16
  export function Response({ name, typeName, response }: Props): KubbReactNode {
17
17
  const statusCode = Number(response.statusCode)
18
18
  const contentType = getContentType(response)
19
- const headers = [contentType ? `'Content-Type': '${contentType}'` : undefined].filter(Boolean)
19
+ const headers = [contentType ? `'Content-Type': '${contentType}'` : null].filter(Boolean)
20
20
 
21
21
  const params = declarationPrinter.print(
22
22
  ast.createFunctionParameters({
@@ -1,40 +1,46 @@
1
1
  import { defineGenerator } from '@kubb/core'
2
- import { File, jsxRenderer } from '@kubb/renderer-jsx'
2
+ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
3
3
  import { Handlers } from '../components/Handlers.tsx'
4
4
  import type { PluginMsw } from '../types'
5
- import { transformName } from '../utils.ts'
6
5
 
6
+ /**
7
+ * Aggregate generator enabled by `pluginMsw({ handlers: true })`. Emits a
8
+ * `handlers.ts` file that re-exports every generated handler grouped by HTTP
9
+ * method, ready to spread into `setupServer(...handlers)` or
10
+ * `setupWorker(...handlers)`.
11
+ */
7
12
  export const handlersGenerator = defineGenerator<PluginMsw>({
8
13
  name: 'plugin-msw',
9
- renderer: jsxRenderer,
14
+ renderer: jsxRendererSync,
10
15
  operations(nodes, ctx) {
11
- const { resolver, config, root, adapter } = ctx
12
- const { output, group, transformers } = ctx.options
16
+ const { resolver, config, root } = ctx
17
+ const { output, group } = ctx.options
13
18
 
14
- const file = resolver.resolveFile({ name: 'handlers', extname: '.ts' }, { root, output, group })
19
+ const handlersName = resolver.resolveHandlersName()
20
+ const file = resolver.resolveFile({ name: resolver.resolvePathName(handlersName, 'file'), extname: '.ts' }, { root, output, group: group ?? undefined })
15
21
 
16
22
  const imports = nodes.map((node) => {
17
- const operationName = transformName(resolver.resolveName(node.operationId), 'function', transformers)
23
+ const operationName = resolver.resolveHandlerName(node)
18
24
  const operationFile = resolver.resolveFile(
19
25
  { name: resolver.resolveName(node.operationId), extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
20
- { root, output, group },
26
+ { root, output, group: group ?? undefined },
21
27
  )
22
28
 
23
29
  return <File.Import key={operationFile.path} name={[operationName]} root={file.path} path={operationFile.path} />
24
30
  })
25
31
 
26
- const handlers = nodes.map((node) => `${transformName(resolver.resolveName(node.operationId), 'function', transformers)}()`)
32
+ const handlers = nodes.map((node) => `${resolver.resolveHandlerName(node)}()`)
27
33
 
28
34
  return (
29
35
  <File
30
36
  baseName={file.baseName}
31
37
  path={file.path}
32
38
  meta={file.meta}
33
- banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
34
- footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
39
+ banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
40
+ footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: file.path, baseName: file.baseName } })}
35
41
  >
36
42
  {imports}
37
- <Handlers name={'handlers'} handlers={handlers} />
43
+ <Handlers name={handlersName} handlers={handlers} />
38
44
  </File>
39
45
  )
40
46
  },
@@ -1,34 +1,44 @@
1
+ import { getOperationSuccessResponses, resolveResponseTypes } from '@internals/shared'
1
2
  import { defineGenerator } from '@kubb/core'
2
3
  import { pluginFakerName } from '@kubb/plugin-faker'
3
4
  import { pluginTsName } from '@kubb/plugin-ts'
4
- import { File, jsxRenderer } from '@kubb/renderer-jsx'
5
+ import { File, jsxRendererSync } from '@kubb/renderer-jsx'
5
6
  import { Mock, MockWithFaker, Response } from '../components'
6
7
  import type { PluginMsw } from '../types'
7
- import { getResponseTypes, getSuccessResponses, resolveFakerMeta, transformName } from '../utils.ts'
8
+ import { resolveFakerMeta } from '../utils.ts'
8
9
 
10
+ /**
11
+ * Built-in operation generator for `@kubb/plugin-msw`. Emits one MSW handler
12
+ * per OpenAPI operation. With `parser: 'faker'` the handler returns a value
13
+ * from `@kubb/plugin-faker`; with `parser: 'data'` it returns a typed empty
14
+ * payload for tests to fill in.
15
+ */
9
16
  export const mswGenerator = defineGenerator<PluginMsw>({
10
17
  name: 'msw',
11
- renderer: jsxRenderer,
18
+ renderer: jsxRendererSync,
12
19
  operation(node, ctx) {
13
- const { driver, resolver, config, root, adapter } = ctx
14
- const { output, parser, baseURL, group, transformers } = ctx.options
20
+ const { driver, resolver, config, root } = ctx
21
+ const { output, parser, baseURL, group } = ctx.options
15
22
 
16
23
  const fileName = resolver.resolveName(node.operationId)
17
24
  const mock = {
18
- name: transformName(fileName, 'function', transformers),
19
- file: resolver.resolveFile({ name: fileName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
25
+ name: resolver.resolveHandlerName(node),
26
+ file: resolver.resolveFile(
27
+ { name: fileName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
28
+ { root, output, group: group ?? undefined },
29
+ ),
20
30
  }
21
31
 
22
- const fakerPlugin = parser === 'faker' ? driver.getPlugin(pluginFakerName) : undefined
32
+ const fakerPlugin = parser === 'faker' ? driver.getPlugin(pluginFakerName) : null
23
33
  const faker =
24
34
  parser === 'faker' && fakerPlugin
25
35
  ? resolveFakerMeta(node, {
26
36
  root,
27
37
  fakerResolver: driver.getResolver(pluginFakerName),
28
38
  fakerOutput: fakerPlugin.options?.output ?? output,
29
- fakerGroup: fakerPlugin.options?.group,
39
+ fakerGroup: fakerPlugin.options?.group ?? null,
30
40
  })
31
- : undefined
41
+ : null
32
42
 
33
43
  const pluginTs = driver.getPlugin(pluginTsName)
34
44
  if (!pluginTs) return null
@@ -37,24 +47,24 @@ export const mswGenerator = defineGenerator<PluginMsw>({
37
47
  const type = {
38
48
  file: tsResolver.resolveFile(
39
49
  { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
40
- { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
50
+ { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group ?? undefined },
41
51
  ),
42
52
  responseName: tsResolver.resolveResponseName(node),
43
53
  }
44
54
 
45
- const types = getResponseTypes(node, tsResolver)
46
- const successResponses = getSuccessResponses(node)
47
- const hasSuccessSchema = successResponses.some((response) => !!response.schema)
55
+ const types = resolveResponseTypes(node, tsResolver)
56
+ const successResponses = getOperationSuccessResponses(node)
57
+ const hasSuccessSchema = successResponses.some((response) => !!response.content?.[0]?.schema)
48
58
 
49
- const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined
59
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : null
50
60
 
51
61
  return (
52
62
  <File
53
63
  baseName={mock.file.baseName}
54
64
  path={mock.file.path}
55
65
  meta={mock.file.meta}
56
- banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
57
- footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
66
+ banner={resolver.resolveBanner(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}
67
+ footer={resolver.resolveFooter(ctx.meta, { output, config, file: { path: mock.file.path, baseName: mock.file.baseName } })}
58
68
  >
59
69
  <File.Import name={['http']} path="msw" />
60
70
  <File.Import name={['HttpResponseResolver']} isTypeOnly path="msw" />
package/src/plugin.ts CHANGED
@@ -6,8 +6,38 @@ import { handlersGenerator, mswGenerator } from './generators'
6
6
  import { resolverMsw } from './resolvers/resolverMsw.ts'
7
7
  import type { PluginMsw } from './types.ts'
8
8
 
9
+ /**
10
+ * Canonical plugin name for `@kubb/plugin-msw`. Used for driver lookups and
11
+ * cross-plugin dependency references.
12
+ */
9
13
  export const pluginMswName = 'plugin-msw' satisfies PluginMsw['name']
10
14
 
15
+ /**
16
+ * Generates MSW request handlers from an OpenAPI spec. Drop them into your
17
+ * test setup or service worker to mock the API end-to-end. Request path,
18
+ * method, status, and response body all stay in sync with the spec. Combine
19
+ * with `@kubb/plugin-faker` (via `parser: 'faker'`) to seed handlers with
20
+ * realistic data.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { defineConfig } from 'kubb'
25
+ * import { pluginTs } from '@kubb/plugin-ts'
26
+ * import { pluginMsw } from '@kubb/plugin-msw'
27
+ *
28
+ * export default defineConfig({
29
+ * input: { path: './petStore.yaml' },
30
+ * output: { path: './src/gen' },
31
+ * plugins: [
32
+ * pluginTs(),
33
+ * pluginMsw({
34
+ * output: { path: './handlers' },
35
+ * handlers: true,
36
+ * }),
37
+ * ],
38
+ * })
39
+ * ```
40
+ */
11
41
  export const pluginMsw = definePlugin<PluginMsw>((options) => {
12
42
  const {
13
43
  output = { path: 'handlers', barrelType: 'named' },
@@ -15,7 +45,6 @@ export const pluginMsw = definePlugin<PluginMsw>((options) => {
15
45
  exclude = [],
16
46
  include,
17
47
  override = [],
18
- transformers = {},
19
48
  handlers = false,
20
49
  parser = 'data',
21
50
  baseURL,
@@ -36,12 +65,12 @@ export const pluginMsw = definePlugin<PluginMsw>((options) => {
36
65
  return `${camelCase(ctx.group)}Controller`
37
66
  },
38
67
  } satisfies Group)
39
- : undefined
68
+ : null
40
69
 
41
70
  return {
42
71
  name: pluginMswName,
43
72
  options,
44
- dependencies: [pluginTsName, parser === 'faker' ? pluginFakerName : undefined].filter(Boolean),
73
+ dependencies: [pluginTsName, parser === 'faker' ? pluginFakerName : null].filter((dependency): dependency is string => Boolean(dependency)),
45
74
  hooks: {
46
75
  'kubb:plugin:setup'(ctx) {
47
76
  const resolver = userResolver ? { ...resolverMsw, ...userResolver } : resolverMsw
@@ -55,7 +84,6 @@ export const pluginMsw = definePlugin<PluginMsw>((options) => {
55
84
  include,
56
85
  override,
57
86
  handlers,
58
- transformers,
59
87
  resolver,
60
88
  })
61
89
  ctx.setResolver(resolver)
@@ -3,11 +3,18 @@ import { defineResolver } from '@kubb/core'
3
3
  import type { PluginMsw } from '../types.ts'
4
4
 
5
5
  /**
6
- * Naming convention resolver for MSW plugin.
6
+ * Default resolver used by `@kubb/plugin-msw`. Decides the names and file
7
+ * paths for every generated MSW handler. Function names get a `Handler`
8
+ * suffix; the aggregate export is always `handlers`.
7
9
  *
8
- * Provides default naming helpers using camelCase with a `handler` suffix.
10
+ * @example Resolve a handler name
11
+ * ```ts
12
+ * import { resolverMsw } from '@kubb/plugin-msw'
13
+ *
14
+ * resolverMsw.resolveName('addPet') // 'addPetHandler'
15
+ * ```
9
16
  */
10
- export const resolverMsw = defineResolver<PluginMsw>((_ctx) => ({
17
+ export const resolverMsw = defineResolver<PluginMsw>(() => ({
11
18
  name: 'default',
12
19
  pluginName: 'plugin-msw',
13
20
  default(name, type) {
@@ -16,4 +23,13 @@ export const resolverMsw = defineResolver<PluginMsw>((_ctx) => ({
16
23
  resolveName(name) {
17
24
  return camelCase(name, { suffix: 'handler' })
18
25
  },
26
+ resolvePathName(name, type) {
27
+ return this.default(name, type)
28
+ },
29
+ resolveHandlerName(node) {
30
+ return this.resolveName(node.operationId)
31
+ },
32
+ resolveHandlersName() {
33
+ return 'handlers'
34
+ },
19
35
  }))
package/src/types.ts CHANGED
@@ -1,79 +1,93 @@
1
- import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginFactoryOptions, ResolveNameParams, Resolver } from '@kubb/core'
1
+ import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginFactoryOptions, Resolver } from '@kubb/core'
2
2
 
3
3
  /**
4
4
  * Resolver for MSW that provides naming methods for handler functions.
5
5
  */
6
6
  export type ResolverMsw = Resolver & {
7
7
  /**
8
- * Resolves the handler function name for an operation.
8
+ * Resolves the base handler function name for an operation.
9
9
  */
10
10
  resolveName(this: ResolverMsw, name: string): string
11
+ /**
12
+ * Resolves the output file name for an MSW handler module.
13
+ */
14
+ resolvePathName(this: ResolverMsw, name: string, type?: 'file' | 'function' | 'type' | 'const'): string
15
+ /**
16
+ * Resolves the handler function name for an operation.
17
+ */
18
+ resolveHandlerName(this: ResolverMsw, node: ast.OperationNode): string
19
+ /**
20
+ * Resolves the exported handlers collection name.
21
+ */
22
+ resolveHandlersName(this: ResolverMsw): string
11
23
  }
12
24
 
13
25
  export type Options = {
14
26
  /**
15
- * Specify the export location for the files and define the behavior of the output
16
- * @default { path: 'handlers', barrelType: 'named' }
27
+ * Where the generated MSW handlers are written and how they are exported.
28
+ *
29
+ * @default { path: 'handlers', barrel: { type: 'named' } }
17
30
  */
18
31
  output?: Output
32
+ /**
33
+ * Base URL prepended to every handler's request URL. When omitted, falls back
34
+ * to the adapter's server URL (typically `servers[0].url`).
35
+ */
19
36
  baseURL?: string
20
37
  /**
21
- * Group the MSW mocks based on the provided name.
38
+ * Split generated files into subfolders based on the operation's tag.
22
39
  */
23
40
  group?: Group
24
41
  /**
25
- * Tags, operations, or paths to exclude from generation.
42
+ * Skip operations matching at least one entry in the list.
26
43
  */
27
44
  exclude?: Array<Exclude>
28
45
  /**
29
- * Tags, operations, or paths to include in generation.
46
+ * Restrict generation to operations matching at least one entry in the list.
30
47
  */
31
48
  include?: Array<Include>
32
49
  /**
33
- * Override options for specific tags, operations, or paths.
50
+ * Apply a different options object to operations matching a pattern.
34
51
  */
35
52
  override?: Array<Override<ResolvedOptions>>
36
- transformers?: {
37
- /**
38
- * Override the default naming for handlers.
39
- */
40
- name?: (name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
41
- }
42
53
  /**
43
- * Override naming conventions for function names and types.
54
+ * Override how handler names and file paths are built.
44
55
  */
45
56
  resolver?: Partial<ResolverMsw> & ThisType<ResolverMsw>
46
57
  /**
47
- * AST visitor to transform generated nodes.
58
+ * AST visitor applied to operation nodes before printing.
48
59
  */
49
60
  transformer?: ast.Visitor
50
61
  /**
51
- * Create `handlers.ts` file with all handlers grouped by methods.
62
+ * Emit a `handlers.ts` file that re-exports every handler grouped by HTTP method.
63
+ * Drop the file into `setupServer(...handlers)` or `setupWorker(...handlers)`.
64
+ *
52
65
  * @default false
53
66
  */
54
67
  handlers?: boolean
55
68
  /**
56
- * Which parser to use for generating response data.
69
+ * Source of the response body returned by each generated handler.
70
+ * - `'data'` — typed empty/example payload, ready for you to fill in from tests.
71
+ * - `'faker'` — value produced by `@kubb/plugin-faker`.
57
72
  *
58
73
  * @default 'data'
59
74
  */
60
75
  parser?: 'data' | 'faker'
61
76
  /**
62
- * Additional generators alongside the default generators.
77
+ * Custom generators that run alongside the built-in MSW generators.
63
78
  */
64
79
  generators?: Array<Generator<PluginMsw>>
65
80
  }
66
81
 
67
82
  type ResolvedOptions = {
68
83
  output: Output
69
- group: Group | undefined
84
+ group: Group | null
70
85
  exclude: NonNullable<Options['exclude']>
71
86
  include: Options['include']
72
87
  override: NonNullable<Options['override']>
73
88
  parser: NonNullable<Options['parser']>
74
89
  baseURL: Options['baseURL'] | undefined
75
90
  handlers: boolean
76
- transformers: NonNullable<Options['transformers']>
77
91
  resolver: ResolverMsw
78
92
  }
79
93
 
package/src/utils.ts CHANGED
@@ -1,76 +1,29 @@
1
1
  import type { ast } from '@kubb/core'
2
2
  import type { ResolverFaker } from '@kubb/plugin-faker'
3
- import type { ResolverTs } from '@kubb/plugin-ts'
4
3
  import type { PluginMsw } from './types.ts'
5
4
 
6
- /**
7
- * Applies a name transformer function to a name if configured, otherwise returns it unchanged.
8
- */
9
- export function transformName(name: string, type: 'function' | 'type' | 'file' | 'const', transformers?: PluginMsw['resolvedOptions']['transformers']): string {
10
- return transformers?.name?.(name, type) || name
11
- }
12
-
13
- /**
14
- * Filters responses to only those with 2xx status codes.
15
- */
16
- export function getSuccessResponses(node: ast.OperationNode): ast.ResponseNode[] {
17
- return node.responses.filter((response) => {
18
- const code = Number.parseInt(response.statusCode, 10)
19
- return !Number.isNaN(code) && code >= 200 && code < 300
20
- })
21
- }
22
-
23
- /**
24
- * Returns the first 2xx response for an operation, if any.
25
- */
26
- export function getPrimarySuccessResponse(node: ast.OperationNode): ast.ResponseNode | undefined {
27
- return getSuccessResponses(node)[0]
28
- }
29
-
30
5
  /**
31
6
  * Gets the content type from a response, defaulting to 'application/json' if a schema exists.
32
7
  */
33
- export function getContentType(response: ast.ResponseNode | undefined): string | undefined {
34
- return getResponseContentType(response) ?? (hasResponseSchema(response) ? 'application/json' : undefined)
8
+ export function getContentType(response: ast.ResponseNode | null | undefined): string | null {
9
+ if (!hasResponseSchema(response)) {
10
+ return null
11
+ }
12
+
13
+ return getResponseContentType(response) ?? 'application/json'
35
14
  }
36
15
 
37
16
  /**
38
17
  * Determines if a response has a schema that is not void or any.
39
18
  */
40
- export function hasResponseSchema(response: ast.ResponseNode | undefined): boolean {
41
- return !!getResponseContentType(response) || (!!response?.schema && response.schema.type !== 'void' && response.schema.type !== 'any')
42
- }
43
-
44
- function getResponseContentType(response: ast.ResponseNode | undefined): string | undefined {
45
- const contentType = response as unknown as { mediaType?: string | null; contentType?: string | null } | undefined
46
- const value = contentType?.mediaType ?? contentType?.contentType
47
- return typeof value === 'string' && value.length > 0 ? value : undefined
19
+ export function hasResponseSchema(response: ast.ResponseNode | null | undefined): boolean {
20
+ const schema = response?.content?.[0]?.schema
21
+ return !!schema && schema.type !== 'void' && schema.type !== 'any'
48
22
  }
49
23
 
50
- /**
51
- * Maps all operation responses to their type names, including status code or 'default' for default responses.
52
- */
53
- export function getResponseTypes(node: ast.OperationNode, tsResolver: ResolverTs): Array<[statusCode: number | 'default', typeName: string]> {
54
- const types: Array<[number | 'default', string]> = []
55
-
56
- for (const response of node.responses) {
57
- if (response.statusCode === 'default') {
58
- types.push(['default', tsResolver.resolveResponseName(node)])
59
- continue
60
- }
61
-
62
- const code = Number.parseInt(response.statusCode, 10)
63
- if (Number.isNaN(code)) continue
64
-
65
- if (code >= 200 && code < 300) {
66
- types.push([code, tsResolver.resolveResponseName(node)])
67
- continue
68
- }
69
-
70
- types.push([code, tsResolver.resolveResponseStatusName(node, response.statusCode)])
71
- }
72
-
73
- return types
24
+ function getResponseContentType(response: ast.ResponseNode | null | undefined): string | null {
25
+ const value = response?.content?.[0]?.contentType
26
+ return typeof value === 'string' && value.length > 0 ? value : null
74
27
  }
75
28
 
76
29
  /**
@@ -104,6 +57,9 @@ export function resolveFakerMeta(
104
57
 
105
58
  return {
106
59
  name: fakerResolver.resolveResponseName(node),
107
- file: fakerResolver.resolveFile({ name: node.operationId, extname: '.ts', tag, path: node.path }, { root, output: fakerOutput, group: fakerGroup }),
60
+ file: fakerResolver.resolveFile(
61
+ { name: node.operationId, extname: '.ts', tag, path: node.path },
62
+ { root, output: fakerOutput, group: fakerGroup ?? undefined },
63
+ ),
108
64
  }
109
65
  }