@kubb/plugin-cypress 5.0.0-alpha.23 → 5.0.0-alpha.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-cypress",
3
- "version": "5.0.0-alpha.23",
3
+ "version": "5.0.0-alpha.25",
4
4
  "description": "Cypress test generator plugin for Kubb, creating end-to-end tests from OpenAPI specifications for automated API testing.",
5
5
  "keywords": [
6
6
  "cypress",
@@ -34,33 +34,10 @@
34
34
  "import": "./dist/index.js",
35
35
  "require": "./dist/index.cjs"
36
36
  },
37
- "./components": {
38
- "import": "./dist/components.js",
39
- "require": "./dist/components.cjs"
40
- },
41
- "./generators": {
42
- "import": "./dist/generators.js",
43
- "require": "./dist/generators.cjs"
44
- },
45
37
  "./package.json": "./package.json"
46
38
  },
47
39
  "types": "./dist/index.d.ts",
48
- "typesVersions": {
49
- "*": {
50
- "utils": [
51
- "./dist/utils.d.ts"
52
- ],
53
- "hooks": [
54
- "./dist/hooks.d.ts"
55
- ],
56
- "components": [
57
- "./dist/components.d.ts"
58
- ],
59
- "generators": [
60
- "./dist/generators.d.ts"
61
- ]
62
- }
63
- },
40
+ "typesVersions": {},
64
41
  "files": [
65
42
  "src",
66
43
  "dist",
@@ -77,10 +54,9 @@
77
54
  ],
78
55
  "dependencies": {
79
56
  "@kubb/react-fabric": "0.15.1",
80
- "@kubb/core": "5.0.0-alpha.23",
81
- "@kubb/oas": "5.0.0-alpha.23",
82
- "@kubb/plugin-oas": "5.0.0-alpha.23",
83
- "@kubb/plugin-ts": "5.0.0-alpha.23"
57
+ "@kubb/ast": "5.0.0-alpha.25",
58
+ "@kubb/core": "5.0.0-alpha.25",
59
+ "@kubb/plugin-ts": "5.0.0-alpha.25"
84
60
  },
