@kubb/renderer-jsx 5.0.0-beta.4 → 5.0.0-beta.40

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.
Files changed (45) hide show
  1. package/README.md +134 -0
  2. package/dist/index.cjs +621 -193
  3. package/dist/index.d.ts +326 -90
  4. package/dist/index.js +615 -193
  5. package/dist/jsx-dev-runtime.cjs +1 -1
  6. package/dist/jsx-dev-runtime.d.ts +7 -8
  7. package/dist/jsx-dev-runtime.js +2 -2
  8. package/dist/{jsx-namespace-CNp0arTN.d.ts → jsx-namespace-dmStM1a2.d.ts} +3 -3
  9. package/dist/{jsx-runtime-DdmO3p0U.cjs → jsx-runtime-4M1bV6ub.cjs} +9 -9
  10. package/dist/{jsx-runtime-Cvu_ZYgL.js → jsx-runtime-CQ6-_gue.js} +10 -10
  11. package/dist/jsx-runtime.cjs +1 -1
  12. package/dist/jsx-runtime.d.ts +9 -10
  13. package/dist/jsx-runtime.js +2 -2
  14. package/dist/{types-nAFMiWFw.d.ts → types-B5VGpHs0.d.ts} +11 -10
  15. package/dist/types.d.ts +1 -1
  16. package/package.json +8 -12
  17. package/src/Renderer.ts +1 -5
  18. package/src/Runtime.tsx +7 -18
  19. package/src/SyncRuntime.tsx +309 -0
  20. package/src/components/Callout.tsx +59 -0
  21. package/src/components/CodeBlock.tsx +37 -0
  22. package/src/components/Const.tsx +4 -4
  23. package/src/components/File.tsx +7 -5
  24. package/src/components/Frontmatter.tsx +38 -0
  25. package/src/components/Function.tsx +8 -8
  26. package/src/components/Heading.tsx +34 -0
  27. package/src/components/Jsx.tsx +1 -1
  28. package/src/components/List.tsx +40 -0
  29. package/src/components/Paragraph.tsx +28 -0
  30. package/src/components/Type.tsx +3 -3
  31. package/src/constants.ts +19 -9
  32. package/src/createRenderer.tsx +81 -62
  33. package/src/dom.ts +7 -19
  34. package/src/index.ts +7 -1
  35. package/src/types.ts +9 -8
  36. package/src/utils.ts +153 -174
  37. package/dist/index.cjs.map +0 -1
  38. package/dist/index.js.map +0 -1
  39. package/dist/jsx-dev-runtime.cjs.map +0 -1
  40. package/dist/jsx-dev-runtime.js.map +0 -1
  41. package/dist/jsx-runtime-Cvu_ZYgL.js.map +0 -1
  42. package/dist/jsx-runtime-DdmO3p0U.cjs.map +0 -1
  43. package/dist/jsx-runtime.cjs.map +0 -1
  44. package/dist/jsx-runtime.js.map +0 -1
  45. /package/dist/{chunk-Bb7HlUDG.js → chunk-DoukXa0m.js} +0 -0
