@kubb/plugin-cypress 5.0.0-beta.64 → 5.0.0-beta.73

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/dist/index.d.ts CHANGED
@@ -1,8 +1,31 @@
1
- import { t as __name } from "./chunk-C0LytTxp.js";
2
- import { Exclude, Generator, Group, Include, Output, OutputOptions, Override, PluginFactoryOptions, Resolver, ast } from "@kubb/core";
1
+ import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
+ import { Exclude, Group, Include, Output, OutputOptions, Override, PluginFactoryOptions, Resolver, ast } from "@kubb/core";
3
3
  import { ResolverTs } from "@kubb/plugin-ts";
4
4
  import { KubbReactNode } from "@kubb/renderer-jsx/types";
5
5
 
6
+ //#region src/components/Request.d.ts
7
+ type Props = {
8
+ /**
9
+ * Name of the function
10
+ */
11
+ name: string;
12
+ /**
13
+ * AST operation node
14
+ */
15
+ node: ast.OperationNode;
16
+ /**
17
+ * TypeScript resolver for resolving param/data/response type names
18
+ */
19
+ resolver: ResolverTs;
20
+ baseURL: string | null | undefined;
21
+ };
22
+ declare function Request({
23
+ baseURL,
24
+ name,
25
+ resolver,
26
+ node
27
+ }: Props): KubbReactNode;
28
+ //#endregion
6
29
  //#region src/types.d.ts
7
30
  /**
8
31
  * Resolver for Cypress that provides naming methods for test functions.
@@ -20,32 +43,6 @@ type ResolverCypress = Resolver & {
20
43
  */
21
44
  resolvePathName(this: ResolverCypress, name: string, type?: 'file' | 'function' | 'type' | 'const'): string;
22
45
  };
23
- /**
24
- * Parameter handling mode that determines how path params and query/body params are arranged in function signatures.
25
- */
26
- type ParamsTypeOptions = {
27
- /**
28
- * Every operation parameter is wrapped in a single destructured object argument.
29
- */
30
- paramsType: 'object';
31
- /**
32
- * `pathParamsType` has no effect when `paramsType` is `'object'`.
33
- */
34
- pathParamsType?: never;
35
- } | {
36
- /**
37
- * Each parameter group is emitted as a separate positional function argument.
38
- */
39
- paramsType?: 'inline';
40
- /**
41
- * How URL path parameters are arranged inside the inline argument list.
42
- * - `'object'` groups them into one destructured object.
43
- * - `'inline'` emits each path param as its own argument.
44
- *
45
- * @default 'inline'
46
- */
47
- pathParamsType?: 'object' | 'inline';
48
- };
49
46
  /**
50
47
  * Where the generated Cypress helpers are written and how they are exported, plus the optional
51
48
  * `group` strategy. The `group` option organizes `output.mode: 'directory'` output into per-tag or per-path subdirectories.
@@ -53,20 +50,6 @@ type ParamsTypeOptions = {
53
50
  * @default { path: 'cypress', barrel: { type: 'named' } }
54
51
  */
