@kubb/parser-ts 5.0.0-beta.6 → 5.0.0-beta.61

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