@kubb/plugin-ts 5.0.0-beta.56 → 5.0.0-beta.64

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,6 +1,6 @@
1
1
  import { t as __name } from "./chunk-C0LytTxp.js";
2
- import { Exclude, Generator, Group, Include, Output, OutputOptions, Override, PluginFactoryOptions, Resolver, ast } from "@kubb/core";
3
2
  import ts from "typescript";
3
+ import { Exclude, Generator, Group, Include, Output, OutputOptions, Override, PluginFactoryOptions, Resolver, ast } from "@kubb/core";
4
4
  import { KubbReactNode } from "@kubb/renderer-jsx/types";
5
5
 
6
6
  //#region src/printers/printerTs.d.ts
@@ -368,19 +368,22 @@ type Options = OutputOptions & {
368
368
  */
369
369
  resolver?: Partial<ResolverTs> & ThisType<ResolverTs>;
370
370
  /**
371
- * AST visitor applied to each schema or operation node before printing.
372
- * Methods you omit fall back to the preset transformer.
371
+ * Macros applied to each schema or operation node before printing.
372
+ * Callbacks you omit fall back to the preset behavior.
373
373
  *
374
374
  * @example Drop writeOnly properties from response types
375
375
  * ```ts
376
- * transformer: {
377
- * property(node) {
378
- * if (node.schema.writeOnly) return undefined
379
- * }
380
- * }
376
+ * macros: [
377
+ * {
378
+ * name: 'drop-write-only',
379
+ * property(node) {
380
+ * if (node.schema.writeOnly) return undefined
381
+ * },
382
+ * },
383
+ * ]
381
384
  * ```
382
385
  */
383
- transformer?: ast.Visitor;
386
+ macros?: Array<ast.Macro>;
384
387
  /**
385
388
  * Replace the TypeScript handler for a specific schema type (`'integer'`, `'date'`, ...).
386
389
  * Each handler returns a TypeScript AST node for that schema type. Use `this.transform`
@@ -533,18 +536,27 @@ declare const pluginTsName = "plugin-ts";
533
536
  declare const pluginTs: (options?: Options | undefined) => import("@kubb/core").Plugin<PluginTs>;
534
537
  //#endregion
535
538
  //#region src/printers/functionPrinter.d.ts
539
+ /**
540
+ * Renders a {@link ast.TypeExpression} to its TypeScript source.
541
+ *
542
+ * - a `string` is a type reference, returned as-is
543
+ * - an `IndexedAccessType` becomes `objectType['indexType']`
544
+ * - a `TypeLiteral` becomes `{ key: Type; key?: Type }`
545
+ *
546
+ * `transformType` is applied once to the fully rendered type, matching how the
547
+ * printer wrapped reference types before the `ts.factory` model.
548
+ */
549
+ declare function renderType(type: ast.TypeExpression, transformType?: (type: string) => string): string;
536
550
  type FunctionPrinterOptions = {
537
551
  /**
538
552
  * Rendering modes supported by `functionPrinter`.
539
553
  *
540
- * | Mode | Output example | Use case |
541
- * |---------------|---------------------------------------------|---------------------------------|
542
- * | `declaration` | `id: string, config: Config = {}` | Function parameter declaration |
543
- * | `call` | `id, { method, url }` | Function call arguments |
544
- * | `keys` | `{ id, config }` | Key names only (destructuring) |
545
- * | `values` | `{ id: id, config: config }` | Key/value object entries |
554
+ * | Mode | Output example | Use case |
555
+ * |---------------|-----------------------------------|--------------------------------|
556
+ * | `declaration` | `id: string, config: Config = {}` | Function parameter declaration |
557
+ * | `call` | `id, { method, url }` | Function call arguments |
546
558
  */
547
- mode: 'declaration' | 'call' | 'keys' | 'values';
559
+ mode: 'declaration' | 'call';
548
560
  /**
549
561
  * Optional transformation applied to every parameter name before printing.
550
562
  */
@@ -555,8 +567,8 @@ type FunctionPrinterOptions = {
555
567
  transformType?: (type: string) => string;
556
568
  };
557
569
  /**
558
- * Default function-signature printer.
559
- * Covers the four standard output modes used across Kubb plugins.
570
+ * Default function-signature printer. Renders a parameter list in one of two modes:
571
+ * `declaration` for the function signature and `call` for the call arguments.
560
572
  *
561
573
  * @example
562
574
  * ```ts
@@ -600,5 +612,5 @@ declare const functionPrinter: (options?: FunctionPrinterOptions | undefined) =>
600
612
  */
601
613
  declare const resolverTs: ResolverTs;
602
614
  //#endregion
603
- export { Enum, type PluginTs, type PrinterTsFactory, type PrinterTsNodes, type PrinterTsOptions, type ResolverTs, Type, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, resolverTs, typeGenerator };
615
+ export { Enum, type PluginTs, type PrinterTsFactory, type PrinterTsNodes, type PrinterTsOptions, type ResolverTs, Type, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, renderType, resolverTs, typeGenerator };
604
616
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { t as __name } from "./chunk-C0LytTxp.js";
2
- import { extractRefName, jsStringEscape, stringify, trimQuotes } from "@kubb/ast/utils";
2
+ import { caseParams, extractRefName, isStringType, jsStringEscape, mapSchemaItems, mapSchemaProperties, stringify, syncSchemaRef, trimQuotes } from "@kubb/ast/utils";
3
3
  import { parserTs } from "@kubb/parser-ts";
4
4
  import { File, jsxRenderer } from "@kubb/renderer-jsx";
5
- import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
6
5
  import ts from "typescript";
7
6
  import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
7
+ import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
8
8
  //#region ../../internals/utils/src/casing.ts
9
9
  /**
10
10
  * Shared implementation for camelCase and PascalCase conversion.
@@ -454,7 +454,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
454
454
  return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()));
455
455
  }
456
456
  if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
457
- if (value) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
457
+ if (value !== null && value !== void 0) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
458
458
  }).filter((node) => node !== void 0)))];
459
459
  if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(ts.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value]) => {
460
460
  let initializer = factory.createStringLiteral(value?.toString());
@@ -465,7 +465,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
465
465
  const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing);
466
466
  return factory.createEnumMember(propertyName(casingKey), initializer);
467
467
  }
468
- if (key) {
468
+ if (key !== null && key !== void 0) {
469
469
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
470
470
  return factory.createEnumMember(propertyName(casingKey), initializer);
471
471
  }
@@ -477,7 +477,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
477
477
  if (isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
478
478
  else initializer = factory.createNumericLiteral(value);
479
479
  if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
480
- if (key) {
480
+ if (key !== null && key !== void 0) {
481
481
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
482
482
  return factory.createPropertyAssignment(propertyName(casingKey), initializer);
483
483
  }
@@ -638,12 +638,12 @@ function buildTupleNode(node, print) {
638
638
  /**
639
639
  * Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
640
640
  */
641
- function buildPropertyType(schema, baseType, optionalType) {
641
+ function buildPropertyType(schema, baseType, optionalType, optional) {
642
642
  const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType);
643
- const meta = ast.syncSchemaRef(schema);
643
+ const meta = syncSchemaRef(schema);
644
644
  let type = baseType;
645
645
  if (meta.nullable) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] });
