@kubb/plugin-msw 5.0.0-alpha.8 → 5.0.0-beta.10

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 (44) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +26 -7
  3. package/dist/components-BYvgvrY7.cjs +569 -0
  4. package/dist/components-BYvgvrY7.cjs.map +1 -0
  5. package/dist/components-Cm17DMTE.js +510 -0
  6. package/dist/components-Cm17DMTE.js.map +1 -0
  7. package/dist/components.cjs +1 -1
  8. package/dist/components.d.ts +17 -21
  9. package/dist/components.js +1 -1
  10. package/dist/generators-D9gvdP7Z.js +177 -0
  11. package/dist/generators-D9gvdP7Z.js.map +1 -0
  12. package/dist/generators-rZ99WaWQ.cjs +187 -0
  13. package/dist/generators-rZ99WaWQ.cjs.map +1 -0
  14. package/dist/generators.cjs +1 -1
  15. package/dist/generators.d.ts +4 -471
  16. package/dist/generators.js +1 -1
  17. package/dist/index.cjs +62 -65
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +59 -65
  21. package/dist/index.js.map +1 -1
  22. package/dist/types-ItsHsMvC.d.ts +94 -0
  23. package/extension.yaml +233 -0
  24. package/package.json +57 -58
  25. package/src/components/Handlers.tsx +3 -3
  26. package/src/components/Mock.tsx +37 -28
  27. package/src/components/MockWithFaker.tsx +37 -24
  28. package/src/components/Response.tsx +23 -17
  29. package/src/generators/handlersGenerator.tsx +19 -19
  30. package/src/generators/mswGenerator.tsx +50 -60
  31. package/src/index.ts +1 -1
  32. package/src/plugin.ts +47 -86
  33. package/src/resolvers/resolverMsw.ts +28 -0
  34. package/src/types.ts +55 -27
  35. package/src/utils.ts +58 -0
  36. package/dist/components-8XBwMbFa.cjs +0 -343
  37. package/dist/components-8XBwMbFa.cjs.map +0 -1
  38. package/dist/components-DgtTZkWX.js +0 -277
  39. package/dist/components-DgtTZkWX.js.map +0 -1
  40. package/dist/generators-CY1SNd5X.cjs +0 -171
  41. package/dist/generators-CY1SNd5X.cjs.map +0 -1
  42. package/dist/generators-CvyZTxOm.js +0 -161
  43. package/dist/generators-CvyZTxOm.js.map +0 -1
  44. package/dist/types-MdHRNpgi.d.ts +0 -68
@@ -1,48 +1,57 @@
1
+ import { getPrimarySuccessResponse } from '@internals/shared'
1
2
  import { URLPath } from '@internals/utils'
2
- import type { OasTypes, Operation } from '@kubb/oas'
3
- import { File, Function, FunctionParams } from '@kubb/react-fabric'
4
- import type { FabricReactNode } from '@kubb/react-fabric/types'
3
+ import { ast } from '@kubb/core'
4
+ import { functionPrinter } from '@kubb/plugin-ts'
5
+ import { File, Function } from '@kubb/renderer-jsx'
6
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
+ import { getContentType, getMswMethod, getMswUrl, hasResponseSchema } from '../utils.ts'
5
8
 
6
9
  type Props = {
7
- /**
8
- * Name of the function
9
- */
10
10
  name: string
11
11
  typeName: string
12
- fakerName: string
12
+ requestTypeName?: string
13
13
  baseURL: string | undefined
14
- operation: Operation
14
+ node: ast.OperationNode
15
15
  }
16
16
 
