@kubb/parser-ts 5.0.0-alpha.31 → 5.0.0-alpha.33

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
@@ -91,6 +91,215 @@ function createExport({ path, asAlias, isTypeOnly = false, name }) {
91
91
  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);
92
92
  }
93
93
  /**
94
+ * Converts a {@link JSDocNode} to a JSDoc comment block string.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * printJSDoc({ comments: ['@description A pet', '@deprecated'] })
99
+ * // /**
100
+ * // * @description A pet
101
+ * // * @deprecated
102
+ * // *\/
103
+ * ```
104
+ */
105
+ function printJSDoc(jsDoc) {
106
+ const comments = (jsDoc.comments ?? []).filter((c) => c != null);
107
+ if (comments.length === 0) return "";
108
+ const lines = comments.flatMap((c) => c.split(/\r?\n/)).map((l) => l.replace(/\*\//g, "* /").replace(/\r/g, "")).filter((l) => l.trim().length > 0);
109
+ if (lines.length === 0) return "";
110
+ return [
111
+ "/**",
112
+ ...lines.map((l) => ` * ${l}`),
113
+ " */"
114
+ ].join("\n");
115
+ }
116
+ /**
117
+ * Serializes the body / value content from a `nodes` array.
118
+ *
119
+ * Each element is either a raw string or a structured {@link CodeNode}
120
+ * (recursively converted via {@link printCodeNode}).
121
+ * Elements are joined with `\n`.
122
+ */
123
+ function printNodes(nodes) {
124
+ if (!nodes || nodes.length === 0) return "";
125
+ return nodes.map(printCodeNode).join("\n");
126
+ }
127
+ /**
128
+ * Indents every non-empty line of `text` by `spaces` spaces.
129
+ */
130
+ function indentLines(text, spaces = 2) {
131
+ if (!text) return "";
132
+ const pad = " ".repeat(spaces);
133
+ return text.split("\n").map((line) => line.trim() ? `${pad}${line}` : "").join("\n");
134
+ }
135
+ /**
136
+ * Converts a {@link ConstNode} to a TypeScript `const` declaration string.
137
+ *
138
+ * Mirrors the `Const` component from `@kubb/renderer-jsx`.
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * printConst(createConst({ name: 'pet', export: true, nodes: ['{}'] }))
143
+ * // 'export const pet = {}'
144
+ * ```
145
+ *
146
+ * @example With type and `as const`
147
+ * ```ts
148
+ * printConst(createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))
149
+ * // 'export const pets: Pet[] = [] as const'
150
+ * ```
151
+ */
152
+ function printConst(node) {
153
+ const { name, export: canExport, type, JSDoc, asConst, nodes } = node;
154
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : "";
155
+ const body = printNodes(nodes);
156
+ const parts = [];
157
+ if (canExport) parts.push("export ");
158
+ parts.push("const ");
159
+ parts.push(name);
160
+ if (type) parts.push(`: ${type}`);
161
+ parts.push(" = ");
162
+ parts.push(body);
163
+ if (asConst) parts.push(" as const");
164
+ return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
165
+ }
166
+ /**
167
+ * Converts a {@link TypeNode} to a TypeScript `type` alias declaration string.
168
+ *
169
+ * Mirrors the `Type` component from `@kubb/renderer-jsx`.
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * printType(createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))
174
+ * // 'export type Pet = { id: number }'
175
+ * ```
176
+ */
177
+ function printType(node) {
178
+ const { name, export: canExport, JSDoc, nodes } = node;
179
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : "";
180
+ const body = printNodes(nodes);
181
+ const parts = [];
182
+ if (canExport) parts.push("export ");
183
+ parts.push("type ");
184
+ parts.push(name);
185
+ parts.push(" = ");
186
+ parts.push(body);
187
+ return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
188
+ }
189
+ /**
190
+ * Converts a {@link FunctionNode} to a TypeScript `function` declaration string.
191
+ *
192
+ * Mirrors the `Function` component from `@kubb/renderer-jsx`.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * printFunction(createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))
197
+ * // 'export function getPet(id: string): Pet {\n return fetch(id)\n}'
198
+ * ```
199
+ *
200
+ * @example Async with generics
201
+ * ```ts
202
+ * printFunction(createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))
203
+ * // 'export async function fetchPet<T>(id: string): Promise<T> {\n}'
204
+ * ```
205
+ */
206
+ function printFunction(node) {
207
+ const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes } = node;
208
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : "";
209
+ const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(", ") : generics}>` : "";
210
+ const returnTypeStr = returnType ? isAsync ? `: Promise<${returnType}>` : `: ${returnType}` : "";
211
+ const body = printNodes(nodes);
212
+ const indented = body ? indentLines(body) : "";
213
+ const parts = [];
214
+ if (canExport) parts.push("export ");
215
+ if (isDefault) parts.push("default ");
216
+ if (isAsync) parts.push("async ");
217
+ parts.push("function ");
218
+ parts.push(name);
219
+ parts.push(genericsStr);
220
+ parts.push(`(${params ?? ""})`);
221
+ parts.push(returnTypeStr);
222
+ parts.push(" {");
223
+ if (indented) parts.push(`\n${indented}\n`);
224
+ parts.push("}");
225
+ return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
226
+ }
227
+ /**
228
+ * Converts an {@link ArrowFunctionNode} to a TypeScript arrow function declaration string.
229
+ *
230
+ * Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
231
+ *
232
+ * @example Multi-line arrow function
233
+ * ```ts
234
+ * printArrowFunction(createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))
235
+ * // 'export const getPet = (id: string) => {\n return fetch(id)\n}'
236
+ * ```
237
+ *
238
+ * @example Single-line arrow function
239
+ * ```ts
240
+ * printArrowFunction(createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))
241
+ * // 'const double = (n: number) => n * 2'
242
+ * ```
243
+ */
244
+ function printArrowFunction(node) {
245
+ const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes, singleLine } = node;
246
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : "";
247
+ const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(", ") : generics}>` : "";
248
+ const returnTypeStr = returnType ? isAsync ? `: Promise<${returnType}>` : `: ${returnType}` : "";
249
+ const body = printNodes(nodes);
250
+ const arrowBody = singleLine ? ` => ${body}` : body ? ` => {\n${indentLines(body)}\n}` : " => {}";
251
+ const parts = [];
252
+ if (canExport) parts.push("export ");
253
+ if (isDefault) parts.push("default ");
254
+ parts.push("const ");
255
+ parts.push(name);
256
+ parts.push(" = ");
257
+ if (isAsync) parts.push("async ");
258
+ parts.push(genericsStr);
259
+ parts.push(`(${params ?? ""})`);
260
+ parts.push(returnTypeStr);
261
+ parts.push(arrowBody);
262
+ return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
263
+ }
264
+ /**
265
+ * Converts a {@link CodeNode} to its TypeScript string representation.
266
+ *
267
+ * Dispatches to the appropriate printer based on the node's `kind`.
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * printCodeNode(createConst({ name: 'x', nodes: ['1'] }))
272
+ * // 'const x = 1'
273
+ * ```
274
+ */
275
+ function printCodeNode(node) {
276
+ switch (node.kind) {
277
+ case "Break": return "";
278
+ case "Text": return node.value;
279
+ case "Jsx": return node.value;
280
+ case "Const": return printConst(node);
281
+ case "Type": return printType(node);
282
+ case "Function": return printFunction(node);
283
+ case "ArrowFunction": return printArrowFunction(node);
284
+ }
285
+ }
286
+ /**
287
+ * Converts a {@link SourceNode} to its TypeScript string representation.
288
+ *
289
+ * Iterates `nodes` in DOM order, rendering each {@link CodeNode} via
290
+ * {@link printCodeNode}.
291
+ *
292
+ * @example From nodes
293
+ * ```ts
294
+ * printSource({ kind: 'Source', nodes: [createConst({ name: 'x', nodes: [createText('1')] }), createText('x.toString()')] })
295
+ * // 'const x = 1\nx.toString()'
296
+ * ```
297
+ */
298
+ function printSource(node) {
299
+ if (node.nodes && node.nodes.length > 0) return node.nodes.map(printCodeNode).join("\n");
300
+ return "";
301
+ }
302
+ /**
94
303
  * Parser that converts `.ts` and `.js` files to strings using the TypeScript
95
304
  * compiler. Handles import/export statement generation from file metadata.
96
305
  *
@@ -101,7 +310,10 @@ const parserTs = (0, _kubb_core.defineParser)({
101
310
  extNames: [".ts", ".js"],
102
311
  async parse(file, options = { extname: ".ts" }) {
103
312
  const sourceParts = [];
104
- for (const item of file.sources) if (item.value) sourceParts.push(item.value);
313
+ for (const item of file.sources) {
314
+ const sourceStr = printSource(item);
315
+ if (sourceStr) sourceParts.push(sourceStr.trimEnd());
316
+ }
105
317
  const source = sourceParts.join("\n\n");
106
318
  const importNodes = [];
107
319
  for (const item of file.imports) {
@@ -130,7 +342,7 @@ const parserTs = (0, _kubb_core.defineParser)({
130
342
  print(...importNodes, ...exportNodes),
131
343
  source,
132
344
  file.footer
133
- ].filter((segment) => segment != null).join("\n");
345
+ ].filter((segment) => Boolean(segment)).map((s) => s.trimEnd()).join("\n\n");
134
346
  }
135
347
  });
136
348
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["ts"],"sources":["../src/parserTs.ts","../src/parserTsx.ts"],"sourcesContent":["import { normalize, relative } from 'node:path'\nimport type { KubbFile, Parser } from '@kubb/core'\nimport { defineParser } from '@kubb/core'\nimport ts from 'typescript'\n\nconst { factory } = ts\n\nfunction slash(path: string): string {\n return normalize(path).replaceAll(/\\\\/g, '/').replace('../', '')\n}\n\nfunction getRelativePath(rootDir: string, filePath: string): string {\n const rel = relative(rootDir, filePath)\n const slashed = slash(rel)\n return slashed.startsWith('../') ? slashed : `./${slashed}`\n}\n\nfunction trimExtName(text: string): string {\n return text.replace(/\\.[^/.]+$/, '')\n}\n\n/**\n * Validates TypeScript AST nodes before printing.\n * Throws an error if any node has SyntaxKind.Unknown which would cause the\n * TypeScript printer to crash.\n */\nexport function validateNodes(...nodes: ts.Node[]): void {\n for (const node of nodes) {\n if (!node) {\n throw new Error('Attempted to print undefined or null TypeScript node')\n }\n if (node.kind === ts.SyntaxKind.Unknown) {\n throw new Error(\n 'Invalid TypeScript AST node detected with SyntaxKind.Unknown. ' +\n 'This typically indicates a schema pattern that could not be properly converted to TypeScript. ' +\n `Node: ${JSON.stringify(node, null, 2)}`,\n )\n }\n }\n}\n\n/**\n * Converts TypeScript/TSX AST nodes to a string using the TypeScript printer.\n */\nexport function print(...elements: Array<ts.Node>): string {\n const sourceFile = ts.createSourceFile('print.tsx', '', ts.ScriptTarget.ES2022, true, ts.ScriptKind.TSX)\n\n const printer = ts.createPrinter({\n omitTrailingSemicolon: true,\n newLine: ts.NewLineKind.LineFeed,\n removeComments: false,\n noEmitHelpers: true,\n })\n\n const output = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray(elements.filter(Boolean)), sourceFile)\n\n return output.replace(/\\r\\n/g, '\\n')\n}\n\n/**\n * Like `print` but validates nodes first to surface issues early.\n */\nexport function safePrint(...elements: Array<ts.Node>): string {\n validateNodes(...elements)\n return print(...elements)\n}\n\nexport function createImport({\n name,\n path,\n root,\n isTypeOnly = false,\n isNameSpace = false,\n}: {\n name: string | Array<string | { propertyName: string; name?: string }>\n path: string\n root?: string\n /** @default false */\n isTypeOnly?: boolean\n /** @default false */\n isNameSpace?: boolean\n}): ts.ImportDeclaration {\n const resolvePath = root ? getRelativePath(root, path) : path\n\n if (!Array.isArray(name)) {\n if (isNameSpace) {\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, undefined, factory.createNamespaceImport(factory.createIdentifier(name))),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n }\n\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, factory.createIdentifier(name), undefined),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n }\n\n const specifiers = name.map((item) => {\n if (typeof item === 'object') {\n const { propertyName, name: alias } = item\n return factory.createImportSpecifier(false, alias ? factory.createIdentifier(propertyName) : undefined, factory.createIdentifier(alias ?? propertyName))\n }\n return factory.createImportSpecifier(false, undefined, factory.createIdentifier(item))\n })\n\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, undefined, factory.createNamedImports(specifiers)),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n}\n\nexport function createExport({\n path,\n asAlias,\n isTypeOnly = false,\n name,\n}: {\n path: string\n /** @default false */\n asAlias?: boolean\n /** @default false */\n isTypeOnly?: boolean\n name?: string | Array<ts.Identifier | string>\n}): ts.ExportDeclaration {\n if (name && !Array.isArray(name) && !asAlias) {\n console.warn(`When using name as string, asAlias should be true: ${name}`)\n }\n\n if (!Array.isArray(name)) {\n const parsedName = name?.match(/^\\d/) ? `_${name?.slice(1)}` : name\n\n return factory.createExportDeclaration(\n undefined,\n isTypeOnly,\n asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : undefined,\n factory.createStringLiteral(path),\n undefined,\n )\n }\n\n return factory.createExportDeclaration(\n undefined,\n isTypeOnly,\n factory.createNamedExports(\n name.map((propertyName) =>\n factory.createExportSpecifier(false, undefined, typeof propertyName === 'string' ? factory.createIdentifier(propertyName) : propertyName),\n ),\n ),\n factory.createStringLiteral(path),\n undefined,\n )\n}\n\n/**\n * Parser that converts `.ts` and `.js` files to strings using the TypeScript\n * compiler. Handles import/export statement generation from file metadata.\n *\n * @default Used automatically when no `parsers` option is set in `defineConfig`.\n */\nexport const parserTs: Parser = defineParser({\n name: 'typescript',\n extNames: ['.ts', '.js'],\n async parse(file, options = { extname: '.ts' }) {\n const sourceParts: Array<string> = []\n for (const item of file.sources) {\n if (item.value) {\n sourceParts.push(item.value)\n }\n }\n const source = sourceParts.join('\\n\\n')\n\n const importNodes: Array<ts.ImportDeclaration> = []\n for (const item of (file as KubbFile.ResolvedFile).imports) {\n const importPath = item.root ? getRelativePath(item.root, item.path) : item.path\n const hasExtname = !!/\\.[^/.]+$/.exec(importPath)\n\n importNodes.push(\n createImport({\n name: item.name as string | Array<string | { propertyName: string; name?: string }>,\n path: options?.extname && hasExtname ? `${trimExtName(importPath)}${options.extname}` : item.root ? trimExtName(importPath) : importPath,\n isTypeOnly: item.isTypeOnly,\n isNameSpace: item.isNameSpace,\n }),\n )\n }\n\n const exportNodes: Array<ts.ExportDeclaration> = []\n for (const item of (file as KubbFile.ResolvedFile).exports) {\n const exportPath = item.path\n const hasExtname = !!/\\.[^/.]+$/.exec(exportPath)\n\n exportNodes.push(\n createExport({\n name: item.name as string | Array<ts.Identifier | string> | undefined,\n path: options?.extname && hasExtname ? `${trimExtName(item.path)}${options.extname}` : trimExtName(item.path),\n isTypeOnly: item.isTypeOnly,\n asAlias: item.asAlias,\n }),\n )\n }\n\n const parts = [file.banner, print(...importNodes, ...exportNodes), source, file.footer].filter((segment): segment is string => segment != null)\n return parts.join('\\n')\n },\n})\n","import type { Parser } from '@kubb/core'\nimport { defineParser } from '@kubb/core'\nimport { parserTs } from './parserTs.ts'\n\n/**\n * Parser that converts `.tsx` and `.jsx` files to strings.\n * Delegates to `typescriptParser` since the TypeScript compiler natively\n * supports JSX/TSX syntax via `ScriptKind.TSX`.\n *\n * Add this parser to the `parsers` option in `defineConfig` when generating `.tsx`/`.jsx` files.\n *\n * @default extname '.tsx'\n */\nexport const parserTsx: Parser = defineParser({\n name: 'tsx',\n extNames: ['.tsx', '.jsx'],\n async parse(file, options = { extname: '.tsx' }) {\n return parserTs.parse(file, options)\n },\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["ts"],"sources":["../src/parserTs.ts","../src/parserTsx.ts"],"sourcesContent":["import { normalize, relative } from 'node:path'\nimport type { ArrowFunctionNode, CodeNode, ConstNode, FileNode, FunctionNode, JSDocNode, JsxNode, SourceNode, TextNode, TypeNode } from '@kubb/ast/types'\nimport type { Parser } from '@kubb/core'\nimport { defineParser } from '@kubb/core'\nimport ts from 'typescript'\n\nconst { factory } = ts\n\nfunction slash(path: string): string {\n return normalize(path).replaceAll(/\\\\/g, '/').replace('../', '')\n}\n\nfunction getRelativePath(rootDir: string, filePath: string): string {\n const rel = relative(rootDir, filePath)\n const slashed = slash(rel)\n return slashed.startsWith('../') ? slashed : `./${slashed}`\n}\n\nfunction trimExtName(text: string): string {\n return text.replace(/\\.[^/.]+$/, '')\n}\n\n/**\n * Validates TypeScript AST nodes before printing.\n * Throws an error if any node has SyntaxKind.Unknown which would cause the\n * TypeScript printer to crash.\n */\nexport function validateNodes(...nodes: ts.Node[]): void {\n for (const node of nodes) {\n if (!node) {\n throw new Error('Attempted to print undefined or null TypeScript node')\n }\n if (node.kind === ts.SyntaxKind.Unknown) {\n throw new Error(\n 'Invalid TypeScript AST node detected with SyntaxKind.Unknown. ' +\n 'This typically indicates a schema pattern that could not be properly converted to TypeScript. ' +\n `Node: ${JSON.stringify(node, null, 2)}`,\n )\n }\n }\n}\n\n/**\n * Converts TypeScript/TSX AST nodes to a string using the TypeScript printer.\n */\nexport function print(...elements: Array<ts.Node>): string {\n const sourceFile = ts.createSourceFile('print.tsx', '', ts.ScriptTarget.ES2022, true, ts.ScriptKind.TSX)\n\n const printer = ts.createPrinter({\n omitTrailingSemicolon: true,\n newLine: ts.NewLineKind.LineFeed,\n removeComments: false,\n noEmitHelpers: true,\n })\n\n const output = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray(elements.filter(Boolean)), sourceFile)\n\n return output.replace(/\\r\\n/g, '\\n')\n}\n\n/**\n * Like `print` but validates nodes first to surface issues early.\n */\nexport function safePrint(...elements: Array<ts.Node>): string {\n validateNodes(...elements)\n return print(...elements)\n}\n\nexport function createImport({\n name,\n path,\n root,\n isTypeOnly = false,\n isNameSpace = false,\n}: {\n name: string | Array<string | { propertyName: string; name?: string }>\n path: string\n root?: string\n /** @default false */\n isTypeOnly?: boolean\n /** @default false */\n isNameSpace?: boolean\n}): ts.ImportDeclaration {\n const resolvePath = root ? getRelativePath(root, path) : path\n\n if (!Array.isArray(name)) {\n if (isNameSpace) {\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, undefined, factory.createNamespaceImport(factory.createIdentifier(name))),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n }\n\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, factory.createIdentifier(name), undefined),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n }\n\n const specifiers = name.map((item) => {\n if (typeof item === 'object') {\n const { propertyName, name: alias } = item\n return factory.createImportSpecifier(false, alias ? factory.createIdentifier(propertyName) : undefined, factory.createIdentifier(alias ?? propertyName))\n }\n return factory.createImportSpecifier(false, undefined, factory.createIdentifier(item))\n })\n\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, undefined, factory.createNamedImports(specifiers)),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n}\n\nexport function createExport({\n path,\n asAlias,\n isTypeOnly = false,\n name,\n}: {\n path: string\n /** @default false */\n asAlias?: boolean\n /** @default false */\n isTypeOnly?: boolean\n name?: string | Array<ts.Identifier | string>\n}): ts.ExportDeclaration {\n if (name && !Array.isArray(name) && !asAlias) {\n console.warn(`When using name as string, asAlias should be true: ${name}`)\n }\n\n if (!Array.isArray(name)) {\n const parsedName = name?.match(/^\\d/) ? `_${name?.slice(1)}` : name\n\n return factory.createExportDeclaration(\n undefined,\n isTypeOnly,\n asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : undefined,\n factory.createStringLiteral(path),\n undefined,\n )\n }\n\n return factory.createExportDeclaration(\n undefined,\n isTypeOnly,\n factory.createNamedExports(\n name.map((propertyName) =>\n factory.createExportSpecifier(false, undefined, typeof propertyName === 'string' ? factory.createIdentifier(propertyName) : propertyName),\n ),\n ),\n factory.createStringLiteral(path),\n undefined,\n )\n}\n\n/**\n * Converts a {@link JSDocNode} to a JSDoc comment block string.\n *\n * @example\n * ```ts\n * printJSDoc({ comments: ['@description A pet', '@deprecated'] })\n * // /**\n * // * @description A pet\n * // * @deprecated\n * // *\\/\n * ```\n */\nexport function printJSDoc(jsDoc: JSDocNode): string {\n const comments = (jsDoc.comments ?? []).filter((c) => c != null)\n if (comments.length === 0) return ''\n\n const lines = comments\n .flatMap((c) => c.split(/\\r?\\n/))\n .map((l) => l.replace(/\\*\\//g, '* /').replace(/\\r/g, ''))\n .filter((l) => l.trim().length > 0)\n\n if (lines.length === 0) return ''\n\n return ['/**', ...lines.map((l) => ` * ${l}`), ' */'].join('\\n')\n}\n\n/**\n * Serializes the body / value content from a `nodes` array.\n *\n * Each element is either a raw string or a structured {@link CodeNode}\n * (recursively converted via {@link printCodeNode}).\n * Elements are joined with `\\n`.\n */\nfunction printNodes(nodes: Array<CodeNode> | undefined): string {\n if (!nodes || nodes.length === 0) return ''\n return nodes.map(printCodeNode).join('\\n')\n}\n\n/**\n * Indents every non-empty line of `text` by `spaces` spaces.\n */\nfunction indentLines(text: string, spaces = 2): string {\n if (!text) return ''\n const pad = ' '.repeat(spaces)\n return text\n .split('\\n')\n .map((line) => (line.trim() ? `${pad}${line}` : ''))\n .join('\\n')\n}\n\n/**\n * Converts a {@link ConstNode} to a TypeScript `const` declaration string.\n *\n * Mirrors the `Const` component from `@kubb/renderer-jsx`.\n *\n * @example\n * ```ts\n * printConst(createConst({ name: 'pet', export: true, nodes: ['{}'] }))\n * // 'export const pet = {}'\n * ```\n *\n * @example With type and `as const`\n * ```ts\n * printConst(createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))\n * // 'export const pets: Pet[] = [] as const'\n * ```\n */\nexport function printConst(node: ConstNode): string {\n const { name, export: canExport, type, JSDoc, asConst, nodes } = node\n\n const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''\n const body = printNodes(nodes)\n\n const parts: string[] = []\n if (canExport) parts.push('export ')\n parts.push('const ')\n parts.push(name)\n if (type) {\n parts.push(`: ${type}`)\n }\n parts.push(' = ')\n parts.push(body)\n if (asConst) parts.push(' as const')\n\n const declaration = parts.join('')\n return [jsDocStr, declaration].filter(Boolean).join('\\n')\n}\n\n/**\n * Converts a {@link TypeNode} to a TypeScript `type` alias declaration string.\n *\n * Mirrors the `Type` component from `@kubb/renderer-jsx`.\n *\n * @example\n * ```ts\n * printType(createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))\n * // 'export type Pet = { id: number }'\n * ```\n */\nexport function printType(node: TypeNode): string {\n const { name, export: canExport, JSDoc, nodes } = node\n\n const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''\n const body = printNodes(nodes)\n\n const parts: string[] = []\n if (canExport) parts.push('export ')\n parts.push('type ')\n parts.push(name)\n parts.push(' = ')\n parts.push(body)\n\n const declaration = parts.join('')\n return [jsDocStr, declaration].filter(Boolean).join('\\n')\n}\n\n/**\n * Converts a {@link FunctionNode} to a TypeScript `function` declaration string.\n *\n * Mirrors the `Function` component from `@kubb/renderer-jsx`.\n *\n * @example\n * ```ts\n * printFunction(createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))\n * // 'export function getPet(id: string): Pet {\\n return fetch(id)\\n}'\n * ```\n *\n * @example Async with generics\n * ```ts\n * printFunction(createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))\n * // 'export async function fetchPet<T>(id: string): Promise<T> {\\n}'\n * ```\n */\nexport function printFunction(node: FunctionNode): string {\n const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes } = node\n\n const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''\n\n const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(', ') : generics}>` : ''\n\n const returnTypeStr = returnType ? (isAsync ? `: Promise<${returnType}>` : `: ${returnType}`) : ''\n\n const body = printNodes(nodes)\n const indented = body ? indentLines(body) : ''\n\n const parts: string[] = []\n if (canExport) parts.push('export ')\n if (isDefault) parts.push('default ')\n if (isAsync) parts.push('async ')\n parts.push('function ')\n parts.push(name)\n parts.push(genericsStr)\n parts.push(`(${params ?? ''})`)\n parts.push(returnTypeStr)\n parts.push(' {')\n if (indented) {\n parts.push(`\\n${indented}\\n`)\n }\n parts.push('}')\n\n const declaration = parts.join('')\n return [jsDocStr, declaration].filter(Boolean).join('\\n')\n}\n\n/**\n * Converts an {@link ArrowFunctionNode} to a TypeScript arrow function declaration string.\n *\n * Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.\n *\n * @example Multi-line arrow function\n * ```ts\n * printArrowFunction(createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))\n * // 'export const getPet = (id: string) => {\\n return fetch(id)\\n}'\n * ```\n *\n * @example Single-line arrow function\n * ```ts\n * printArrowFunction(createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))\n * // 'const double = (n: number) => n * 2'\n * ```\n */\nexport function printArrowFunction(node: ArrowFunctionNode): string {\n const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes, singleLine } = node\n\n const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''\n\n const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(', ') : generics}>` : ''\n\n const returnTypeStr = returnType ? (isAsync ? `: Promise<${returnType}>` : `: ${returnType}`) : ''\n\n const body = printNodes(nodes)\n\n const arrowBody = singleLine ? ` => ${body}` : body ? ` => {\\n${indentLines(body)}\\n}` : ' => {}'\n\n const parts: string[] = []\n if (canExport) parts.push('export ')\n if (isDefault) parts.push('default ')\n parts.push('const ')\n parts.push(name)\n parts.push(' = ')\n if (isAsync) parts.push('async ')\n parts.push(genericsStr)\n parts.push(`(${params ?? ''})`)\n parts.push(returnTypeStr)\n parts.push(arrowBody)\n\n const declaration = parts.join('')\n return [jsDocStr, declaration].filter(Boolean).join('\\n')\n}\n\n/**\n * Converts a {@link CodeNode} to its TypeScript string representation.\n *\n * Dispatches to the appropriate printer based on the node's `kind`.\n *\n * @example\n * ```ts\n * printCodeNode(createConst({ name: 'x', nodes: ['1'] }))\n * // 'const x = 1'\n * ```\n */\nexport function printCodeNode(node: CodeNode): string {\n switch (node.kind) {\n case 'Break':\n return ''\n case 'Text':\n return (node as TextNode).value\n case 'Jsx':\n return (node as JsxNode).value\n case 'Const':\n return printConst(node)\n case 'Type':\n return printType(node)\n case 'Function':\n return printFunction(node)\n case 'ArrowFunction':\n return printArrowFunction(node)\n }\n}\n\n/**\n * Converts a {@link SourceNode} to its TypeScript string representation.\n *\n * Iterates `nodes` in DOM order, rendering each {@link CodeNode} via\n * {@link printCodeNode}.\n *\n * @example From nodes\n * ```ts\n * printSource({ kind: 'Source', nodes: [createConst({ name: 'x', nodes: [createText('1')] }), createText('x.toString()')] })\n * // 'const x = 1\\nx.toString()'\n * ```\n */\nexport function printSource(node: SourceNode): string {\n if (node.nodes && node.nodes.length > 0) {\n return node.nodes.map(printCodeNode).join('\\n')\n }\n return ''\n}\n\n/**\n * Parser that converts `.ts` and `.js` files to strings using the TypeScript\n * compiler. Handles import/export statement generation from file metadata.\n *\n * @default Used automatically when no `parsers` option is set in `defineConfig`.\n */\nexport const parserTs: Parser = defineParser({\n name: 'typescript',\n extNames: ['.ts', '.js'],\n async parse(file, options = { extname: '.ts' }) {\n const sourceParts: Array<string> = []\n for (const item of file.sources) {\n const sourceStr = printSource(item as SourceNode)\n if (sourceStr) {\n sourceParts.push(sourceStr.trimEnd())\n }\n }\n const source = sourceParts.join('\\n\\n')\n\n const importNodes: Array<ts.ImportDeclaration> = []\n for (const item of (file as FileNode).imports) {\n const importPath = item.root ? getRelativePath(item.root, item.path) : item.path\n const hasExtname = !!/\\.[^/.]+$/.exec(importPath)\n\n importNodes.push(\n createImport({\n name: item.name as string | Array<string | { propertyName: string; name?: string }>,\n path: options?.extname && hasExtname ? `${trimExtName(importPath)}${options.extname}` : item.root ? trimExtName(importPath) : importPath,\n isTypeOnly: item.isTypeOnly,\n isNameSpace: item.isNameSpace,\n }),\n )\n }\n\n const exportNodes: Array<ts.ExportDeclaration> = []\n for (const item of (file as FileNode).exports) {\n const exportPath = item.path\n const hasExtname = !!/\\.[^/.]+$/.exec(exportPath)\n\n exportNodes.push(\n createExport({\n name: item.name as string | Array<ts.Identifier | string> | undefined,\n path: options?.extname && hasExtname ? `${trimExtName(item.path)}${options.extname}` : trimExtName(item.path),\n isTypeOnly: item.isTypeOnly,\n asAlias: item.asAlias,\n }),\n )\n }\n\n const parts = [file.banner, print(...importNodes, ...exportNodes), source, file.footer]\n .filter((segment): segment is string => Boolean(segment))\n .map((s) => s.trimEnd())\n return parts.join('\\n\\n')\n },\n})\n","import type { Parser } from '@kubb/core'\nimport { defineParser } from '@kubb/core'\nimport { parserTs } from './parserTs.ts'\n\n/**\n * Parser that converts `.tsx` and `.jsx` files to strings.\n * Delegates to `typescriptParser` since the TypeScript compiler natively\n * supports JSX/TSX syntax via `ScriptKind.TSX`.\n *\n * Add this parser to the `parsers` option in `defineConfig` when generating `.tsx`/`.jsx` files.\n *\n * @default extname '.tsx'\n */\nexport const parserTsx: Parser = defineParser({\n name: 'tsx',\n extNames: ['.tsx', '.jsx'],\n async parse(file, options = { extname: '.tsx' }) {\n return parserTs.parse(file, options)\n },\n})\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { t as __name } from "./chunk--u3MIqq1.js";
2
2
  import { Parser } from "@kubb/core";
3
3
  import ts from "typescript";
4
-
5
4
  //#region src/parserTs.d.ts
6
5
  /**
7
6
  * Validates TypeScript AST nodes before printing.
package/dist/index.js CHANGED
@@ -68,6 +68,215 @@ function createExport({ path, asAlias, isTypeOnly = false, name }) {
68
68
  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);
69
69
  }
70
70
  /**
71
+ * Converts a {@link JSDocNode} to a JSDoc comment block string.
72
+ *
73
+ * @example
74
+ * ```ts
75
+ * printJSDoc({ comments: ['@description A pet', '@deprecated'] })
76
+ * // /**
77
+ * // * @description A pet
78
+ * // * @deprecated
79
+ * // *\/
80
+ * ```
81
+ */
82
+ function printJSDoc(jsDoc) {
83
+ const comments = (jsDoc.comments ?? []).filter((c) => c != null);
84
+ if (comments.length === 0) return "";
85
+ const lines = comments.flatMap((c) => c.split(/\r?\n/)).map((l) => l.replace(/\*\//g, "* /").replace(/\r/g, "")).filter((l) => l.trim().length > 0);
86
+ if (lines.length === 0) return "";
87
+ return [
88
+ "/**",
89
+ ...lines.map((l) => ` * ${l}`),
90
+ " */"
91
+ ].join("\n");
92
+ }
93
+ /**
94
+ * Serializes the body / value content from a `nodes` array.
95
+ *
96
+ * Each element is either a raw string or a structured {@link CodeNode}
97
+ * (recursively converted via {@link printCodeNode}).
98
+ * Elements are joined with `\n`.
99
+ */
100
+ function printNodes(nodes) {
101
+ if (!nodes || nodes.length === 0) return "";
102
+ return nodes.map(printCodeNode).join("\n");
103
+ }
104
+ /**
105
+ * Indents every non-empty line of `text` by `spaces` spaces.
106
+ */
107
+ function indentLines(text, spaces = 2) {
108
+ if (!text) return "";
109
+ const pad = " ".repeat(spaces);
110
+ return text.split("\n").map((line) => line.trim() ? `${pad}${line}` : "").join("\n");
111
+ }
112
+ /**
113
+ * Converts a {@link ConstNode} to a TypeScript `const` declaration string.
114
+ *
115
+ * Mirrors the `Const` component from `@kubb/renderer-jsx`.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * printConst(createConst({ name: 'pet', export: true, nodes: ['{}'] }))
120
+ * // 'export const pet = {}'
121
+ * ```
122
+ *
123
+ * @example With type and `as const`
124
+ * ```ts
125
+ * printConst(createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))
126
+ * // 'export const pets: Pet[] = [] as const'
127
+ * ```
128
+ */
129
+ function printConst(node) {
130
+ const { name, export: canExport, type, JSDoc, asConst, nodes } = node;
131
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : "";
132
+ const body = printNodes(nodes);
133
+ const parts = [];
134
+ if (canExport) parts.push("export ");
135
+ parts.push("const ");
136
+ parts.push(name);
137
+ if (type) parts.push(`: ${type}`);
138
+ parts.push(" = ");
139
+ parts.push(body);
140
+ if (asConst) parts.push(" as const");
141
+ return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
142
+ }
143
+ /**
144
+ * Converts a {@link TypeNode} to a TypeScript `type` alias declaration string.
145
+ *
146
+ * Mirrors the `Type` component from `@kubb/renderer-jsx`.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * printType(createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))
151
+ * // 'export type Pet = { id: number }'
152
+ * ```
153
+ */
154
+ function printType(node) {
155
+ const { name, export: canExport, JSDoc, nodes } = node;
156
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : "";
157
+ const body = printNodes(nodes);
158
+ const parts = [];
159
+ if (canExport) parts.push("export ");
160
+ parts.push("type ");
161
+ parts.push(name);
162
+ parts.push(" = ");
163
+ parts.push(body);
164
+ return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
165
+ }
166
+ /**
167
+ * Converts a {@link FunctionNode} to a TypeScript `function` declaration string.
168
+ *
169
+ * Mirrors the `Function` component from `@kubb/renderer-jsx`.
170
+ *
171
+ * @example
172
+ * ```ts
173
+ * printFunction(createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))
174
+ * // 'export function getPet(id: string): Pet {\n return fetch(id)\n}'
175
+ * ```
176
+ *
177
+ * @example Async with generics
178
+ * ```ts
179
+ * printFunction(createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))
180
+ * // 'export async function fetchPet<T>(id: string): Promise<T> {\n}'
181
+ * ```
182
+ */
183
+ function printFunction(node) {
184
+ const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes } = node;
185
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : "";
186
+ const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(", ") : generics}>` : "";
187
+ const returnTypeStr = returnType ? isAsync ? `: Promise<${returnType}>` : `: ${returnType}` : "";
188
+ const body = printNodes(nodes);
189
+ const indented = body ? indentLines(body) : "";
190
+ const parts = [];
191
+ if (canExport) parts.push("export ");
192
+ if (isDefault) parts.push("default ");
193
+ if (isAsync) parts.push("async ");
194
+ parts.push("function ");
195
+ parts.push(name);
196
+ parts.push(genericsStr);
197
+ parts.push(`(${params ?? ""})`);
198
+ parts.push(returnTypeStr);
199
+ parts.push(" {");
200
+ if (indented) parts.push(`\n${indented}\n`);
201
+ parts.push("}");
202
+ return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
203
+ }
204
+ /**
205
+ * Converts an {@link ArrowFunctionNode} to a TypeScript arrow function declaration string.
206
+ *
207
+ * Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
208
+ *
209
+ * @example Multi-line arrow function
210
+ * ```ts
211
+ * printArrowFunction(createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))
212
+ * // 'export const getPet = (id: string) => {\n return fetch(id)\n}'
213
+ * ```
214
+ *
215
+ * @example Single-line arrow function
216
+ * ```ts
217
+ * printArrowFunction(createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))
218
+ * // 'const double = (n: number) => n * 2'
219
+ * ```
220
+ */
221
+ function printArrowFunction(node) {
222
+ const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes, singleLine } = node;
223
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : "";
224
+ const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(", ") : generics}>` : "";
225
+ const returnTypeStr = returnType ? isAsync ? `: Promise<${returnType}>` : `: ${returnType}` : "";
226
+ const body = printNodes(nodes);
227
+ const arrowBody = singleLine ? ` => ${body}` : body ? ` => {\n${indentLines(body)}\n}` : " => {}";
228
+ const parts = [];
229
+ if (canExport) parts.push("export ");
230
+ if (isDefault) parts.push("default ");
231
+ parts.push("const ");
232
+ parts.push(name);
233
+ parts.push(" = ");
234
+ if (isAsync) parts.push("async ");
235
+ parts.push(genericsStr);
236
+ parts.push(`(${params ?? ""})`);
237
+ parts.push(returnTypeStr);
238
+ parts.push(arrowBody);
239
+ return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
240
+ }
241
+ /**
242
+ * Converts a {@link CodeNode} to its TypeScript string representation.
243
+ *
244
+ * Dispatches to the appropriate printer based on the node's `kind`.
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * printCodeNode(createConst({ name: 'x', nodes: ['1'] }))
249
+ * // 'const x = 1'
250
+ * ```
251
+ */
252
+ function printCodeNode(node) {
253
+ switch (node.kind) {
254
+ case "Break": return "";
255
+ case "Text": return node.value;
256
+ case "Jsx": return node.value;
257
+ case "Const": return printConst(node);
258
+ case "Type": return printType(node);
259
+ case "Function": return printFunction(node);
260
+ case "ArrowFunction": return printArrowFunction(node);
261
+ }
262
+ }
263
+ /**
264
+ * Converts a {@link SourceNode} to its TypeScript string representation.
265
+ *
266
+ * Iterates `nodes` in DOM order, rendering each {@link CodeNode} via
267
+ * {@link printCodeNode}.
268
+ *
269
+ * @example From nodes
270
+ * ```ts
271
+ * printSource({ kind: 'Source', nodes: [createConst({ name: 'x', nodes: [createText('1')] }), createText('x.toString()')] })
272
+ * // 'const x = 1\nx.toString()'
273
+ * ```
274
+ */
275
+ function printSource(node) {
276
+ if (node.nodes && node.nodes.length > 0) return node.nodes.map(printCodeNode).join("\n");
277
+ return "";
278
+ }
279
+ /**
71
280
  * Parser that converts `.ts` and `.js` files to strings using the TypeScript
72
281
  * compiler. Handles import/export statement generation from file metadata.
73
282
  *
@@ -78,7 +287,10 @@ const parserTs = defineParser({
78
287
  extNames: [".ts", ".js"],
79
288
  async parse(file, options = { extname: ".ts" }) {
80
289
  const sourceParts = [];
81
- for (const item of file.sources) if (item.value) sourceParts.push(item.value);
290
+ for (const item of file.sources) {
291
+ const sourceStr = printSource(item);
292
+ if (sourceStr) sourceParts.push(sourceStr.trimEnd());
293
+ }
82
294
  const source = sourceParts.join("\n\n");
83
295
  const importNodes = [];
84
296
  for (const item of file.imports) {
@@ -107,7 +319,7 @@ const parserTs = defineParser({
107
319
  print(...importNodes, ...exportNodes),
108
320
  source,
109
321
  file.footer
110
- ].filter((segment) => segment != null).join("\n");
322
+ ].filter((segment) => Boolean(segment)).map((s) => s.trimEnd()).join("\n\n");
111
323
  }
112
324
  });
113
325
  //#endregion
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/parserTs.ts","../src/parserTsx.ts"],"sourcesContent":["import { normalize, relative } from 'node:path'\nimport type { KubbFile, Parser } from '@kubb/core'\nimport { defineParser } from '@kubb/core'\nimport ts from 'typescript'\n\nconst { factory } = ts\n\nfunction slash(path: string): string {\n return normalize(path).replaceAll(/\\\\/g, '/').replace('../', '')\n}\n\nfunction getRelativePath(rootDir: string, filePath: string): string {\n const rel = relative(rootDir, filePath)\n const slashed = slash(rel)\n return slashed.startsWith('../') ? slashed : `./${slashed}`\n}\n\nfunction trimExtName(text: string): string {\n return text.replace(/\\.[^/.]+$/, '')\n}\n\n/**\n * Validates TypeScript AST nodes before printing.\n * Throws an error if any node has SyntaxKind.Unknown which would cause the\n * TypeScript printer to crash.\n */\nexport function validateNodes(...nodes: ts.Node[]): void {\n for (const node of nodes) {\n if (!node) {\n throw new Error('Attempted to print undefined or null TypeScript node')\n }\n if (node.kind === ts.SyntaxKind.Unknown) {\n throw new Error(\n 'Invalid TypeScript AST node detected with SyntaxKind.Unknown. ' +\n 'This typically indicates a schema pattern that could not be properly converted to TypeScript. ' +\n `Node: ${JSON.stringify(node, null, 2)}`,\n )\n }\n }\n}\n\n/**\n * Converts TypeScript/TSX AST nodes to a string using the TypeScript printer.\n */\nexport function print(...elements: Array<ts.Node>): string {\n const sourceFile = ts.createSourceFile('print.tsx', '', ts.ScriptTarget.ES2022, true, ts.ScriptKind.TSX)\n\n const printer = ts.createPrinter({\n omitTrailingSemicolon: true,\n newLine: ts.NewLineKind.LineFeed,\n removeComments: false,\n noEmitHelpers: true,\n })\n\n const output = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray(elements.filter(Boolean)), sourceFile)\n\n return output.replace(/\\r\\n/g, '\\n')\n}\n\n/**\n * Like `print` but validates nodes first to surface issues early.\n */\nexport function safePrint(...elements: Array<ts.Node>): string {\n validateNodes(...elements)\n return print(...elements)\n}\n\nexport function createImport({\n name,\n path,\n root,\n isTypeOnly = false,\n isNameSpace = false,\n}: {\n name: string | Array<string | { propertyName: string; name?: string }>\n path: string\n root?: string\n /** @default false */\n isTypeOnly?: boolean\n /** @default false */\n isNameSpace?: boolean\n}): ts.ImportDeclaration {\n const resolvePath = root ? getRelativePath(root, path) : path\n\n if (!Array.isArray(name)) {\n if (isNameSpace) {\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, undefined, factory.createNamespaceImport(factory.createIdentifier(name))),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n }\n\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, factory.createIdentifier(name), undefined),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n }\n\n const specifiers = name.map((item) => {\n if (typeof item === 'object') {\n const { propertyName, name: alias } = item\n return factory.createImportSpecifier(false, alias ? factory.createIdentifier(propertyName) : undefined, factory.createIdentifier(alias ?? propertyName))\n }\n return factory.createImportSpecifier(false, undefined, factory.createIdentifier(item))\n })\n\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, undefined, factory.createNamedImports(specifiers)),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n}\n\nexport function createExport({\n path,\n asAlias,\n isTypeOnly = false,\n name,\n}: {\n path: string\n /** @default false */\n asAlias?: boolean\n /** @default false */\n isTypeOnly?: boolean\n name?: string | Array<ts.Identifier | string>\n}): ts.ExportDeclaration {\n if (name && !Array.isArray(name) && !asAlias) {\n console.warn(`When using name as string, asAlias should be true: ${name}`)\n }\n\n if (!Array.isArray(name)) {\n const parsedName = name?.match(/^\\d/) ? `_${name?.slice(1)}` : name\n\n return factory.createExportDeclaration(\n undefined,\n isTypeOnly,\n asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : undefined,\n factory.createStringLiteral(path),\n undefined,\n )\n }\n\n return factory.createExportDeclaration(\n undefined,\n isTypeOnly,\n factory.createNamedExports(\n name.map((propertyName) =>\n factory.createExportSpecifier(false, undefined, typeof propertyName === 'string' ? factory.createIdentifier(propertyName) : propertyName),\n ),\n ),\n factory.createStringLiteral(path),\n undefined,\n )\n}\n\n/**\n * Parser that converts `.ts` and `.js` files to strings using the TypeScript\n * compiler. Handles import/export statement generation from file metadata.\n *\n * @default Used automatically when no `parsers` option is set in `defineConfig`.\n */\nexport const parserTs: Parser = defineParser({\n name: 'typescript',\n extNames: ['.ts', '.js'],\n async parse(file, options = { extname: '.ts' }) {\n const sourceParts: Array<string> = []\n for (const item of file.sources) {\n if (item.value) {\n sourceParts.push(item.value)\n }\n }\n const source = sourceParts.join('\\n\\n')\n\n const importNodes: Array<ts.ImportDeclaration> = []\n for (const item of (file as KubbFile.ResolvedFile).imports) {\n const importPath = item.root ? getRelativePath(item.root, item.path) : item.path\n const hasExtname = !!/\\.[^/.]+$/.exec(importPath)\n\n importNodes.push(\n createImport({\n name: item.name as string | Array<string | { propertyName: string; name?: string }>,\n path: options?.extname && hasExtname ? `${trimExtName(importPath)}${options.extname}` : item.root ? trimExtName(importPath) : importPath,\n isTypeOnly: item.isTypeOnly,\n isNameSpace: item.isNameSpace,\n }),\n )\n }\n\n const exportNodes: Array<ts.ExportDeclaration> = []\n for (const item of (file as KubbFile.ResolvedFile).exports) {\n const exportPath = item.path\n const hasExtname = !!/\\.[^/.]+$/.exec(exportPath)\n\n exportNodes.push(\n createExport({\n name: item.name as string | Array<ts.Identifier | string> | undefined,\n path: options?.extname && hasExtname ? `${trimExtName(item.path)}${options.extname}` : trimExtName(item.path),\n isTypeOnly: item.isTypeOnly,\n asAlias: item.asAlias,\n }),\n )\n }\n\n const parts = [file.banner, print(...importNodes, ...exportNodes), source, file.footer].filter((segment): segment is string => segment != null)\n return parts.join('\\n')\n },\n})\n","import type { Parser } from '@kubb/core'\nimport { defineParser } from '@kubb/core'\nimport { parserTs } from './parserTs.ts'\n\n/**\n * Parser that converts `.tsx` and `.jsx` files to strings.\n * Delegates to `typescriptParser` since the TypeScript compiler natively\n * supports JSX/TSX syntax via `ScriptKind.TSX`.\n *\n * Add this parser to the `parsers` option in `defineConfig` when generating `.tsx`/`.jsx` files.\n *\n * @default extname '.tsx'\n */\nexport const parserTsx: Parser = defineParser({\n name: 'tsx',\n extNames: ['.tsx', '.jsx'],\n async parse(file, options = { extname: '.tsx' }) {\n return parserTs.parse(file, options)\n },\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/parserTs.ts","../src/parserTsx.ts"],"sourcesContent":["import { normalize, relative } from 'node:path'\nimport type { ArrowFunctionNode, CodeNode, ConstNode, FileNode, FunctionNode, JSDocNode, JsxNode, SourceNode, TextNode, TypeNode } from '@kubb/ast/types'\nimport type { Parser } from '@kubb/core'\nimport { defineParser } from '@kubb/core'\nimport ts from 'typescript'\n\nconst { factory } = ts\n\nfunction slash(path: string): string {\n return normalize(path).replaceAll(/\\\\/g, '/').replace('../', '')\n}\n\nfunction getRelativePath(rootDir: string, filePath: string): string {\n const rel = relative(rootDir, filePath)\n const slashed = slash(rel)\n return slashed.startsWith('../') ? slashed : `./${slashed}`\n}\n\nfunction trimExtName(text: string): string {\n return text.replace(/\\.[^/.]+$/, '')\n}\n\n/**\n * Validates TypeScript AST nodes before printing.\n * Throws an error if any node has SyntaxKind.Unknown which would cause the\n * TypeScript printer to crash.\n */\nexport function validateNodes(...nodes: ts.Node[]): void {\n for (const node of nodes) {\n if (!node) {\n throw new Error('Attempted to print undefined or null TypeScript node')\n }\n if (node.kind === ts.SyntaxKind.Unknown) {\n throw new Error(\n 'Invalid TypeScript AST node detected with SyntaxKind.Unknown. ' +\n 'This typically indicates a schema pattern that could not be properly converted to TypeScript. ' +\n `Node: ${JSON.stringify(node, null, 2)}`,\n )\n }\n }\n}\n\n/**\n * Converts TypeScript/TSX AST nodes to a string using the TypeScript printer.\n */\nexport function print(...elements: Array<ts.Node>): string {\n const sourceFile = ts.createSourceFile('print.tsx', '', ts.ScriptTarget.ES2022, true, ts.ScriptKind.TSX)\n\n const printer = ts.createPrinter({\n omitTrailingSemicolon: true,\n newLine: ts.NewLineKind.LineFeed,\n removeComments: false,\n noEmitHelpers: true,\n })\n\n const output = printer.printList(ts.ListFormat.MultiLine, factory.createNodeArray(elements.filter(Boolean)), sourceFile)\n\n return output.replace(/\\r\\n/g, '\\n')\n}\n\n/**\n * Like `print` but validates nodes first to surface issues early.\n */\nexport function safePrint(...elements: Array<ts.Node>): string {\n validateNodes(...elements)\n return print(...elements)\n}\n\nexport function createImport({\n name,\n path,\n root,\n isTypeOnly = false,\n isNameSpace = false,\n}: {\n name: string | Array<string | { propertyName: string; name?: string }>\n path: string\n root?: string\n /** @default false */\n isTypeOnly?: boolean\n /** @default false */\n isNameSpace?: boolean\n}): ts.ImportDeclaration {\n const resolvePath = root ? getRelativePath(root, path) : path\n\n if (!Array.isArray(name)) {\n if (isNameSpace) {\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, undefined, factory.createNamespaceImport(factory.createIdentifier(name))),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n }\n\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, factory.createIdentifier(name), undefined),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n }\n\n const specifiers = name.map((item) => {\n if (typeof item === 'object') {\n const { propertyName, name: alias } = item\n return factory.createImportSpecifier(false, alias ? factory.createIdentifier(propertyName) : undefined, factory.createIdentifier(alias ?? propertyName))\n }\n return factory.createImportSpecifier(false, undefined, factory.createIdentifier(item))\n })\n\n return factory.createImportDeclaration(\n undefined,\n factory.createImportClause(isTypeOnly, undefined, factory.createNamedImports(specifiers)),\n factory.createStringLiteral(resolvePath),\n undefined,\n )\n}\n\nexport function createExport({\n path,\n asAlias,\n isTypeOnly = false,\n name,\n}: {\n path: string\n /** @default false */\n asAlias?: boolean\n /** @default false */\n isTypeOnly?: boolean\n name?: string | Array<ts.Identifier | string>\n}): ts.ExportDeclaration {\n if (name && !Array.isArray(name) && !asAlias) {\n console.warn(`When using name as string, asAlias should be true: ${name}`)\n }\n\n if (!Array.isArray(name)) {\n const parsedName = name?.match(/^\\d/) ? `_${name?.slice(1)}` : name\n\n return factory.createExportDeclaration(\n undefined,\n isTypeOnly,\n asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : undefined,\n factory.createStringLiteral(path),\n undefined,\n )\n }\n\n return factory.createExportDeclaration(\n undefined,\n isTypeOnly,\n factory.createNamedExports(\n name.map((propertyName) =>\n factory.createExportSpecifier(false, undefined, typeof propertyName === 'string' ? factory.createIdentifier(propertyName) : propertyName),\n ),\n ),\n factory.createStringLiteral(path),\n undefined,\n )\n}\n\n/**\n * Converts a {@link JSDocNode} to a JSDoc comment block string.\n *\n * @example\n * ```ts\n * printJSDoc({ comments: ['@description A pet', '@deprecated'] })\n * // /**\n * // * @description A pet\n * // * @deprecated\n * // *\\/\n * ```\n */\nexport function printJSDoc(jsDoc: JSDocNode): string {\n const comments = (jsDoc.comments ?? []).filter((c) => c != null)\n if (comments.length === 0) return ''\n\n const lines = comments\n .flatMap((c) => c.split(/\\r?\\n/))\n .map((l) => l.replace(/\\*\\//g, '* /').replace(/\\r/g, ''))\n .filter((l) => l.trim().length > 0)\n\n if (lines.length === 0) return ''\n\n return ['/**', ...lines.map((l) => ` * ${l}`), ' */'].join('\\n')\n}\n\n/**\n * Serializes the body / value content from a `nodes` array.\n *\n * Each element is either a raw string or a structured {@link CodeNode}\n * (recursively converted via {@link printCodeNode}).\n * Elements are joined with `\\n`.\n */\nfunction printNodes(nodes: Array<CodeNode> | undefined): string {\n if (!nodes || nodes.length === 0) return ''\n return nodes.map(printCodeNode).join('\\n')\n}\n\n/**\n * Indents every non-empty line of `text` by `spaces` spaces.\n */\nfunction indentLines(text: string, spaces = 2): string {\n if (!text) return ''\n const pad = ' '.repeat(spaces)\n return text\n .split('\\n')\n .map((line) => (line.trim() ? `${pad}${line}` : ''))\n .join('\\n')\n}\n\n/**\n * Converts a {@link ConstNode} to a TypeScript `const` declaration string.\n *\n * Mirrors the `Const` component from `@kubb/renderer-jsx`.\n *\n * @example\n * ```ts\n * printConst(createConst({ name: 'pet', export: true, nodes: ['{}'] }))\n * // 'export const pet = {}'\n * ```\n *\n * @example With type and `as const`\n * ```ts\n * printConst(createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))\n * // 'export const pets: Pet[] = [] as const'\n * ```\n */\nexport function printConst(node: ConstNode): string {\n const { name, export: canExport, type, JSDoc, asConst, nodes } = node\n\n const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''\n const body = printNodes(nodes)\n\n const parts: string[] = []\n if (canExport) parts.push('export ')\n parts.push('const ')\n parts.push(name)\n if (type) {\n parts.push(`: ${type}`)\n }\n parts.push(' = ')\n parts.push(body)\n if (asConst) parts.push(' as const')\n\n const declaration = parts.join('')\n return [jsDocStr, declaration].filter(Boolean).join('\\n')\n}\n\n/**\n * Converts a {@link TypeNode} to a TypeScript `type` alias declaration string.\n *\n * Mirrors the `Type` component from `@kubb/renderer-jsx`.\n *\n * @example\n * ```ts\n * printType(createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))\n * // 'export type Pet = { id: number }'\n * ```\n */\nexport function printType(node: TypeNode): string {\n const { name, export: canExport, JSDoc, nodes } = node\n\n const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''\n const body = printNodes(nodes)\n\n const parts: string[] = []\n if (canExport) parts.push('export ')\n parts.push('type ')\n parts.push(name)\n parts.push(' = ')\n parts.push(body)\n\n const declaration = parts.join('')\n return [jsDocStr, declaration].filter(Boolean).join('\\n')\n}\n\n/**\n * Converts a {@link FunctionNode} to a TypeScript `function` declaration string.\n *\n * Mirrors the `Function` component from `@kubb/renderer-jsx`.\n *\n * @example\n * ```ts\n * printFunction(createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))\n * // 'export function getPet(id: string): Pet {\\n return fetch(id)\\n}'\n * ```\n *\n * @example Async with generics\n * ```ts\n * printFunction(createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))\n * // 'export async function fetchPet<T>(id: string): Promise<T> {\\n}'\n * ```\n */\nexport function printFunction(node: FunctionNode): string {\n const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes } = node\n\n const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''\n\n const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(', ') : generics}>` : ''\n\n const returnTypeStr = returnType ? (isAsync ? `: Promise<${returnType}>` : `: ${returnType}`) : ''\n\n const body = printNodes(nodes)\n const indented = body ? indentLines(body) : ''\n\n const parts: string[] = []\n if (canExport) parts.push('export ')\n if (isDefault) parts.push('default ')\n if (isAsync) parts.push('async ')\n parts.push('function ')\n parts.push(name)\n parts.push(genericsStr)\n parts.push(`(${params ?? ''})`)\n parts.push(returnTypeStr)\n parts.push(' {')\n if (indented) {\n parts.push(`\\n${indented}\\n`)\n }\n parts.push('}')\n\n const declaration = parts.join('')\n return [jsDocStr, declaration].filter(Boolean).join('\\n')\n}\n\n/**\n * Converts an {@link ArrowFunctionNode} to a TypeScript arrow function declaration string.\n *\n * Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.\n *\n * @example Multi-line arrow function\n * ```ts\n * printArrowFunction(createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))\n * // 'export const getPet = (id: string) => {\\n return fetch(id)\\n}'\n * ```\n *\n * @example Single-line arrow function\n * ```ts\n * printArrowFunction(createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))\n * // 'const double = (n: number) => n * 2'\n * ```\n */\nexport function printArrowFunction(node: ArrowFunctionNode): string {\n const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes, singleLine } = node\n\n const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''\n\n const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(', ') : generics}>` : ''\n\n const returnTypeStr = returnType ? (isAsync ? `: Promise<${returnType}>` : `: ${returnType}`) : ''\n\n const body = printNodes(nodes)\n\n const arrowBody = singleLine ? ` => ${body}` : body ? ` => {\\n${indentLines(body)}\\n}` : ' => {}'\n\n const parts: string[] = []\n if (canExport) parts.push('export ')\n if (isDefault) parts.push('default ')\n parts.push('const ')\n parts.push(name)\n parts.push(' = ')\n if (isAsync) parts.push('async ')\n parts.push(genericsStr)\n parts.push(`(${params ?? ''})`)\n parts.push(returnTypeStr)\n parts.push(arrowBody)\n\n const declaration = parts.join('')\n return [jsDocStr, declaration].filter(Boolean).join('\\n')\n}\n\n/**\n * Converts a {@link CodeNode} to its TypeScript string representation.\n *\n * Dispatches to the appropriate printer based on the node's `kind`.\n *\n * @example\n * ```ts\n * printCodeNode(createConst({ name: 'x', nodes: ['1'] }))\n * // 'const x = 1'\n * ```\n */\nexport function printCodeNode(node: CodeNode): string {\n switch (node.kind) {\n case 'Break':\n return ''\n case 'Text':\n return (node as TextNode).value\n case 'Jsx':\n return (node as JsxNode).value\n case 'Const':\n return printConst(node)\n case 'Type':\n return printType(node)\n case 'Function':\n return printFunction(node)\n case 'ArrowFunction':\n return printArrowFunction(node)\n }\n}\n\n/**\n * Converts a {@link SourceNode} to its TypeScript string representation.\n *\n * Iterates `nodes` in DOM order, rendering each {@link CodeNode} via\n * {@link printCodeNode}.\n *\n * @example From nodes\n * ```ts\n * printSource({ kind: 'Source', nodes: [createConst({ name: 'x', nodes: [createText('1')] }), createText('x.toString()')] })\n * // 'const x = 1\\nx.toString()'\n * ```\n */\nexport function printSource(node: SourceNode): string {\n if (node.nodes && node.nodes.length > 0) {\n return node.nodes.map(printCodeNode).join('\\n')\n }\n return ''\n}\n\n/**\n * Parser that converts `.ts` and `.js` files to strings using the TypeScript\n * compiler. Handles import/export statement generation from file metadata.\n *\n * @default Used automatically when no `parsers` option is set in `defineConfig`.\n */\nexport const parserTs: Parser = defineParser({\n name: 'typescript',\n extNames: ['.ts', '.js'],\n async parse(file, options = { extname: '.ts' }) {\n const sourceParts: Array<string> = []\n for (const item of file.sources) {\n const sourceStr = printSource(item as SourceNode)\n if (sourceStr) {\n sourceParts.push(sourceStr.trimEnd())\n }\n }\n const source = sourceParts.join('\\n\\n')\n\n const importNodes: Array<ts.ImportDeclaration> = []\n for (const item of (file as FileNode).imports) {\n const importPath = item.root ? getRelativePath(item.root, item.path) : item.path\n const hasExtname = !!/\\.[^/.]+$/.exec(importPath)\n\n importNodes.push(\n createImport({\n name: item.name as string | Array<string | { propertyName: string; name?: string }>,\n path: options?.extname && hasExtname ? `${trimExtName(importPath)}${options.extname}` : item.root ? trimExtName(importPath) : importPath,\n isTypeOnly: item.isTypeOnly,\n isNameSpace: item.isNameSpace,\n }),\n )\n }\n\n const exportNodes: Array<ts.ExportDeclaration> = []\n for (const item of (file as FileNode).exports) {\n const exportPath = item.path\n const hasExtname = !!/\\.[^/.]+$/.exec(exportPath)\n\n exportNodes.push(\n createExport({\n name: item.name as string | Array<ts.Identifier | string> | undefined,\n path: options?.extname && hasExtname ? `${trimExtName(item.path)}${options.extname}` : trimExtName(item.path),\n isTypeOnly: item.isTypeOnly,\n asAlias: item.asAlias,\n }),\n )\n }\n\n const parts = [file.banner, print(...importNodes, ...exportNodes), source, file.footer]\n .filter((segment): segment is string => Boolean(segment))\n .map((s) => s.trimEnd())\n return parts.join('\\n\\n')\n },\n})\n","import type { Parser } from '@kubb/core'\nimport { defineParser } from '@kubb/core'\nimport { parserTs } from './parserTs.ts'\n\n/**\n * Parser that converts `.tsx` and `.jsx` files to strings.\n * Delegates to `typescriptParser` since the TypeScript compiler natively\n * supports JSX/TSX syntax via `ScriptKind.TSX`.\n *\n * Add this parser to the `parsers` option in `defineConfig` when generating `.tsx`/`.jsx` files.\n *\n * @default extname '.tsx'\n */\nexport const parserTsx: Parser = defineParser({\n name: 'tsx',\n extNames: ['.tsx', '.jsx'],\n async parse(file, options = { extname: '.tsx' }) {\n return parserTs.parse(file, options)\n },\n})\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/parser-ts",
3
- "version": "5.0.0-alpha.31",
3
+ "version": "5.0.0-alpha.33",
4
4
  "description": "TypeScript and TSX file parser for Kubb, converting generated files to strings using the TypeScript compiler.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -36,9 +36,10 @@
36
36
  ],
37
37
  "dependencies": {
38
38
  "typescript": "^6.0.2",
39
- "@kubb/core": "5.0.0-alpha.31"
39
+ "@kubb/core": "5.0.0-alpha.33"
40
40
  },
41
41
  "devDependencies": {
42
+ "@kubb/ast": "5.0.0-alpha.33",
42
43
  "@internals/utils": "0.0.0"
43
44
  },
44
45
  "engines": {
package/src/parserTs.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { normalize, relative } from 'node:path'
2
- import type { KubbFile, Parser } from '@kubb/core'
2
+ import type { ArrowFunctionNode, CodeNode, ConstNode, FileNode, FunctionNode, JSDocNode, JsxNode, SourceNode, TextNode, TypeNode } from '@kubb/ast/types'
3
+ import type { Parser } from '@kubb/core'
3
4
  import { defineParser } from '@kubb/core'
4
5
  import ts from 'typescript'
5
6
 
@@ -158,6 +159,265 @@ export function createExport({
158
159
  )
159
160
  }
160
161
 
162
+ /**
163
+ * Converts a {@link JSDocNode} to a JSDoc comment block string.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * printJSDoc({ comments: ['@description A pet', '@deprecated'] })
168
+ * // /**
169
+ * // * @description A pet
170
+ * // * @deprecated
171
+ * // *\/
172
+ * ```
173
+ */
174
+ export function printJSDoc(jsDoc: JSDocNode): string {
175
+ const comments = (jsDoc.comments ?? []).filter((c) => c != null)
176
+ if (comments.length === 0) return ''
177
+
178
+ const lines = comments
179
+ .flatMap((c) => c.split(/\r?\n/))
180
+ .map((l) => l.replace(/\*\//g, '* /').replace(/\r/g, ''))
181
+ .filter((l) => l.trim().length > 0)
182
+
183
+ if (lines.length === 0) return ''
184
+
185
+ return ['/**', ...lines.map((l) => ` * ${l}`), ' */'].join('\n')
186
+ }
187
+
188
+ /**
189
+ * Serializes the body / value content from a `nodes` array.
190
+ *
191
+ * Each element is either a raw string or a structured {@link CodeNode}
192
+ * (recursively converted via {@link printCodeNode}).
193
+ * Elements are joined with `\n`.
194
+ */
195
+ function printNodes(nodes: Array<CodeNode> | undefined): string {
196
+ if (!nodes || nodes.length === 0) return ''
197
+ return nodes.map(printCodeNode).join('\n')
198
+ }
199
+
200
+ /**
201
+ * Indents every non-empty line of `text` by `spaces` spaces.
202
+ */
203
+ function indentLines(text: string, spaces = 2): string {
204
+ if (!text) return ''
205
+ const pad = ' '.repeat(spaces)
206
+ return text
207
+ .split('\n')
208
+ .map((line) => (line.trim() ? `${pad}${line}` : ''))
209
+ .join('\n')
210
+ }
211
+
212
+ /**
213
+ * Converts a {@link ConstNode} to a TypeScript `const` declaration string.
214
+ *
215
+ * Mirrors the `Const` component from `@kubb/renderer-jsx`.
216
+ *
217
+ * @example
218
+ * ```ts
219
+ * printConst(createConst({ name: 'pet', export: true, nodes: ['{}'] }))
220
+ * // 'export const pet = {}'
221
+ * ```
222
+ *
223
+ * @example With type and `as const`
224
+ * ```ts
225
+ * printConst(createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))
226
+ * // 'export const pets: Pet[] = [] as const'
227
+ * ```
228
+ */
229
+ export function printConst(node: ConstNode): string {
230
+ const { name, export: canExport, type, JSDoc, asConst, nodes } = node
231
+
232
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''
233
+ const body = printNodes(nodes)
234
+
235
+ const parts: string[] = []
236
+ if (canExport) parts.push('export ')
237
+ parts.push('const ')
238
+ parts.push(name)
239
+ if (type) {
240
+ parts.push(`: ${type}`)
241
+ }
242
+ parts.push(' = ')
243
+ parts.push(body)
244
+ if (asConst) parts.push(' as const')
245
+
246
+ const declaration = parts.join('')
247
+ return [jsDocStr, declaration].filter(Boolean).join('\n')
248
+ }
249
+
250
+ /**
251
+ * Converts a {@link TypeNode} to a TypeScript `type` alias declaration string.
252
+ *
253
+ * Mirrors the `Type` component from `@kubb/renderer-jsx`.
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * printType(createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))
258
+ * // 'export type Pet = { id: number }'
259
+ * ```
260
+ */
261
+ export function printType(node: TypeNode): string {
262
+ const { name, export: canExport, JSDoc, nodes } = node
263
+
264
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''
265
+ const body = printNodes(nodes)
266
+
267
+ const parts: string[] = []
268
+ if (canExport) parts.push('export ')
269
+ parts.push('type ')
270
+ parts.push(name)
271
+ parts.push(' = ')
272
+ parts.push(body)
273
+
274
+ const declaration = parts.join('')
275
+ return [jsDocStr, declaration].filter(Boolean).join('\n')
276
+ }
277
+
278
+ /**
279
+ * Converts a {@link FunctionNode} to a TypeScript `function` declaration string.
280
+ *
281
+ * Mirrors the `Function` component from `@kubb/renderer-jsx`.
282
+ *
283
+ * @example
284
+ * ```ts
285
+ * printFunction(createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))
286
+ * // 'export function getPet(id: string): Pet {\n return fetch(id)\n}'
287
+ * ```
288
+ *
289
+ * @example Async with generics
290
+ * ```ts
291
+ * printFunction(createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))
292
+ * // 'export async function fetchPet<T>(id: string): Promise<T> {\n}'
293
+ * ```
294
+ */
295
+ export function printFunction(node: FunctionNode): string {
296
+ const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes } = node
297
+
298
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''
299
+
300
+ const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(', ') : generics}>` : ''
301
+
302
+ const returnTypeStr = returnType ? (isAsync ? `: Promise<${returnType}>` : `: ${returnType}`) : ''
303
+
304
+ const body = printNodes(nodes)
305
+ const indented = body ? indentLines(body) : ''
306
+
307
+ const parts: string[] = []
308
+ if (canExport) parts.push('export ')
309
+ if (isDefault) parts.push('default ')
310
+ if (isAsync) parts.push('async ')
311
+ parts.push('function ')
312
+ parts.push(name)
313
+ parts.push(genericsStr)
314
+ parts.push(`(${params ?? ''})`)
315
+ parts.push(returnTypeStr)
316
+ parts.push(' {')
317
+ if (indented) {
318
+ parts.push(`\n${indented}\n`)
319
+ }
320
+ parts.push('}')
321
+
322
+ const declaration = parts.join('')
323
+ return [jsDocStr, declaration].filter(Boolean).join('\n')
324
+ }
325
+
326
+ /**
327
+ * Converts an {@link ArrowFunctionNode} to a TypeScript arrow function declaration string.
328
+ *
329
+ * Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
330
+ *
331
+ * @example Multi-line arrow function
332
+ * ```ts
333
+ * printArrowFunction(createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))
334
+ * // 'export const getPet = (id: string) => {\n return fetch(id)\n}'
335
+ * ```
336
+ *
337
+ * @example Single-line arrow function
338
+ * ```ts
339
+ * printArrowFunction(createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))
340
+ * // 'const double = (n: number) => n * 2'
341
+ * ```
342
+ */
343
+ export function printArrowFunction(node: ArrowFunctionNode): string {
344
+ const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes, singleLine } = node
345
+
346
+ const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''
347
+
348
+ const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(', ') : generics}>` : ''
349
+
350
+ const returnTypeStr = returnType ? (isAsync ? `: Promise<${returnType}>` : `: ${returnType}`) : ''
351
+
352
+ const body = printNodes(nodes)
353
+
354
+ const arrowBody = singleLine ? ` => ${body}` : body ? ` => {\n${indentLines(body)}\n}` : ' => {}'
355
+
356
+ const parts: string[] = []
357
+ if (canExport) parts.push('export ')
358
+ if (isDefault) parts.push('default ')
359
+ parts.push('const ')
360
+ parts.push(name)
361
+ parts.push(' = ')
362
+ if (isAsync) parts.push('async ')
363
+ parts.push(genericsStr)
364
+ parts.push(`(${params ?? ''})`)
365
+ parts.push(returnTypeStr)
366
+ parts.push(arrowBody)
367
+
368
+ const declaration = parts.join('')
369
+ return [jsDocStr, declaration].filter(Boolean).join('\n')
370
+ }
371
+
372
+ /**
373
+ * Converts a {@link CodeNode} to its TypeScript string representation.
374
+ *
375
+ * Dispatches to the appropriate printer based on the node's `kind`.
376
+ *
377
+ * @example
378
+ * ```ts
379
+ * printCodeNode(createConst({ name: 'x', nodes: ['1'] }))
380
+ * // 'const x = 1'
381
+ * ```
382
+ */
383
+ export function printCodeNode(node: CodeNode): string {
384
+ switch (node.kind) {
385
+ case 'Break':
386
+ return ''
387
+ case 'Text':
388
+ return (node as TextNode).value
389
+ case 'Jsx':
390
+ return (node as JsxNode).value
391
+ case 'Const':
392
+ return printConst(node)
393
+ case 'Type':
394
+ return printType(node)
395
+ case 'Function':
396
+ return printFunction(node)
397
+ case 'ArrowFunction':
398
+ return printArrowFunction(node)
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Converts a {@link SourceNode} to its TypeScript string representation.
404
+ *
405
+ * Iterates `nodes` in DOM order, rendering each {@link CodeNode} via
406
+ * {@link printCodeNode}.
407
+ *
408
+ * @example From nodes
409
+ * ```ts
410
+ * printSource({ kind: 'Source', nodes: [createConst({ name: 'x', nodes: [createText('1')] }), createText('x.toString()')] })
411
+ * // 'const x = 1\nx.toString()'
412
+ * ```
413
+ */
414
+ export function printSource(node: SourceNode): string {
415
+ if (node.nodes && node.nodes.length > 0) {
416
+ return node.nodes.map(printCodeNode).join('\n')
417
+ }
418
+ return ''
419
+ }
420
+
161
421
  /**
162
422
  * Parser that converts `.ts` and `.js` files to strings using the TypeScript
163
423
  * compiler. Handles import/export statement generation from file metadata.
@@ -170,14 +430,15 @@ export const parserTs: Parser = defineParser({
170
430
  async parse(file, options = { extname: '.ts' }) {
171
431
  const sourceParts: Array<string> = []
172
432
  for (const item of file.sources) {
173
- if (item.value) {
174
- sourceParts.push(item.value)
433
+ const sourceStr = printSource(item as SourceNode)
434
+ if (sourceStr) {
435
+ sourceParts.push(sourceStr.trimEnd())
175
436
  }
176
437
  }
177
438
  const source = sourceParts.join('\n\n')
178
439
 
179
440
  const importNodes: Array<ts.ImportDeclaration> = []
180
- for (const item of (file as KubbFile.ResolvedFile).imports) {
441
+ for (const item of (file as FileNode).imports) {
181
442
  const importPath = item.root ? getRelativePath(item.root, item.path) : item.path
182
443
  const hasExtname = !!/\.[^/.]+$/.exec(importPath)
183
444
 
@@ -192,7 +453,7 @@ export const parserTs: Parser = defineParser({
192
453
  }
193
454
 
194
455
  const exportNodes: Array<ts.ExportDeclaration> = []
195
- for (const item of (file as KubbFile.ResolvedFile).exports) {
456
+ for (const item of (file as FileNode).exports) {
196
457
  const exportPath = item.path
197
458
  const hasExtname = !!/\.[^/.]+$/.exec(exportPath)
198
459
 
@@ -206,7 +467,9 @@ export const parserTs: Parser = defineParser({
206
467
  )
207
468
  }
208
469
 
209
- const parts = [file.banner, print(...importNodes, ...exportNodes), source, file.footer].filter((segment): segment is string => segment != null)
210
- return parts.join('\n')
470
+ const parts = [file.banner, print(...importNodes, ...exportNodes), source, file.footer]
471
+ .filter((segment): segment is string => Boolean(segment))
472
+ .map((s) => s.trimEnd())
473
+ return parts.join('\n\n')
211
474
  },
212
475
  })