@kubb/parser-ts 5.0.0-beta.8 → 5.0.0-beta.80

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.cjs CHANGED
@@ -25,10 +25,29 @@ let _kubb_core = require("@kubb/core");
25
25
  let node_path = require("node:path");
26
26
  let typescript = require("typescript");
27
27
  typescript = __toESM(typescript, 1);
28
- //#region src/constants.ts
28
+ //#region ../../internals/utils/src/fs.ts
29
29
  /**
30
- * Matches the trailing `.<ext>` segment of a path (keeps segments like `foo.bar.ts`
31
- * intact by only trimming the last run of non-`/`/`.` characters).
30
+ * Strips the file extension from a path or file name.
31
+ * Only removes the last `.ext` segment when the dot is not part of a directory name.
32
+ *
33
+ * @example
34
+ * trimExtName('petStore.ts') // 'petStore'
35
+ * trimExtName('/src/models/pet.ts') // '/src/models/pet'
36
+ * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'
37
+ * trimExtName('noExtension') // 'noExtension'
38
+ */
39
+ function trimExtName(text) {
40
+ const dotIndex = text.lastIndexOf(".");
41
+ if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
42
+ return text;
43
+ }
44
+ /**
45
+ * Indentation unit prepended once per nesting level when pretty-printing.
46
+ */
47
+ const INDENT = " ".repeat(2);
48
+ /**
49
+ * Matches only the final `.<ext>` of a path, so a name like `foo.bar.ts` keeps
50
+ * `foo.bar` and loses just `.ts`.
32
51
  */
33
52
  const FILE_EXTENSION_PATTERN = /\.[^/.]+$/;
34
53
  /**
@@ -36,28 +55,28 @@ const FILE_EXTENSION_PATTERN = /\.[^/.]+$/;
36
55
  */
37
56
  const WINDOWS_PATH_SEPARATOR = /\\/g;
38
57
  /**
39
- * Matches `*\/` in free-form text so JSDoc bodies can neutralise premature
58
+ * Matches `*\/` in free-form text so JSDoc bodies can neutralize premature
40
59
  * comment terminators (`*\/` → `* /`).
41
60
  */
42
61
  const JSDOC_TERMINATOR_PATTERN = /\*\//g;
43
62
  /**
44
- * Matches carriage returns for normalising CRLF/CR line endings to LF.
63
+ * Matches carriage returns for normalizing CRLF/CR line endings to LF.
45
64
  */
46
65
  const CARRIAGE_RETURN_PATTERN = /\r/g;
47
66
  /**
48
- * Matches CRLF sequences used when normalising TypeScript printer output.
67
+ * Matches CRLF sequences used when normalizing TypeScript printer output.
49
68
  */
50
69
  const CRLF_PATTERN = /\r\n/g;
51
70
  /**
52
- * Matches an identifier that starts with a digit JavaScript disallows this
53
- * so the printer prefixes such names with `_`.
71
+ * Matches an identifier that starts with a digit. JavaScript disallows this,
72
+ * so the printer replaces the leading digit with `_`.
54
73
  */
55
74
  const LEADING_DIGIT_PATTERN = /^\d/;
56
75
  //#endregion
57
76
  //#region src/utils.ts
58
77
  const { factory } = typescript.default;
59
78
  /**
60
- * Normalises a file-system path to POSIX separators and strips any leading `../` segment.
79
+ * Normalizes a file-system path to POSIX separators and strips any leading `../` segment.
61
80
  */
