@kubb/parser-ts 5.0.0-beta.63 → 5.0.0-beta.64
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.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -4
- package/src/constants.ts +0 -57
- package/src/index.ts +0 -2
- package/src/parserTs.ts +0 -77
- package/src/parserTsx.ts +0 -37
- package/src/utils.ts +0 -486
- /package/dist/{chunk-C0LytTxp.js → rolldown-runtime-C0LytTxp.js} +0 -0
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kubb/parser-ts",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.64",
|
|
4
4
|
"description": "TypeScript and TSX source file parser for Kubb. Converts AST nodes and raw TypeScript code into formatted source strings using the TypeScript compiler API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codegen",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"directory": "packages/parser-ts"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
-
"src",
|
|
21
20
|
"dist",
|
|
22
21
|
"!/**/**.test.**",
|
|
23
22
|
"!/**/__tests__/**",
|
|
@@ -41,11 +40,11 @@
|
|
|
41
40
|
},
|
|
42
41
|
"dependencies": {
|
|
43
42
|
"typescript": "^6.0.3",
|
|
44
|
-
"@kubb/core": "5.0.0-beta.
|
|
43
|
+
"@kubb/core": "5.0.0-beta.64"
|
|
45
44
|
},
|
|
46
45
|
"devDependencies": {
|
|
47
46
|
"@internals/utils": "0.0.0",
|
|
48
|
-
"@kubb/ast": "5.0.0-beta.
|
|
47
|
+
"@kubb/ast": "5.0.0-beta.64"
|
|
49
48
|
},
|
|
50
49
|
"engines": {
|
|
51
50
|
"node": ">=22"
|
package/src/constants.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Character used for a single indent step. Set to `'\t'` to emit tab-indented output.
|
|
3
|
-
*/
|
|
4
|
-
export const INDENT_CHAR = ' '
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Number of {@link INDENT_CHAR} repeats that make up one nesting level.
|
|
8
|
-
*/
|
|
9
|
-
const INDENT_SIZE = 2 as const
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Indentation unit prepended once per nesting level when pretty-printing.
|
|
13
|
-
*/
|
|
14
|
-
export const INDENT = INDENT_CHAR.repeat(INDENT_SIZE)
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Matches only the final `.<ext>` of a path, so a name like `foo.bar.ts` keeps
|
|
18
|
-
* `foo.bar` and loses just `.ts`.
|
|
19
|
-
*/
|
|
20
|
-
export const FILE_EXTENSION_PATTERN = /\.[^/.]+$/
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Matches Windows-style backslash path separators.
|
|
24
|
-
*/
|
|
25
|
-
export const WINDOWS_PATH_SEPARATOR = /\\/g
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Matches `*\/` in free-form text so JSDoc bodies can neutralize premature
|
|
29
|
-
* comment terminators (`*\/` → `* /`).
|
|
30
|
-
*/
|
|
31
|
-
export const JSDOC_TERMINATOR_PATTERN = /\*\//g
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Matches carriage returns for normalizing CRLF/CR line endings to LF.
|
|
35
|
-
*/
|
|
36
|
-
export const CARRIAGE_RETURN_PATTERN = /\r/g
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Matches CRLF sequences used when normalizing TypeScript printer output.
|
|
40
|
-
*/
|
|
41
|
-
export const CRLF_PATTERN = /\r\n/g
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Matches an identifier that starts with a digit. JavaScript disallows this,
|
|
45
|
-
* so the printer replaces the leading digit with `_`.
|
|
46
|
-
*/
|
|
47
|
-
export const LEADING_DIGIT_PATTERN = /^\d/
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Relative path prefix used to detect traversal segments (`../`).
|
|
51
|
-
*/
|
|
52
|
-
export const PARENT_DIRECTORY_PREFIX = '../' as const
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Relative path prefix used when resolving imports within the output root.
|
|
56
|
-
*/
|
|
57
|
-
export const CURRENT_DIRECTORY_PREFIX = './' as const
|
package/src/index.ts
DELETED
package/src/parserTs.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { FileNode, SourceNode } from '@kubb/ast'
|
|
2
|
-
import { defineParser } from '@kubb/core'
|
|
3
|
-
import type * as ts from 'typescript'
|
|
4
|
-
import { getRelativePath, print, printExport, printImport, printSource, resolveOutputPath } from './utils.ts'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Default Kubb parser for `.ts` and `.js` files. Takes the universal AST
|
|
8
|
-
* produced by an adapter and prints it as TypeScript source using the official
|
|
9
|
-
* TypeScript compiler. Imports and exports are rewritten based on each file's
|
|
10
|
-
* metadata.
|
|
11
|
-
*
|
|
12
|
-
* Used automatically when no `parsers` option is set on `defineConfig`. Use
|
|
13
|
-
* `parserTsx` instead for React projects that emit JSX.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```ts
|
|
17
|
-
* import { defineConfig } from 'kubb'
|
|
18
|
-
* import { adapterOas } from '@kubb/adapter-oas'
|
|
19
|
-
* import { parserTs } from '@kubb/parser-ts'
|
|
20
|
-
*
|
|
21
|
-
* export default defineConfig({
|
|
22
|
-
* input: { path: './petStore.yaml' },
|
|
23
|
-
* output: { path: './src/gen' },
|
|
24
|
-
* adapter: adapterOas(),
|
|
25
|
-
* parsers: [parserTs],
|
|
26
|
-
* plugins: [],
|
|
27
|
-
* })
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export const parserTs = defineParser({
|
|
31
|
-
name: 'typescript',
|
|
32
|
-
extNames: ['.ts', '.js'],
|
|
33
|
-
print(...nodes: Array<ts.Node>) {
|
|
34
|
-
return print(...nodes)
|
|
35
|
-
},
|
|
36
|
-
parse(file, options = { extname: '.ts' }) {
|
|
37
|
-
const sourceParts: Array<string> = []
|
|
38
|
-
for (const item of file.sources) {
|
|
39
|
-
const sourceStr = printSource(item as SourceNode)
|
|
40
|
-
if (sourceStr) {
|
|
41
|
-
sourceParts.push(sourceStr.trimEnd())
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const source = sourceParts.join('\n\n')
|
|
45
|
-
|
|
46
|
-
const importLines: Array<string> = []
|
|
47
|
-
for (const item of (file as FileNode).imports) {
|
|
48
|
-
const importPath = item.root ? getRelativePath(item.root, item.path) : item.path
|
|
49
|
-
importLines.push(
|
|
50
|
-
printImport({
|
|
51
|
-
name: item.name as string | Array<string | { propertyName: string; name?: string }>,
|
|
52
|
-
path: resolveOutputPath(importPath, options, Boolean(item.root)),
|
|
53
|
-
isTypeOnly: item.isTypeOnly,
|
|
54
|
-
isNameSpace: item.isNameSpace,
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const exportLines: Array<string> = []
|
|
60
|
-
for (const item of (file as FileNode).exports) {
|
|
61
|
-
exportLines.push(
|
|
62
|
-
printExport({
|
|
63
|
-
name: item.name as string | Array<ts.Identifier | string> | null | undefined,
|
|
64
|
-
path: resolveOutputPath(item.path, options, true),
|
|
65
|
-
isTypeOnly: item.isTypeOnly,
|
|
66
|
-
asAlias: item.asAlias,
|
|
67
|
-
}),
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const importExportBlock = [...importLines, ...exportLines].join('\n')
|
|
72
|
-
|
|
73
|
-
const parts = [file.banner, importExportBlock, source, file.footer].filter((segment): segment is string => Boolean(segment)).map((s) => s.trimEnd())
|
|
74
|
-
|
|
75
|
-
return parts.join('\n\n')
|
|
76
|
-
},
|
|
77
|
-
})
|
package/src/parserTsx.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { defineParser } from '@kubb/core'
|
|
2
|
-
import type * as ts from 'typescript'
|
|
3
|
-
import { parserTs } from './parserTs.ts'
|
|
4
|
-
import { print } from './utils.ts'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Kubb parser for `.tsx` and `.jsx` files. Delegates to `parserTs` because the
|
|
8
|
-
* TypeScript compiler handles JSX natively via `ScriptKind.TSX`.
|
|
9
|
-
*
|
|
10
|
-
* Add to the `parsers` array on `defineConfig` when generating components for
|
|
11
|
-
* React (or any framework that emits JSX).
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* import { defineConfig } from 'kubb'
|
|
16
|
-
* import { adapterOas } from '@kubb/adapter-oas'
|
|
17
|
-
* import { parserTsx } from '@kubb/parser-ts'
|
|
18
|
-
*
|
|
19
|
-
* export default defineConfig({
|
|
20
|
-
* input: { path: './petStore.yaml' },
|
|
21
|
-
* output: { path: './src/gen' },
|
|
22
|
-
* adapter: adapterOas(),
|
|
23
|
-
* parsers: [parserTsx],
|
|
24
|
-
* plugins: [],
|
|
25
|
-
* })
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export const parserTsx = defineParser({
|
|
29
|
-
name: 'tsx',
|
|
30
|
-
extNames: ['.tsx', '.jsx'],
|
|
31
|
-
print(...nodes: Array<ts.Node>) {
|
|
32
|
-
return print(...nodes)
|
|
33
|
-
},
|
|
34
|
-
parse(file, options = { extname: '.tsx' }) {
|
|
35
|
-
return parserTs.parse(file, options)
|
|
36
|
-
},
|
|
37
|
-
})
|
package/src/utils.ts
DELETED
|
@@ -1,486 +0,0 @@
|
|
|
1
|
-
import { normalize, relative } from 'node:path'
|
|
2
|
-
import { trimExtName } from '@internals/utils'
|
|
3
|
-
import type { ArrowFunctionNode, CodeNode, ConstNode, FunctionNode, JSDocNode, JsxNode, SourceNode, TextNode, TypeNode } from '@kubb/ast'
|
|
4
|
-
import ts from 'typescript'
|
|
5
|
-
import {
|
|
6
|
-
CARRIAGE_RETURN_PATTERN,
|
|
7
|
-
CRLF_PATTERN,
|
|
8
|
-
CURRENT_DIRECTORY_PREFIX,
|
|
9
|
-
FILE_EXTENSION_PATTERN,
|
|
10
|
-
INDENT,
|
|
11
|
-
INDENT_CHAR,
|
|
12
|
-
JSDOC_TERMINATOR_PATTERN,
|
|
13
|
-
LEADING_DIGIT_PATTERN,
|
|
14
|
-
PARENT_DIRECTORY_PREFIX,
|
|
15
|
-
WINDOWS_PATH_SEPARATOR,
|
|
16
|
-
} from './constants.ts'
|
|
17
|
-
|
|
18
|
-
const { factory } = ts
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Normalizes a file-system path to POSIX separators and strips any leading `../` segment.
|
|
22
|
-
*/
|
|
23
|
-
export function slash(path: string): string {
|
|
24
|
-
return normalize(path).replaceAll(WINDOWS_PATH_SEPARATOR, '/').replace(PARENT_DIRECTORY_PREFIX, '')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Resolves `filePath` relative to `rootDir` and returns a POSIX-style path
|
|
29
|
-
* prefixed with `./` when the target sits inside the root, or `../` when it escapes it.
|
|
30
|
-
*/
|
|
31
|
-
export function getRelativePath(rootDir: string, filePath: string): string {
|
|
32
|
-
const rel = relative(rootDir, filePath)
|
|
33
|
-
const slashed = slash(rel)
|
|
34
|
-
return slashed.startsWith(PARENT_DIRECTORY_PREFIX) ? slashed : `${CURRENT_DIRECTORY_PREFIX}${slashed}`
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Rewrites an import/export path so its extension matches the caller-supplied
|
|
39
|
-
* `options.extname`. When the source path has no extension the original is kept,
|
|
40
|
-
* so virtual/module-only paths flow through unchanged.
|
|
41
|
-
*/
|
|
42
|
-
export function resolveOutputPath(path: string, options: { extname?: string } | undefined, rootAware: boolean): string {
|
|
43
|
-
const hasExtname = FILE_EXTENSION_PATTERN.test(path)
|
|
44
|
-
if (options?.extname && hasExtname) {
|
|
45
|
-
return `${trimExtName(path)}${options.extname}`
|
|
46
|
-
}
|
|
47
|
-
return rootAware ? trimExtName(path) : path
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Serializes a `nodes` array into source text. Each entry is rendered via {@link printCodeNode}
|
|
52
|
-
* and joined with a single newline. A `Break` node (`<br/>`) inserts one blank line between
|
|
53
|
-
* statements. Consecutive breaks, and breaks at the very start or end, are folded into the
|
|
54
|
-
* separator, so a double `<br/>` never emits more than one blank line.
|
|
55
|
-
*/
|
|
56
|
-
export function printNodes(nodes: Array<CodeNode> | undefined): string {
|
|
57
|
-
if (!nodes || nodes.length === 0) return ''
|
|
58
|
-
|
|
59
|
-
let result = ''
|
|
60
|
-
let hasContent = false
|
|
61
|
-
let pendingBreak = false
|
|
62
|
-
|
|
63
|
-
for (const node of nodes) {
|
|
64
|
-
if (node.kind === 'Break') {
|
|
65
|
-
if (hasContent) pendingBreak = true
|
|
66
|
-
continue
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const text = printCodeNode(node)
|
|
70
|
-
if (!text) continue
|
|
71
|
-
|
|
72
|
-
if (hasContent) result += pendingBreak ? '\n\n' : '\n'
|
|
73
|
-
result += text
|
|
74
|
-
hasContent = true
|
|
75
|
-
pendingBreak = false
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return result
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Indents every non-empty line of `text` by one indent unit. Pass a number to repeat
|
|
83
|
-
* {@link INDENT_CHAR} that many times, or a string to use as the indent verbatim.
|
|
84
|
-
*/
|
|
85
|
-
export function indentLines(text: string, indent: number | string = INDENT): string {
|
|
86
|
-
if (!text) return ''
|
|
87
|
-
const pad = typeof indent === 'string' ? indent : INDENT_CHAR.repeat(indent)
|
|
88
|
-
return text
|
|
89
|
-
.split('\n')
|
|
90
|
-
.map((line) => (line.trim() ? `${pad}${line}` : ''))
|
|
91
|
-
.join('\n')
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Removes the common leading whitespace shared by every non-blank line and trims
|
|
96
|
-
* surrounding blank lines, so multi-line content authored inside an indented template
|
|
97
|
-
* literal lines up at a column-zero baseline. Leading whitespace is counted by
|
|
98
|
-
* character, so N tabs and N spaces are treated as the same depth.
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* ```ts
|
|
102
|
-
* dedent('\n foo\n bar\n ')
|
|
103
|
-
* // 'foo\n bar'
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
export function dedent(text: string): string {
|
|
107
|
-
if (!text) return ''
|
|
108
|
-
|
|
109
|
-
const lines = text.split('\n')
|
|
110
|
-
const isBlank = (line: string) => line.trim() === ''
|
|
111
|
-
|
|
112
|
-
const start = lines.findIndex((line) => !isBlank(line))
|
|
113
|
-
if (start === -1) return ''
|
|
114
|
-
const end = lines.findLastIndex((line) => !isBlank(line))
|
|
115
|
-
|
|
116
|
-
const trimmed = lines.slice(start, end + 1)
|
|
117
|
-
const indents = trimmed.filter((line) => !isBlank(line)).map((line) => line.match(/^\s*/)?.[0].length ?? 0)
|
|
118
|
-
const min = indents.length ? Math.min(...indents) : 0
|
|
119
|
-
|
|
120
|
-
return trimmed.map((line) => (isBlank(line) ? '' : line.slice(min))).join('\n')
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Renders the generic clause (`<T, U>`) shared by function and arrow-function nodes.
|
|
125
|
-
* Accepts either a raw string (rendered verbatim) or an array of type-parameter names.
|
|
126
|
-
*/
|
|
127
|
-
export function formatGenerics(generics: FunctionNode['generics'] | ArrowFunctionNode['generics']): string {
|
|
128
|
-
if (!generics) return ''
|
|
129
|
-
return `<${Array.isArray(generics) ? generics.join(', ') : generics}>`
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Renders the return-type suffix (`: T` or `: Promise<T>` when `isAsync` is true).
|
|
134
|
-
* Returns an empty string when no return type is provided.
|
|
135
|
-
*/
|
|
136
|
-
export function formatReturnType(returnType: string | null | undefined, isAsync: boolean | null | undefined): string {
|
|
137
|
-
if (!returnType) return ''
|
|
138
|
-
return isAsync ? `: Promise<${returnType}>` : `: ${returnType}`
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Module-scoped TypeScript printer instance. A printer does not mutate the source file, so one
|
|
143
|
-
* instance is reused across every `print()` call instead of constructing a new printer each time.
|
|
144
|
-
*/
|
|
145
|
-
const TS_PRINTER = ts.createPrinter({
|
|
146
|
-
omitTrailingSemicolon: true,
|
|
147
|
-
newLine: ts.NewLineKind.LineFeed,
|
|
148
|
-
removeComments: false,
|
|
149
|
-
noEmitHelpers: true,
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Module-scoped source file used as the print target. `printList` only reads the source
|
|
154
|
-
* file's compiler options / language version. It never mutates it.
|
|
155
|
-
*/
|
|
156
|
-
const PRINT_SOURCE_FILE = ts.createSourceFile('print.tsx', '', ts.ScriptTarget.ES2022, true, ts.ScriptKind.TSX)
|
|
157
|
-
|
|
158
|
-
// Pre-warm the printer at module load. The first `printList` call lazily initializes
|
|
159
|
-
// the printer's internal string-builder and identifier tables. Doing it once at import
|
|
160
|
-
// time keeps that cost off the critical path for short-lived CLI builds.
|
|
161
|
-
TS_PRINTER.printList(ts.ListFormat.MultiLine, factory.createNodeArray([]), PRINT_SOURCE_FILE)
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Converts TypeScript/TSX AST nodes to a string using the TypeScript printer.
|
|
165
|
-
*/
|
|
166
|
-
export function print(...elements: Array<ts.Node>): string {
|
|
167
|
-
const filtered = elements.filter(Boolean)
|
|
168
|
-
if (filtered.length === 0) return ''
|
|
169
|
-
|
|
170
|
-
const output = TS_PRINTER.printList(ts.ListFormat.MultiLine, factory.createNodeArray(filtered), PRINT_SOURCE_FILE)
|
|
171
|
-
|
|
172
|
-
return output.replace(CRLF_PATTERN, '\n')
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Converts a {@link JSDocNode} to a JSDoc comment block string.
|
|
177
|
-
*
|
|
178
|
-
* @example
|
|
179
|
-
* ```ts
|
|
180
|
-
* printJSDoc({ comments: ['@description A pet', '@deprecated'] })
|
|
181
|
-
* // /**
|
|
182
|
-
* // * @description A pet
|
|
183
|
-
* // * @deprecated
|
|
184
|
-
* // *\/
|
|
185
|
-
* ```
|
|
186
|
-
*/
|
|
187
|
-
export function printJSDoc(jsDoc: JSDocNode): string {
|
|
188
|
-
const comments = (jsDoc.comments ?? []).filter((c) => c != null)
|
|
189
|
-
if (comments.length === 0) return ''
|
|
190
|
-
|
|
191
|
-
const lines = comments
|
|
192
|
-
.flatMap((c) => c.split(/\r?\n/))
|
|
193
|
-
.map((l) => l.replace(JSDOC_TERMINATOR_PATTERN, '* /').replace(CARRIAGE_RETURN_PATTERN, ''))
|
|
194
|
-
.filter((l) => l.trim().length > 0)
|
|
195
|
-
|
|
196
|
-
if (lines.length === 0) return ''
|
|
197
|
-
|
|
198
|
-
return ['/**', ...lines.map((l) => ` * ${l}`), ' */'].join('\n')
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Converts a {@link ConstNode} to a TypeScript `const` declaration string.
|
|
203
|
-
*
|
|
204
|
-
* Mirrors the `Const` component from `@kubb/renderer-jsx`.
|
|
205
|
-
*
|
|
206
|
-
* @example
|
|
207
|
-
* ```ts
|
|
208
|
-
* printConst(factory.createConst({ name: 'pet', export: true, nodes: ['{}'] }))
|
|
209
|
-
* // 'export const pet = {}'
|
|
210
|
-
* ```
|
|
211
|
-
*
|
|
212
|
-
* @example With type and `as const`
|
|
213
|
-
* ```ts
|
|
214
|
-
* printConst(factory.createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true, nodes: ['[]'] }))
|
|
215
|
-
* // 'export const pets: Pet[] = [] as const'
|
|
216
|
-
* ```
|
|
217
|
-
*/
|
|
218
|
-
export function printConst(node: ConstNode): string {
|
|
219
|
-
const { name, export: canExport, type, JSDoc, asConst, nodes } = node
|
|
220
|
-
|
|
221
|
-
const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''
|
|
222
|
-
const body = printNodes(nodes)
|
|
223
|
-
|
|
224
|
-
const parts: Array<string> = []
|
|
225
|
-
if (canExport) parts.push('export ')
|
|
226
|
-
parts.push('const ')
|
|
227
|
-
parts.push(name)
|
|
228
|
-
if (type) {
|
|
229
|
-
parts.push(`: ${type}`)
|
|
230
|
-
}
|
|
231
|
-
parts.push(' = ')
|
|
232
|
-
parts.push(body)
|
|
233
|
-
if (asConst) parts.push(' as const')
|
|
234
|
-
|
|
235
|
-
const declaration = parts.join('')
|
|
236
|
-
return [jsDocStr, declaration].filter(Boolean).join('\n')
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Converts a {@link TypeNode} to a TypeScript `type` alias declaration string.
|
|
241
|
-
*
|
|
242
|
-
* Mirrors the `Type` component from `@kubb/renderer-jsx`.
|
|
243
|
-
*
|
|
244
|
-
* @example
|
|
245
|
-
* ```ts
|
|
246
|
-
* printType(factory.createType({ name: 'Pet', export: true, nodes: ['{ id: number }'] }))
|
|
247
|
-
* // 'export type Pet = { id: number }'
|
|
248
|
-
* ```
|
|
249
|
-
*/
|
|
250
|
-
export function printType(node: TypeNode): string {
|
|
251
|
-
const { name, export: canExport, JSDoc, nodes } = node
|
|
252
|
-
|
|
253
|
-
const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''
|
|
254
|
-
const body = printNodes(nodes)
|
|
255
|
-
|
|
256
|
-
const parts: Array<string> = []
|
|
257
|
-
if (canExport) parts.push('export ')
|
|
258
|
-
parts.push('type ')
|
|
259
|
-
parts.push(name)
|
|
260
|
-
parts.push(' = ')
|
|
261
|
-
parts.push(body)
|
|
262
|
-
|
|
263
|
-
const declaration = parts.join('')
|
|
264
|
-
return [jsDocStr, declaration].filter(Boolean).join('\n')
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Converts a {@link FunctionNode} to a TypeScript `function` declaration string.
|
|
269
|
-
*
|
|
270
|
-
* Mirrors the `Function` component from `@kubb/renderer-jsx`.
|
|
271
|
-
*
|
|
272
|
-
* @example
|
|
273
|
-
* ```ts
|
|
274
|
-
* printFunction(factory.createFunction({ name: 'getPet', export: true, params: 'id: string', returnType: 'Pet', nodes: ['return fetch(id)'] }))
|
|
275
|
-
* // 'export function getPet(id: string): Pet {\n return fetch(id)\n}'
|
|
276
|
-
* ```
|
|
277
|
-
*
|
|
278
|
-
* @example Async with generics
|
|
279
|
-
* ```ts
|
|
280
|
-
* printFunction(factory.createFunction({ name: 'fetchPet', export: true, async: true, generics: ['T'], params: 'id: string', returnType: 'T' }))
|
|
281
|
-
* // 'export async function fetchPet<T>(id: string): Promise<T> {\n}'
|
|
282
|
-
* ```
|
|
283
|
-
*/
|
|
284
|
-
export function printFunction(node: FunctionNode): string {
|
|
285
|
-
const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes } = node
|
|
286
|
-
|
|
287
|
-
const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''
|
|
288
|
-
const body = printNodes(nodes)
|
|
289
|
-
const indented = body ? indentLines(body) : ''
|
|
290
|
-
|
|
291
|
-
const parts: Array<string> = []
|
|
292
|
-
if (canExport) parts.push('export ')
|
|
293
|
-
if (isDefault) parts.push('default ')
|
|
294
|
-
if (isAsync) parts.push('async ')
|
|
295
|
-
parts.push('function ')
|
|
296
|
-
parts.push(name)
|
|
297
|
-
parts.push(formatGenerics(generics))
|
|
298
|
-
parts.push(`(${params ?? ''})`)
|
|
299
|
-
parts.push(formatReturnType(returnType, isAsync))
|
|
300
|
-
parts.push(' {')
|
|
301
|
-
if (indented) {
|
|
302
|
-
parts.push(`\n${indented}\n`)
|
|
303
|
-
}
|
|
304
|
-
parts.push('}')
|
|
305
|
-
|
|
306
|
-
const declaration = parts.join('')
|
|
307
|
-
return [jsDocStr, declaration].filter(Boolean).join('\n')
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Converts an {@link ArrowFunctionNode} to a TypeScript arrow function declaration string.
|
|
312
|
-
*
|
|
313
|
-
* Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
|
|
314
|
-
*
|
|
315
|
-
* @example Multi-line arrow function
|
|
316
|
-
* ```ts
|
|
317
|
-
* printArrowFunction(factory.createArrowFunction({ name: 'getPet', export: true, params: 'id: string', nodes: ['return fetch(id)'] }))
|
|
318
|
-
* // 'export const getPet = (id: string) => {\n return fetch(id)\n}'
|
|
319
|
-
* ```
|
|
320
|
-
*
|
|
321
|
-
* @example Single-line arrow function
|
|
322
|
-
* ```ts
|
|
323
|
-
* printArrowFunction(factory.createArrowFunction({ name: 'double', params: 'n: number', singleLine: true, nodes: ['n * 2'] }))
|
|
324
|
-
* // 'const double = (n: number) => n * 2'
|
|
325
|
-
* ```
|
|
326
|
-
*/
|
|
327
|
-
export function printArrowFunction(node: ArrowFunctionNode): string {
|
|
328
|
-
const { name, default: isDefault, export: canExport, async: isAsync, generics, params, returnType, JSDoc, nodes, singleLine } = node
|
|
329
|
-
|
|
330
|
-
const jsDocStr = JSDoc ? printJSDoc(JSDoc) : ''
|
|
331
|
-
const body = printNodes(nodes)
|
|
332
|
-
const arrowBody = singleLine ? ` => ${body}` : body ? ` => {\n${indentLines(body)}\n}` : ' => {}'
|
|
333
|
-
|
|
334
|
-
const parts: Array<string> = []
|
|
335
|
-
if (canExport) parts.push('export ')
|
|
336
|
-
if (isDefault) parts.push('default ')
|
|
337
|
-
parts.push('const ')
|
|
338
|
-
parts.push(name)
|
|
339
|
-
parts.push(' = ')
|
|
340
|
-
if (isAsync) parts.push('async ')
|
|
341
|
-
parts.push(formatGenerics(generics))
|
|
342
|
-
parts.push(`(${params ?? ''})`)
|
|
343
|
-
parts.push(formatReturnType(returnType, isAsync))
|
|
344
|
-
parts.push(arrowBody)
|
|
345
|
-
|
|
346
|
-
const declaration = parts.join('')
|
|
347
|
-
return [jsDocStr, declaration].filter(Boolean).join('\n')
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Converts a {@link CodeNode} to its TypeScript string representation.
|
|
352
|
-
*
|
|
353
|
-
* Dispatches to the appropriate printer based on the node's `kind`.
|
|
354
|
-
*
|
|
355
|
-
* @example
|
|
356
|
-
* ```ts
|
|
357
|
-
* printCodeNode(factory.createConst({ name: 'x', nodes: ['1'] }))
|
|
358
|
-
* // 'const x = 1'
|
|
359
|
-
* ```
|
|
360
|
-
*/
|
|
361
|
-
export function printCodeNode(node: CodeNode): string {
|
|
362
|
-
if (node.kind === 'Break') return ''
|
|
363
|
-
if (node.kind === 'Text') return dedent((node as TextNode).value)
|
|
364
|
-
if (node.kind === 'Jsx') return dedent((node as JsxNode).value)
|
|
365
|
-
if (node.kind === 'Const') return printConst(node)
|
|
366
|
-
if (node.kind === 'Type') return printType(node)
|
|
367
|
-
if (node.kind === 'Function') return printFunction(node)
|
|
368
|
-
if (node.kind === 'ArrowFunction') return printArrowFunction(node)
|
|
369
|
-
return ''
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Converts a {@link SourceNode} to its TypeScript string representation.
|
|
374
|
-
*
|
|
375
|
-
* Iterates `nodes` in DOM order, rendering each {@link CodeNode} via
|
|
376
|
-
* {@link printCodeNode}.
|
|
377
|
-
*
|
|
378
|
-
* Top-level declarations are separated by a blank line so the source reads
|
|
379
|
-
* cleanly without an external formatter.
|
|
380
|
-
*
|
|
381
|
-
* @example From nodes
|
|
382
|
-
* ```ts
|
|
383
|
-
* printSource({ kind: 'Source', nodes: [factory.createConst({ name: 'x', nodes: [factory.createText('1')] }), factory.createText('x.toString()')] })
|
|
384
|
-
* // 'const x = 1\n\nx.toString()'
|
|
385
|
-
* ```
|
|
386
|
-
*/
|
|
387
|
-
export function printSource(node: SourceNode): string {
|
|
388
|
-
const nodes = node.nodes
|
|
389
|
-
|
|
390
|
-
if (!nodes || nodes.length === 0) return ''
|
|
391
|
-
|
|
392
|
-
return nodes
|
|
393
|
-
.map((child) => printCodeNode(child as CodeNode))
|
|
394
|
-
.filter(Boolean)
|
|
395
|
-
.join('\n\n')
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Wraps a module specifier in single quotes, escaping any embedded backslash or quote so the emitted
|
|
400
|
-
* statement stays valid even for unusual paths.
|
|
401
|
-
*/
|
|
402
|
-
function quoteModulePath(path: string): string {
|
|
403
|
-
return `'${path.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Renders an import declaration string in the repo style (single quotes, no semicolons), covering
|
|
408
|
-
* default, namespace (`* as`), and named imports with `{ a as b }` aliases, each optionally
|
|
409
|
-
* `type`-only. `path` is used verbatim, so resolve it first.
|
|
410
|
-
*
|
|
411
|
-
* @example
|
|
412
|
-
* ```ts
|
|
413
|
-
* printImport({ name: ['z'], path: './zod.ts' })
|
|
414
|
-
* // "import { z } from './zod.ts'"
|
|
415
|
-
* ```
|
|
416
|
-
*/
|
|
417
|
-
export function printImport({
|
|
418
|
-
name,
|
|
419
|
-
path,
|
|
420
|
-
isTypeOnly = false,
|
|
421
|
-
isNameSpace = false,
|
|
422
|
-
}: {
|
|
423
|
-
name: string | Array<string | { propertyName: string; name?: string }>
|
|
424
|
-
path: string
|
|
425
|
-
isTypeOnly?: boolean | null
|
|
426
|
-
isNameSpace?: boolean | null
|
|
427
|
-
}): string {
|
|
428
|
-
const typePrefix = isTypeOnly ? 'type ' : ''
|
|
429
|
-
const from = quoteModulePath(path)
|
|
430
|
-
|
|
431
|
-
if (!Array.isArray(name)) {
|
|
432
|
-
if (isNameSpace) return `import ${typePrefix}* as ${name} from ${from}`
|
|
433
|
-
return `import ${typePrefix}${name} from ${from}`
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
const specifiers = name.map((item) => {
|
|
437
|
-
if (typeof item === 'object') {
|
|
438
|
-
return item.name ? `${item.propertyName} as ${item.name}` : item.propertyName
|
|
439
|
-
}
|
|
440
|
-
return item
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
return `import ${typePrefix}{ ${specifiers.join(', ')} } from ${from}`
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Renders an export declaration string in the repo style (single quotes, no semicolons), covering
|
|
448
|
-
* named re-exports, namespace alias (`* as name`), and wildcard, each optionally `type`-only.
|
|
449
|
-
* `path` is used verbatim, so resolve it first.
|
|
450
|
-
*
|
|
451
|
-
* @example
|
|
452
|
-
* ```ts
|
|
453
|
-
* printExport({ name: ['Pet', 'Order'], path: './models.ts' })
|
|
454
|
-
* // "export { Pet, Order } from './models.ts'"
|
|
455
|
-
* ```
|
|
456
|
-
*/
|
|
457
|
-
export function printExport({
|
|
458
|
-
path,
|
|
459
|
-
name,
|
|
460
|
-
isTypeOnly = false,
|
|
461
|
-
asAlias = false,
|
|
462
|
-
}: {
|
|
463
|
-
path: string
|
|
464
|
-
name?: string | Array<ts.Identifier | string> | null
|
|
465
|
-
isTypeOnly?: boolean | null
|
|
466
|
-
asAlias?: boolean | null
|
|
467
|
-
}): string {
|
|
468
|
-
const typePrefix = isTypeOnly ? 'type ' : ''
|
|
469
|
-
const from = quoteModulePath(path)
|
|
470
|
-
|
|
471
|
-
if (Array.isArray(name)) {
|
|
472
|
-
const specifiers = name.map((item) => (typeof item === 'string' ? item : item.text))
|
|
473
|
-
return `export ${typePrefix}{ ${specifiers.join(', ')} } from ${from}`
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (asAlias && name) {
|
|
477
|
-
const parsedName = LEADING_DIGIT_PATTERN.test(name) ? `_${name.slice(1)}` : name
|
|
478
|
-
return `export ${typePrefix}* as ${parsedName} from ${from}`
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (name) {
|
|
482
|
-
console.warn(`When using name as string, asAlias should be true: ${name}`)
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
return `export ${typePrefix}* from ${from}`
|
|
486
|
-
}
|
|
File without changes
|