55
52
  type Options = OutputOptions & {
56
- /**
57
- * Shape of the value returned from each helper.
58
- * - `'data'` — only the response body.
59
- * - `'full'` — the full Cypress response object (headers, status, body).
60
- *
61
- * @default 'data'
62
- */
63
- dataReturnType?: 'data' | 'full';
64
- /**
65
- * Rename parameter properties in the generated helpers (path, query, headers).
66
- *
67
- * @note Must match the value of `paramsCasing` on `@kubb/plugin-ts`.
68
- */
69
- paramsCasing?: 'camelcase';
70
53
  /**
71
54
  * Base URL prepended to every request URL. When omitted, falls back to the
72
55
  * adapter's server URL (typically `servers[0].url`).
@@ -92,11 +75,7 @@ type Options = OutputOptions & {
92
75
  * Macros applied to each operation node before printing.
93
76
  */
94
77
  macros?: Array<ast.Macro>;
95
- /**
96
- * Custom generators that run alongside the built-in Cypress generators.
97
- */
98
- generators?: Array<Generator<PluginCypress>>;
99
- } & ParamsTypeOptions;
78
+ };
100
79
  type ResolvedOptions = {
101
80
  output: Output;
102
81
  exclude: Array<Exclude>;
@@ -104,10 +83,6 @@ type ResolvedOptions = {
104
83
  override: Array<Override<ResolvedOptions>>;
105
84
  group: Group | null;
106
85
  baseURL: Options['baseURL'] | undefined;
107
- dataReturnType: NonNullable<Options['dataReturnType']>;
108
- pathParamsType: NonNullable<NonNullable<Options['pathParamsType']>>;
109
- paramsType: NonNullable<Options['paramsType']>;
110
- paramsCasing: Options['paramsCasing'];
111
86
  resolver: ResolverCypress;
112
87
  };
113
88
  type PluginCypress = PluginFactoryOptions<'plugin-cypress', Options, ResolvedOptions, ResolverCypress>;
@@ -119,37 +94,6 @@ declare global {
119
94
  }
120
95
  }
121
96
  //#endregion
122
- //#region src/components/Request.d.ts
123
- type Props = {
124
- /**
125
- * Name of the function
126
- */
127
- name: string;
128
- /**
129
- * AST operation node
130
- */
131
- node: ast.OperationNode;
132
- /**
133
- * TypeScript resolver for resolving param/data/response type names
134
- */
135
- resolver: ResolverTs;
136
- baseURL: string | null | undefined;
137
- dataReturnType: PluginCypress['resolvedOptions']['dataReturnType'];
138
- paramsCasing: PluginCypress['resolvedOptions']['paramsCasing'];
139
- paramsType: PluginCypress['resolvedOptions']['paramsType'];
140
- pathParamsType: PluginCypress['resolvedOptions']['pathParamsType'];
141
- };
142
- declare function Request({
143
- baseURL,
144
- name,
145
- dataReturnType,
146
- resolver,
147
- node,
148
- paramsType,
149
- pathParamsType,
150
- paramsCasing
151
- }: Props): KubbReactNode;
152
- //#endregion
153
97
  //#region src/generators/cypressGenerator.d.ts
