@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.
- package/LICENSE +17 -10
- package/README.md +26 -7
- package/dist/components-BYvgvrY7.cjs +569 -0
- package/dist/components-BYvgvrY7.cjs.map +1 -0
- package/dist/components-Cm17DMTE.js +510 -0
- package/dist/components-Cm17DMTE.js.map +1 -0
- package/dist/components.cjs +1 -1
- package/dist/components.d.ts +17 -21
- package/dist/components.js +1 -1
- package/dist/generators-D9gvdP7Z.js +177 -0
- package/dist/generators-D9gvdP7Z.js.map +1 -0
- package/dist/generators-rZ99WaWQ.cjs +187 -0
- package/dist/generators-rZ99WaWQ.cjs.map +1 -0
- package/dist/generators.cjs +1 -1
- package/dist/generators.d.ts +4 -500
- package/dist/generators.js +1 -1
- package/dist/index.cjs +62 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +59 -65
- package/dist/index.js.map +1 -1
- package/dist/types-ItsHsMvC.d.ts +94 -0
- package/extension.yaml +233 -0
- package/package.json +57 -58
- package/src/components/Handlers.tsx +3 -3
- package/src/components/Mock.tsx +37 -28
- package/src/components/MockWithFaker.tsx +37 -24
- package/src/components/Response.tsx +23 -17
- package/src/generators/handlersGenerator.tsx +19 -19
- package/src/generators/mswGenerator.tsx +50 -60
- package/src/index.ts +1 -1
- package/src/plugin.ts +47 -86
- package/src/resolvers/resolverMsw.ts +28 -0
- package/src/types.ts +55 -27
- package/src/utils.ts +58 -0
- package/dist/components-8XBwMbFa.cjs +0 -343
- package/dist/components-8XBwMbFa.cjs.map +0 -1
- package/dist/components-DgtTZkWX.js +0 -277
- package/dist/components-DgtTZkWX.js.map +0 -1
- package/dist/generators-CY1SNd5X.cjs +0 -171
- package/dist/generators-CY1SNd5X.cjs.map +0 -1
- package/dist/generators-CvyZTxOm.js +0 -161
- package/dist/generators-CvyZTxOm.js.map +0 -1
- 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 {
|
|
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 =
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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,
|
|
1
|
+
import type { ast, Exclude, Generator, Group, Include, Output, Override, PluginFactoryOptions, Resolver } from '@kubb/core'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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: '
|
|
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
|
-
|
|
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
|
-
*
|
|
37
|
+
* Tags, operations, or paths to exclude from generation.
|
|
25
38
|
*/
|
|
26
39
|
exclude?: Array<Exclude>
|
|
27
40
|
/**
|
|
28
|
-
*
|
|
41
|
+
* Tags, operations, or paths to include in generation.
|
|
29
42
|
*/
|
|
30
43
|
include?: Array<Include>
|
|
31
44
|
/**
|
|
32
|
-
*
|
|
45
|
+
* Override options for specific tags, operations, or paths.
|
|
33
46
|
*/
|
|
34
47
|
override?: Array<Override<ResolvedOptions>>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
48
|
-
*
|
|
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
|
-
*
|
|
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
|
|
60
|
-
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,
|
|
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
|
+
}
|