@@ -16,12 +16,12 @@ type TypeProps = {
16
16
  * - `false` generates `type Name = …`
17
17
  * @default false
18
18
  */
19
- export?: boolean
19
+ export?: boolean | null
20
20
  /**
21
21
  * JSDoc block to prepend to the type alias declaration.
22
22
  * Each entry in `comments` becomes one line inside the emitted `/** … *\/` block.
23
23
  */
24
- JSDoc?: JSDoc
24
+ JSDoc?: JSDoc | null
25
25
  /**
26
26
  * Child nodes rendered as the type expression on the right-hand side of the alias.
27
27
  */
@@ -31,7 +31,7 @@ type TypeProps = {
31
31
  /**
32
32
  * Generates a TypeScript type alias declaration.
33
33
  *
34
- * Throws if `name` does not start with an uppercase letter TypeScript type aliases
34
+ * Throws if `name` does not start with an uppercase letter. TypeScript type aliases
35
35
  * should follow PascalCase naming conventions.
36
36
  *
37
37
  * @example Simple exported type alias
package/src/constants.ts CHANGED
@@ -5,20 +5,30 @@ import type { ElementNames } from './types.ts'
5
5
  */
6
6
  export const TEXT_NODE_NAME = '#text' as const
7
7
 
8
+ export const KUBB_FILE = 'kubb-file' as const
9
+ export const KUBB_SOURCE = 'kubb-source' as const
10
+ export const KUBB_EXPORT = 'kubb-export' as const
11
+ export const KUBB_IMPORT = 'kubb-import' as const
12
+ export const KUBB_FUNCTION = 'kubb-function' as const
13
+ export const KUBB_ARROW_FUNCTION = 'kubb-arrow-function' as const
14
+ export const KUBB_CONST = 'kubb-const' as const
15
+ export const KUBB_TYPE = 'kubb-type' as const
16
+ export const KUBB_JSX = 'kubb-jsx' as const
17
+
8
18
  /**
9
19
  * Set of all element names recognized by the Kubb renderer.
10
20
  * Used to distinguish Kubb-owned elements from unrecognized or text nodes during tree traversal.
11
21
  */
12
22
  export const nodeNames = new Set<ElementNames>([
13
- 'kubb-export',
14
- 'kubb-file',
15
- 'kubb-source',
16
- 'kubb-import',
17
- 'kubb-function',
18
- 'kubb-arrow-function',
19
- 'kubb-const',
20
- 'kubb-type',
21
- 'kubb-jsx',
23
+ KUBB_EXPORT,
24
+ KUBB_FILE,
25
+ KUBB_SOURCE,
26
+ KUBB_IMPORT,
27
+ KUBB_FUNCTION,
28
+ KUBB_ARROW_FUNCTION,
29
+ KUBB_CONST,
30
+ KUBB_TYPE,
31
+ KUBB_JSX,
22
32
  'kubb-text',
23
33
  'kubb-root',
24
34
  'kubb-app',
@@ -1,93 +1,112 @@
1
1
  import type { FileNode } from '@kubb/ast'
2
2
  import { Runtime } from './Runtime.tsx'
3
+ import { SyncRuntime } from './SyncRuntime.tsx'
3
4
  import type { KubbReactElement } from './types.ts'
4
5
 
5
- type Options = {
6
- /**
7
- * Print each render result to the console for debugging.
8
- * Useful when diagnosing output differences between renders.
9
- * @default false
10
- */
11
- debug?: boolean
12
- }
13
-
14
- /**
15
- * The renderer instance returned by {@link createRenderer}.
16
- */
17
- type Renderer = {
18
- /**
19
- * Render a JSX element tree and collect the resulting {@link FileNode} entries.
20
- * Resolves once all synchronous render work (including React's flush) is done.
21
- */
22
- render(Element: KubbReactElement): Promise<void>
23
- /**
24
- * Tear down the renderer and release all React resources.
25
- * Pass an `Error` to signal an abnormal shutdown.
26
- */
27
- unmount(error?: Error | number | null): void
28
- /**
29
- * The {@link FileNode} entries collected from the most recent `render` call.
30
- */
31
- files: Array<FileNode>
32
- }
33
-
34
6
  /**
35
- * Create a Kubb JSX renderer.
7
+ * Renderer factory that turns the JSX produced by a generator into
8
+ * `FileNode`s using React's reconciler under the hood. Pass as the `renderer`
9
+ * property on `defineGenerator`. Kubb core stays generic, with no hard
10
+ * dependency on `@kubb/renderer-jsx`.
36
11
  *
37
- * The renderer converts a React JSX element tree built from the components in this
38
- * package into an array of {@link FileNode} entries representing the generated files.
12
+ * Use this when generators rely on React features (hooks, suspense, context).
13
+ * For pure-function components, see {@link jsxRendererSync} for ~2-4× faster
14
+ * rendering.
39
15
  *
40
- * @example Basic usage
41
- * ```ts
42
- * import { createRenderer, File } from '@kubb/renderer-jsx'
16
+ * @example Wire up a JSX generator
17
+ * ```tsx
18
+ * import { defineGenerator } from '@kubb/core'
19
+ * import { jsxRenderer } from '@kubb/renderer-jsx'
43
20
  *
44
- * const renderer = createRenderer()
45
- * await renderer.render(
46
- * <File baseName="pet.ts" path="src/models/pet.ts">
47
- * <File.Source name="Pet" isExportable isIndexable>
48
- * {`export type Pet = { id: number; name: string }`}
49
- * </File.Source>
50
- * </File>
51
- * )
52
- * console.log(renderer.files) // [FileNode]
53
- * renderer.unmount()
21
+ * export const myGenerator = defineGenerator<PluginTs>({
22
+ * name: 'types',
23
+ * renderer: jsxRenderer,
24
+ * schema(node, ctx) {
25
+ * return (
26
+ * <File baseName="output.ts" path={`${ctx.root}/output.ts`}>
27
+ * <Type node={node} resolver={ctx.resolver} />
28
+ * </File>
29
+ * )
30
+ * },
31
+ * })
54
32
  * ```
55
33
  */
56
- export function createRenderer(options: Options = {}): Renderer {
57
- const runtime = new Runtime(options)
34
+ export const jsxRenderer = () => {
35
+ const runtime = new Runtime()
58
36
 
59
37
  return {
60
- async render(Element) {
61
- await runtime.render(Element)
38
+ async render(element: KubbReactElement) {
39
+ await runtime.render(element)
62
40
  },
63
41
  get files() {
64
42
  return runtime.nodes
65
43
  },
66
- unmount(error) {
44
+ dispose() {
45
+ runtime.unmount()
46
+ },
47
+ unmount(error?: Error | number | null) {
67
48
  runtime.unmount(error)
68
49
  },
50
+ [Symbol.dispose]() {
51
+ this.dispose()
52
+ },
69
53
  }
70
54
  }
71
55
 
72
56
  /**
73
- * A renderer factory for generators that produce JSX output.
57
+ * Lightweight renderer that walks the JSX tree in a single recursive pass, * no React reconciler, no scheduler. Drop-in replacement for
58
+ * {@link jsxRenderer} at roughly 2, 4× the throughput.
74
59
  *
75
- * Pass this as the `renderer` property of a `defineGenerator` call so that
76
- * core can render the JSX element tree returned by your generator methods
77
- * without a hard dependency on `@kubb/renderer-jsx`.
60
+ * Constraints: every component must be a pure function. Hooks, suspense, and
61
+ * class components are not supported.
78
62
  *
79
- * @example
80
- * ```ts
81
- * import { jsxRenderer } from '@kubb/renderer-jsx'
63
+ * Use this for generators that produce large amounts of output and do not need
64
+ * React's runtime features. It also exposes `stream()` for incremental file
65
+ * emission.
66
+ *
67
+ * @example Drop-in faster renderer
68
+ * ```tsx
82
69
  * import { defineGenerator } from '@kubb/core'
70
+ * import { jsxRendererSync } from '@kubb/renderer-jsx'
83
71
  *
84
72
  * export const myGenerator = defineGenerator<PluginTs>({
85
- * name: 'my-generator',
86
- * renderer: jsxRenderer,
87
- * schema(node, options) {
88
- * return <File baseName="output.ts" path="src/output.ts">...</File>
73
+ * name: 'types',
74
+ * renderer: jsxRendererSync,
75
+ * schema(node, ctx) {
76
+ * return (
77
+ * <File baseName="output.ts" path={`${ctx.root}/output.ts`}>
78
+ * <Type node={node} resolver={ctx.resolver} />
79
+ * </File>
80
+ * )
89
81
  * },
90
82
  * })
91
83
  * ```
84
+ *
85
+ * @example Stream files as they are produced
86
+ * ```tsx
87
+ * const renderer = jsxRendererSync()
88
+ * for (const file of renderer.stream(element)) {
89
+ * await writeFile(file.path, file.sources[0])
90
+ * }
91
+ * ```
92
92
  */
93
- export const jsxRenderer: () => Renderer = () => createRenderer()
93
+ export const jsxRendererSync = () => {
94
+ const runtime = new SyncRuntime()
95
+
96
+ return {
97
+ async render(element: KubbReactElement): Promise<void> {
98
+ runtime.render(element)
99
+ },
100
+ get files() {
101
+ return runtime.nodes
102
+ },
103
+ stream(element: KubbReactElement): Generator<FileNode> {
104
+ return runtime.stream(element)
105
+ },
106
+ dispose() {},
107
+ unmount(_error?: Error | number | null) {},
108
+ [Symbol.dispose]() {
109
+ this.dispose()
110
+ },
111
+ }
112
+ }
package/src/dom.ts CHANGED
@@ -6,14 +6,12 @@ import type { DOMElement, DOMNode, DOMNodeAttribute, TextNode } from './types.ts
6
6
  * The element has no attributes, no children, and no parent.
7
7
  */
8
8
  export const createNode = (nodeName: string): DOMElement => {
9
- const node: DOMElement = {
9
+ return {
10
10
  nodeName: nodeName as DOMElement['nodeName'],
11
- attributes: new Map(),
11
+ attributes: Object.create(null) as Record<string, DOMNodeAttribute>,
12
12
  childNodes: [],
13
- parentNode: undefined,
13
+ parentNode: null,
14
14
  }
15
-
16
- return node
17
15
  }
18
16
 
19
17
  /**
@@ -50,7 +48,6 @@ export const insertBeforeNode = (node: DOMElement, newChildNode: DOMNode, before
50
48
  const index = node.childNodes.indexOf(beforeChildNode)
51
49
  if (index >= 0) {
52
50
  node.childNodes.splice(index, 0, newChildNode)
53
-
54
51
  return
55
52
  }
56
53
 
@@ -62,7 +59,7 @@ export const insertBeforeNode = (node: DOMElement, newChildNode: DOMNode, before
62
59
  * Does nothing if `removeNode` is not a direct child of `node`.
63
60
  */
64
61
  export const removeChildNode = (node: DOMElement, removeNode: DOMNode): void => {
65
- removeNode.parentNode = undefined
62
+ removeNode.parentNode = null
66
63
 
67
64
  const index = node.childNodes.indexOf(removeNode)
68
65
  if (index >= 0) {
@@ -74,32 +71,23 @@ export const removeChildNode = (node: DOMElement, removeNode: DOMNode): void =>
74
71
  * Set an attribute on `node`, storing it in the node's `attributes` map.
75
72
  */
76
73
  export const setAttribute = (node: DOMElement, key: string, value: DOMNodeAttribute): void => {
77
- node.attributes.set(key, value)
74
+ node.attributes[key] = value
78
75
  }
79
76
 
80
77
  /**
81
78
  * Create a new {@link TextNode} with the given text value.
82
79
  */
83
80
  export const createTextNode = (text: string): TextNode => {
84
- const node: TextNode = {
81
+ return {
85
82
  nodeName: TEXT_NODE_NAME,
86
83
  nodeValue: text,
87
- parentNode: undefined,
84
+ parentNode: null,
88
85
  }
89
-
90
- setTextNodeValue(node, text)
91
-
92
- return node
93
86
  }
94
87
 
95
88
  /**
96
89
  * Update the `nodeValue` of an existing {@link TextNode}.
97
- * Non-string values are coerced to strings via `String(text)`.
98
90
  */
99
91
  export const setTextNodeValue = (node: TextNode, text: string): void => {
100
- if (typeof text !== 'string') {
101
- text = String(text)
102
- }
103
-
104
92
  node.nodeValue = text
105
93
  }
package/src/index.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  export { createContext, inject, provide, unprovide } from '@internals/utils'
2
+ export { Callout } from './components/Callout.tsx'
3
+ export { CodeBlock } from './components/CodeBlock.tsx'
2
4
  export { Const } from './components/Const.tsx'
3
5
  export { File } from './components/File.tsx'
6
+ export { Frontmatter } from './components/Frontmatter.tsx'
4
7
  export { Function } from './components/Function.tsx'
8
+ export { Heading } from './components/Heading.tsx'
5
9
  export { Jsx } from './components/Jsx.tsx'
10
+ export { List } from './components/List.tsx'
11
+ export { Paragraph } from './components/Paragraph.tsx'
6
12
  export { Root } from './components/Root.tsx'
7
13
  export { Type } from './components/Type.tsx'
8
- export { createRenderer, jsxRenderer } from './createRenderer.tsx'
14
+ export { jsxRenderer, jsxRendererSync } from './createRenderer.tsx'
package/src/types.ts CHANGED
@@ -30,14 +30,15 @@ export type ElementNames =
30
30
  | 'kubb-app'
31
31
 
32
32
  type Node = {
33
- parentNode: DOMElement | undefined
33
+ parentNode: DOMElement | null
34
34
  internal_static?: boolean
35
35
  }
36
36
 
37
37
  /**
38
38
  * Allowed attribute value types for DOM elements.
39
+ * `null` signals intentionally empty, the prop was explicitly cleared.
39
40
  */
40
- export type DOMNodeAttribute = boolean | string | number | Record<string, unknown> | Array<unknown>
41
+ export type DOMNodeAttribute = boolean | string | number | null | Record<string, unknown> | Array<unknown>
41
42
 
42
43
  type TextName = '#text'
43
44
 
@@ -50,7 +51,7 @@ export type TextNode = {
50
51
  } & Node
51
52
 
52
53
  /**
53
- * Virtual DOM node either a text node or a named element.
54
+ * Virtual DOM node, either a text node or a named element.
54
55
  */
55
56
  export type DOMNode<T = { nodeName: NodeNames }> = T extends {
56
57
  nodeName: infer U
@@ -71,11 +72,11 @@ export type DOMElement = {
71
72
  /**
72
73
  * Key/value attributes passed as JSX props to this element.
73
74
  */
74
- attributes: Map<string, DOMNodeAttribute>
75
+ attributes: Record<string, DOMNodeAttribute>
75
76
  /**
76
77
  * Ordered list of child nodes attached to this element.
77
78
  */
78
- childNodes: DOMNode[]
79
+ childNodes: Array<DOMNode>
79
80
  internal_transform?: OutputTransformer
80
81
 
81
82
  // Internal properties
@@ -119,12 +120,12 @@ export type KubbTextProps = {
119
120
  * Represents a generated file.
120
121
  */
121
122
  export type KubbFileProps = {
122
- id?: string
123
+ id?: string | null
123
124
  children?: KubbReactNode
124
125
  baseName: string
125
126
  path: string
126
- override?: boolean
127
- meta?: FileNode['meta']
127
+ override?: boolean | null
128
+ meta?: FileNode['meta'] | null
128
129
  }
129
130
 
130
131
  /**