@kubb/plugin-msw 5.0.0-alpha.9 → 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 -500
  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
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
+ }))
package/src/types.ts CHANGED
@@ -1,65 +1,93 @@
1
- import type { Group, Output, PluginFactoryOptions, ResolveNameParams } from '@kubb/core'
1
+ import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginFactoryOptions, Resolver } from '@kubb/core'
2
2
 
3
- import type { contentType, Oas } from '@kubb/oas'
4
- import type { Exclude, Include, Override, ResolvePathOptions } from '@kubb/plugin-oas'
5
- import type { Generator } from '@kubb/plugin-oas/generators'
3
+ /**
4
+ * Resolver for MSW that provides naming methods for handler functions.
5
+ */
6
+ export type ResolverMsw = Resolver & {
7
+ /**
8
+ * Resolves the base handler function name for an operation.
9
+ */
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
23
+ }
6
24
 
7
25
  export type Options = {
8
26
  /**
9
27
  * Specify the export location for the files and define the behavior of the output
10
- * @default { path: 'mocks', barrelType: 'named' }
11
- */
12
- output?: Output<Oas>
13
- /**
14
- * Define which contentType should be used.
15
- * By default, the first JSON valid mediaType is used
28
+ * @default { path: 'handlers', barrelType: 'named' }
16
29
  */
17
- contentType?: contentType
30
+ output?: Output
18
31
  baseURL?: string
19
32
  /**
20
33
  * Group the MSW mocks based on the provided name.
21
34
  */
22
35
  group?: Group
23
36
  /**
24
- * Array containing exclude parameters to exclude/skip tags/operations/methods/paths.
37
+ * Tags, operations, or paths to exclude from generation.
25
38
  */
26
39
  exclude?: Array<Exclude>
27
40
  /**
28
- * Array containing include parameters to include tags/operations/methods/paths.
41
+ * Tags, operations, or paths to include in generation.
29
42
  */
30
43
  include?: Array<Include>
31
44
  /**
32
- * Array containing override parameters to override `options` based on tags/operations/methods/paths.
45
+ * Override options for specific tags, operations, or paths.
33
46
  */
34
47
  override?: Array<Override<ResolvedOptions>>
35
- transformers?: {
36
- /**
37
- * Customize the names based on the type that is provided by the plugin.
38
- */
39
- name?: (name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
40
- }
48
+ /**
49
+ * Override naming conventions for function names and types.
50
+ */
51
+ resolver?: Partial<ResolverMsw> & ThisType<ResolverMsw>
52
+ /**
53
+ * AST visitor to transform generated nodes.
54
+ */
55
+ transformer?: ast.Visitor
41
56
  /**
42
57
  * Create `handlers.ts` file with all handlers grouped by methods.
43
58
  * @default false
44
59
  */
45
60
  handlers?: boolean
46
61
  /**
47
- * Which parser should be used before returning the data to the Response of MSW.
48
- * - 'data' uses your custom data to generate the data for the response.
49
- * - 'faker' uses @kubb/plugin-faker to generate the data for the response.
62
+ * Which parser to use for generating response data.
63
+ *
50
64
  * @default 'data'
51
65
  */
52
66
  parser?: 'data' | 'faker'
53
67
  /**
54
- * Define some generators next to the msw generators
68
+ * Additional generators alongside the default generators.
55
69
  */
56
70
  generators?: Array<Generator<PluginMsw>>
57
71
  }
72
+
58
73
  type ResolvedOptions = {
59
- output: Output<Oas>
60
- group: Options['group']
74
+ output: Output
75
+ group: Group | undefined
76
+ exclude: NonNullable<Options['exclude']>
77
+ include: Options['include']
78
+ override: NonNullable<Options['override']>
61
79
  parser: NonNullable<Options['parser']>
62
80
  baseURL: Options['baseURL'] | undefined
81
+ handlers: boolean
82
+ resolver: ResolverMsw
63
83
  }
64
84
 
65
- export type PluginMsw = PluginFactoryOptions<'plugin-msw', Options, ResolvedOptions, never, ResolvePathOptions>
85
+ export type PluginMsw = PluginFactoryOptions<'plugin-msw', Options, ResolvedOptions, ResolverMsw>
86
+
87
+ declare global {
88
+ namespace Kubb {
89
+ interface PluginRegistry {
90
+ 'plugin-msw': PluginMsw
91
+ }
92
+ }
93
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,58 @@
1
+ import type { ast } from '@kubb/core'
2
+ import type { ResolverFaker } from '@kubb/plugin-faker'
3
+ import type { PluginMsw } from './types.ts'
4
+
5
+ /**
6
+ * Gets the content type from a response, defaulting to 'application/json' if a schema exists.
7
+ */
8
+ export function getContentType(response: ast.ResponseNode | undefined): string | undefined {
9
+ return getResponseContentType(response) ?? (hasResponseSchema(response) ? 'application/json' : undefined)
10
+ }
11
+
12
+ /**
13
+ * Determines if a response has a schema that is not void or any.
14
+ */
15
+ export function hasResponseSchema(response: ast.ResponseNode | undefined): boolean {
16
+ return !!getResponseContentType(response) || (!!response?.schema && response.schema.type !== 'void' && response.schema.type !== 'any')
17
+ }
18
+
19
+ function getResponseContentType(response: ast.ResponseNode | undefined): string | undefined {
20
+ const contentType = response as unknown as { mediaType?: string | null; contentType?: string | null } | undefined
21
+ const value = contentType?.mediaType ?? contentType?.contentType
22
+ return typeof value === 'string' && value.length > 0 ? value : undefined
23
+ }
24
+
25
+ /**
26
+ * Converts an HTTP method to its lowercase MSW equivalent (e.g., 'POST' → 'post').
27
+ */
28
+ export function getMswMethod(node: ast.OperationNode): string {
29
+ return node.method.toLowerCase()
30
+ }
31
+
32
+ /**
33
+ * Converts an OpenAPI-style path to an Express/MSW-style path by replacing `{param}` with `:param`.
34
+ */
35
+ export function getMswUrl(node: ast.OperationNode): string {
36
+ return node.path.replaceAll('{', ':').replaceAll('}', '')
37
+ }
38
+
39
+ /**
40
+ * Resolves faker metadata for an MSW operation, including response name and file path.
41
+ */
42
+ export function resolveFakerMeta(
43
+ node: ast.OperationNode,
44
+ options: {
45
+ root: string
46
+ fakerResolver: ResolverFaker
47
+ fakerOutput: PluginMsw['resolvedOptions']['output']
48
+ fakerGroup: PluginMsw['resolvedOptions']['group']
49
+ },
50
+ ): { name: string; file: { path: string } } {
51
+ const { root, fakerResolver, fakerOutput, fakerGroup } = options
52
+ const tag = node.tags[0] ?? 'default'
53
+
54
+ return {
55
+ name: fakerResolver.resolveResponseName(node),
56
+ file: fakerResolver.resolveFile({ name: node.operationId, extname: '.ts', tag, path: node.path }, { root, output: fakerOutput, group: fakerGroup }),
57
+ }
58
+ }