85
61
  "engines": {
86
62
  "node": ">=22"
@@ -1,8 +1,9 @@
1
- import { URLPath } from '@internals/utils'
2
- import { type HttpMethod, isAllOptional, isOptional } from '@kubb/oas'
3
- import type { OperationSchemas } from '@kubb/plugin-oas'
4
- import { getPathParams } from '@kubb/plugin-oas/utils'
5
- import { File, Function, FunctionParams } from '@kubb/react-fabric'
1
+ import { camelCase, URLPath } from '@internals/utils'
2
+ import { caseParams, createFunctionParameter, createOperationParams, createTypeNode } from '@kubb/ast'
3
+ import type { OperationNode } from '@kubb/ast/types'
4
+ import type { ResolverTs } from '@kubb/plugin-ts'
5
+ import { functionPrinter } from '@kubb/plugin-ts'
6
+ import { File, Function } from '@kubb/react-fabric'
6
7
  import type { FabricReactNode } from '@kubb/react-fabric/types'
7
8
  import type { PluginCypress } from '../types.ts'
8
9
 
@@ -11,132 +12,114 @@ type Props = {
11
12
  * Name of the function
12
13
  */
13
14
  name: string
14
- typeSchemas: OperationSchemas
15
- url: string
15
+ /**
16
+ * AST operation node
17
+ */
18
+ node: OperationNode
19
+ /**
20
+ * TypeScript resolver for resolving param/data/response type names
21
+ */
22
+ resolver: ResolverTs
16
23
  baseURL: string | undefined
17
24
  dataReturnType: PluginCypress['resolvedOptions']['dataReturnType']
18
25
  paramsCasing: PluginCypress['resolvedOptions']['paramsCasing']
19
26
  paramsType: PluginCypress['resolvedOptions']['paramsType']
20
27
  pathParamsType: PluginCypress['resolvedOptions']['pathParamsType']
21
- method: HttpMethod
22
28
  }
23
29
 
24
- type GetParamsProps = {
25
- paramsCasing: PluginCypress['resolvedOptions']['paramsCasing']
30
+ const declarationPrinter = functionPrinter({ mode: 'declaration' })
31
+
32
+ function getParams({
33
+ paramsType,
34
+ pathParamsType,
35
+ paramsCasing,
36
+ resolver,
37
+ node,
38
+ }: {
26
39
  paramsType: PluginCypress['resolvedOptions']['paramsType']
27
40
  pathParamsType: PluginCypress['resolvedOptions']['pathParamsType']
28
- typeSchemas: OperationSchemas
29
- }
30
-
31
- function getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas }: GetParamsProps) {
32
- if (paramsType === 'object') {
33
- const pathParams = getPathParams(typeSchemas.pathParams, { typed: true, casing: paramsCasing })
34
-
35
- return FunctionParams.factory({
36
- data: {
37
- mode: 'object',
38
- children: {
39
- ...pathParams,
40
- data: typeSchemas.request?.name
41
- ? {
42
- type: typeSchemas.request?.name,
43
- optional: isOptional(typeSchemas.request?.schema),
44
- }
45
- : undefined,
46
- params: typeSchemas.queryParams?.name
47
- ? {
48
- type: typeSchemas.queryParams?.name,
49
- optional: isOptional(typeSchemas.queryParams?.schema),
50
- }
51
- : undefined,
52
- headers: typeSchemas.headerParams?.name
53
- ? {
54
- type: typeSchemas.headerParams?.name,
55
- optional: isOptional(typeSchemas.headerParams?.schema),
56
- }
57
- : undefined,
58
- },
59
- },
60
- options: {
61
- type: 'Partial<Cypress.RequestOptions>',
62
- default: '{}',
63
- },
64
- })
65
- }
66
-
67
- return FunctionParams.factory({
68
- pathParams: typeSchemas.pathParams?.name
69
- ? {
70
- mode: pathParamsType === 'object' ? 'object' : 'inlineSpread',
71
- children: getPathParams(typeSchemas.pathParams, { typed: true, casing: paramsCasing }),
72
- default: isAllOptional(typeSchemas.pathParams?.schema) ? '{}' : undefined,
73
- }
74
- : undefined,
75
- data: typeSchemas.request?.name
76
- ? {
77
- type: typeSchemas.request?.name,
78
- optional: isOptional(typeSchemas.request?.schema),
79
- }
80
- : undefined,
81
- params: typeSchemas.queryParams?.name
82
- ? {
83
- type: typeSchemas.queryParams?.name,
84
- optional: isOptional(typeSchemas.queryParams?.schema),
85
- }
86
- : undefined,
87
- headers: typeSchemas.headerParams?.name
88
- ? {
89
- type: typeSchemas.headerParams?.name,
90
- optional: isOptional(typeSchemas.headerParams?.schema),
91
- }
92
- : undefined,
93
- options: {
94
- type: 'Partial<Cypress.RequestOptions>',
95
- default: '{}',
96
- },
41
+ paramsCasing: PluginCypress['resolvedOptions']['paramsCasing']
42
+ resolver: ResolverTs
43
+ node: OperationNode
44
+ }): string {
45
+ const paramsNode = createOperationParams(node, {
46
+ paramsType,
47
+ pathParamsType,
48
+ paramsCasing,
49
+ resolver,
50
+ extraParams: [
51
+ createFunctionParameter({ name: 'options', type: createTypeNode({ variant: 'reference', name: 'Partial<Cypress.RequestOptions>' }), default: '{}' }),
52
+ ],
97
53
  })
98
- }
99
54
 
100
- export function Request({ baseURL = '', name, dataReturnType, typeSchemas, url, method, paramsType, paramsCasing, pathParamsType }: Props): FabricReactNode {
101
- const path = new URLPath(url, { casing: paramsCasing })
102
-
103
- const params = getParams({ paramsType, paramsCasing, pathParamsType, typeSchemas })
55
+ return declarationPrinter.print(paramsNode) ?? ''
56
+ }
104
57
 
105
- const returnType =
106
- dataReturnType === 'data' ? `Cypress.Chainable<${typeSchemas.response.name}>` : `Cypress.Chainable<Cypress.Response<${typeSchemas.response.name}>>`
58
+ export function Request({ baseURL = '', name, dataReturnType, resolver, node, paramsType, pathParamsType, paramsCasing }: Props): FabricReactNode {
59
+ const paramsSignature = getParams({ paramsType, pathParamsType, paramsCasing, resolver, node })
107
60
 
108
- // Build the URL template string - this will convert /pets/:petId to /pets/${petId}
109
- const urlTemplate = path.toTemplateString({ prefix: baseURL })
61
+ const responseType = resolver.resolveResponseName(node)
62
+ const returnType = dataReturnType === 'data' ? `Cypress.Chainable<${responseType}>` : `Cypress.Chainable<Cypress.Response<${responseType}>>`
110
63
 
111
- // Build request options object
112
- const requestOptions: string[] = [`method: '${method}'`, `url: ${urlTemplate}`]
64
+ const casedPathParams = caseParams(
65
+ node.parameters.filter((p) => p.in === 'path'),
66
+ paramsCasing,
67
+ )
68
+ // Build a lookup keyed by camelCase-normalized name so that path-template names
69
+ // (e.g. `{pet_id}`) correctly resolve to the function-parameter name (`petId`)
70
+ // even when the OpenAPI spec has inconsistent casing between the two.
71
+ const pathParamNameMap = new Map(casedPathParams.map((p) => [camelCase(p.name), p.name]))
72
+
73
+ const urlPath = new URLPath(node.path, { casing: paramsCasing })
74
+ const urlTemplate = urlPath.toTemplateString({
75
+ prefix: baseURL,
76
+ replacer: (param) => pathParamNameMap.get(camelCase(param)) ?? param,
77
+ })
113
78
 
114
- // Add query params if they exist
115
- if (typeSchemas.queryParams?.name) {
116
- requestOptions.push('qs: params')
79
+ const requestOptions: string[] = [`method: '${node.method}'`, `url: ${urlTemplate}`]
80
+
81
+ const queryParams = node.parameters.filter((p) => p.in === 'query')
82
+ if (queryParams.length > 0) {
83
+ const casedQueryParams = caseParams(queryParams, paramsCasing)
84
+ // When paramsCasing renames query params (e.g. page_size → pageSize), we must remap
85
+ // the camelCase keys back to the original API names before passing them to `qs`.
86
+ const needsQsTransform = casedQueryParams.some((p, i) => p.name !== queryParams[i]!.name)
87
+ if (needsQsTransform) {
88
+ const pairs = queryParams.map((orig, i) => `${orig.name}: params.${casedQueryParams[i]!.name}`).join(', ')
89
+ requestOptions.push(`qs: params ? { ${pairs} } : undefined`)
90
+ } else {
91
+ requestOptions.push('qs: params')
92
+ }
117
93
  }
118
94
 
119
- // Add headers if they exist
120
- if (typeSchemas.headerParams?.name) {
121
- requestOptions.push('headers')
95
+ const headerParams = node.parameters.filter((p) => p.in === 'header')
96
+ if (headerParams.length > 0) {
97
+ const casedHeaderParams = caseParams(headerParams, paramsCasing)
98
+ // When paramsCasing renames header params (e.g. x-api-key → xApiKey), we must remap
99
+ // the camelCase keys back to the original API names before passing them to `headers`.
100
+ const needsHeaderTransform = casedHeaderParams.some((p, i) => p.name !== headerParams[i]!.name)
101
+ if (needsHeaderTransform) {
102
+ const pairs = headerParams.map((orig, i) => `'${orig.name}': headers.${casedHeaderParams[i]!.name}`).join(', ')
103
+ requestOptions.push(`headers: headers ? { ${pairs} } : undefined`)
104
+ } else {
105
+ requestOptions.push('headers')
106
+ }
122
107
  }
123
108
 
124
- // Add body if request schema exists
125
- if (typeSchemas.request?.name) {
109
+ if (node.requestBody?.schema) {
126
110
  requestOptions.push('body: data')
127
111
  }
128
112
 
129
- // Spread additional Cypress options
130
113
  requestOptions.push('...options')
131
114
 
132
115
  return (
133
116
  <File.Source name={name} isIndexable isExportable>
134
- <Function name={name} export params={params.toConstructor()} returnType={returnType}>
117
+ <Function name={name} export params={paramsSignature} returnType={returnType}>
135
118
  {dataReturnType === 'data'
136
- ? `return cy.request<${typeSchemas.response.name}>({
119
+ ? `return cy.request<${responseType}>({
137
120
  ${requestOptions.join(',\n ')}
138
121
  }).then((res) => res.body)`
139
- : `return cy.request<${typeSchemas.response.name}>({
122
+ : `return cy.request<${responseType}>({
140
123
  ${requestOptions.join(',\n ')}
141
124
  })`}
142
125
  </Function>
@@ -1,64 +1,77 @@
1
- import { useDriver } 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'
1
+ import path from 'node:path'
2
+ import { caseParams, composeTransformers, transform } from '@kubb/ast'
3
+ import { defineGenerator } from '@kubb/core'
4
+ import type { PluginTs } from '@kubb/plugin-ts'
5
5
  import { pluginTsName } from '@kubb/plugin-ts'
6
6
  import { File } from '@kubb/react-fabric'
7
- import { Request } from '../components'
8
- import type { PluginCypress } from '../types'
7
+ import { Request } from '../components/Request.tsx'
8
+ import type { PluginCypress } from '../types.ts'
9
9
 
10
- export const cypressGenerator = createReactGenerator<PluginCypress>({
10
+ export const cypressGenerator = defineGenerator<PluginCypress>({
11
11
  name: 'cypress',
12
- Operation({ operation, generator, plugin }) {
13
- const {
14
- options: { output, baseURL, dataReturnType, paramsCasing, paramsType, pathParamsType },
15
- } = plugin
16
- const driver = useDriver()
12
+ type: 'react',
13
+ Operation({ node, adapter, options, config, driver, resolver }) {
14
+ const { output, baseURL, dataReturnType, paramsCasing, paramsType, pathParamsType, group, transformers } = options
15
+ const root = path.resolve(config.root, config.output.path)
17
16
 
18
- const oas = useOas()
19
- const { getSchemas, getName, getFile } = useOperationManager(generator)
17
+ const pluginTs = driver.getPlugin<PluginTs>(pluginTsName)
20
18
 
21
- const request = {
22
- name: getName(operation, { type: 'function' }),
23
- file: getFile(operation),
19
+ if (!pluginTs) {
20
+ return null
24
21
  }
25
22
 
26
- const type = {
27
- file: getFile(operation, { pluginName: pluginTsName }),
28
- schemas: getSchemas(operation, { pluginName: pluginTsName, type: 'type' }),
29
- }
23
+ const transformedNode = transform(node, composeTransformers(...transformers))
24
+
25
+ const casedParams = caseParams(transformedNode.parameters, paramsCasing)
26
+
27
+ const pathParams = casedParams.filter((p) => p.in === 'path')
28
+ const queryParams = casedParams.filter((p) => p.in === 'query')
29
+ const headerParams = casedParams.filter((p) => p.in === 'header')
30
+
31
+ const importedTypeNames = [
32
+ ...pathParams.map((p) => pluginTs.resolver.resolvePathParamsName(transformedNode, p)),
33
+ ...queryParams.map((p) => pluginTs.resolver.resolveQueryParamsName(transformedNode, p)),
34
+ ...headerParams.map((p) => pluginTs.resolver.resolveHeaderParamsName(transformedNode, p)),
35
+ transformedNode.requestBody?.schema ? pluginTs.resolver.resolveDataName(transformedNode) : undefined,
36
+ pluginTs.resolver.resolveResponseName(transformedNode),
37
+ ].filter(Boolean)
38
+
39
+ const meta = {
40
+ name: resolver.resolveName(transformedNode.operationId),
41
+ file: resolver.resolveFile(
42
+ { name: transformedNode.operationId, extname: '.ts', tag: transformedNode.tags[0] ?? 'default', path: transformedNode.path },
43
+ { root, output, group },
44
+ ),
45
+ fileTs: pluginTs.resolver.resolveFile(
46
+ { name: transformedNode.operationId, extname: '.ts', tag: transformedNode.tags[0] ?? 'default', path: transformedNode.path },
47
+ {
48
+ root,
49
+ output: pluginTs.options?.output ?? output,
50
+ group: pluginTs.options?.group,
51
+ },
52
+ ),
53
+ } as const
30
54
 
31
55
  return (
32
56
  <File
33
- baseName={request.file.baseName}
34
- path={request.file.path}
35
- meta={request.file.meta}
36
- banner={getBanner({ oas, output, config: driver.config })}
37
- footer={getFooter({ oas, output })}
57
+ baseName={meta.file.baseName}
58
+ path={meta.file.path}
59
+ meta={meta.file.meta}
60
+ banner={resolver.resolveBanner(adapter.rootNode, { output, config })}
61
+ footer={resolver.resolveFooter(adapter.rootNode, { output, config })}
38
62
  >
39
- <File.Import
40
- name={[
41
- type.schemas.request?.name,
42
- type.schemas.response.name,
43
- type.schemas.pathParams?.name,
44
- type.schemas.queryParams?.name,
45
- type.schemas.headerParams?.name,
46
- ...(type.schemas.statusCodes?.map((item) => item.name) || []),
47
- ].filter(Boolean)}
48
- root={request.file.path}
49
- path={type.file.path}
50
- isTypeOnly
51
- />
63
+ {meta.fileTs && importedTypeNames.length > 0 && (
64
+ <File.Import name={Array.from(new Set(importedTypeNames))} root={meta.file.path} path={meta.fileTs.path} isTypeOnly />
65
+ )}
52
66
  <Request
53
- name={request.name}
67
+ name={meta.name}
68
+ node={transformedNode}
69
+ resolver={pluginTs.resolver}
54
70
  dataReturnType={dataReturnType}
55
71
  paramsCasing={paramsCasing}
56
72
  paramsType={paramsType}
57
73
  pathParamsType={pathParamsType}
58
- typeSchemas={type.schemas}
59
- method={operation.method}
60
74
  baseURL={baseURL}
61
- url={operation.path}
62
75
  />
63
76
  </File>
64
77
  )
package/src/index.ts CHANGED
@@ -1,2 +1,9 @@
1
+ export { Request } from './components/Request.tsx'
2
+
3
+ export { cypressGenerator } from './generators/cypressGenerator.tsx'
4
+
1
5
  export { pluginCypress, pluginCypressName } from './plugin.ts'
2
- export type { PluginCypress } from './types.ts'
6
+
7
+ export { resolverCypress } from './resolvers/resolverCypress.ts'
8
+
9
+ export type { PluginCypress, ResolverCypress } from './types.ts'
package/src/plugin.ts CHANGED
@@ -1,13 +1,35 @@
1
1
  import path from 'node:path'
2
2
  import { camelCase } from '@internals/utils'
3
- import { createPlugin, type Group, getBarrelFiles, getMode } from '@kubb/core'
4
- import { OperationGenerator, pluginOasName } from '@kubb/plugin-oas'
3
+ import { walk } from '@kubb/ast'
4
+ import type { OperationNode } from '@kubb/ast/types'
5
+ import { createPlugin, type Group, getBarrelFiles, getPreset, runGeneratorOperation, runGeneratorOperations, runGeneratorSchema } from '@kubb/core'
5
6
  import { pluginTsName } from '@kubb/plugin-ts'
6
- import { cypressGenerator } from './generators'
7
+ import { presets } from './presets.ts'
8
+ import { resolverCypress } from './resolvers/resolverCypress.ts'
7
9
  import type { PluginCypress } from './types.ts'
8
10
 
11
+ /**
12
+ * Canonical plugin name for `@kubb/plugin-cypress`, used to identify the plugin
13
+ * in driver lookups and warnings.
14
+ */
9
15
  export const pluginCypressName = 'plugin-cypress' satisfies PluginCypress['name']
10
16
 
17
+ /**
18
+ * The `@kubb/plugin-cypress` plugin factory.
19
+ *
20
+ * Generates Cypress `cy.request()` test functions from an OpenAPI/AST `RootNode`.
21
+ * Walks operations, delegates rendering to the active generators,
22
+ * and writes barrel files based on `output.barrelType`.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { pluginCypress } from '@kubb/plugin-cypress'
27
+ *
28
+ * export default defineConfig({
29
+ * plugins: [pluginCypress({ output: { path: 'cypress' } })],
30
+ * })
31
+ * ```
32
+ */
11
33
  export const pluginCypress = createPlugin<PluginCypress>((options) => {
12
34
  const {
13
35
  output = { path: 'cypress', barrelType: 'named' },
@@ -16,93 +38,84 @@ export const pluginCypress = createPlugin<PluginCypress>((options) => {
16
38
  exclude = [],
17
39
  include,
18
40
  override = [],
19
- transformers = {},
20
- generators = [cypressGenerator].filter(Boolean),
21
- contentType,
22
41
  baseURL,
23
42
  paramsCasing,
24
43
  paramsType = 'inline',
25
44
  pathParamsType = paramsType === 'object' ? 'object' : options.pathParamsType || 'inline',
45
+ compatibilityPreset = 'default',
46
+ resolvers: userResolvers = [],
47
+ transformers: userTransformers = [],
48
+ generators: userGenerators = [],
26
49
  } = options
27
50
 
51
+ const preset = getPreset({
52
+ preset: compatibilityPreset,
53
+ presets,
54
+ resolvers: [resolverCypress, ...userResolvers],
55
+ transformers: userTransformers,
56
+ generators: userGenerators,
57
+ })
58
+
28
59
  return {
29
60
  name: pluginCypressName,
30
- options: {
31
- output,
32
- dataReturnType,
33
- group,
34
- baseURL,
35
-
36
- paramsCasing,
37
- paramsType,
38
- pathParamsType,
61
+ get resolver() {
62
+ return preset.resolver
39
63
  },
40
- pre: [pluginOasName, pluginTsName].filter(Boolean),
41
- resolvePath(baseName, pathMode, options) {
42
- const root = path.resolve(this.config.root, this.config.output.path)
43
- const mode = pathMode ?? getMode(path.resolve(root, output.path))
44
-
45
- if (mode === 'single') {
46
- /**
47
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
48
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
49
- */
50
- return path.resolve(root, output.path)
64
+ get options() {
65
+ return {
66
+ output,
67
+ dataReturnType,
68
+ group: group
69
+ ? ({
70
+ ...group,
71
+ name: group.name
72
+ ? group.name
73
+ : (ctx: { group: string }) => {
74
+ if (group.type === 'path') {
75
+ return `${ctx.group.split('/')[1]}`
76
+ }
77
+ return `${camelCase(ctx.group)}Requests`
78
+ },
79
+ } satisfies Group)
80
+ : undefined,
81
+ baseURL,
82
+ paramsCasing,
83
+ paramsType,
84
+ pathParamsType,
85
+ resolver: preset.resolver,
86
+ transformers: preset.transformers,
51
87
  }
88
+ },
89
+ pre: [pluginTsName].filter(Boolean),
90
+ async install() {
91
+ const { config, fabric, plugin, adapter, rootNode, driver } = this
92
+ const root = path.resolve(config.root, config.output.path)
93
+ const resolver = preset.resolver
52
94
 
53
- if (group && (options?.group?.path || options?.group?.tag)) {
54
- const groupName: Group['name'] = group?.name
55
- ? group.name
56
- : (ctx) => {
57
- if (group?.type === 'path') {
58
- return `${ctx.group.split('/')[1]}`
59
- }
60
- return `${camelCase(ctx.group)}Requests`
61
- }
62
-
63
- return path.resolve(
64
- root,
65
- output.path,
66
- groupName({
67
- group: group.type === 'path' ? options.group.path! : options.group.tag!,
68
- }),
69
- baseName,
70
- )
95
+ if (!adapter) {
96
+ throw new Error('Plugin cannot work without adapter being set')
71
97
  }
72
98
 
73
- return path.resolve(root, output.path, baseName)
74
- },
75
- resolveName(name, type) {
76
- const resolvedName = camelCase(name, {
77
- isFile: type === 'file',
78
- })
99
+ const collectedOperations: Array<OperationNode> = []
100
+ const generatorContext = { generators: preset.generators, plugin, resolver, exclude, include, override, fabric, adapter, config, driver }
79
101
 
80
- if (type) {
81
- return transformers?.name?.(resolvedName, type) || resolvedName
82
- }
102
+ await walk(rootNode, {
103
+ depth: 'shallow',
104
+ async schema(schemaNode) {
105
+ await runGeneratorSchema(schemaNode, generatorContext)
106
+ },
107
+ async operation(operationNode) {
108
+ const baseOptions = resolver.resolveOptions(operationNode, { options: plugin.options, exclude, include, override })
83
109
 
84
- return resolvedName
85
- },
86
- async install() {
87
- const root = path.resolve(this.config.root, this.config.output.path)
88
- const mode = getMode(path.resolve(root, output.path))
89
- const oas = await this.getOas()
110
+ if (baseOptions !== null) {
111
+ collectedOperations.push(operationNode)
112
+ }
90
113
 
91
- const operationGenerator = new OperationGenerator(this.plugin.options, {
92
- fabric: this.fabric,
93
- oas,
94
- driver: this.driver,
95
- events: this.events,
96
- plugin: this.plugin,
97
- contentType,
98
- exclude,
99
- include,
100
- override,
101
- mode,
114
+ await runGeneratorOperation(operationNode, generatorContext)
115
+ },
102
116
  })
103
117
 
104
- const files = await operationGenerator.build(...generators)
105
- await this.upsertFile(...files)
118
+ await runGeneratorOperations(collectedOperations, generatorContext)
106
119
 
107
120
  const barrelFiles = await getBarrelFiles(this.fabric.files, {
108
121
  type: output.barrelType ?? 'named',
package/src/presets.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { definePresets } from '@kubb/core'
2
+ import { cypressGenerator } from './generators/cypressGenerator.tsx'
3
+ import { resolverCypress } from './resolvers/resolverCypress.ts'
4
+ import type { ResolverCypress } from './types.ts'
5
+
6
+ /**
7
+ * Built-in preset registry for `@kubb/plugin-cypress`.
8
+ *
9
+ * - `default` — uses `resolverCypress` and `cypressGenerator`.
10
+ * - `kubbV4` — uses `resolverCypress` and `cypressGenerator`.
11
+ */
12
+ export const presets = definePresets<ResolverCypress>({
13
+ default: {
14
+ name: 'default',
15
+ resolvers: [resolverCypress],
16
+ generators: [cypressGenerator],
17
+ },
18
+ kubbV4: {
19
+ name: 'kubbV4',
20
+ resolvers: [resolverCypress],
21
+ generators: [cypressGenerator],
22
+ },
23
+ })
@@ -0,0 +1,26 @@
1
+ import { camelCase } from '@internals/utils'
2
+ import { defineResolver } from '@kubb/core'
3
+ import type { PluginCypress } from '../types.ts'
4
+
5
+ /**
6
+ * Resolver for `@kubb/plugin-cypress` that provides the default naming
7
+ * and path-resolution helpers used by the plugin.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { resolverCypress } from '@kubb/plugin-cypress'
12
+ *
13
+ * resolverCypress.default('list pets', 'function') // -> 'listPets'
14
+ * resolverCypress.resolveName('show pet by id') // -> 'showPetById'
15
+ * ```
16
+ */
17
+ export const resolverCypress = defineResolver<PluginCypress>(() => ({
18
+ name: 'default',
19
+ pluginName: 'plugin-cypress',
20
+ default(name, type) {
21
+ return camelCase(name, { isFile: type === 'file' })
22
+ },
23
+ resolveName(name) {
24
+ return this.default(name, 'function')
25
+ },
26
+ }))