@kubb/parser-ts 5.0.0-beta.1 → 5.0.0-beta.100

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