17
- export function Mock({ baseURL = '', name, typeName, operation }: Props): FabricReactNode {
18
- const method = operation.method
19
- const successStatusCodes = operation.getResponseStatusCodes().filter((code) => code.startsWith('2'))
20
- const statusCode = successStatusCodes.length > 0 ? Number(successStatusCodes[0]) : 200
17
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
21
18
 
22
- const responseObject = operation.getResponseByStatusCode(statusCode) as OasTypes.ResponseObject
23
- const contentType = Object.keys(responseObject.content || {})?.[0]
24
- const url = new URLPath(operation.path).toURLPath().replace(/([^/]):/g, '$1\\\\:')
19
+ export function Mock({ baseURL = '', name, typeName, requestTypeName, node }: Props): KubbReactNode {
20
+ const method = getMswMethod(node)
21
+ const successResponse = getPrimarySuccessResponse(node)
22
+ const statusCode = successResponse ? Number(successResponse.statusCode) : 200
23
+ const contentType = getContentType(successResponse)
24
+ const url = new URLPath(getMswUrl(node)).toURLPath()
25
25
 
26
26
  const headers = [contentType ? `'Content-Type': '${contentType}'` : undefined].filter(Boolean)
27
+ const responseHasSchema = hasResponseSchema(successResponse)
28
+ const dataType = responseHasSchema ? typeName : 'string | number | boolean | null | object'
27
29
 
28
- const hasResponseSchema = contentType && responseObject?.content?.[contentType]?.schema !== undefined
30
+ const callbackType = requestTypeName
31
+ ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}, any>`
32
+ : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`
29
33
 
30
- // If no response schema, uses any type but function to avoid overriding callback
31
- const dataType = hasResponseSchema ? typeName : 'string | number | boolean | null | object'
34
+ const params = declarationPrinter.print(
35
+ ast.createFunctionParameters({
36
+ params: [
37
+ ast.createFunctionParameter({
38
+ name: 'data',
39
+ type: ast.createParamsType({
40
+ variant: 'reference',
41
+ name: `${dataType} | ${callbackType}`,
42
+ }),
43
+ optional: true,
44
+ }),
45
+ ],
46
+ }),
47
+ )
32
48
 
33
- const params = FunctionParams.factory({
34
- data: {
35
- type: `${dataType} | ((
36
- info: Parameters<Parameters<typeof http.${method}>[1]>[0],
37
- ) => Response | Promise<Response>)`,
38
- optional: true,
39
- },
40
- })
49
+ const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}, any>` : `http.${method}`
41
50
 
42
51
  return (
43
52
  <File.Source name={name} isIndexable isExportable>
44
- <Function name={name} export params={params.toConstructor()}>
45
- {`return http.${method}(\`${baseURL}${url.replace(/([^/]):/g, '$1\\\\:')}\`, function handler(info) {
53
+ <Function name={name} export params={params ?? ''}>
54
+ {`return ${httpCall}(\`${baseURL}${url.replace(/([^/]):/g, '$1\\\\:')}\`, function handler(info) {
46
55
  if(typeof data === 'function') return data(info)
47
56
 
48
57
  return new Response(JSON.stringify(data), {
@@ -1,43 +1,56 @@
1
+ import { getPrimarySuccessResponse } from '@internals/shared'
1
2
  import { URLPath } from '@internals/utils'
2
- import type { OasTypes, Operation } from '@kubb/oas'
3
- import { File, Function, FunctionParams } from '@kubb/react-fabric'
4
- import type { FabricReactNode } from '@kubb/react-fabric/types'
3
+ import { ast } from '@kubb/core'
4
+ import { functionPrinter } from '@kubb/plugin-ts'
5
+ import { File, Function } from '@kubb/renderer-jsx'
6
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
7
+ import { getContentType, getMswMethod, getMswUrl } from '../utils.ts'
5
8
 
6
9
  type Props = {
7
- /**
8
- * Name of the function
9
- */
10
10
  name: string
11
11
  typeName: string
12
+ requestTypeName?: string
12
13
  fakerName: string
13
14
  baseURL: string | undefined
14
- operation: Operation
15
+ node: ast.OperationNode
15
16
  }
16
17
 
17
- export function MockWithFaker({ baseURL = '', name, fakerName, typeName, operation }: Props): FabricReactNode {
18
- const method = operation.method
19
- const successStatusCodes = operation.getResponseStatusCodes().filter((code) => code.startsWith('2'))
20
- const statusCode = successStatusCodes.length > 0 ? Number(successStatusCodes[0]) : 200
18
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
21
19
 
22
- const responseObject = operation.getResponseByStatusCode(statusCode) as OasTypes.ResponseObject
23
- const contentType = Object.keys(responseObject.content || {})?.[0]
24
- const url = new URLPath(operation.path).toURLPath().replace(/([^/]):/g, '$1\\\\:')
20
+ export function MockWithFaker({ baseURL = '', name, fakerName, typeName, requestTypeName, node }: Props): KubbReactNode {
21
+ const method = getMswMethod(node)
22
+ const successResponse = getPrimarySuccessResponse(node)
23
+ const statusCode = successResponse ? Number(successResponse.statusCode) : 200
24
+ const contentType = getContentType(successResponse)
25
+ const url = new URLPath(getMswUrl(node)).toURLPath()
25
26
 
26
27
  const headers = [contentType ? `'Content-Type': '${contentType}'` : undefined].filter(Boolean)
27
28
 
28
- const params = FunctionParams.factory({
29
- data: {
30
- type: `${typeName} | ((
31
- info: Parameters<Parameters<typeof http.${method}>[1]>[0],
32
- ) => Response | Promise<Response>)`,
33
- optional: true,
34
- },
35
- })
29
+ const callbackType = requestTypeName
30
+ ? `HttpResponseResolver<Record<string, string>, ${requestTypeName}, any>`
31
+ : `((info: Parameters<Parameters<typeof http.${method}>[1]>[0]) => Response | Promise<Response>)`
32
+
33
+ const params = declarationPrinter.print(
34
+ ast.createFunctionParameters({
35
+ params: [
36
+ ast.createFunctionParameter({
37
+ name: 'data',
38
+ type: ast.createParamsType({
39
+ variant: 'reference',
40
+ name: `${typeName} | ${callbackType}`,
41
+ }),
42
+ optional: true,
43
+ }),
44
+ ],
45
+ }),
46
+ )
47
+
48
+ const httpCall = requestTypeName ? `http.${method}<Record<string, string>, ${requestTypeName}, any>` : `http.${method}`
36
49
 
37
50
  return (
38
51
  <File.Source name={name} isIndexable isExportable>
39
- <Function name={name} export params={params.toConstructor()}>
40
- {`return http.${method}('${baseURL}${url.replace(/([^/]):/g, '$1\\\\:')}', function handler(info) {
52
+ <Function name={name} export params={params ?? ''}>
53
+ {`return ${httpCall}('${baseURL}${url.replace(/([^/]):/g, '$1\\\\:')}', function handler(info) {
41
54
  if(typeof data === 'function') return data(info)
42
55
 
43
56
  return new Response(JSON.stringify(data || ${fakerName}(data)), {
@@ -1,34 +1,40 @@
1
- import type { OasTypes, Operation } from '@kubb/oas'
2
- import { File, Function, FunctionParams } from '@kubb/react-fabric'
3
- import type { FabricReactNode } from '@kubb/react-fabric/types'
1
+ import { ast } from '@kubb/core'
2
+ import { functionPrinter } from '@kubb/plugin-ts'
3
+ import { File, Function } from '@kubb/renderer-jsx'
4
+ import type { KubbReactNode } from '@kubb/renderer-jsx/types'
5
+ import { getContentType, hasResponseSchema } from '../utils.ts'
4
6
 
5
7
  type Props = {
6
8
  typeName: string
7
- operation: Operation
8
9
  name: string
9
- statusCode: number
10
+ response: ast.ResponseNode
11
+ key?: string | number | null
10
12
  }
11
13
 
12
- export function Response({ name, typeName, operation, statusCode }: Props): FabricReactNode {
13
- const responseObject = operation.getResponseByStatusCode(statusCode) as OasTypes.ResponseObject
14
- const contentType = Object.keys(responseObject.content || {})?.[0]
14
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
15
15
 
16
+ export function Response({ name, typeName, response }: Props): KubbReactNode {
17
+ const statusCode = Number(response.statusCode)
18
+ const contentType = getContentType(response)
16
19
  const headers = [contentType ? `'Content-Type': '${contentType}'` : undefined].filter(Boolean)
17
20
 
18
- const hasResponseSchema = contentType && responseObject?.content?.[contentType]?.schema !== undefined
19
-
20
- const params = FunctionParams.factory({
21
- data: {
22
- type: `${typeName}`,
23
- optional: !hasResponseSchema,
24
- },
25
- })
21
+ const params = declarationPrinter.print(
22
+ ast.createFunctionParameters({
23
+ params: [
24
+ ast.createFunctionParameter({
25
+ name: 'data',
26
+ type: ast.createParamsType({ variant: 'reference', name: typeName }),
27
+ optional: !hasResponseSchema(response),
28
+ }),
29
+ ],
30
+ }),
31
+ )
26
32
 
27
33
  const responseName = `${name}Response${statusCode}`
28
34
 
29
35
  return (
30
36
  <File.Source name={responseName} isIndexable isExportable>
31
- <Function name={responseName} export params={params.toConstructor()}>
37
+ <Function name={responseName} export params={params ?? ''}>
32
38
  {`
33
39
  return new Response(JSON.stringify(data), {
34
40
  status: ${statusCode},
@@ -1,40 +1,40 @@
1
- import { usePluginDriver } from '@kubb/core/hooks'
2
- import { createReactGenerator } from '@kubb/plugin-oas/generators'
3
- import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
4
- import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
5
- import { File } from '@kubb/react-fabric'
1
+ import { defineGenerator } from '@kubb/core'
2
+ import { File, jsxRenderer } from '@kubb/renderer-jsx'
6
3
  import { Handlers } from '../components/Handlers.tsx'
7
4
  import type { PluginMsw } from '../types'
8
5
 
9
- export const handlersGenerator = createReactGenerator<PluginMsw>({
6
+ export const handlersGenerator = defineGenerator<PluginMsw>({
10
7
  name: 'plugin-msw',
11
- Operations({ operations, generator, plugin }) {
12
- const driver = usePluginDriver()
8
+ renderer: jsxRenderer,
9
+ operations(nodes, ctx) {
10
+ const { resolver, config, root, adapter } = ctx
11
+ const { output, group } = ctx.options
13
12
 
14
- const oas = useOas()
15
- const { getName, getFile } = useOperationManager(generator)
13
+ const handlersName = resolver.resolveHandlersName()
14
+ const file = resolver.resolveFile({ name: resolver.resolvePathName(handlersName, 'file'), extname: '.ts' }, { root, output, group })
16
15
 
17
- const file = driver.getFile({ name: 'handlers', extname: '.ts', pluginName: plugin.name })
18
-
19
- const imports = operations.map((operation) => {
20
- const operationFile = getFile(operation, { pluginName: plugin.name })
21
- const operationName = getName(operation, { pluginName: plugin.name, type: 'function' })
16
+ const imports = nodes.map((node) => {
17
+ const operationName = resolver.resolveHandlerName(node)
18
+ const operationFile = resolver.resolveFile(
19
+ { name: resolver.resolveName(node.operationId), extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
20
+ { root, output, group },
21
+ )
22
22
 
23
23
  return <File.Import key={operationFile.path} name={[operationName]} root={file.path} path={operationFile.path} />
24
24
  })
25
25
 
26
- const handlers = operations.map((operation) => `${getName(operation, { type: 'function', pluginName: plugin.name })}()`)
26
+ const handlers = nodes.map((node) => `${resolver.resolveHandlerName(node)}()`)
27
27
 
28
28
  return (
29
29
  <File
30
30
  baseName={file.baseName}
31
31
  path={file.path}
32
32
  meta={file.meta}
33
- banner={getBanner({ oas, output: plugin.options.output, config: driver.config })}
34
- footer={getFooter({ oas, output: plugin.options.output })}
33
+ banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
34
+ footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
35
35
  >
36
36
  {imports}
37
- <Handlers name={'handlers'} handlers={handlers} />
37
+ <Handlers name={handlersName} handlers={handlers} />
38
38
  </File>
39
39
  )
40
40
  },
@@ -1,94 +1,84 @@
1
- import { usePluginDriver } from '@kubb/core/hooks'
1
+ import { getOperationSuccessResponses, resolveResponseTypes } from '@internals/shared'
2
+ import { defineGenerator } from '@kubb/core'
2
3
  import { pluginFakerName } from '@kubb/plugin-faker'
3
- import { createReactGenerator } from '@kubb/plugin-oas/generators'
4
- import { useOas, useOperationManager } from '@kubb/plugin-oas/hooks'
5
- import { getBanner, getFooter } from '@kubb/plugin-oas/utils'
6
4
  import { pluginTsName } from '@kubb/plugin-ts'
7
- import { File } from '@kubb/react-fabric'
5
+ import { File, jsxRenderer } from '@kubb/renderer-jsx'
8
6
  import { Mock, MockWithFaker, Response } from '../components'
9
7
  import type { PluginMsw } from '../types'
8
+ import { resolveFakerMeta } from '../utils.ts'
10
9
 
11
- export const mswGenerator = createReactGenerator<PluginMsw>({
10
+ export const mswGenerator = defineGenerator<PluginMsw>({
12
11
  name: 'msw',
13
- Operation({ operation, generator, plugin }) {
14
- const {
15
- options: { output, parser, baseURL },
16
- } = plugin
17
- const driver = usePluginDriver()
18
-
19
- const oas = useOas()
20
- const { getSchemas, getName, getFile } = useOperationManager(generator)
12
+ renderer: jsxRenderer,
13
+ operation(node, ctx) {
14
+ const { driver, resolver, config, root, adapter } = ctx
15
+ const { output, parser, baseURL, group } = ctx.options
21
16
 
17
+ const fileName = resolver.resolveName(node.operationId)
22
18
  const mock = {
23
- name: getName(operation, { type: 'function' }),
24
- file: getFile(operation),
19
+ name: resolver.resolveHandlerName(node),
20
+ file: resolver.resolveFile({ name: fileName, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path }, { root, output, group }),
25
21
  }
26
22
 
27
- const faker = {
28
- file: getFile(operation, { pluginName: pluginFakerName }),
29
- schemas: getSchemas(operation, { pluginName: pluginFakerName, type: 'function' }),
30
- }
23
+ const fakerPlugin = parser === 'faker' ? driver.getPlugin(pluginFakerName) : undefined
24
+ const faker =
25
+ parser === 'faker' && fakerPlugin
26
+ ? resolveFakerMeta(node, {
27
+ root,
28
+ fakerResolver: driver.getResolver(pluginFakerName),
29
+ fakerOutput: fakerPlugin.options?.output ?? output,
30
+ fakerGroup: fakerPlugin.options?.group,
31
+ })
32
+ : undefined
33
+
34
+ const pluginTs = driver.getPlugin(pluginTsName)
35
+ if (!pluginTs) return null
36
+ const tsResolver = driver.getResolver(pluginTsName)
31
37
 
32
38
  const type = {
33
- file: getFile(operation, { pluginName: pluginTsName }),
34
- schemas: getSchemas(operation, { pluginName: pluginTsName, type: 'type' }),
39
+ file: tsResolver.resolveFile(
40
+ { name: node.operationId, extname: '.ts', tag: node.tags[0] ?? 'default', path: node.path },
41
+ { root, output: pluginTs.options?.output ?? output, group: pluginTs.options?.group },
42
+ ),
43
+ responseName: tsResolver.resolveResponseName(node),
35
44
  }
36
45
 
37
- const responseStatusCodes = operation.getResponseStatusCodes()
38
-
39
- const types: [statusCode: number | 'default', typeName: string][] = []
40
-
41
- for (const code of responseStatusCodes) {
42
- if (code === 'default') {
43
- types.push(['default', type.schemas.response.name])
44
- continue
45
- }
46
+ const types = resolveResponseTypes(node, tsResolver)
47
+ const successResponses = getOperationSuccessResponses(node)
48
+ const hasSuccessSchema = successResponses.some((response) => !!response.schema)
46
49
 
47
- if (code.startsWith('2')) {
48
- types.push([Number(code), type.schemas.response.name])
49
- continue
50
- }
51
-
52
- const codeType = type.schemas.errors?.find((err) => err.statusCode === Number(code))
53
- if (codeType) types.push([Number(code), codeType.name])
54
- }
50
+ const requestName = node.requestBody?.content?.[0]?.schema ? tsResolver.resolveDataName(node) : undefined
55
51
 
56
52
  return (
57
53
  <File
58
54
  baseName={mock.file.baseName}
59
55
  path={mock.file.path}
60
56
  meta={mock.file.meta}
61
- banner={getBanner({ oas, output, config: driver.config })}
62
- footer={getFooter({ oas, output })}
57
+ banner={resolver.resolveBanner(adapter.inputNode, { output, config })}
58
+ footer={resolver.resolveFooter(adapter.inputNode, { output, config })}
63
59
  >
64
60
  <File.Import name={['http']} path="msw" />
65
- <File.Import name={['ResponseResolver']} isTypeOnly path="msw" />
61
+ <File.Import name={['HttpResponseResolver']} isTypeOnly path="msw" />
66
62
  <File.Import
67
- name={Array.from(new Set([type.schemas.response.name, ...types.map((t) => t[1])]))}
63
+ name={Array.from(new Set([type.responseName, ...types.map((t) => t[1]), ...(requestName ? [requestName] : [])]))}
68
64
  path={type.file.path}
69
65
  root={mock.file.path}
70
66
  isTypeOnly
71
67
  />
72
- {parser === 'faker' && faker.file && faker.schemas.response && (
73
- <File.Import name={[faker.schemas.response.name]} root={mock.file.path} path={faker.file.path} />
74
- )}
68
+ {parser === 'faker' && faker && <File.Import name={[faker.name]} root={mock.file.path} path={faker.file.path} />}
75
69
 
76
70
  {types
77
71
  .filter(([code]) => code !== 'default')
78
- .map(([code, typeName]) => (
79
- <Response typeName={typeName} operation={operation} name={mock.name} statusCode={code as number} />
80
- ))}
81
- {parser === 'faker' && (
82
- <MockWithFaker
83
- name={mock.name}
84
- typeName={type.schemas.response.name}
85
- fakerName={faker.schemas.response.name}
86
- operation={operation}
87
- baseURL={baseURL}
88
- />
89
- )}
90
- {parser === 'data' && (
91
- <Mock name={mock.name} typeName={type.schemas.response.name} fakerName={faker.schemas.response.name} operation={operation} baseURL={baseURL} />
72
+ .map(([code, typeName]) => {
73
+ const response = node.responses.find((item) => item.statusCode === String(code))
74
+ if (!response) return null
75
+ return <Response key={typeName} typeName={typeName} response={response} name={mock.name} />
76
+ })}
77
+
78
+ {parser === 'faker' && faker && hasSuccessSchema ? (
79
+ <MockWithFaker name={mock.name} typeName={type.responseName} requestTypeName={requestName} fakerName={faker.name} node={node} baseURL={baseURL} />
80
+ ) : (
81
+ <Mock name={mock.name} typeName={type.responseName} requestTypeName={requestName} node={node} baseURL={baseURL} />
92
82
  )}
93
83
  </File>
94
84
  )
package/src/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { pluginMsw, pluginMswName } from './plugin.ts'
1
+ export { default, pluginMsw, pluginMswName } from './plugin.ts'
2
2
  export type { PluginMsw } from './types.ts'
package/src/plugin.ts CHANGED
@@ -1,115 +1,76 @@
1
- import path from 'node:path'
2
1
  import { camelCase } from '@internals/utils'
3
- import { createPlugin, type Group, getBarrelFiles, getMode } from '@kubb/core'
2
+ import { definePlugin, type Group } from '@kubb/core'
4
3
  import { pluginFakerName } from '@kubb/plugin-faker'
5
- import { OperationGenerator, pluginOasName } from '@kubb/plugin-oas'
6
4
  import { pluginTsName } from '@kubb/plugin-ts'
7
5
  import { handlersGenerator, mswGenerator } from './generators'
6
+ import { resolverMsw } from './resolvers/resolverMsw.ts'
8
7
  import type { PluginMsw } from './types.ts'
9
8
 
10
9
  export const pluginMswName = 'plugin-msw' satisfies PluginMsw['name']
11
10
 
12
- export const pluginMsw = createPlugin<PluginMsw>((options) => {
11
+ export const pluginMsw = definePlugin<PluginMsw>((options) => {
13
12
  const {
14
13
  output = { path: 'handlers', barrelType: 'named' },
15
14
  group,
16
15
  exclude = [],
17
16
  include,
18
17
  override = [],
19
- transformers = {},
20
18
  handlers = false,
21
19
  parser = 'data',
22
- generators = [mswGenerator, handlers ? handlersGenerator : undefined].filter(Boolean),
23
- contentType,
24
20
  baseURL,
21
+ resolver: userResolver,
22
+ transformer: userTransformer,
23
+ generators: userGenerators = [],
25
24
  } = options
26
25
 
27
- return {
28
- name: pluginMswName,
29
- options: {
30
- output,
31
- parser,
32
- group,
33
- baseURL,
34
- },
35
- pre: [pluginOasName, pluginTsName, parser === 'faker' ? pluginFakerName : undefined].filter(Boolean),
36
- resolvePath(baseName, pathMode, options) {
37
- const root = path.resolve(this.config.root, this.config.output.path)
38
- const mode = pathMode ?? getMode(path.resolve(root, output.path))
39
-
40
- if (mode === 'single') {
41
- /**
42
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
43
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
44
- */
45
- return path.resolve(root, output.path)
46
- }
47
-
48
- if (group && (options?.group?.path || options?.group?.tag)) {
49
- const groupName: Group['name'] = group?.name
26
+ const groupConfig = group
27
+ ? ({
28
+ ...group,
29
+ name: group.name
50
30
  ? group.name
51
- : (ctx) => {
52
- if (group?.type === 'path') {
31
+ : (ctx: { group: string }) => {
32
+ if (group.type === 'path') {
53
33
  return `${ctx.group.split('/')[1]}`
54
34
  }
55
35
  return `${camelCase(ctx.group)}Controller`
56
- }
57
-
58
- return path.resolve(
59
- root,
60
- output.path,
61
- groupName({
62
- group: group.type === 'path' ? options.group.path! : options.group.tag!,
63
- }),
64
- baseName,
65
- )
66
- }
67
-
68
- return path.resolve(root, output.path, baseName)
69
- },
70
- resolveName(name, type) {
71
- const resolvedName = camelCase(name, {
72
- suffix: type ? 'handler' : undefined,
73
- isFile: type === 'file',
74
- })
36
+ },
37
+ } satisfies Group)
38
+ : undefined
75
39
 
76
- if (type) {
77
- return transformers?.name?.(resolvedName, type) || resolvedName
78
- }
79
-
80
- return resolvedName
81
- },
82
- async install() {
83
- const root = path.resolve(this.config.root, this.config.output.path)
84
- const mode = getMode(path.resolve(root, output.path))
85
- const oas = await this.getOas()
86
-
87
- const operationGenerator = new OperationGenerator(this.plugin.options, {
88
- fabric: this.fabric,
89
- oas,
90
- driver: this.driver,
91
- events: this.events,
92
- plugin: this.plugin,
93
- contentType,
94
- exclude,
95
- include,
96
- override,
97
- mode,
98
- })
99
-
100
- const files = await operationGenerator.build(...generators)
101
- await this.upsertFile(...files)
40
+ return {
41
+ name: pluginMswName,
42
+ options,
43
+ dependencies: [pluginTsName, parser === 'faker' ? pluginFakerName : undefined].filter((dependency): dependency is string => Boolean(dependency)),
44
+ hooks: {
45
+ 'kubb:plugin:setup'(ctx) {
46
+ const resolver = userResolver ? { ...resolverMsw, ...userResolver } : resolverMsw
102
47
 
103
- const barrelFiles = await getBarrelFiles(this.fabric.files, {
104
- type: output.barrelType ?? 'named',
105
- root,
106
- output,
107
- meta: {
108
- pluginName: this.plugin.name,
109
- },
110
- })
48
+ ctx.setOptions({
49
+ output,
50
+ parser,
51
+ baseURL,
52
+ group: groupConfig,
53
+ exclude,
54
+ include,
55
+ override,
56
+ handlers,
57
+ resolver,
58
+ })
59
+ ctx.setResolver(resolver)
60
+ if (userTransformer) {
61
+ ctx.setTransformer(userTransformer)
62
+ }
111
63
 
112
- await this.upsertFile(...barrelFiles)
64
+ ctx.addGenerator(mswGenerator)
65
+ if (handlers) {
66
+ ctx.addGenerator(handlersGenerator)
67
+ }
68
+ for (const gen of userGenerators) {
69
+ ctx.addGenerator(gen)
70
+ }
71
+ },
113
72
  },
114
73
  }
115
74
  })
75
+
76
+ export default pluginMsw
@@ -0,0 +1,28 @@
1
+ import { camelCase } from '@internals/utils'
2
+ import { defineResolver } from '@kubb/core'
3
+ import type { PluginMsw } from '../types.ts'
4
+
5
+ /**
6
+ * Naming convention resolver for MSW plugin.
7
+ *
8
+ * Provides default naming helpers using camelCase with a `handler` suffix.
9
+ */
10
+ export const resolverMsw = defineResolver<PluginMsw>(() => ({
11
+ name: 'default',
12
+ pluginName: 'plugin-msw',
13
+ default(name, type) {
14
+ return camelCase(name, { isFile: type === 'file' })
15
+ },
16
+ resolveName(name) {
17
+ return camelCase(name, { suffix: 'handler' })
18
+ },
19
+ resolvePathName(name, type) {
20
+ return this.default(name, type)
21
+ },
22
+ resolveHandlerName(node) {
23
+ return this.resolveName(node.operationId)
24
+ },
25
+ resolveHandlersName() {
26
+ return 'handlers'
27
+ },
28
+ }))