@kubb/parser-ts 5.0.0-beta.11 → 5.0.0-beta.110

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