154
98
  /**
155
99
  * Built-in generator for `@kubb/plugin-cypress`. Emits one typed
@@ -193,7 +137,7 @@ declare const pluginCypress: (options?: Options | undefined) => import("@kubb/co
193
137
  /**
194
138
  * Default resolver used by `@kubb/plugin-cypress`. Decides the names and file
195
139
  * paths for every generated `cy.request()` wrapper. Functions and files use
196
- * camelCase, matching the convention from `@kubb/plugin-client`.
140
+ * camelCase, matching the convention from `@kubb/plugin-axios` and `@kubb/plugin-fetch`.
197
141
  *
198
142
  * @example Resolve a helper name
199
143
  * ```ts
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
- import "./chunk-C0LytTxp.js";
1
+ import "./rolldown-runtime-C0LytTxp.js";
2
2
  import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
3
- import { caseParams, createOperationParams } from "@kubb/ast/utils";
4
- import { functionPrinter, pluginTsName } from "@kubb/plugin-ts";
5
3
  import { File, Function, jsxRenderer } from "@kubb/renderer-jsx";
6
4
  import { jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
5
+ import { pluginTsName } from "@kubb/plugin-ts";
7
6
  //#region ../../internals/utils/src/casing.ts
8
7
  /**
9
8
  * Shared implementation for camelCase and PascalCase conversion.
@@ -216,6 +215,22 @@ var Url = class Url {
216
215
  return `\`${prefix ?? ""}${result}\``;
217
216
  }
218
217
  /**
218
+ * Converts an OpenAPI/Swagger path to a template literal that reads each parameter off the
219
+ * grouped `path` request option, e.g. `/pet/{petId}` becomes `` `/pet/${path.petId}` ``. Parameter
220
+ * names are camelCased to match the generated `path` type, and `prefix` is prepended inside the
221
+ * literal. Shared by the client and cypress generators that pass a grouped `path` object.
222
+ *
223
+ * @example
224
+ * Url.toGroupedTemplateString('/pet/{petId}') // '`/pet/${path.petId}`'
225
+ */
226
+ static toGroupedTemplateString(path, { prefix } = {}) {
227
+ return Url.toTemplateString(path, {
228
+ prefix,
229
+ casing: "camelcase",
230
+ replacer: (name) => `path.${name}`
231
+ });
232
+ }
233
+ /**
219
234
  * Returns the path and its extracted params as a structured `URLObject`, or as a stringified
220
235
  * expression when `stringify` is set.
221
236
  *
@@ -243,9 +258,108 @@ var Url = class Url {
243
258
  }
244
259
  };
245
260
  //#endregion
261
+ //#region ../../internals/shared/src/params.ts
262
+ const caseParamsCache = /* @__PURE__ */ new WeakMap();
263
+ /**
264
+ * Applies camelCase to parameter names and returns a new array without mutating the input.
265
+ *
266
+ * Run it before handing parameters to schema builders so output property keys get the right casing
267
+ * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
268
+ * original array is returned unchanged. Results are cached per input array.
269
+ */
270
+ function caseParams(params, casing) {
271
+ if (!casing) return params;
272
+ const cached = caseParamsCache.get(params);
273
+ if (cached) return cached;
274
+ const result = params.map((param) => ({
275
+ ...param,
276
+ name: camelCase(param.name)
277
+ }));
278
+ caseParamsCache.set(params, result);
279
+ return result;
280
+ }
281
+ function buildParamsMapping(originalParams, mappedParams) {
282
+ const mapping = {};
283
+ let hasChanged = false;
284
+ originalParams.forEach((param, i) => {
285
+ const mappedName = mappedParams[i]?.name ?? param.name;
286
+ mapping[param.name] = mappedName;
287
+ if (param.name !== mappedName) hasChanged = true;
288
+ });
289
+ return hasChanged ? mapping : null;
290
+ }
291
+ //#endregion
246
292
  //#region ../../internals/shared/src/operation.ts
