@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/LICENSE +17 -10
- package/README.md +120 -0
- package/dist/index.cjs +294 -174
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +62 -53
- package/dist/index.js +296 -171
- package/dist/index.js.map +1 -1
- package/package.json +6 -10
- package/src/constants.ts +0 -47
- package/src/index.ts +0 -2
- package/src/parserTs.ts +0 -509
- package/src/parserTsx.ts +0 -20
- /package/dist/{chunk--u3MIqq1.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
|
-
import "./
|
|
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/
|
|
5
|
+
//#region ../../internals/utils/src/fs.ts
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
30
|
-
* so the printer
|
|
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/
|
|
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 =
|
|
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
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
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
|
|
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 (
|
|
72
|
-
|
|
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
|
-
*
|
|
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
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
*
|
|
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
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return
|
|
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
|
-
*
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
340
|
-
*
|
|
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
|
-
* @
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* printImport({ name: ['z'], path: './zod.ts' })
|
|
381
|
+
* // "import { z } from './zod.ts'"
|
|
382
|
+
* ```
|
|
343
383
|
*/
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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(
|
|
470
|
+
path: resolveOutputPath(item.path, { extname }, true),
|
|
360
471
|
isTypeOnly: item.isTypeOnly,
|
|
361
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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 {
|
|
523
|
+
export { parserTs, parserTsx };
|
|
399
524
|
|
|
400
525
|
//# sourceMappingURL=index.js.map
|