62
81
  function slash(path) {
63
82
  return (0, node_path.normalize)(path).replaceAll(WINDOWS_PATH_SEPARATOR, "/").replace("../", "");
@@ -71,13 +90,6 @@ function getRelativePath(rootDir, filePath) {
71
90
  return slashed.startsWith("../") ? slashed : `./${slashed}`;
72
91
  }
73
92
  /**
74
- * Strips the trailing file extension (for example `.ts`) from a path.
75
- * Preserves intermediate dots like `foo.bar.ts` → `foo.bar`.
76
- */
77
- function trimExtName(text) {
78
- return text.replace(FILE_EXTENSION_PATTERN, "");
79
- }
80
- /**
81
93
  * Rewrites an import/export path so its extension matches the caller-supplied
82
94
  * `options.extname`. When the source path has no extension the original is kept,
83
95
  * so virtual/module-only paths flow through unchanged.
@@ -88,25 +100,64 @@ function resolveOutputPath(path, options, rootAware) {
88
100
  return rootAware ? trimExtName(path) : path;
89
101
  }
90
102
  /**
91
- * Serializes the body / value content from a `nodes` array.
92
- *
93
- * Each element is either a raw string or a structured {@link CodeNode}
94
- * (recursively converted via {@link printCodeNode}).
95
- * Elements are joined with `\n`.
103
+ * Serializes a `nodes` array into source text. Each entry is rendered via {@link printCodeNode}
104
+ * and joined with a single newline. A `Break` node (`<br/>`) inserts one blank line between
105
+ * statements. Consecutive breaks, and breaks at the very start or end, are folded into the
106
+ * separator, so a double `<br/>` never emits more than one blank line.
96
107
  */
97
108
  function printNodes(nodes) {
98
109
  if (!nodes || nodes.length === 0) return "";
99
- return nodes.map(printCodeNode).join("\n");
110
+ let result = "";
111
+ let hasContent = false;
112
+ let pendingBreak = false;
113
+ for (const node of nodes) {
114
+ if (node.kind === "Break") {
115
+ if (hasContent) pendingBreak = true;
116
+ continue;
117
+ }
118
+ const text = printCodeNode(node);
119
+ if (!text) continue;
120
+ if (hasContent) result += pendingBreak ? "\n\n" : "\n";
121
+ result += text;
122
+ hasContent = true;
123
+ pendingBreak = false;
124
+ }
125
+ return result;
100
126
  }
101
127
  /**
102
- * Indents every non-empty line of `text` by `spaces` spaces.
128
+ * Indents every non-empty line of `text` by one indent unit. Pass a number to repeat
129
+ * {@link INDENT_CHAR} that many times, or a string to use as the indent verbatim.
103
130
  */
104
- function indentLines(text, spaces = 2) {
131
+ function indentLines(text, indent = INDENT) {
105
132
  if (!text) return "";
106
- const pad = " ".repeat(spaces);
133
+ const pad = typeof indent === "string" ? indent : " ".repeat(indent);
107
134
  return text.split("\n").map((line) => line.trim() ? `${pad}${line}` : "").join("\n");
108
135
  }
109
136
  /**
137
+ * Removes the common leading whitespace shared by every non-blank line and trims
138
+ * surrounding blank lines, so multi-line content authored inside an indented template
139
+ * literal lines up at a column-zero baseline. Leading whitespace is counted by
140
+ * character, so N tabs and N spaces are treated as the same depth.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * dedent('\n foo\n bar\n ')
145
+ * // 'foo\n bar'
146
+ * ```
147
+ */
148
+ function dedent(text) {
149
+ if (!text) return "";
150
+ const lines = text.split("\n");
151
+ const isBlank = (line) => line.trim() === "";
152
+ const start = lines.findIndex((line) => !isBlank(line));
153
+ if (start === -1) return "";
154
+ const end = lines.findLastIndex((line) => !isBlank(line));
155
+ const trimmed = lines.slice(start, end + 1);
156
+ const indents = trimmed.filter((line) => !isBlank(line)).map((line) => line.match(/^\s*/)?.[0].length ?? 0);
157
+ const min = indents.length ? Math.min(...indents) : 0;
158
+ return trimmed.map((line) => isBlank(line) ? "" : line.slice(min)).join("\n");
159
+ }
160
+ /**
110
161
  * Renders the generic clause (`<T, U>`) shared by function and arrow-function nodes.
111
162
  * Accepts either a raw string (rendered verbatim) or an array of type-parameter names.
112
163
  */
@@ -123,34 +174,28 @@ function formatReturnType(returnType, isAsync) {
123
174
  return isAsync ? `: Promise<${returnType}>` : `: ${returnType}`;
124
175
  }
125
176
  /**
126
- * Validates TypeScript AST nodes before printing.
127
- * Throws an error if any node has SyntaxKind.Unknown which would cause the
128
- * TypeScript printer to crash.
177
+ * Module-scoped TypeScript printer instance. A printer does not mutate the source file, so one
178
+ * instance is reused across every `print()` call instead of constructing a new printer each time.
129
179
  */
130
- function validateNodes(...nodes) {
131
- for (const node of nodes) {
132
- if (!node) throw new Error("Attempted to print undefined or null TypeScript node");
133
- if (node.kind === typescript.default.SyntaxKind.Unknown) throw new Error(`Invalid TypeScript AST node detected with SyntaxKind.Unknown. This typically indicates a schema pattern that could not be properly converted to TypeScript. Node: ${JSON.stringify(node, null, 2)}`);
134
- }
135
- }
180
+ const TS_PRINTER = typescript.default.createPrinter({
181
+ omitTrailingSemicolon: true,
182
+ newLine: typescript.default.NewLineKind.LineFeed,
183
+ removeComments: false,
184
+ noEmitHelpers: true
185
+ });
136
186
  /**
137
- * Converts TypeScript/TSX AST nodes to a string using the TypeScript printer.
187
+ * Module-scoped source file used as the print target. `printList` only reads the source
188
+ * file's compiler options / language version. It never mutates it.
138
189
  */
139
- function print(...elements) {
140
- const sourceFile = typescript.default.createSourceFile("print.tsx", "", typescript.default.ScriptTarget.ES2022, true, typescript.default.ScriptKind.TSX);
141
- return typescript.default.createPrinter({
142
- omitTrailingSemicolon: true,
143
- newLine: typescript.default.NewLineKind.LineFeed,
144
- removeComments: false,
145
- noEmitHelpers: true
146
- }).printList(typescript.default.ListFormat.MultiLine, factory.createNodeArray(elements.filter(Boolean)), sourceFile).replace(CRLF_PATTERN, "\n");
147
- }
190
+ const PRINT_SOURCE_FILE = typescript.default.createSourceFile("print.tsx", "", typescript.default.ScriptTarget.ES2022, true, typescript.default.ScriptKind.TSX);
191
+ TS_PRINTER.printList(typescript.default.ListFormat.MultiLine, factory.createNodeArray([]), PRINT_SOURCE_FILE);
148
192
  /**
149
- * Like `print` but validates nodes first to surface issues early.
193
+ * Converts TypeScript/TSX AST nodes to a string using the TypeScript printer.
150
194
  */
151
- function safePrint(...elements) {
152
- validateNodes(...elements);
153
- return print(...elements);
195
+ function print(...elements) {
196
+ const filtered = elements.filter(Boolean);
197
+ if (filtered.length === 0) return "";
198
+ return TS_PRINTER.printList(typescript.default.ListFormat.MultiLine, factory.createNodeArray(filtered), PRINT_SOURCE_FILE).replace(CRLF_PATTERN, "\n");
154
199
  }
155
200
  /**
156
201
  * Converts a {@link JSDocNode} to a JSDoc comment block string.
@@ -182,13 +227,13 @@ function printJSDoc(jsDoc) {
182
227
  *
183
228
  * @example
184
229
  * ```ts
185
- * printConst(createConst({ name: 'pet', export: true, nodes: ['{}'] }))
230
+ * printConst(factory.createConst({ name: 'pet', export: true, nodes: ['{}'] }))
186
231
  * // 'export const pet = {}'
187
232
  * ```
188
233
  *
189
234
  * @example With type and `as const`
190
235
  * ```ts
191
- * printConst(createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))
236
+ * printConst(factory.createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))
192
237
  * // 'export const pets: Pet[] = [] as const'
193
238
  * ```
194
239
  */
@@ -213,7 +258,7 @@ function printConst(node) {
213
258
  *
214
259
  * @example
215
260
  * ```ts
216
- * printType(createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))
261
+ * printType(factory.createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))
217
262
  * // 'export type Pet = { id: number }'
218
263
  * ```
219
264
  */
@@ -236,13 +281,13 @@ function printType(node) {
236
281
  *
237
282
  * @example
238
283
  * ```ts
239
- * printFunction(createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))
284
+ * printFunction(factory.createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))
240
285
  * // 'export function getPet(id: string): Pet {\n return fetch(id)\n}'
241
286
  * ```
242
287
  *
243
288
  * @example Async with generics
244
289
  * ```ts
245
- * printFunction(createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))
290
+ * printFunction(factory.createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))
246
291
  * // 'export async function fetchPet<T>(id: string): Promise<T> {\n}'
247
292
  * ```
248
293
  */
@@ -272,13 +317,13 @@ function printFunction(node) {
272
317
  *
273
318
  * @example Multi-line arrow function
274
319
  * ```ts
275
- * printArrowFunction(createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))
320
+ * printArrowFunction(factory.createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))
276
321
  * // 'export const getPet = (id: string) => {\n return fetch(id)\n}'
277
322
  * ```
278
323
  *
279
324
  * @example Single-line arrow function
280
325
  * ```ts
281
- * printArrowFunction(createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))
326
+ * printArrowFunction(factory.createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))
282
327
  * // 'const double = (n: number) => n * 2'
283
328
  * ```
284
329
  */
@@ -307,20 +352,19 @@ function printArrowFunction(node) {
307
352
  *
308
353
  * @example
309
354
  * ```ts
310
- * printCodeNode(createConst({ name: 'x', nodes: ['1'] }))
355
+ * printCodeNode(factory.createConst({ name: 'x', nodes: ['1'] }))
311
356
  * // 'const x = 1'
312
357
  * ```
313
358
  */
314
359
  function printCodeNode(node) {
315
- switch (node.kind) {
316
- case "Break": return "";
317
- case "Text": return node.value;
318
- case "Jsx": return node.value;
319
- case "Const": return printConst(node);
320
- case "Type": return printType(node);
321
- case "Function": return printFunction(node);
322
- case "ArrowFunction": return printArrowFunction(node);
323
- }
360
+ if (node.kind === "Break") return "";
361
+ if (node.kind === "Text") return dedent(node.value);
362
+ if (node.kind === "Jsx") return dedent(node.value);
363
+ if (node.kind === "Const") return printConst(node);
364
+ if (node.kind === "Type") return printType(node);
365
+ if (node.kind === "Function") return printFunction(node);
366
+ if (node.kind === "ArrowFunction") return printArrowFunction(node);
367
+ return "";
324
368
  }
325
369
  /**
326
370
  * Converts a {@link SourceNode} to its TypeScript string representation.
@@ -328,77 +372,135 @@ function printCodeNode(node) {
328
372
  * Iterates `nodes` in DOM order, rendering each {@link CodeNode} via
329
373
  * {@link printCodeNode}.
330
374
  *
375
+ * Top-level declarations are separated by a blank line so the source reads
376
+ * cleanly without an external formatter.
377
+ *
331
378
  * @example From nodes
332
379
  * ```ts
333
- * printSource({ kind: 'Source', nodes: [createConst({ name: 'x', nodes: [createText('1')] }), createText('x.toString()')] })
334
- * // 'const x = 1\nx.toString()'
380
+ * printSource({ kind: 'Source', nodes: [factory.createConst({ name: 'x', nodes: [factory.createText('1')] }), factory.createText('x.toString()')] })
381
+ * // 'const x = 1\n\nx.toString()'
335
382
  * ```
336
383
  */
337
384
  function printSource(node) {
338
- if (node.nodes && node.nodes.length > 0) return node.nodes.map(printCodeNode).join("\n");
339
- return "";
340
- }
341
- function createImport({ name, path, root, isTypeOnly = false, isNameSpace = false }) {
342
- const resolvePath = root ? getRelativePath(root, path) : path;
343
- if (!Array.isArray(name)) {
344
- if (isNameSpace) return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, void 0, factory.createNamespaceImport(factory.createIdentifier(name))), factory.createStringLiteral(resolvePath), void 0);
345
- return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, factory.createIdentifier(name), void 0), factory.createStringLiteral(resolvePath), void 0);
385
+ const nodes = node.nodes;
386
+ if (!nodes || nodes.length === 0) return "";
387
+ let result = "";
388
+ for (const child of nodes) {
389
+ const text = printCodeNode(child);
390
+ if (!text) continue;
391
+ result = result ? `${result}\n\n${text}` : text;
346
392
  }
347
- const specifiers = name.map((item) => {
348
- if (typeof item === "object") {
349
- const { propertyName, name: alias } = item;
350
- return factory.createImportSpecifier(false, alias ? factory.createIdentifier(propertyName) : void 0, factory.createIdentifier(alias ?? propertyName));
351
- }
352
- return factory.createImportSpecifier(false, void 0, factory.createIdentifier(item));
353
- });
354
- return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, void 0, factory.createNamedImports(specifiers)), factory.createStringLiteral(resolvePath), void 0);
393
+ return result;
355
394
  }
356
- function createExport({ path, asAlias, isTypeOnly = false, name }) {
357
- if (name && !Array.isArray(name) && !asAlias) console.warn(`When using name as string, asAlias should be true: ${name}`);
395
+ /**
396
+ * Wraps a module specifier in single quotes, escaping any embedded backslash or quote so the emitted
397
+ * statement stays valid even for unusual paths.
398
+ */
399
+ function quoteModulePath(path) {
400
+ return `'${path.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
401
+ }
402
+ /**
403
+ * Renders an import declaration string in the repo style (single quotes, no semicolons), covering
404
+ * default, namespace (`* as`), and named imports with `{ a as b }` aliases, each optionally
405
+ * `type`-only. `path` is used verbatim, so resolve it first.
406
+ *
407
+ * @example
408
+ * ```ts
409
+ * printImport({ name: ['z'], path: './zod.ts' })
410
+ * // "import { z } from './zod.ts'"
411
+ * ```
412
+ */
413
+ function printImport({ name, path, isTypeOnly = false, isNameSpace = false }) {
414
+ const typePrefix = isTypeOnly ? "type " : "";
415
+ const from = quoteModulePath(path);
358
416
  if (!Array.isArray(name)) {
359
- const parsedName = name && LEADING_DIGIT_PATTERN.test(name) ? `_${name.slice(1)}` : name;
360
- return factory.createExportDeclaration(void 0, isTypeOnly, asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : void 0, factory.createStringLiteral(path), void 0);
417
+ if (isNameSpace) return `import ${typePrefix}* as ${name} from ${from}`;
418
+ return `import ${typePrefix}${name} from ${from}`;
361
419
  }
362
- return factory.createExportDeclaration(void 0, isTypeOnly, factory.createNamedExports(name.map((propertyName) => factory.createExportSpecifier(false, void 0, typeof propertyName === "string" ? factory.createIdentifier(propertyName) : propertyName))), factory.createStringLiteral(path), void 0);
420
+ return `import ${typePrefix}{ ${name.map((item) => {
421
+ if (typeof item === "object") return item.name ? `${item.propertyName} as ${item.name}` : item.propertyName;
422
+ return item;
423
+ }).join(", ")} } from ${from}`;
424
+ }
425
+ /**
426
+ * Renders an export declaration string in the repo style (single quotes, no semicolons), covering
427
+ * named re-exports, namespace alias (`* as name`), and wildcard, each optionally `type`-only.
428
+ * `path` is used verbatim, so resolve it first.
429
+ *
430
+ * @example
431
+ * ```ts
432
+ * printExport({ name: ['Pet', 'Order'], path: './models.ts' })
433
+ * // "export { Pet, Order } from './models.ts'"
434
+ * ```
435
+ */
436
+ function printExport({ path, name, isTypeOnly = false, asAlias = false }) {
437
+ const typePrefix = isTypeOnly ? "type " : "";
438
+ const from = quoteModulePath(path);
439
+ if (Array.isArray(name)) return `export ${typePrefix}{ ${name.map((item) => typeof item === "string" ? item : item.text).join(", ")} } from ${from}`;
440
+ if (asAlias && name) return `export ${typePrefix}* as ${LEADING_DIGIT_PATTERN.test(name) ? `_${name.slice(1)}` : name} from ${from}`;
441
+ if (name) console.warn(`When using name as string, asAlias should be true: ${name}`);
442
+ return `export ${typePrefix}* from ${from}`;
363
443
  }
364
444
  //#endregion
365
445
  //#region src/parserTs.ts
366
446
  /**
367
- * Parser that converts `.ts` and `.js` files to strings using the TypeScript
368
- * compiler. Handles import/export statement generation from file metadata.
447
+ * Default Kubb parser for `.ts` and `.js` files. Takes the universal AST
448
+ * produced by an adapter and prints it as TypeScript source using the official
449
+ * TypeScript compiler. Imports and exports are rewritten based on each file's
450
+ * metadata.
451
+ *
452
+ * Used automatically when no `parsers` option is set on `defineConfig`. Use
453
+ * `parserTsx` instead for React projects that emit JSX.
369
454
  *
370
- * @default Used automatically when no `parsers` option is set in `defineConfig`.
455
+ * @example
456
+ * ```ts
457
+ * import { defineConfig } from 'kubb'
458
+ * import { adapterOas } from '@kubb/adapter-oas'
459
+ * import { parserTs } from '@kubb/parser-ts'
460
+ *
461
+ * export default defineConfig({
462
+ * input: { path: './petStore.yaml' },
463
+ * output: { path: './src/gen' },
464
+ * adapter: adapterOas(),
465
+ * parsers: [parserTs],
466
+ * plugins: [],
467
+ * })
468
+ * ```
371
469
  */
372
470
  const parserTs = (0, _kubb_core.defineParser)({
373
471
  name: "typescript",
374
472
  extNames: [".ts", ".js"],
375
- async parse(file, options = { extname: ".ts" }) {
473
+ print(...nodes) {
474
+ return print(...nodes);
475
+ },
476
+ parse(file, options = { extname: ".ts" }) {
376
477
  const sourceParts = [];
377
478
  for (const item of file.sources) {
378
479
  const sourceStr = printSource(item);
379
480
  if (sourceStr) sourceParts.push(sourceStr.trimEnd());
380
481
  }
381
482
  const source = sourceParts.join("\n\n");
382
- const importNodes = [];
483
+ const importLines = [];
383
484
  for (const item of file.imports) {
384
485
  const importPath = item.root ? getRelativePath(item.root, item.path) : item.path;
385
- importNodes.push(createImport({
486
+ importLines.push(printImport({
386
487
  name: item.name,
387
488
  path: resolveOutputPath(importPath, options, Boolean(item.root)),
388
489
  isTypeOnly: item.isTypeOnly,
389
490
  isNameSpace: item.isNameSpace
390
491
  }));
391
492
  }
392
- const exportNodes = [];
393
- for (const item of file.exports) exportNodes.push(createExport({
493
+ const exportLines = [];
494
+ for (const item of file.exports) exportLines.push(printExport({
394
495
  name: item.name,
395
496
  path: resolveOutputPath(item.path, options, true),
396
497
  isTypeOnly: item.isTypeOnly,
397
498
  asAlias: item.asAlias
398
499
  }));
500
+ const importExportBlock = [...importLines, ...exportLines].join("\n");
399
501
  return [
400
502
  file.banner,
401
- print(...importNodes, ...exportNodes),
503
+ importExportBlock,
402
504
  source,
403
505
  file.footer
404
506
  ].filter((segment) => Boolean(segment)).map((s) => s.trimEnd()).join("\n\n");
@@ -407,28 +509,39 @@ const parserTs = (0, _kubb_core.defineParser)({
407
509
  //#endregion
408
510
  //#region src/parserTsx.ts
409
511
  /**
410
- * Parser that converts `.tsx` and `.jsx` files to strings.
411
- * Delegates to `typescriptParser` since the TypeScript compiler natively
412
- * supports JSX/TSX syntax via `ScriptKind.TSX`.
512
+ * Kubb parser for `.tsx` and `.jsx` files. Delegates to `parserTs` because the
513
+ * TypeScript compiler handles JSX natively via `ScriptKind.TSX`.
413
514
  *
414
- * Add this parser to the `parsers` option in `defineConfig` when generating `.tsx`/`.jsx` files.
515
+ * Add to the `parsers` array on `defineConfig` when generating components for
516
+ * React (or any framework that emits JSX).
415
517
  *
416
- * @default extname '.tsx'
518
+ * @example
519
+ * ```ts
520
+ * import { defineConfig } from 'kubb'
521
+ * import { adapterOas } from '@kubb/adapter-oas'
522
+ * import { parserTsx } from '@kubb/parser-ts'
523
+ *
524
+ * export default defineConfig({
525
+ * input: { path: './petStore.yaml' },
526
+ * output: { path: './src/gen' },
527
+ * adapter: adapterOas(),
528
+ * parsers: [parserTsx],
529
+ * plugins: [],
530
+ * })
531
+ * ```
417
532
  */
418
533
  const parserTsx = (0, _kubb_core.defineParser)({
419
534
  name: "tsx",
420
535
  extNames: [".tsx", ".jsx"],
421
- async parse(file, options = { extname: ".tsx" }) {
536
+ print(...nodes) {
537
+ return print(...nodes);
538
+ },
539
+ parse(file, options = { extname: ".tsx" }) {
422
540
  return parserTs.parse(file, options);
423
541
  }
424
542
  });
425
543
  //#endregion
426
- exports.createExport = createExport;
427
- exports.createImport = createImport;
428
544
  exports.parserTs = parserTs;
429
545
  exports.parserTsx = parserTsx;
430
- exports.print = print;
431
- exports.safePrint = safePrint;
432
- exports.validateNodes = validateNodes;
433
546
 
434
547
  //# sourceMappingURL=index.cjs.map