293
+ function getContentTypeInfo(node) {
294
+ const contentTypes = node.requestBody?.content?.map((e) => e.contentType) ?? [];
295
+ const isMultipleContentTypes = contentTypes.length > 1;
296
+ return {
297
+ contentTypes,
298
+ isMultipleContentTypes,
299
+ contentTypeUnion: isMultipleContentTypes ? contentTypes.map((ct) => JSON.stringify(ct)).join(" | ") : "",
300
+ defaultContentType: contentTypes[0] ?? "application/json",
301
+ hasFormData: contentTypes.some((ct) => ct === "multipart/form-data")
302
+ };
303
+ }
304
+ function buildRequestConfigType(node) {
305
+ const { isMultipleContentTypes, contentTypeUnion } = getContentTypeInfo(node);
306
+ const configType = `Partial<Omit<RequestConfig, 'path' | 'query' | 'body' | 'headers' | 'url'>>`;
307
+ const contentTypeProp = isMultipleContentTypes ? `contentType?: ${contentTypeUnion}` : null;
308
+ return contentTypeProp ? `${configType} & { ${contentTypeProp} }` : configType;
309
+ }
310
+ /**
311
+ * Which of the grouped request options an operation carries.
312
+ */
313
+ function getRequestGroups(node) {
314
+ const { path, query, header } = getOperationParameters(node);
315
+ return {
316
+ path: path.length > 0,
317
+ query: query.length > 0,
318
+ body: Boolean(node.requestBody?.content?.[0]?.schema),
319
+ headers: header.length > 0
320
+ };
321
+ }
322
+ /**
323
+ * Resolves which grouped request options an operation carries together with whether each group
324
+ * holds a required member. The grouped parameter stays optional only when nothing inside it is
325
+ * required, matching the generated `RequestConfig` type.
326
+ */
327
+ function getRequestGroupOptionality(node) {
328
+ const groups = getRequestGroups(node);
329
+ const { path, query, header } = getOperationParameters(node);
330
+ const hasRequiredPath = path.some((param) => param.required);
331
+ const hasRequiredQuery = query.some((param) => param.required);
332
+ const hasRequiredHeader = header.some((param) => param.required);
333
+ return {
334
+ groups,
335
+ hasRequiredPath,
336
+ hasRequiredQuery,
337
+ hasRequiredHeader,
338
+ isOptional: !hasRequiredPath && !hasRequiredQuery && !hasRequiredHeader && !groups.body
339
+ };
340
+ }
341
+ /**
342
+ * Builds the grouped `{ path, query, body, headers }` parameter for a generated client
343
+ * function, typed from the operation's `RequestConfig` (minus `url`). Only the groups the
344
+ * operation actually has are destructured. The trailing `config` parameter carries the
345
+ * runtime `RequestConfig` overrides plus `client`.
346
+ */
347
+ function buildRequestParamsSignature(node, resolver, options = {}) {
348
+ const { isConfigurable = true } = options;
349
+ const { groups, isOptional } = getRequestGroupOptionality(node);
350
+ const names = [
351
+ "path",
352
+ "query",
353
+ "body",
354
+ "headers"
355
+ ].filter((key) => groups[key]);
356
+ return {
357
+ signature: [names.length > 0 ? `{ ${names.join(", ")} }: ${resolver.resolveRequestConfigName(node)}${isOptional ? " = {}" : ""}` : null, isConfigurable ? `config: ${buildRequestConfigType(node)} = {}` : null].filter(Boolean).join(", "),
358
+ groups
359
+ };
360
+ }
247
361
  function getOperationParameters(node, options = {}) {
248
- const params = caseParams(node.parameters, options.paramsCasing);
362
+ const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
249
363
  return {
250
364
  path: params.filter((param) => param.in === "path"),
251
365
  query: params.filter((param) => param.in === "query"),
@@ -269,7 +383,7 @@ function resolveStatusCodeNames(node, resolver) {
269
383
  }
270
384
  const typeNamesByResolver = /* @__PURE__ */ new WeakMap();
271
385
  function resolveOperationTypeNames(node, resolver, options = {}) {
272
- const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${(options.exclude ?? []).join(",")}`;
386
+ const cacheKey = `${node.operationId}\0${options.paramsCasing ?? ""}\0${options.order ?? ""}\0${options.responseStatusNames ?? ""}\0${options.includeParams === false ? "noparams" : ""}\0${(options.exclude ?? []).join(",")}`;
273
387
  let byResolver = typeNamesByResolver.get(resolver);
274
388
  if (byResolver) {
275
389
  const cached = byResolver.get(cacheKey);
@@ -281,7 +395,7 @@ function resolveOperationTypeNames(node, resolver, options = {}) {
281
395
  const { path, query, header } = getOperationParameters(node, { paramsCasing: options.paramsCasing });
282
396
  const responseStatusNames = options.responseStatusNames === "error" ? resolveErrorNames(node, resolver) : options.responseStatusNames === false ? [] : resolveStatusCodeNames(node, resolver);
283
397
  const exclude = new Set(options.exclude ?? []);
284
- const paramNames = [
398
+ const paramNames = options.includeParams === false ? [] : [
285
399
  ...path.map((param) => resolver.resolvePathParamsName(node, param)),
286
400
  ...query.map((param) => resolver.resolveQueryParamsName(node, param)),
287
401
  ...header.map((param) => resolver.resolveHeaderParamsName(node, param))
@@ -332,49 +446,31 @@ function createGroupConfig(group) {
332
446
  }
333
447
  //#endregion
334
448
  //#region src/components/Request.tsx
335
- const declarationPrinter = functionPrinter({ mode: "declaration" });
336
- function Request({ baseURL = "", name, dataReturnType, resolver, node, paramsType, pathParamsType, paramsCasing }) {
449
+ function Request({ baseURL = "", name, resolver, node }) {
337
450
  if (!ast.isHttpOperationNode(node)) return null;
338
- const paramsNode = createOperationParams(node, {
339
- paramsType,
340
- pathParamsType,
341
- paramsCasing,
342
- resolver,
343
- extraParams: [ast.factory.createFunctionParameter({
344
- name: "options",
345
- type: "Partial<Cypress.RequestOptions>",
346
- default: "{}"
347
- })]
348
- });
349
- const paramsSignature = declarationPrinter.print(paramsNode) ?? "";
451
+ const { query: originalQueryParams, header: originalHeaderParams } = getOperationParameters(node, { paramsCasing: "original" });
452
+ const { query: casedQueryParams, header: casedHeaderParams } = getOperationParameters(node);
453
+ const queryParamsMapping = buildParamsMapping(originalQueryParams, casedQueryParams);
454
+ const headerParamsMapping = buildParamsMapping(originalHeaderParams, casedHeaderParams);
455
+ const { signature, groups } = buildRequestParamsSignature(node, resolver, { isConfigurable: false });
456
+ const paramsSignature = [signature, "options: Partial<Cypress.RequestOptions> = {}"].filter(Boolean).join(", ");
350
457
  const responseType = resolver.resolveResponseName(node);
351
- const returnType = dataReturnType === "data" ? `Cypress.Chainable<${responseType}>` : `Cypress.Chainable<Cypress.Response<${responseType}>>`;
352
- const casedPathParams = getOperationParameters(node, { paramsCasing }).path;
353
- const pathParamNameMap = new Map(casedPathParams.map((p) => [camelCase(p.name), p.name]));
354
- const urlTemplate = Url.toTemplateString(node.path, {
355
- prefix: baseURL,
356
- replacer: (param) => pathParamNameMap.get(camelCase(param)) ?? param,
357
- casing: paramsCasing
358
- });
458
+ const returnType = `Cypress.Chainable<${responseType}>`;
459
+ const urlTemplate = Url.toGroupedTemplateString(node.path, { prefix: baseURL });
359
460
  const requestOptions = [`method: '${node.method}'`, `url: ${urlTemplate}`];
360
- const queryParams = getOperationParameters(node).query;
361
- if (queryParams.length > 0) {
362
- const casedQueryParams = getOperationParameters(node, { paramsCasing }).query;
363
- if (casedQueryParams.some((p, i) => p.name !== queryParams[i].name)) {
364
- const pairs = queryParams.map((orig, i) => `${orig.name}: params.${casedQueryParams[i].name}`).join(", ");
365
- requestOptions.push(`qs: params ? { ${pairs} } : undefined`);
366
- } else requestOptions.push("qs: params");
367
- }
368
- const headerParams = getOperationParameters(node).header;
369
- if (headerParams.length > 0) {
370
- const casedHeaderParams = getOperationParameters(node, { paramsCasing }).header;
371
- if (casedHeaderParams.some((p, i) => p.name !== headerParams[i].name)) {
372
- const pairs = headerParams.map((orig, i) => `'${orig.name}': headers.${casedHeaderParams[i].name}`).join(", ");
373
- requestOptions.push(`headers: headers ? { ${pairs} } : undefined`);
374
- } else requestOptions.push("headers");
375
- }
376
- if (node.requestBody?.content?.[0]?.schema) requestOptions.push("body: data");
461
+ if (groups.query) if (queryParamsMapping) {
462
+ const pairs = Object.entries(queryParamsMapping).map(([originalName, camelCaseName]) => `${originalName}: query.${camelCaseName}`).join(", ");
463
+ requestOptions.push(`qs: query ? { ${pairs} } : undefined`);
464
+ } else requestOptions.push("qs: query");
465
+ if (groups.headers) if (headerParamsMapping) {
466
+ const pairs = Object.entries(headerParamsMapping).map(([originalName, camelCaseName]) => `'${originalName}': headers.${camelCaseName}`).join(", ");
467
+ requestOptions.push(`headers: headers ? { ${pairs} } : undefined`);
468
+ } else requestOptions.push("headers");
469
+ if (groups.body) requestOptions.push("body");
377
470
  requestOptions.push("...options");
471
+ const requestCall = `return cy.request<${responseType}>({
472
+ ${requestOptions.join(",\n ")}
473
+ }).then((res) => res.body)`;
378
474
  return /* @__PURE__ */ jsx(File.Source, {
379
475
  name,
380
476
  isIndexable: true,
@@ -384,11 +480,7 @@ function Request({ baseURL = "", name, dataReturnType, resolver, node, paramsTyp
384
480
  export: true,
385
481
  params: paramsSignature,
386
482
  returnType,
387
- children: dataReturnType === "data" ? `return cy.request<${responseType}>({
388
- ${requestOptions.join(",\n ")}
389
- }).then((res) => res.body)` : `return cy.request<${responseType}>({
390
- ${requestOptions.join(",\n ")}
391
- })`
483
+ children: requestCall
392
484
  })
393
485
  });
394
486
  }
@@ -405,11 +497,11 @@ const cypressGenerator = defineGenerator({
405
497
  operation(node, ctx) {
406
498
  if (!ast.isHttpOperationNode(node)) return null;
407
499
  const { config, resolver, driver, root } = ctx;
408
- const { output, baseURL, dataReturnType, paramsCasing, paramsType, pathParamsType, group } = ctx.options;
500
+ const { output, baseURL, group } = ctx.options;
409
501
  const pluginTs = driver.getPlugin(pluginTsName);
410
502
  if (!pluginTs) return null;
411
503
  const tsResolver = driver.getResolver(pluginTsName);
412
- const importedTypeNames = resolveOperationTypeNames(node, tsResolver, { paramsCasing });
504
+ const importedTypeNames = [tsResolver.resolveRequestConfigName(node), ...resolveOperationTypeNames(node, tsResolver, { includeParams: false })];
413
505
  const meta = {
414
506
  name: resolver.resolveName(node.operationId),
415
507
  file: resolver.resolveFile({
@@ -462,10 +554,6 @@ const cypressGenerator = defineGenerator({
462
554
  name: meta.name,
463
555
  node,
464
556
  resolver: tsResolver,
465
- dataReturnType,
466
- paramsCasing,
467
- paramsType,
468
- pathParamsType,
469
557
  baseURL
470
558
  })]
471
559
  });
@@ -476,7 +564,7 @@ const cypressGenerator = defineGenerator({
476
564
  /**
477
565
  * Default resolver used by `@kubb/plugin-cypress`. Decides the names and file
478
566
  * paths for every generated `cy.request()` wrapper. Functions and files use
479
- * camelCase, matching the convention from `@kubb/plugin-client`.
567
+ * camelCase, matching the convention from `@kubb/plugin-axios` and `@kubb/plugin-fetch`.
480
568
  *
481
569
  * @example Resolve a helper name
482
570
  * ```ts
@@ -532,7 +620,7 @@ const pluginCypress = definePlugin((options) => {
532
620
  const { output = {
533
621
  path: "cypress",
534
622
  barrel: { type: "named" }
535
- }, group, exclude = [], include, override = [], dataReturnType = "data", baseURL, paramsCasing, paramsType = "inline", pathParamsType = paramsType === "object" ? "object" : options.pathParamsType || "inline", resolver: userResolver, macros: userMacros, generators: userGenerators = [] } = options;
623
+ }, group, exclude = [], include, override = [], baseURL, resolver: userResolver, macros: userMacros } = options;
536
624
  const groupConfig = createGroupConfig(group);
537
625
  return {
538
626
  name: pluginCypressName,
@@ -548,18 +636,13 @@ const pluginCypress = definePlugin((options) => {
548
636
  exclude,
549
637
  include,
550
638
  override,
551
- dataReturnType,
552
639
  group: groupConfig,
553
640
  baseURL,
554
- paramsCasing,
555
- paramsType,
556
- pathParamsType,
557
641
  resolver
558
642
  });
559
643
  ctx.setResolver(resolver);
560
644
  if (userMacros?.length) ctx.setMacros(userMacros);
561
645
  ctx.addGenerator(cypressGenerator);
562
- for (const gen of userGenerators) ctx.addGenerator(gen);
563
646
  } }
564
647
  };
565
648
  });