646
- if ((meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
646
+ if ((optional || meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
647
647
  return type;
648
648
  }
649
649
  /**
@@ -807,7 +807,7 @@ function resolveContentTypeVariants(entries, baseName) {
807
807
  });
808
808
  }
809
809
  function getOperationParameters(node, options = {}) {
810
- const params = ast.caseParams(node.parameters, options.paramsCasing);
810
+ const params = caseParams(node.parameters, options.paramsCasing);
811
811
  return {
812
812
  path: params.filter((param) => param.in === "path"),
813
813
  query: params.filter((param) => param.in === "query"),
@@ -855,8 +855,11 @@ function createGroupConfig(group) {
855
855
  * Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
856
856
 
857
857
  */
858
- function buildPropertyJSDocComments(schema) {
859
- const meta = ast.syncSchemaRef(schema);
858
+ function isSchemaOptional(schema) {
859
+ return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
860
+ }
861
+ function buildPropertyJSDocComments(schema, optional) {
862
+ const meta = syncSchemaRef(schema);
860
863
  const isArray = meta?.primitive === "array";
861
864
  const hasDescription = meta && "description" in meta && meta.description;
862
865
  const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
@@ -869,83 +872,85 @@ function buildPropertyJSDocComments(schema) {
869
872
  meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
870
873
  meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : null,
871
874
  meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : null,
872
- meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, "optional" in schema && schema.optional ? " | undefined" : null].filter(Boolean).join("") : null
875
+ meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
873
876
  ].filter(Boolean);
874
877
  }
875
878
  function buildParams(node, { params, resolver }) {
876
- return ast.createSchema({
879
+ return ast.factory.createSchema({
877
880
  type: "object",
878
- properties: params.map((param) => ast.createProperty({
881
+ properties: params.map((param) => ast.factory.createProperty({
879
882
  name: param.name,
880
883
  required: param.required,
881
- schema: ast.createSchema({
884
+ schema: ast.factory.createSchema({
882
885
  type: "ref",
883
- name: resolver.resolveParamName(node, param)
886
+ name: resolver.resolveParamName(node, param),
887
+ optional: !param.required
884
888
  })
885
889
  }))
886
890
  });
887
891
  }
888
892
  function buildData(node, { resolver }) {
889
893
  const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
890
- return ast.createSchema({
894
+ return ast.factory.createSchema({
891
895
  type: "object",
892
896
  deprecated: node.deprecated,
893
897
  properties: [
894
- ast.createProperty({
898
+ ast.factory.createProperty({
895
899
  name: "data",
896
- schema: node.requestBody?.content?.[0]?.schema ? ast.createSchema({
900
+ schema: node.requestBody?.content?.[0]?.schema ? ast.factory.createSchema({
897
901
  type: "ref",
898
902
  name: resolver.resolveDataName(node),
899
903
  optional: true
900
- }) : ast.createSchema({
904
+ }) : ast.factory.createSchema({
901
905
  type: "never",
902
906
  primitive: void 0,
903
907
  optional: true
904
908
  })
905
909
  }),
906
- ast.createProperty({
910
+ ast.factory.createProperty({
907
911
  name: "pathParams",
908
912
  required: pathParams.length > 0,
909
913
  schema: pathParams.length > 0 ? buildParams(node, {
910
914
  params: pathParams,
911
915
  resolver
912
- }) : ast.createSchema({
916
+ }) : ast.factory.createSchema({
913
917
  type: "never",
914
- primitive: void 0
918
+ primitive: void 0,
919
+ optional: true
915
920
  })
916
921
  }),
917
- ast.createProperty({
922
+ ast.factory.createProperty({
918
923
  name: "queryParams",
919
- schema: queryParams.length > 0 ? ast.createSchema({
924
+ schema: queryParams.length > 0 ? ast.factory.createSchema({
920
925
  ...buildParams(node, {
921
926
  params: queryParams,
922
927
  resolver
923
928
  }),
924
929
  optional: true
925
- }) : ast.createSchema({
930
+ }) : ast.factory.createSchema({
926
931
  type: "never",
927
932
  primitive: void 0,
928
933
  optional: true
929
934
  })
930
935
  }),
931
- ast.createProperty({
936
+ ast.factory.createProperty({
932
937
  name: "headerParams",
933
- schema: headerParams.length > 0 ? ast.createSchema({
938
+ schema: headerParams.length > 0 ? ast.factory.createSchema({
934
939
  ...buildParams(node, {
935
940
  params: headerParams,
936
941
  resolver
937
942
  }),
938
943
  optional: true
939
- }) : ast.createSchema({
944
+ }) : ast.factory.createSchema({
940
945
  type: "never",
941
946
  primitive: void 0,
942
947
  optional: true
943
948
  })
944
949
  }),
945
- ast.createProperty({
950
+ ast.factory.createProperty({
946
951
  name: "url",
947
952
  required: true,
948
- schema: ast.createSchema({
953
+ schema: ast.factory.createSchema({
949
954
  type: "url",
950
955
  path: node.path
951
956
  })
@@ -955,12 +960,12 @@ function buildData(node, { resolver }) {
955
960
  }
956
961
  function buildResponses(node, { resolver }) {
957
962
  if (node.responses.length === 0) return null;
958
- return ast.createSchema({
963
+ return ast.factory.createSchema({
959
964
  type: "object",
960
- properties: node.responses.map((res) => ast.createProperty({
965
+ properties: node.responses.map((res) => ast.factory.createProperty({
961
966
  name: String(res.statusCode),
962
967
  required: true,
963
- schema: ast.createSchema({
968
+ schema: ast.factory.createSchema({
964
969
  type: "ref",
965
970
  name: resolver.resolveResponseStatusName(node, res.statusCode)
966
971
  })
@@ -970,9 +975,9 @@ function buildResponses(node, { resolver }) {
970
975
  function buildResponseUnion(node, { resolver }) {
971
976
  const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
972
977
  if (responsesWithSchema.length === 0) return null;
973
- return ast.createSchema({
978
+ return ast.factory.createSchema({
974
979
  type: "union",
975
- members: responsesWithSchema.map((res) => ast.createSchema({
980
+ members: responsesWithSchema.map((res) => ast.factory.createSchema({
976
981
  type: "ref",
977
982
  name: resolver.resolveResponseStatusName(node, res.statusCode)
978
983
  }))
@@ -1052,11 +1057,11 @@ const printerTs = ast.definePrinter((options) => {
1052
1057
  const hasStringLiteral = members.some((m) => {
1053
1058
  return ast.narrowSchema(m, ast.schemaTypes.enum)?.primitive === "string";
1054
1059
  });
1055
- const hasPlainString = members.some((m) => ast.isStringType(m));
1060
+ const hasPlainString = members.some((m) => isStringType(m));
1056
1061
  if (hasStringLiteral && hasPlainString) return createUnionDeclaration({
1057
1062
  withParentheses: true,
1058
1063
  nodes: members.map((m) => {
1059
- if (ast.isStringType(m)) return createIntersectionDeclaration({
1064
+ if (isStringType(m)) return createIntersectionDeclaration({
1060
1065
  nodes: [keywordTypeNodes.string, createTypeLiteralNode([])],
1061
1066
  withParentheses: true
1062
1067
  });
@@ -1076,7 +1081,7 @@ const printerTs = ast.definePrinter((options) => {
1076
1081
  },
1077
1082
  array(node) {
1078
1083
  return createArrayDeclaration({
1079
- nodes: (node.items ?? []).map((item) => this.transform(item)).filter(isNonNullable),
1084
+ nodes: mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(isNonNullable),
1080
1085
  arrayType: this.options.arrayType
1081
1086
  }) ?? null;
1082
1087
  },
@@ -1086,18 +1091,19 @@ const printerTs = ast.definePrinter((options) => {
1086
1091
  object(node) {
1087
1092
  const { transform, options } = this;
1088
1093
  const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType);
1089
- const propertyNodes = node.properties.map((prop) => {
1090
- const baseType = transform(prop.schema) ?? keywordTypeNodes.unknown;
1091
- const type = buildPropertyType(prop.schema, baseType, options.optionalType);
1092
- const propMeta = ast.syncSchemaRef(prop.schema);
1094
+ const propertyNodes = mapSchemaProperties(node, (schema) => transform(schema)).map(({ name, property, output }) => {
1095
+ const baseType = output ?? keywordTypeNodes.unknown;
1096
+ const optional = !property.required || !!property.schema.optional || !!property.schema.nullish;
1097
+ const type = buildPropertyType(property.schema, baseType, options.optionalType, optional);
1098
+ const propMeta = syncSchemaRef(property.schema);
1093
1099
  return appendJSDocToNode({
1094
1100
  node: createPropertySignature({
1095
- questionToken: prop.schema.optional || prop.schema.nullish ? addsQuestionToken : false,
1096
- name: prop.name,
1101
+ questionToken: optional ? addsQuestionToken : false,
1102
+ name,
1097
1103
  type,
1098
1104
  readOnly: propMeta?.readOnly
1099
1105
  }),
1100
- comments: buildPropertyJSDocComments(prop.schema)
1106
+ comments: buildPropertyJSDocComments(property.schema, optional)
1101
1107
  });
1102
1108
  });
1103
1109
  const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, transform)];
@@ -1110,7 +1116,7 @@ const printerTs = ast.definePrinter((options) => {
1110
1116
  const { name, syntaxType = "type", description, keysToOmit } = this.options;
1111
1117
  const transformed = this.transform(node);
1112
1118
  if (!transformed) return null;
1113
- const meta = ast.syncSchemaRef(node);
1119
+ const meta = syncSchemaRef(node);
1114
1120
  if (!name) {
1115
1121
  const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [transformed, keywordTypeNodes.null] }) : transformed;
1116
1122
  const result = (meta.nullish || meta.optional) && addsUndefined ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
@@ -1234,7 +1240,7 @@ const typeGenerator = defineGenerator({
1234
1240
  operation(node, ctx) {
1235
1241
  const { enum: enumOptions, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
1236
1242
  const { adapter, config, resolver, root } = ctx;
1237
- const params = ast.caseParams(node.parameters, paramsCasing);
1243
+ const params = caseParams(node.parameters, paramsCasing);
1238
1244
  const meta = { file: resolver.resolveFile({
1239
1245
  name: node.operationId,
1240
1246
  extname: ".ts",
@@ -1298,9 +1304,9 @@ const typeGenerator = defineGenerator({
1298
1304
  */
1299
1305
  function buildContentTypeVariants(entries, baseName, decorate) {
1300
1306
  const variants = resolveContentTypeVariants(entries, baseName);
1301
- const unionSchema = ast.createSchema({
1307
+ const unionSchema = ast.factory.createSchema({
1302
1308
  type: "union",
1303
- members: variants.map((variant) => ast.createSchema({
1309
+ members: variants.map((variant) => ast.factory.createSchema({
1304
1310
  type: "ref",
1305
1311
  name: variant.name
1306
1312
  }))
@@ -1315,7 +1321,10 @@ const typeGenerator = defineGenerator({
1315
1321
  })] });
1316
1322
  }
1317
1323
  const paramTypes = params.map((param) => renderSchemaType({
1318
- schema: param.schema,
1324
+ schema: {
1325
+ ...param.schema,
1326
+ optional: !param.required
1327
+ },
1319
1328
  name: resolver.resolveParamName(node, param)
1320
1329
  }));
1321
1330
  const requestBodyContent = node.requestBody?.content ?? [];
@@ -1511,7 +1520,7 @@ const pluginTs = definePlugin((options) => {
1511
1520
  const { output = {
1512
1521
  path: "types",
1513
1522
  barrel: { type: "named" }
1514
- }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
1523
+ }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, macros: userMacros, generators: userGenerators = [] } = options;
1515
1524
  const groupConfig = createGroupConfig(group);
1516
1525
  const resolvedEnum = {
1517
1526
  type: enumOptions.type ?? "asConst",
@@ -1540,7 +1549,7 @@ const pluginTs = definePlugin((options) => {
1540
1549
  ...resolverTs,
1541
1550
  ...userResolver
1542
1551
  } : resolverTs);
1543
- if (userTransformer) ctx.setTransformer(userTransformer);
1552
+ if (userMacros?.length) ctx.setMacros(userMacros);
1544
1553
  ctx.addGenerator(typeGenerator);
1545
1554
  for (const gen of userGenerators) ctx.addGenerator(gen);
1546
1555
  } }
@@ -1548,37 +1557,75 @@ const pluginTs = definePlugin((options) => {
1548
1557
  });
1549
1558
  //#endregion
1550
1559
  //#region src/printers/functionPrinter.ts
1551
- const kindToHandlerKey = {
1552
- FunctionParameter: "functionParameter",
1553
- ParameterGroup: "parameterGroup",
1554
- FunctionParameters: "functionParameters",
1555
- ParamsType: "paramsType"
1556
- };
1557
1560
  /**
1558
- * Creates a function-parameter printer factory.
1561
+ * Renders a {@link ast.TypeExpression} to its TypeScript source.
1559
1562
  *
1560
- * Uses `createPrinterFactory` and dispatches handlers by `node.kind`
1561
- * (for function nodes) rather than by `node.type` (for schema nodes).
1563
+ * - a `string` is a type reference, returned as-is
1564
+ * - an `IndexedAccessType` becomes `objectType['indexType']`
1565
+ * - a `TypeLiteral` becomes `{ key: Type; key?: Type }`
1566
+ *
1567
+ * `transformType` is applied once to the fully rendered type, matching how the
1568
+ * printer wrapped reference types before the `ts.factory` model.
1562
1569
  */
1563
- const defineFunctionPrinter = ast.createPrinterFactory((node) => kindToHandlerKey[node.kind]);
1570
+ function renderType(type, transformType) {
1571
+ const rendered = renderTypeExpression(type);
1572
+ return transformType ? transformType(rendered) : rendered;
1573
+ }
1574
+ function renderTypeExpression(type) {
1575
+ if (typeof type === "string") return type;
1576
+ if (ast.indexedAccessTypeDef.is(type)) return `${type.target}['${type.key}']`;
1577
+ return `{ ${type.members.map((member) => {
1578
+ const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(member.name) ? member.name : JSON.stringify(member.name);
1579
+ const value = renderTypeExpression(member.type);
1580
+ return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1581
+ }).join("; ")} }`;
1582
+ }
1583
+ function groupMembers(param) {
1584
+ if (typeof param.name === "string") return null;
1585
+ return param.type && ast.typeLiteralDef.is(param.type) ? param.type.members : [];
1586
+ }
1564
1587
  function rank(param) {
1565
- if (param.kind === "ParameterGroup") {
1566
- if (param.default) return PARAM_RANK.withDefault;
1567
- return param.optional ?? param.properties.every((p) => p.optional || p.default !== void 0) ? PARAM_RANK.optional : PARAM_RANK.required;
1568
- }
1569
1588
  if (param.rest) return PARAM_RANK.rest;
1570
1589
  if (param.default) return PARAM_RANK.withDefault;
1590
+ const members = groupMembers(param);
1591
+ if (members) return members.every((m) => m.optional) ? PARAM_RANK.optional : PARAM_RANK.required;
1571
1592
  return param.optional ? PARAM_RANK.optional : PARAM_RANK.required;
1572
1593
  }
1573
1594
  function sortParams(params) {
1574
1595
  return params.toSorted((a, b) => rank(a) - rank(b));
1575
1596
  }
1576
- function sortChildParams(params) {
1577
- return params.toSorted((a, b) => rank(a) - rank(b));
1597
+ /**
1598
+ * Orders a destructured group's binding elements and type members together,
1599
+ * required fields first, matching how grouped children were sorted before.
1600
+ */
1601
+ function sortedGroupMembers(name, type) {
1602
+ const members = type && ast.typeLiteralDef.is(type) ? type.members : [];
1603
+ const memberRank = (optional) => optional ? PARAM_RANK.optional : PARAM_RANK.required;
1604
+ return name.elements.map((element, index) => ({
1605
+ name: element.name,
1606
+ type: members[index]?.type,
1607
+ optional: members[index]?.optional
1608
+ })).toSorted((a, b) => memberRank(a.optional) - memberRank(b.optional));
1609
+ }
1610
+ /**
1611
+ * Renders the type annotation of a destructured group. An inline object type is
1612
+ * rendered from the already-sorted members so its key order matches the binding;
1613
+ * a reference type is rendered as-is.
1614
+ */
1615
+ function renderGroupType(type, sorted, transformType) {
1616
+ if (!type) return void 0;
1617
+ if (!ast.typeLiteralDef.is(type)) return renderType(type);
1618
+ const typed = sorted.filter((member) => member.type !== void 0);
1619
+ if (!typed.length) return void 0;
1620
+ return `{ ${typed.map((member) => {
1621
+ const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(member.name) ? member.name : JSON.stringify(member.name);
1622
+ const value = renderType(member.type, transformType);
1623
+ return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1624
+ }).join("; ")} }`;
1578
1625
  }
1579
1626
  /**
1580
- * Default function-signature printer.
1581
- * Covers the four standard output modes used across Kubb plugins.
1627
+ * Default function-signature printer. Renders a parameter list in one of two modes:
1628
+ * `declaration` for the function signature and `call` for the call arguments.
1582
1629
  *
1583
1630
  * @example
1584
1631
  * ```ts
@@ -1594,28 +1641,31 @@ function sortChildParams(params) {
1594
1641
  * printer.print(sig) // → "petId: string, config: Config = {}"
1595
1642
  * ```
1596
1643
  */
1597
- const functionPrinter = defineFunctionPrinter((options) => ({
1644
+ const functionPrinter = ast.createPrinterFactory((node) => node.kind)((options) => ({
1598
1645
  name: "functionParameters",
1599
1646
  options,
1600
1647
  nodes: {
1601
- paramsType(node) {
1602
- if (node.kind !== "ParamsType") return null;
1603
- if (node.variant === "member") return `${node.base}['${node.key}']`;
1604
- if (node.variant === "struct") return `{ ${node.properties.map((p) => {
1605
- const typeStr = this.transform(p.type);
1606
- const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(p.name) ? p.name : JSON.stringify(p.name);
1607
- return p.optional ? `${key}?: ${typeStr}` : `${key}: ${typeStr}`;
1608
- }).join("; ")} }`;
1609
- if (node.variant === "reference") return node.name;
1610
- return null;
1611
- },
1612
- functionParameter(node) {
1648
+ FunctionParameter(node) {
1613
1649
  const { mode, transformName, transformType } = this.options;
1650
+ const isGroup = typeof node.name !== "string";
1651
+ if (mode === "call") {
1652
+ if (isGroup) return `{ ${sortedGroupMembers(node.name, node.type).map((member) => member.name).join(", ")} }`;
1653
+ const name = transformName ? transformName(node.name) : node.name;
1654
+ return node.rest ? `...${name}` : name;
1655
+ }
1656
+ if (isGroup) {
1657
+ const sorted = sortedGroupMembers(node.name, node.type);
1658
+ const binding = `{ ${sorted.map((member) => transformName ? transformName(member.name) : member.name).join(", ")} }`;
1659
+ const allOptional = sorted.every((member) => member.optional);
1660
+ const type = renderGroupType(node.type, sorted, transformType);
1661
+ if (type) {
1662
+ if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
1663
+ return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
1664
+ }
1665
+ return node.default ? `${binding} = ${node.default}` : binding;
1666
+ }
1614
1667
  const name = transformName ? transformName(node.name) : node.name;
1615
- const rawType = node.type ? this.transform(node.type) : void 0;
1616
- const type = rawType != null && transformType ? transformType(rawType) : rawType;
1617
- if (mode === "keys" || mode === "values") return node.rest ? `...${name}` : name;
1618
- if (mode === "call") return node.rest ? `...${name}` : name;
1668
+ const type = node.type ? renderType(node.type, transformType) : void 0;
1619
1669
  if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
1620
1670
  if (type) {
1621
1671
  if (node.optional) return `${name}?: ${type}`;
@@ -1623,39 +1673,12 @@ const functionPrinter = defineFunctionPrinter((options) => ({
1623
1673
  }
1624
1674
  return node.default ? `${name} = ${node.default}` : name;
1625
1675
  },
1626
- parameterGroup(node) {
1627
- const { mode, transformName, transformType } = this.options;
1628
- const sorted = sortChildParams(node.properties);
1629
- const isOptional = node.optional ?? sorted.every((p) => p.optional || p.default !== void 0);
1630
- if (node.inline) return sorted.map((p) => this.transform(p)).filter(Boolean).join(", ");
1631
- if (mode === "keys" || mode === "values") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
1632
- if (mode === "call") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
1633
- const names = sorted.map((p) => {
1634
- return transformName ? transformName(p.name) : p.name;
1635
- });
1636
- const nameStr = names.length ? `{ ${names.join(", ")} }` : void 0;
1637
- if (!nameStr) return null;
1638
- let typeAnnotation = node.type ? this.transform(node.type) ?? void 0 : void 0;
1639
- if (!typeAnnotation) {
1640
- const typeParts = sorted.filter((p) => p.type).map((p) => {
1641
- const rawT = p.type ? this.transform(p.type) : void 0;
1642
- const t = rawT != null && transformType ? transformType(rawT) : rawT;
1643
- return p.optional || p.default !== void 0 ? `${p.name}?: ${t}` : `${p.name}: ${t}`;
1644
- });
1645
- typeAnnotation = typeParts.length ? `{ ${typeParts.join("; ")} }` : void 0;
1646
- }
1647
- if (typeAnnotation) {
1648
- if (isOptional) return `${nameStr}: ${typeAnnotation} = ${node.default ?? "{}"}`;
1649
- return node.default ? `${nameStr}: ${typeAnnotation} = ${node.default}` : `${nameStr}: ${typeAnnotation}`;
1650
- }
1651
- return node.default ? `${nameStr} = ${node.default}` : nameStr;
1652
- },
1653
- functionParameters(node) {
1676
+ FunctionParameters(node) {
1654
1677
  return sortParams(node.params).map((p) => this.transform(p)).filter(Boolean).join(", ");
1655
1678
  }
1656
1679
  }
1657
1680
  }));
1658
1681
  //#endregion
1659
- export { Enum, Type, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, resolverTs, typeGenerator };
1682
+ export { Enum, Type, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, renderType, resolverTs, typeGenerator };
1660
1683
 
1661
1684
  //# sourceMappingURL=index.js.map