@kubb/parser-ts 5.5.5 → 5.6.0
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 +152 -319
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +153 -320
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,23 @@ let node_module = require("node:module");
|
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
//#region ../../internals/utils/src/fs.ts
|
|
7
7
|
/**
|
|
8
|
+
* Converts a filesystem path to use POSIX (`/`) separators.
|
|
9
|
+
*
|
|
10
|
+
* Most of the codebase compares and composes paths as strings (prefix matching, joining for
|
|
11
|
+
* import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated
|
|
12
|
+
* paths, but on Windows it returns `\`-separated paths, which breaks every such comparison.
|
|
13
|
+
*
|
|
14
|
+
* Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the
|
|
15
|
+
* code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is
|
|
16
|
+
* exercisable from POSIX CI.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* toPosixPath('C:\\repo\\src\\pet.ts') // 'C:/repo/src/pet.ts'
|
|
20
|
+
*/
|
|
21
|
+
function toPosixPath(filePath) {
|
|
22
|
+
return filePath.replaceAll("\\", "/");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
8
25
|
* Strips the file extension from a path or file name.
|
|
9
26
|
* Only removes the last `.ext` segment when the dot is not part of a directory name.
|
|
10
27
|
*
|
|
@@ -19,61 +36,42 @@ function trimExtName(text) {
|
|
|
19
36
|
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
20
37
|
return text;
|
|
21
38
|
}
|
|
22
|
-
/**
|
|
23
|
-
* Indentation unit prepended once per nesting level when pretty-printing.
|
|
24
|
-
*/
|
|
25
|
-
const INDENT = " ".repeat(2);
|
|
26
|
-
/**
|
|
27
|
-
* Matches only the final `.<ext>` of a path, so a name like `foo.bar.ts` keeps
|
|
28
|
-
* `foo.bar` and loses just `.ts`.
|
|
29
|
-
*/
|
|
30
|
-
const FILE_EXTENSION_PATTERN = /\.[^/.]+$/;
|
|
31
|
-
/**
|
|
32
|
-
* Matches Windows-style backslash path separators.
|
|
33
|
-
*/
|
|
34
|
-
const WINDOWS_PATH_SEPARATOR = /\\/g;
|
|
35
|
-
/**
|
|
36
|
-
* Matches `*\/` in free-form text so JSDoc bodies can neutralize premature
|
|
37
|
-
* comment terminators (`*\/` → `* /`).
|
|
38
|
-
*/
|
|
39
|
-
const JSDOC_TERMINATOR_PATTERN = /\*\//g;
|
|
40
|
-
/**
|
|
41
|
-
* Matches carriage returns for normalizing CRLF/CR line endings to LF.
|
|
42
|
-
*/
|
|
43
|
-
const CARRIAGE_RETURN_PATTERN = /\r/g;
|
|
44
|
-
/**
|
|
45
|
-
* Matches CRLF sequences used when normalizing TypeScript printer output.
|
|
46
|
-
*/
|
|
47
|
-
const CRLF_PATTERN = /\r\n/g;
|
|
48
|
-
/**
|
|
49
|
-
* Matches an identifier that starts with a digit. JavaScript disallows this,
|
|
50
|
-
* so the printer replaces the leading digit with `_`.
|
|
51
|
-
*/
|
|
52
|
-
const LEADING_DIGIT_PATTERN = /^\d/;
|
|
53
39
|
//#endregion
|
|
54
40
|
//#region src/utils.ts
|
|
41
|
+
let compiler;
|
|
55
42
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
* Loads TypeScript on the first `print` or `copy`; `parse` is string templating and never needs it.
|
|
44
|
+
*
|
|
45
|
+
* Loaded with `require`, not `import`: when ESM imports a CommonJS package, Node keeps a second copy of its source
|
|
46
|
+
* to detect named exports, and for TypeScript that copy is about 9 MB.
|
|
47
|
+
*/
|
|
48
|
+
function loadCompiler() {
|
|
49
|
+
if (compiler) return compiler;
|
|
50
|
+
const typescript = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("typescript");
|
|
51
|
+
compiler = {
|
|
52
|
+
typescript,
|
|
53
|
+
printer: typescript.createPrinter({
|
|
54
|
+
omitTrailingSemicolon: true,
|
|
55
|
+
newLine: typescript.NewLineKind.LineFeed,
|
|
56
|
+
removeComments: false,
|
|
57
|
+
noEmitHelpers: true
|
|
58
|
+
}),
|
|
59
|
+
sourceFile: typescript.createSourceFile("print.tsx", "", typescript.ScriptTarget.ES2022, true, typescript.ScriptKind.TSX)
|
|
60
|
+
};
|
|
61
|
+
return compiler;
|
|
62
|
+
}
|
|
61
63
|
/**
|
|
62
|
-
*
|
|
63
|
-
* prefixed with `./` when the target sits inside the root, or `../` when it escapes it.
|
|
64
|
+
* Module specifier that imports `filePath` from the file at `root`: POSIX, relative to that file's directory, `./`-prefixed unless it climbs with `../`.
|
|
64
65
|
*/
|
|
65
|
-
function getRelativePath(
|
|
66
|
-
const
|
|
67
|
-
const slashed = (0, node_path.normalize)(rel).replaceAll(WINDOWS_PATH_SEPARATOR, "/").replace("../", "");
|
|
66
|
+
function getRelativePath(root, filePath) {
|
|
67
|
+
const slashed = toPosixPath((0, node_path.relative)((0, node_path.dirname)(root), filePath));
|
|
68
68
|
return slashed.startsWith("../") ? slashed : `./${slashed}`;
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
72
|
-
* `options.extname`. When the source path has no extension the original is kept,
|
|
73
|
-
* so virtual/module-only paths flow through unchanged.
|
|
71
|
+
* Swaps the extension of a relative import/export path for `options.extname`; package specifiers and extension-less paths stay as-is.
|
|
74
72
|
*/
|
|
75
73
|
function resolveOutputPath(path, options, rootAware) {
|
|
76
|
-
const hasExtname =
|
|
74
|
+
const hasExtname = /\.[^/.]+$/.test(path);
|
|
77
75
|
if (rootAware && options?.extname && hasExtname) return `${trimExtName(path)}${options.extname}`;
|
|
78
76
|
return rootAware ? trimExtName(path) : path;
|
|
79
77
|
}
|
|
@@ -82,6 +80,7 @@ function resolveOutputPath(path, options, rootAware) {
|
|
|
82
80
|
*/
|
|
83
81
|
function toImportName(element) {
|
|
84
82
|
if (!element.propertyName) return element.name.text;
|
|
83
|
+
const { typescript } = loadCompiler();
|
|
85
84
|
const { propertyName } = element;
|
|
86
85
|
return {
|
|
87
86
|
propertyName: typescript.isStringLiteral(propertyName) ? quoteModulePath(propertyName.text) : propertyName.text,
|
|
@@ -92,6 +91,7 @@ function toImportName(element) {
|
|
|
92
91
|
* Converts an `import` declaration into `ImportNode`s. Side-effect imports and imports with attributes stay as written.
|
|
93
92
|
*/
|
|
94
93
|
function toImportNodes(statement, filePath) {
|
|
94
|
+
const { typescript } = loadCompiler();
|
|
95
95
|
if (!typescript.isImportDeclaration(statement) || !statement.importClause || !typescript.isStringLiteral(statement.moduleSpecifier) || statement.attributes) return [];
|
|
96
96
|
const { name, namedBindings, phaseModifier } = statement.importClause;
|
|
97
97
|
const specifier = statement.moduleSpecifier.text;
|
|
@@ -132,6 +132,7 @@ function toImportNodes(statement, filePath) {
|
|
|
132
132
|
* Converts an `export … from` declaration into `ExportNode`s. Forms `printExport` cannot print stay as written.
|
|
133
133
|
*/
|
|
134
134
|
function toExportNodes(statement) {
|
|
135
|
+
const { typescript } = loadCompiler();
|
|
135
136
|
if (!typescript.isExportDeclaration(statement) || !statement.moduleSpecifier || !typescript.isStringLiteral(statement.moduleSpecifier) || statement.attributes) return [];
|
|
136
137
|
const { exportClause, isTypeOnly } = statement;
|
|
137
138
|
const path = statement.moduleSpecifier.text;
|
|
@@ -152,14 +153,11 @@ function toExportNodes(statement) {
|
|
|
152
153
|
isTypeOnly
|
|
153
154
|
})];
|
|
154
155
|
}
|
|
155
|
-
function isModuleDeclaration(statement) {
|
|
156
|
-
return typescript.isImportDeclaration(statement) || typescript.isExportDeclaration(statement) && Boolean(statement.moduleSpecifier);
|
|
157
|
-
}
|
|
158
156
|
/**
|
|
159
|
-
* Splits `source` into its
|
|
160
|
-
* (shebang, directives, comments) and the remaining `body`.
|
|
157
|
+
* Splits `source` into its lifted `import`/`export … from` nodes, the `header` above the first one (shebang, directives, comments) and the rest as `body`.
|
|
161
158
|
*/
|
|
162
159
|
function splitModuleDeclarations(source, filePath) {
|
|
160
|
+
const { typescript } = loadCompiler();
|
|
163
161
|
const sourceFile = typescript.createSourceFile(filePath, source, typescript.ScriptTarget.Latest);
|
|
164
162
|
const imports = [];
|
|
165
163
|
const exports = [];
|
|
@@ -170,7 +168,7 @@ function splitModuleDeclarations(source, filePath) {
|
|
|
170
168
|
const importNodes = toImportNodes(statement, filePath);
|
|
171
169
|
const exportNodes = toExportNodes(statement);
|
|
172
170
|
if (!importNodes.length && !exportNodes.length) {
|
|
173
|
-
if (
|
|
171
|
+
if (typescript.isImportDeclaration(statement) || typescript.isExportDeclaration(statement) && statement.moduleSpecifier) break;
|
|
174
172
|
continue;
|
|
175
173
|
}
|
|
176
174
|
const text = source.slice(cursor, statement.getStart(sourceFile));
|
|
@@ -188,50 +186,35 @@ function splitModuleDeclarations(source, filePath) {
|
|
|
188
186
|
};
|
|
189
187
|
}
|
|
190
188
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* separator, so a double `<br/>` never emits more than one blank line.
|
|
189
|
+
* Joins the printed `nodes` with `separator`; a `Break` widens the next separator to a blank line, and leading, trailing and repeated breaks fold into one.
|
|
190
|
+
*
|
|
191
|
+
* Imperative on purpose: this runs once per source fragment and `map().filter().join()` showed up in deopt traces.
|
|
195
192
|
*/
|
|
196
|
-
function printNodes(nodes) {
|
|
197
|
-
if (!nodes || nodes.length === 0) return "";
|
|
193
|
+
function printNodes({ nodes, separator }) {
|
|
198
194
|
let result = "";
|
|
199
|
-
let hasContent = false;
|
|
200
195
|
let pendingBreak = false;
|
|
201
|
-
for (const node of nodes) {
|
|
196
|
+
for (const node of nodes ?? []) {
|
|
202
197
|
if (node.kind === "Break") {
|
|
203
|
-
|
|
198
|
+
pendingBreak = result !== "";
|
|
204
199
|
continue;
|
|
205
200
|
}
|
|
206
201
|
const text = printCodeNode(node);
|
|
207
202
|
if (!text) continue;
|
|
208
|
-
if (
|
|
203
|
+
if (result) result += pendingBreak ? "\n\n" : separator;
|
|
209
204
|
result += text;
|
|
210
|
-
hasContent = true;
|
|
211
205
|
pendingBreak = false;
|
|
212
206
|
}
|
|
213
207
|
return result;
|
|
214
208
|
}
|
|
215
209
|
/**
|
|
216
|
-
* Indents every non-empty line of `text` by
|
|
217
|
-
* {@link INDENT_CHAR} that many times, or a string to use as the indent verbatim.
|
|
210
|
+
* Indents every non-empty line of `text` by two spaces.
|
|
218
211
|
*/
|
|
219
|
-
function indentLines(text
|
|
212
|
+
function indentLines(text) {
|
|
220
213
|
if (!text) return "";
|
|
221
|
-
|
|
222
|
-
return text.split("\n").map((line) => line.trim() ? `${pad}${line}` : "").join("\n");
|
|
214
|
+
return text.split("\n").map((line) => line.trim() ? ` ${line}` : "").join("\n");
|
|
223
215
|
}
|
|
224
216
|
/**
|
|
225
|
-
*
|
|
226
|
-
* surrounding blank lines, so multi-line content authored inside an indented template
|
|
227
|
-
* literal lines up at a column-zero baseline. Leading whitespace is counted by
|
|
228
|
-
* character, so N tabs and N spaces are treated as the same depth.
|
|
229
|
-
*
|
|
230
|
-
* @example
|
|
231
|
-
* ```ts
|
|
232
|
-
* dedent('\n foo\n bar\n ')
|
|
233
|
-
* // 'foo\n bar'
|
|
234
|
-
* ```
|
|
217
|
+
* Strips the common leading whitespace and the surrounding blank lines, counting tabs and spaces alike, so indented template literals start at column zero.
|
|
235
218
|
*/
|
|
236
219
|
function dedent(text) {
|
|
237
220
|
if (!text) return "";
|
|
@@ -246,61 +229,37 @@ function dedent(text) {
|
|
|
246
229
|
return trimmed.map((line) => isBlank(line) ? "" : line.slice(min)).join("\n");
|
|
247
230
|
}
|
|
248
231
|
/**
|
|
249
|
-
* Renders
|
|
250
|
-
* Accepts either a raw string (rendered verbatim) or an array of type-parameter names.
|
|
232
|
+
* Renders `<T>(params): ReturnType`; an async function wraps its return type in `Promise<>`.
|
|
251
233
|
*/
|
|
252
|
-
function
|
|
253
|
-
|
|
254
|
-
|
|
234
|
+
function signature({ async: isAsync, generics, params, returnType }) {
|
|
235
|
+
const genericsStr = generics ? `<${Array.isArray(generics) ? generics.join(", ") : generics}>` : "";
|
|
236
|
+
const wrapped = isAsync ? `Promise<${returnType}>` : returnType;
|
|
237
|
+
const returnTypeStr = returnType ? `: ${wrapped}` : "";
|
|
238
|
+
return `${genericsStr}(${params ?? ""})${returnTypeStr}`;
|
|
255
239
|
}
|
|
256
240
|
/**
|
|
257
|
-
*
|
|
258
|
-
* Returns an empty string when no return type is provided.
|
|
241
|
+
* Puts the JSDoc block, when it renders to something, above `declaration`.
|
|
259
242
|
*/
|
|
260
|
-
function
|
|
261
|
-
|
|
262
|
-
return
|
|
243
|
+
function withJSDoc({ jsDoc, declaration }) {
|
|
244
|
+
const jsDocStr = jsDoc ? printJSDoc(jsDoc) : "";
|
|
245
|
+
return jsDocStr ? `${jsDocStr}\n${declaration}` : declaration;
|
|
263
246
|
}
|
|
264
247
|
/**
|
|
265
|
-
* Module-scoped TypeScript printer instance. A printer does not mutate the source file, so one
|
|
266
|
-
* instance is reused across every `print()` call instead of constructing a new printer each time.
|
|
267
|
-
*/
|
|
268
|
-
const TS_PRINTER = typescript.createPrinter({
|
|
269
|
-
omitTrailingSemicolon: true,
|
|
270
|
-
newLine: typescript.NewLineKind.LineFeed,
|
|
271
|
-
removeComments: false,
|
|
272
|
-
noEmitHelpers: true
|
|
273
|
-
});
|
|
274
|
-
/**
|
|
275
|
-
* Module-scoped source file used as the print target. `printList` only reads the source
|
|
276
|
-
* file's compiler options / language version. It never mutates it.
|
|
277
|
-
*/
|
|
278
|
-
const PRINT_SOURCE_FILE = typescript.createSourceFile("print.tsx", "", typescript.ScriptTarget.ES2022, true, typescript.ScriptKind.TSX);
|
|
279
|
-
TS_PRINTER.printList(typescript.ListFormat.MultiLine, factory.createNodeArray([]), PRINT_SOURCE_FILE);
|
|
280
|
-
/**
|
|
281
248
|
* Converts TypeScript/TSX AST nodes to a string using the TypeScript printer.
|
|
282
249
|
*/
|
|
283
250
|
function print(...elements) {
|
|
284
251
|
const filtered = elements.filter(Boolean);
|
|
285
252
|
if (filtered.length === 0) return "";
|
|
286
|
-
|
|
253
|
+
const { typescript, printer, sourceFile } = loadCompiler();
|
|
254
|
+
return printer.printList(typescript.ListFormat.MultiLine, typescript.factory.createNodeArray(filtered), sourceFile).replace(/\r\n/g, "\n");
|
|
287
255
|
}
|
|
288
256
|
/**
|
|
289
|
-
* Converts a {@link ast.JSDocNode} to a
|
|
290
|
-
*
|
|
291
|
-
* @example
|
|
292
|
-
* ```ts
|
|
293
|
-
* printJSDoc({ comments: ['@description A pet', '@deprecated'] })
|
|
294
|
-
* // /**
|
|
295
|
-
* // * @description A pet
|
|
296
|
-
* // * @deprecated
|
|
297
|
-
* // *\/
|
|
298
|
-
* ```
|
|
257
|
+
* Converts a {@link ast.JSDocNode} to a `/** … *\/` block, dropping blank comments.
|
|
299
258
|
*/
|
|
300
259
|
function printJSDoc(jsDoc) {
|
|
301
260
|
const comments = (jsDoc.comments ?? []).filter((c) => c != null);
|
|
302
261
|
if (comments.length === 0) return "";
|
|
303
|
-
const lines = comments.flatMap((c) => c.split(/\r?\n/)).map((l) => l.replace(
|
|
262
|
+
const lines = comments.flatMap((c) => c.split(/\r?\n/)).map((l) => l.replace(/\*\//g, "* /").replace(/\r/g, "")).filter((l) => l.trim().length > 0);
|
|
304
263
|
if (lines.length === 0) return "";
|
|
305
264
|
return [
|
|
306
265
|
"/**",
|
|
@@ -309,194 +268,101 @@ function printJSDoc(jsDoc) {
|
|
|
309
268
|
].join("\n");
|
|
310
269
|
}
|
|
311
270
|
/**
|
|
312
|
-
* Converts a {@link ast.ConstNode} to a
|
|
313
|
-
*
|
|
314
|
-
* Mirrors the `Const` component from `@kubb/renderer-jsx`.
|
|
315
|
-
*
|
|
316
|
-
* @example
|
|
317
|
-
* ```ts
|
|
318
|
-
* printConst(factory.createConst({ name: 'pet', export: true, nodes: ['{}'] }))
|
|
319
|
-
* // 'export const pet = {}'
|
|
320
|
-
* ```
|
|
321
|
-
*
|
|
322
|
-
* @example With type and `as const`
|
|
323
|
-
* ```ts
|
|
324
|
-
* printConst(factory.createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))
|
|
325
|
-
* // 'export const pets: Pet[] = [] as const'
|
|
326
|
-
* ```
|
|
271
|
+
* Converts a {@link ast.ConstNode} to a `const` declaration, mirroring the `Const` component from `@kubb/renderer-jsx`.
|
|
327
272
|
*/
|
|
328
273
|
function printConst(node) {
|
|
329
274
|
const { name, export: canExport, type, JSDoc, asConst, nodes } = node;
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
parts.push(" = ");
|
|
338
|
-
parts.push(body);
|
|
339
|
-
if (asConst) parts.push(" as const");
|
|
340
|
-
return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
|
|
275
|
+
return withJSDoc({
|
|
276
|
+
jsDoc: JSDoc,
|
|
277
|
+
declaration: `${canExport ? "export " : ""}const ${name}${type ? `: ${type}` : ""} = ${printNodes({
|
|
278
|
+
nodes,
|
|
279
|
+
separator: "\n"
|
|
280
|
+
})}${asConst ? " as const" : ""}`
|
|
281
|
+
});
|
|
341
282
|
}
|
|
342
283
|
/**
|
|
343
|
-
* Converts a {@link ast.TypeNode} to a
|
|
344
|
-
*
|
|
345
|
-
* Mirrors the `Type` component from `@kubb/renderer-jsx`.
|
|
346
|
-
*
|
|
347
|
-
* @example
|
|
348
|
-
* ```ts
|
|
349
|
-
* printType(factory.createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))
|
|
350
|
-
* // 'export type Pet = { id: number }'
|
|
351
|
-
* ```
|
|
284
|
+
* Converts a {@link ast.TypeNode} to a `type` alias, mirroring the `Type` component from `@kubb/renderer-jsx`.
|
|
352
285
|
*/
|
|
353
286
|
function printType(node) {
|
|
354
287
|
const { name, export: canExport, JSDoc, nodes } = node;
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
parts.push(body);
|
|
363
|
-
return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
|
|
288
|
+
return withJSDoc({
|
|
289
|
+
jsDoc: JSDoc,
|
|
290
|
+
declaration: `${canExport ? "export " : ""}type ${name} = ${printNodes({
|
|
291
|
+
nodes,
|
|
292
|
+
separator: "\n"
|
|
293
|
+
})}`
|
|
294
|
+
});
|
|
364
295
|
}
|
|
365
296
|
/**
|
|
366
|
-
* Converts a {@link ast.FunctionNode} to a
|
|
367
|
-
*
|
|
368
|
-
* Mirrors the `Function` component from `@kubb/renderer-jsx`.
|
|
369
|
-
*
|
|
370
|
-
* @example
|
|
371
|
-
* ```ts
|
|
372
|
-
* printFunction(factory.createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))
|
|
373
|
-
* // 'export function getPet(id: string): Pet {\n return fetch(id)\n}'
|
|
374
|
-
* ```
|
|
375
|
-
*
|
|
376
|
-
* @example Async with generics
|
|
377
|
-
* ```ts
|
|
378
|
-
* printFunction(factory.createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))
|
|
379
|
-
* // 'export async function fetchPet<T>(id: string): Promise<T> {\n}'
|
|
380
|
-
* ```
|
|
297
|
+
* Converts a {@link ast.FunctionNode} to a `function` declaration, mirroring the `Function` component from `@kubb/renderer-jsx`.
|
|
381
298
|
*/
|
|
382
299
|
function printFunction(node) {
|
|
383
300
|
const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes } = node;
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if (indented) parts.push(`\n${indented}\n`);
|
|
398
|
-
parts.push("}");
|
|
399
|
-
return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
|
|
301
|
+
const body = indentLines(printNodes({
|
|
302
|
+
nodes,
|
|
303
|
+
separator: "\n"
|
|
304
|
+
}));
|
|
305
|
+
return withJSDoc({
|
|
306
|
+
jsDoc: JSDoc,
|
|
307
|
+
declaration: `${`${canExport ? "export " : ""}${isDefault ? "default " : ""}${isAsync ? "async " : ""}`}function ${name}${signature({
|
|
308
|
+
async: isAsync,
|
|
309
|
+
generics,
|
|
310
|
+
params,
|
|
311
|
+
returnType
|
|
312
|
+
})} {${body ? `\n${body}\n` : ""}}`
|
|
313
|
+
});
|
|
400
314
|
}
|
|
401
315
|
/**
|
|
402
|
-
* Converts an {@link ast.ArrowFunctionNode} to a
|
|
403
|
-
*
|
|
404
|
-
* Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
|
|
405
|
-
*
|
|
406
|
-
* @example Multi-line arrow function
|
|
407
|
-
* ```ts
|
|
408
|
-
* printArrowFunction(factory.createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))
|
|
409
|
-
* // 'export const getPet = (id: string) => {\n return fetch(id)\n}'
|
|
410
|
-
* ```
|
|
411
|
-
*
|
|
412
|
-
* @example Single-line arrow function
|
|
413
|
-
* ```ts
|
|
414
|
-
* printArrowFunction(factory.createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))
|
|
415
|
-
* // 'const double = (n: number) => n * 2'
|
|
416
|
-
* ```
|
|
316
|
+
* Converts an {@link ast.ArrowFunctionNode} to a `const` arrow function, mirroring the `Function.Arrow` component from `@kubb/renderer-jsx`.
|
|
417
317
|
*/
|
|
418
318
|
function printArrowFunction(node) {
|
|
419
319
|
const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes, singleLine } = node;
|
|
420
|
-
const
|
|
421
|
-
|
|
320
|
+
const body = printNodes({
|
|
321
|
+
nodes,
|
|
322
|
+
separator: "\n"
|
|
323
|
+
});
|
|
422
324
|
const arrowBody = singleLine ? ` => ${body}` : body ? ` => {\n${indentLines(body)}\n}` : " => {}";
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
parts.push(formatReturnType(returnType, isAsync));
|
|
433
|
-
parts.push(arrowBody);
|
|
434
|
-
return [jsDocStr, parts.join("")].filter(Boolean).join("\n");
|
|
325
|
+
return withJSDoc({
|
|
326
|
+
jsDoc: JSDoc,
|
|
327
|
+
declaration: `${`${canExport ? "export " : ""}${isDefault ? "default " : ""}`}const ${name} = ${isAsync ? "async " : ""}${signature({
|
|
328
|
+
async: isAsync,
|
|
329
|
+
generics,
|
|
330
|
+
params,
|
|
331
|
+
returnType
|
|
332
|
+
})}${arrowBody}`
|
|
333
|
+
});
|
|
435
334
|
}
|
|
436
335
|
/**
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
* Dispatches to the appropriate printer based on the node's `kind`.
|
|
440
|
-
*
|
|
441
|
-
* @example
|
|
442
|
-
* ```ts
|
|
443
|
-
* printCodeNode(factory.createConst({ name: 'x', nodes: ['1'] }))
|
|
444
|
-
* // 'const x = 1'
|
|
445
|
-
* ```
|
|
336
|
+
* Dispatches a {@link ast.CodeNode} to the printer for its `kind`.
|
|
446
337
|
*/
|
|
447
338
|
function printCodeNode(node) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
339
|
+
switch (node.kind) {
|
|
340
|
+
case "Break": return "";
|
|
341
|
+
case "Text":
|
|
342
|
+
case "Jsx": return dedent(node.value);
|
|
343
|
+
case "Const": return printConst(node);
|
|
344
|
+
case "Type": return printType(node);
|
|
345
|
+
case "Function": return printFunction(node);
|
|
346
|
+
case "ArrowFunction": return printArrowFunction(node);
|
|
347
|
+
}
|
|
456
348
|
}
|
|
457
349
|
/**
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
* Iterates `nodes` in DOM order, rendering each {@link ast.CodeNode} via
|
|
461
|
-
* {@link printCodeNode}.
|
|
462
|
-
*
|
|
463
|
-
* Top-level declarations are separated by a blank line so the source reads
|
|
464
|
-
* cleanly without an external formatter.
|
|
465
|
-
*
|
|
466
|
-
* @example From nodes
|
|
467
|
-
* ```ts
|
|
468
|
-
* printSource({ kind: 'Source', nodes: [factory.createConst({ name: 'x', nodes: [factory.createText('1')] }), factory.createText('x.toString()')] })
|
|
469
|
-
* // 'const x = 1\n\nx.toString()'
|
|
470
|
-
* ```
|
|
350
|
+
* Prints a {@link ast.SourceNode} with a blank line between its top-level nodes.
|
|
471
351
|
*/
|
|
472
352
|
function printSource(node) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const text = printCodeNode(child);
|
|
478
|
-
if (!text) continue;
|
|
479
|
-
result = result ? `${result}\n\n${text}` : text;
|
|
480
|
-
}
|
|
481
|
-
return result;
|
|
353
|
+
return printNodes({
|
|
354
|
+
nodes: node.nodes,
|
|
355
|
+
separator: "\n\n"
|
|
356
|
+
});
|
|
482
357
|
}
|
|
483
358
|
/**
|
|
484
|
-
* Wraps a module specifier in single quotes, escaping
|
|
485
|
-
* statement stays valid even for unusual paths.
|
|
359
|
+
* Wraps a module specifier in single quotes, escaping embedded backslashes and quotes.
|
|
486
360
|
*/
|
|
487
361
|
function quoteModulePath(path) {
|
|
488
362
|
return `'${path.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
489
363
|
}
|
|
490
364
|
/**
|
|
491
|
-
* Renders an import declaration
|
|
492
|
-
* default, namespace (`* as`), and named imports with `{ a as b }` aliases, each optionally
|
|
493
|
-
* `type`-only. `path` is used verbatim, so resolve it first.
|
|
494
|
-
*
|
|
495
|
-
* @example
|
|
496
|
-
* ```ts
|
|
497
|
-
* printImport({ name: ['z'], path: './zod.ts' })
|
|
498
|
-
* // "import { z } from './zod.ts'"
|
|
499
|
-
* ```
|
|
365
|
+
* Renders an `import` declaration (default, `* as`, or named with `{ a as b }`, each optionally `type`-only). `path` is used verbatim, so resolve it first.
|
|
500
366
|
*/
|
|
501
367
|
function printImport({ name, path, isTypeOnly = false, isNameSpace = false }) {
|
|
502
368
|
const typePrefix = isTypeOnly ? "type " : "";
|
|
@@ -511,21 +377,13 @@ function printImport({ name, path, isTypeOnly = false, isNameSpace = false }) {
|
|
|
511
377
|
}).join(", ")} } from ${from}`;
|
|
512
378
|
}
|
|
513
379
|
/**
|
|
514
|
-
* Renders an export declaration
|
|
515
|
-
* named re-exports, namespace alias (`* as name`), and wildcard, each optionally `type`-only.
|
|
516
|
-
* `path` is used verbatim, so resolve it first.
|
|
517
|
-
*
|
|
518
|
-
* @example
|
|
519
|
-
* ```ts
|
|
520
|
-
* printExport({ name: ['Pet', 'Order'], path: './models.ts' })
|
|
521
|
-
* // "export { Pet, Order } from './models.ts'"
|
|
522
|
-
* ```
|
|
380
|
+
* Renders an `export … from` declaration (named, `* as name`, or wildcard, each optionally `type`-only). `path` is used verbatim, so resolve it first.
|
|
523
381
|
*/
|
|
524
382
|
function printExport({ path, name, isTypeOnly = false, asAlias = false }) {
|
|
525
383
|
const typePrefix = isTypeOnly ? "type " : "";
|
|
526
384
|
const from = quoteModulePath(path);
|
|
527
|
-
if (Array.isArray(name)) return `export ${typePrefix}{ ${name.
|
|
528
|
-
if (asAlias && name) return `export ${typePrefix}* as ${
|
|
385
|
+
if (Array.isArray(name)) return `export ${typePrefix}{ ${name.join(", ")} } from ${from}`;
|
|
386
|
+
if (asAlias && name) return `export ${typePrefix}* as ${/^\d/.test(name) ? `_${name.slice(1)}` : name} from ${from}`;
|
|
529
387
|
if (name) return `export ${typePrefix}{ ${name} } from ${from}`;
|
|
530
388
|
return `export ${typePrefix}* from ${from}`;
|
|
531
389
|
}
|
|
@@ -560,33 +418,17 @@ const parserTs = (0, _kubb_kit.defineParser)(({ extension = DEFAULT_EXTENSION }
|
|
|
560
418
|
return {
|
|
561
419
|
name: "typescript",
|
|
562
420
|
extNames: [".ts", ".js"],
|
|
563
|
-
print
|
|
564
|
-
return print(...nodes);
|
|
565
|
-
},
|
|
421
|
+
print,
|
|
566
422
|
parse(file) {
|
|
567
423
|
const extname = extension[file.extname] || void 0;
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
const importPath = item.root ? getRelativePath(item.root, item.path) : item.path;
|
|
577
|
-
importLines.push(printImport({
|
|
578
|
-
name: item.name,
|
|
579
|
-
path: resolveOutputPath(importPath, { extname }, Boolean(item.root)),
|
|
580
|
-
isTypeOnly: item.isTypeOnly,
|
|
581
|
-
isNameSpace: item.isNameSpace
|
|
582
|
-
}));
|
|
583
|
-
}
|
|
584
|
-
const exportLines = [];
|
|
585
|
-
for (const item of file.exports) exportLines.push(printExport({
|
|
586
|
-
name: item.name,
|
|
587
|
-
path: resolveOutputPath(item.path, { extname }, item.path.startsWith(".")),
|
|
588
|
-
isTypeOnly: item.isTypeOnly,
|
|
589
|
-
asAlias: item.asAlias
|
|
424
|
+
const source = file.sources.map((item) => printSource(item)).filter(Boolean).map((text) => text.trimEnd()).join("\n\n");
|
|
425
|
+
const importLines = file.imports.map((item) => printImport({
|
|
426
|
+
...item,
|
|
427
|
+
path: resolveOutputPath(item.root ? getRelativePath(item.root, item.path) : item.path, { extname }, Boolean(item.root))
|
|
428
|
+
}));
|
|
429
|
+
const exportLines = file.exports.map((item) => printExport({
|
|
430
|
+
...item,
|
|
431
|
+
path: resolveOutputPath(item.path, { extname }, item.path.startsWith("."))
|
|
590
432
|
}));
|
|
591
433
|
const importExportBlock = [...importLines, ...exportLines].join("\n");
|
|
592
434
|
return [
|
|
@@ -633,20 +475,11 @@ const parserTs = (0, _kubb_kit.defineParser)(({ extension = DEFAULT_EXTENSION }
|
|
|
633
475
|
* })
|
|
634
476
|
* ```
|
|
635
477
|
*/
|
|
636
|
-
const parserTsx = (0, _kubb_kit.defineParser)((options
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
print(...nodes) {
|
|
642
|
-
return print(...nodes);
|
|
643
|
-
},
|
|
644
|
-
parse(file) {
|
|
645
|
-
return parser.parse(file);
|
|
646
|
-
},
|
|
647
|
-
copy: parser.copy
|
|
648
|
-
};
|
|
649
|
-
});
|
|
478
|
+
const parserTsx = (0, _kubb_kit.defineParser)((options) => ({
|
|
479
|
+
...parserTs(options),
|
|
480
|
+
name: "tsx",
|
|
481
|
+
extNames: [".tsx", ".jsx"]
|
|
482
|
+
}));
|
|
650
483
|
//#endregion
|
|
651
484
|
exports.parserTs = parserTs;
|
|
652
485
|
exports.parserTsx = parserTsx;
|