@kubb/renderer-jsx 5.0.0-beta.3 → 5.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,17 +1,21 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_jsx_runtime = require("./jsx-runtime-DdmO3p0U.cjs");
3
3
  const require_jsx_runtime$1 = require("./jsx-runtime.cjs");
4
+ let yaml = require("yaml");
4
5
  let _kubb_ast = require("@kubb/ast");
5
6
  //#region ../../internals/utils/src/context.ts
6
7
  /**
7
- * Context stack for tracking the current context values
8
+ * Context stack for tracking the current context values.
9
+ *
10
+ * WeakMap keyed by symbol so entries are GC-eligible once no external code
11
+ * holds a reference to the context key — important for long-running agent
12
+ * builds where plugins create and discard context keys across repeated runs.
8
13
  *
9
- * Note: This uses a global Map for simplicity in code generation scenarios.
10
14
  * For concurrent runtime execution, consider using AsyncLocalStorage or
11
15
  * instance-based context management.
12
16
  */
13
- const contextStack = /* @__PURE__ */ new Map();
14
- const contextDefaults = /* @__PURE__ */ new Map();
17
+ const contextStack = /* @__PURE__ */ new WeakMap();
18
+ const contextDefaults = /* @__PURE__ */ new WeakMap();
15
19
  /**
16
20
  * Provides a value to descendant components (Vue 3 style)
17
21
  *
@@ -106,6 +110,64 @@ function onProcessExit(callback) {
106
110
  return unsubscribe;
107
111
  }
108
112
  //#endregion
113
+ //#region src/components/Callout.tsx
114
+ const CALLOUT_LABEL = {
115
+ tip: "TIP",
116
+ note: "NOTE",
117
+ important: "IMPORTANT",
118
+ warning: "WARNING",
119
+ caution: "CAUTION"
120
+ };
121
+ /**
122
+ * Renders a GitHub-style alert callout — portable across GitHub, GitLab,
123
+ * VitePress, Obsidian, and MDX.
124
+ *
125
+ * Emits a `<File.Source>` block containing `> [!TYPE] Title` followed by the
126
+ * body with every line prefixed by `> `.
127
+ *
128
+ * @example
129
+ * ```tsx
130
+ * <Callout type="tip">Run `kubb start --watch` to keep the generator hot.</Callout>
131
+ * // > [!TIP]
132
+ * // > Run `kubb start --watch` to keep the generator hot.
133
+ *
134
+ * <Callout type="warning" title="Heads up">Breaking change in v6.</Callout>
135
+ * // > [!WARNING] Heads up
136
+ * // > Breaking change in v6.
137
+ * ```
138
+ */
139
+ function Callout({ type, title, children }) {
140
+ const label = CALLOUT_LABEL[type];
141
+ return /* @__PURE__ */ require_jsx_runtime$1.jsx("kubb-source", {
142
+ name: "callout",
143
+ children: `${title ? `> [!${label}] ${title}` : `> [!${label}]`}\n${children.trimEnd().split("\n").map((line) => line.length > 0 ? `> ${line}` : ">").join("\n")}`
144
+ });
145
+ }
146
+ Callout.displayName = "Callout";
147
+ //#endregion
148
+ //#region src/components/CodeBlock.tsx
149
+ /**
150
+ * Renders a fenced markdown code block.
151
+ *
152
+ * Emits a `<File.Source>` block containing the children wrapped in
153
+ * triple-backtick fences with an optional language tag.
154
+ *
155
+ * @example
156
+ * ```tsx
157
+ * <CodeBlock lang="typescript">{'const pet = { id: 1 }'}</CodeBlock>
158
+ * // ```typescript
159
+ * // const pet = { id: 1 }
160
+ * // ```
161
+ * ```
162
+ */
163
+ function CodeBlock({ lang, children }) {
164
+ return /* @__PURE__ */ require_jsx_runtime$1.jsx("kubb-source", {
165
+ name: "codeBlock",
166
+ children: `\`\`\`${lang ?? ""}\n${children}\n\`\`\``
167
+ });
168
+ }
169
+ CodeBlock.displayName = "CodeBlock";
170
+ //#endregion
109
171
  //#region src/components/Const.tsx
110
172
  /**
111
173
  * Generates a TypeScript constant declaration.
@@ -260,6 +322,33 @@ File.Export = FileExport;
260
322
  File.Import = FileImport;
261
323
  File.Source = FileSource;
262
324
  //#endregion
325
+ //#region src/components/Frontmatter.tsx
326
+ /**
327
+ * Emits a YAML frontmatter envelope at the top of a generated markdown file.
328
+ *
329
+ * Renders a `<File.Source>` block containing `---\n<yaml>\n---`. Place it as
330
+ * the first child of `<File>` so it appears at the top of the output. Pair with
331
+ * `parserMd` to write `.md` files that downstream tooling (VitePress, MDX,
332
+ * static-site generators) treats as frontmatter.
333
+ *
334
+ * @example Page frontmatter at the top of a generated markdown file
335
+ * ```tsx
336
+ * <File baseName="pets.md" path="src/pets.md">
337
+ * <Frontmatter data={{ title: 'Pets', layout: 'doc' }} />
338
+ * <File.Source>
339
+ * {'# Pets\n\nList of pets.'}
340
+ * </File.Source>
341
+ * </File>
342
+ * ```
343
+ */
344
+ function Frontmatter({ data }) {
345
+ return /* @__PURE__ */ require_jsx_runtime$1.jsx("kubb-source", {
346
+ name: "frontmatter",
347
+ children: `---\n${(0, yaml.stringify)(data).trimEnd()}\n---`
348
+ });
349
+ }
350
+ Frontmatter.displayName = "Frontmatter";
351
+ //#endregion
263
352
  //#region src/components/Function.tsx
264
353
  /**
265
354
  * Generates a TypeScript function declaration.
@@ -321,6 +410,27 @@ function ArrowFunction({ children, ...props }) {
321
410
  ArrowFunction.displayName = "ArrowFunction";
322
411
  Function$1.Arrow = ArrowFunction;
323
412
  //#endregion
413
+ //#region src/components/Heading.tsx
414
+ /**
415
+ * Renders an ATX-style markdown heading.
416
+ *
417
+ * Emits a `<File.Source>` block containing `${'#'.repeat(level)} ${children}`.
418
+ * Use inside a `<File>` rendered by `parserMd`.
419
+ *
420
+ * @example
421
+ * ```tsx
422
+ * <Heading level={2}>Installation</Heading>
423
+ * // ## Installation
424
+ * ```
425
+ */
426
+ function Heading({ level, children }) {
427
+ return /* @__PURE__ */ require_jsx_runtime$1.jsx("kubb-source", {
428
+ name: "heading",
429
+ children: `${"#".repeat(level)} ${children}`
430
+ });
431
+ }
432
+ Heading.displayName = "Heading";
433
+ //#endregion
324
434
  //#region src/components/Jsx.tsx
325
435
  /**
326
436
  * Embeds a raw JSX string verbatim in the generated source code.
@@ -342,8 +452,55 @@ function Jsx({ children }) {
342
452
  }
343
453
  Jsx.displayName = "Jsx";
344
454
  //#endregion
455
+ //#region src/components/List.tsx
456
+ /**
457
+ * Renders a markdown list.
458
+ *
459
+ * Emits a `<File.Source>` block containing one entry per line, prefixed with
460
+ * `1.` / `2.` … when `ordered`, or `-` otherwise.
461
+ *
462
+ * @example
463
+ * ```tsx
464
+ * <List items={['Add the parser', 'Render the page']} />
465
+ * // - Add the parser
466
+ * // - Render the page
467
+ *
468
+ * <List ordered items={['First', 'Second']} />
469
+ * // 1. First
470
+ * // 2. Second
471
+ * ```
472
+ */
473
+ function List({ ordered, items }) {
474
+ return /* @__PURE__ */ require_jsx_runtime$1.jsx("kubb-source", {
475
+ name: "list",
476
+ children: items.map((item, index) => `${ordered ? `${index + 1}.` : "-"} ${item}`).join("\n")
477
+ });
478
+ }
479
+ List.displayName = "List";
480
+ //#endregion
481
+ //#region src/components/Paragraph.tsx
482
+ /**
483
+ * Renders a markdown paragraph.
484
+ *
485
+ * Emits a `<File.Source>` block containing the text as-is. Paragraphs are
486
+ * separated from surrounding blocks by blank lines via the parser's source
487
+ * joining.
488
+ *
489
+ * @example
490
+ * ```tsx
491
+ * <Paragraph>{'A pet object with `id` and `name` fields.'}</Paragraph>
492
+ * ```
493
+ */
494
+ function Paragraph({ children }) {
495
+ return /* @__PURE__ */ require_jsx_runtime$1.jsx("kubb-source", {
496
+ name: "paragraph",
497
+ children
498
+ });
499
+ }
500
+ Paragraph.displayName = "Paragraph";
501
+ //#endregion
345
502
  //#region src/components/Root.tsx
346
- var import_react = require_jsx_runtime.require_react();
503
+ var import_react = /* @__PURE__ */ require_jsx_runtime.__toESM(require_jsx_runtime.require_react());
347
504
  var ErrorBoundary = class extends import_react.Component {
348
505
  state = { hasError: false };
349
506
  static displayName = "ErrorBoundary";
@@ -481,9 +638,9 @@ const nodeNames = new Set([
481
638
  const createNode = (nodeName) => {
482
639
  return {
483
640
  nodeName,
484
- attributes: /* @__PURE__ */ new Map(),
641
+ attributes: Object.create(null),
485
642
  childNodes: [],
486
- parentNode: void 0
643
+ parentNode: null
487
644
  };
488
645
  };
489
646
  /**
@@ -521,7 +678,7 @@ const insertBeforeNode = (node, newChildNode, beforeChildNode) => {
521
678
  * Does nothing if `removeNode` is not a direct child of `node`.
522
679
  */
523
680
  const removeChildNode = (node, removeNode) => {
524
- removeNode.parentNode = void 0;
681
+ removeNode.parentNode = null;
525
682
  const index = node.childNodes.indexOf(removeNode);
526
683
  if (index >= 0) node.childNodes.splice(index, 1);
527
684
  };
@@ -529,26 +686,22 @@ const removeChildNode = (node, removeNode) => {
529
686
  * Set an attribute on `node`, storing it in the node's `attributes` map.
530
687
  */
531
688
  const setAttribute = (node, key, value) => {
532
- node.attributes.set(key, value);
689
+ node.attributes[key] = value;
533
690
  };
534
691
  /**
535
692
  * Create a new {@link TextNode} with the given text value.
536
693
  */
537
694
  const createTextNode = (text) => {
538
- const node = {
695
+ return {
539
696
  nodeName: TEXT_NODE_NAME,
540
697
  nodeValue: text,
541
- parentNode: void 0
698
+ parentNode: null
542
699
  };
543
- setTextNodeValue(node, text);
544
- return node;
545
700
  };
546
701
  /**
547
702
  * Update the `nodeValue` of an existing {@link TextNode}.
548
- * Non-string values are coerced to strings via `String(text)`.
549
703
  */
550
704
  const setTextNodeValue = (node, text) => {
551
- if (typeof text !== "string") text = String(text);
552
705
  node.nodeValue = text;
553
706
  };
554
707
  //#endregion
@@ -17820,7 +17973,7 @@ const Renderer = (0, import_react_reconciler.default)({
17820
17973
  return false;
17821
17974
  },
17822
17975
  supportsMutation: true,
17823
- isPrimaryRenderer: true,
17976
+ isPrimaryRenderer: false,
17824
17977
  supportsPersistence: false,
17825
17978
  supportsHydration: false,
17826
17979
  scheduleTimeout: setTimeout,
@@ -17886,20 +18039,24 @@ const Renderer = (0, import_react_reconciler.default)({
17886
18039
  });
17887
18040
  //#endregion
17888
18041
  //#region src/utils.ts
18042
+ function toBool$1(val) {
18043
+ return val ?? false;
18044
+ }
18045
+ require_jsx_runtime.__name(toBool$1, "toBool");
17889
18046
  /**
17890
18047
  * Collect the text and nested AST-node children of a single kubb-* element.
17891
18048
  *
17892
- * `#text` children become raw {@link TextNode}s; nested `kubb-function`, `kubb-const`,
18049
+ * `#text` children become raw text nodes; nested `kubb-function`, `kubb-const`,
17893
18050
  * `kubb-type`, and similar elements are converted into their respective {@link CodeNode}s.
17894
- * Any unrecognized DOM elements are silently skipped.
18051
+ * Any unrecognized element names are silently skipped.
17895
18052
  */
17896
- function collectChildNodes(element) {
18053
+ function collectCodeNodes$1(element) {
17897
18054
  const result = [];
17898
18055
  for (const child of element.childNodes) {
17899
18056
  if (!child) continue;
17900
18057
  if (child.nodeName === "#text") {
17901
18058
  const text = child.nodeValue;
17902
- if (text && text.trim().length > 0) result.push((0, _kubb_ast.createText)(text));
18059
+ if (text && text.trim()) result.push((0, _kubb_ast.createText)(text));
17903
18060
  continue;
17904
18061
  }
17905
18062
  if (child.nodeName === "br") {
@@ -17909,164 +18066,162 @@ function collectChildNodes(element) {
17909
18066
  if (child.nodeName === "kubb-function") {
17910
18067
  const attrs = child.attributes;
17911
18068
  result.push((0, _kubb_ast.createFunction)({
17912
- name: attrs.get("name"),
17913
- params: attrs.get("params"),
17914
- export: attrs.get("export"),
17915
- default: attrs.get("default"),
17916
- async: attrs.get("async"),
17917
- generics: attrs.get("generics"),
17918
- returnType: attrs.get("returnType"),
17919
- JSDoc: attrs.get("JSDoc"),
17920
- nodes: collectChildNodes(child)
18069
+ name: attrs["name"],
18070
+ params: attrs["params"],
18071
+ export: attrs["export"],
18072
+ default: attrs["default"],
18073
+ async: attrs["async"],
18074
+ generics: attrs["generics"],
18075
+ returnType: attrs["returnType"],
18076
+ JSDoc: attrs["JSDoc"],
18077
+ nodes: collectCodeNodes$1(child)
17921
18078
  }));
17922
- } else if (child.nodeName === "kubb-arrow-function") {
18079
+ continue;
18080
+ }
18081
+ if (child.nodeName === "kubb-arrow-function") {
17923
18082
  const attrs = child.attributes;
17924
18083
  result.push((0, _kubb_ast.createArrowFunction)({
17925
- name: attrs.get("name"),
17926
- params: attrs.get("params"),
17927
- export: attrs.get("export"),
17928
- default: attrs.get("default"),
17929
- async: attrs.get("async"),
17930
- generics: attrs.get("generics"),
17931
- returnType: attrs.get("returnType"),
17932
- singleLine: attrs.get("singleLine"),
17933
- JSDoc: attrs.get("JSDoc"),
17934
- nodes: collectChildNodes(child)
18084
+ name: attrs["name"],
18085
+ params: attrs["params"],
18086
+ export: attrs["export"],
18087
+ default: attrs["default"],
18088
+ async: attrs["async"],
18089
+ generics: attrs["generics"],
18090
+ returnType: attrs["returnType"],
18091
+ singleLine: attrs["singleLine"],
18092
+ JSDoc: attrs["JSDoc"],
18093
+ nodes: collectCodeNodes$1(child)
17935
18094
  }));
17936
- } else if (child.nodeName === "kubb-const") {
18095
+ continue;
18096
+ }
18097
+ if (child.nodeName === "kubb-const") {
17937
18098
  const attrs = child.attributes;
17938
18099
  result.push((0, _kubb_ast.createConst)({
17939
- name: attrs.get("name"),
17940
- type: attrs.get("type"),
17941
- export: attrs.get("export"),
17942
- asConst: attrs.get("asConst"),
17943
- JSDoc: attrs.get("JSDoc"),
17944
- nodes: collectChildNodes(child)
18100
+ name: attrs["name"],
18101
+ type: attrs["type"],
18102
+ export: attrs["export"],
18103
+ asConst: attrs["asConst"],
18104
+ JSDoc: attrs["JSDoc"],
18105
+ nodes: collectCodeNodes$1(child)
17945
18106
  }));
17946
- } else if (child.nodeName === "kubb-type") {
18107
+ continue;
18108
+ }
18109
+ if (child.nodeName === "kubb-type") {
17947
18110
  const attrs = child.attributes;
17948
18111
  result.push((0, _kubb_ast.createType)({
17949
- name: attrs.get("name"),
17950
- export: attrs.get("export"),
17951
- JSDoc: attrs.get("JSDoc"),
17952
- nodes: collectChildNodes(child)
18112
+ name: attrs["name"],
18113
+ export: attrs["export"],
18114
+ JSDoc: attrs["JSDoc"],
18115
+ nodes: collectCodeNodes$1(child)
17953
18116
  }));
17954
- } else if (child.nodeName === "kubb-jsx") {
18117
+ continue;
18118
+ }
18119
+ if (child.nodeName === "kubb-jsx") {
17955
18120
  const textChild = child.childNodes[0];
17956
18121
  const value = textChild?.nodeName === "#text" ? textChild.nodeValue : "";
17957
18122
  if (value) result.push((0, _kubb_ast.createJsx)(value));
18123
+ continue;
17958
18124
  }
17959
18125
  }
17960
18126
  return result;
17961
18127
  }
18128
+ require_jsx_runtime.__name(collectCodeNodes$1, "collectCodeNodes");
17962
18129
  /**
17963
- * Traverse `node` and collect all `<kubb-source>` elements into a `Set<SourceNode>`.
18130
+ * Yields every {@link SourceNode}, {@link ExportNode}, and {@link ImportNode}
18131
+ * within a `<kubb-file>` subtree in a single tree walk.
17964
18132
  *
17965
- * Elements whose `nodeName` is in `ignores` are skipped entirely (including their subtrees).
17966
- * This is used to collect source blocks from a file node while excluding import/export subtrees.
17967
- *
17968
- * @example Collect sources while ignoring export and import elements
17969
- * ```ts
17970
- * const sources = squashSourceNodes(fileElement, ['kubb-export', 'kubb-import'])
17971
- * ```
18133
+ * Import and export elements are leaf nodes. Once yielded, the walker does not
18134
+ * recurse into them, which also prevents source collection from descending into
18135
+ * their subtrees. Dispatch on `.kind` (`'Source'`, `'Export'`, `'Import'`) to
18136
+ * separate the results.
17972
18137
  */
17973
- function squashSourceNodes(node, ignores) {
17974
- const ignoreSet = new Set(ignores);
17975
- const sources = /* @__PURE__ */ new Set();
17976
- const walk = (current) => {
17977
- for (const child of current.childNodes) {
17978
- if (!child) continue;
17979
- if (child.nodeName !== "#text" && ignoreSet.has(child.nodeName)) continue;
17980
- if (child.nodeName === "kubb-source") {
17981
- const source = (0, _kubb_ast.createSource)({
17982
- name: child.attributes.get("name")?.toString(),
17983
- isTypeOnly: child.attributes.get("isTypeOnly") ?? false,
17984
- isExportable: child.attributes.get("isExportable") ?? false,
17985
- isIndexable: child.attributes.get("isIndexable") ?? false,
17986
- nodes: collectChildNodes(child)
17987
- });
17988
- sources.add(source);
17989
- continue;
17990
- }
17991
- if (child.nodeName !== "#text" && nodeNames.has(child.nodeName)) walk(child);
18138
+ function* collectFileEntries(node) {
18139
+ for (const child of node.childNodes) {
18140
+ if (!child || child.nodeName === "#text") continue;
18141
+ if (child.nodeName === "kubb-source") {
18142
+ yield (0, _kubb_ast.createSource)({
18143
+ name: child.attributes["name"]?.toString(),
18144
+ isTypeOnly: toBool$1(child.attributes["isTypeOnly"]),
18145
+ isExportable: toBool$1(child.attributes["isExportable"]),
18146
+ isIndexable: toBool$1(child.attributes["isIndexable"]),
18147
+ nodes: collectCodeNodes$1(child)
18148
+ });
18149
+ continue;
17992
18150
  }
17993
- };
17994
- walk(node);
17995
- return sources;
18151
+ if (child.nodeName === "kubb-export") {
18152
+ yield (0, _kubb_ast.createExport)({
18153
+ name: child.attributes["name"],
18154
+ path: child.attributes["path"],
18155
+ isTypeOnly: toBool$1(child.attributes["isTypeOnly"]),
18156
+ asAlias: toBool$1(child.attributes["asAlias"])
18157
+ });
18158
+ continue;
18159
+ }
18160
+ if (child.nodeName === "kubb-import") {
18161
+ yield (0, _kubb_ast.createImport)({
18162
+ name: child.attributes["name"],
18163
+ path: child.attributes["path"],
18164
+ root: child.attributes["root"],
18165
+ isTypeOnly: toBool$1(child.attributes["isTypeOnly"]),
18166
+ isNameSpace: toBool$1(child.attributes["isNameSpace"])
18167
+ });
18168
+ continue;
18169
+ }
18170
+ if (nodeNames.has(child.nodeName)) yield* collectFileEntries(child);
18171
+ }
17996
18172
  }
17997
18173
  /**
17998
- * Traverse `node` and collect all `<kubb-export>` elements into a `Set<ExportNode>`.
18174
+ * Runs a single {@link collectFileEntries} pass over a `<kubb-file>` DOM element
18175
+ * and assembles the result into a {@link FileNode}, bucketing each yielded
18176
+ * node by its `.kind`.
17999
18177
  */
18000
- function squashExportNodes(node) {
18001
- const exports = /* @__PURE__ */ new Set();
18002
- const walk = (current) => {
18003
- for (const child of current.childNodes) {
18004
- if (!child) continue;
18005
- if (child.nodeName !== "#text" && nodeNames.has(child.nodeName)) walk(child);
18006
- if (child.nodeName === "kubb-export") exports.add((0, _kubb_ast.createExport)({
18007
- name: child.attributes.get("name"),
18008
- path: child.attributes.get("path"),
18009
- isTypeOnly: child.attributes.get("isTypeOnly") ?? false,
18010
- asAlias: child.attributes.get("asAlias") ?? false
18011
- }));
18178
+ function createFileNode(child) {
18179
+ const sources = [];
18180
+ const exports = [];
18181
+ const imports = [];
18182
+ for (const node of collectFileEntries(child)) {
18183
+ if (node.kind === "Source") {
18184
+ sources.push(node);
18185
+ continue;
18186
+ }
18187
+ if (node.kind === "Export") {
18188
+ exports.push(node);
18189
+ continue;
18012
18190
  }
18191
+ imports.push(node);
18192
+ }
18193
+ return {
18194
+ baseName: child.attributes["baseName"],
18195
+ path: child.attributes["path"],
18196
+ meta: child.attributes["meta"] || {},
18197
+ footer: child.attributes["footer"],
18198
+ banner: child.attributes["banner"],
18199
+ sources,
18200
+ exports,
18201
+ imports
18013
18202
  };
18014
- walk(node);
18015
- return exports;
18016
18203
  }
18017
18204
  /**
18018
- * Traverse `node` and collect all `<kubb-import>` elements into a `Set<ImportNode>`.
18205
+ * Yields each {@link FileNode} as it is encountered during the tree walk,
18206
+ * without collecting into an intermediate array. Callers can begin processing
18207
+ * each file before the rest of the tree is traversed.
18019
18208
  */
18020
- function squashImportNodes(node) {
18021
- const imports = /* @__PURE__ */ new Set();
18022
- const walk = (current) => {
18023
- for (const child of current.childNodes) {
18024
- if (!child) continue;
18025
- if (child.nodeName !== "#text" && nodeNames.has(child.nodeName)) walk(child);
18026
- if (child.nodeName === "kubb-import") imports.add((0, _kubb_ast.createImport)({
18027
- name: child.attributes.get("name"),
18028
- path: child.attributes.get("path"),
18029
- root: child.attributes.get("root"),
18030
- isTypeOnly: child.attributes.get("isTypeOnly") ?? false,
18031
- isNameSpace: child.attributes.get("isNameSpace") ?? false
18032
- }));
18033
- }
18034
- };
18035
- walk(node);
18036
- return imports;
18209
+ function* streamFiles(node) {
18210
+ for (const child of node.childNodes) {
18211
+ if (!child) continue;
18212
+ if (child.nodeName !== "#text" && child.nodeName !== "kubb-file" && nodeNames.has(child.nodeName)) yield* streamFiles(child);
18213
+ if (child.nodeName === "kubb-file" && child.attributes["baseName"] !== void 0 && child.attributes["path"] !== void 0) yield createFileNode(child);
18214
+ }
18037
18215
  }
18038
18216
  /**
18039
18217
  * Walk the virtual DOM tree rooted at `node` and convert every `<kubb-file>` element
18040
18218
  * into a {@link FileNode}, collecting its source blocks, imports, and exports.
18041
18219
  *
18042
- * Returns the list of file nodes in document order. Nested files are supported
18220
+ * Returns the list of file nodes in document order. Nested files are supported;
18043
18221
  * the walker descends into non-file elements and recurses through them.
18044
18222
  */
18045
- function processFiles(node) {
18046
- const collected = [];
18047
- function walk(current) {
18048
- for (const child of current.childNodes) {
18049
- if (!child) continue;
18050
- if (child.nodeName !== "#text" && child.nodeName !== "kubb-file" && nodeNames.has(child.nodeName)) walk(child);
18051
- if (child.nodeName === "kubb-file") {
18052
- if (child.attributes.has("baseName") && child.attributes.has("path")) {
18053
- const sources = squashSourceNodes(child, ["kubb-export", "kubb-import"]);
18054
- collected.push({
18055
- baseName: child.attributes.get("baseName"),
18056
- path: child.attributes.get("path"),
18057
- meta: child.attributes.get("meta") || {},
18058
- footer: child.attributes.get("footer"),
18059
- banner: child.attributes.get("banner"),
18060
- sources: [...sources],
18061
- exports: [...squashExportNodes(child)],
18062
- imports: [...squashImportNodes(child)]
18063
- });
18064
- }
18065
- }
18066
- }
18067
- }
18068
- walk(node);
18069
- return collected;
18223
+ function collectFiles(node) {
18224
+ return [...streamFiles(node)];
18070
18225
  }
18071
18226
  //#endregion
18072
18227
  //#region src/Runtime.tsx
@@ -18078,7 +18233,7 @@ var Runtime = class {
18078
18233
  nodes = [];
18079
18234
  #container;
18080
18235
  #rootNode;
18081
- constructor(options) {
18236
+ constructor(options = {}) {
18082
18237
  this.#options = options;
18083
18238
  this.#rootNode = createNode("kubb-root");
18084
18239
  this.#rootNode.onRender = this.onRender;
@@ -18091,7 +18246,7 @@ var Runtime = class {
18091
18246
  console.log(data);
18092
18247
  };
18093
18248
  const logRecoverableError = typeof reportError === "function" ? reportError : console.error;
18094
- const rootTag = import_constants.ConcurrentRoot;
18249
+ const rootTag = import_constants.LegacyRoot;
18095
18250
  this.#container = Renderer.createContainer(this.#rootNode, rootTag, null, false, false, "id", logRecoverableError, logRecoverableError, logRecoverableError, null);
18096
18251
  this.unsubscribeExit = onProcessExit((code) => {
18097
18252
  this.unmount(code);
@@ -18104,7 +18259,7 @@ var Runtime = class {
18104
18259
  onRender = () => {
18105
18260
  const task = this.#renderPromise.catch(() => {}).then(() => {
18106
18261
  if (this.#isUnmounted) return;
18107
- const files = processFiles(this.#rootNode);
18262
+ const files = collectFiles(this.#rootNode);
18108
18263
  this.nodes.push(...files);
18109
18264
  if (!this.#options?.debug) return;
18110
18265
  });
@@ -18149,85 +18304,359 @@ var Runtime = class {
18149
18304
  }
18150
18305
  this.resolveExitPromise();
18151
18306
  }
18152
- async waitUntilExit() {
18153
- if (!this.exitPromise) this.exitPromise = new Promise((resolve, reject) => {
18154
- this.resolveExitPromise = resolve;
18155
- this.rejectExitPromise = reject;
18156
- });
18157
- return this.exitPromise;
18307
+ };
18308
+ //#endregion
18309
+ //#region src/SyncRuntime.tsx
18310
+ /**
18311
+ * Walks `element`, resolving arrays, Fragments, and function components
18312
+ * transparently, then calls `onText` for primitive values and `onHost` for
18313
+ * every host element encountered. Pure function components are called
18314
+ * synchronously; hooks and class components are not supported.
18315
+ */
18316
+ function walkElement(element, onText, onHost) {
18317
+ if (element == null || typeof element === "boolean") return;
18318
+ if (typeof element === "string" || typeof element === "number" || typeof element === "bigint") {
18319
+ onText(String(element));
18320
+ return;
18321
+ }
18322
+ if (Array.isArray(element)) {
18323
+ for (const child of element) walkElement(child, onText, onHost);
18324
+ return;
18325
+ }
18326
+ if (typeof element === "object" && "$$typeof" in element) {
18327
+ const el = element;
18328
+ const { type } = el;
18329
+ const props = el.props;
18330
+ if (type === import_react.Fragment) {
18331
+ walkElement(props["children"], onText, onHost);
18332
+ return;
18333
+ }
18334
+ if (typeof type === "function") {
18335
+ walkElement(type(props), onText, onHost);
18336
+ return;
18337
+ }
18338
+ if (typeof type === "string") onHost(type, props);
18339
+ }
18340
+ }
18341
+ function toBool(val) {
18342
+ return val ?? false;
18343
+ }
18344
+ function collectCodeNodes(props) {
18345
+ const nodes = [];
18346
+ collectCode(props["children"], nodes);
18347
+ return nodes;
18348
+ }
18349
+ function collectCode(element, nodes) {
18350
+ walkElement(element, (text) => {
18351
+ if (text.trim()) nodes.push((0, _kubb_ast.createText)(text));
18352
+ }, (type, props) => resolveCodeNode(type, props, nodes));
18353
+ }
18354
+ function resolveCodeNode(type, props, nodes) {
18355
+ if (type === "br") {
18356
+ nodes.push((0, _kubb_ast.createBreak)());
18357
+ return;
18358
+ }
18359
+ if (type === "kubb-jsx") {
18360
+ let value = "";
18361
+ walkElement(props["children"], (t) => {
18362
+ value += t;
18363
+ }, () => {});
18364
+ if (value) nodes.push((0, _kubb_ast.createJsx)(value));
18365
+ return;
18366
+ }
18367
+ if (type === "kubb-function") {
18368
+ nodes.push((0, _kubb_ast.createFunction)({
18369
+ name: props["name"],
18370
+ params: props["params"],
18371
+ export: props["export"],
18372
+ default: props["default"],
18373
+ async: props["async"],
18374
+ generics: props["generics"],
18375
+ returnType: props["returnType"],
18376
+ JSDoc: props["JSDoc"],
18377
+ nodes: collectCodeNodes(props)
18378
+ }));
18379
+ return;
18380
+ }
18381
+ if (type === "kubb-arrow-function") {
18382
+ nodes.push((0, _kubb_ast.createArrowFunction)({
18383
+ name: props["name"],
18384
+ params: props["params"],
18385
+ export: props["export"],
18386
+ default: props["default"],
18387
+ async: props["async"],
18388
+ generics: props["generics"],
18389
+ returnType: props["returnType"],
18390
+ singleLine: props["singleLine"],
18391
+ JSDoc: props["JSDoc"],
18392
+ nodes: collectCodeNodes(props)
18393
+ }));
18394
+ return;
18395
+ }
18396
+ if (type === "kubb-const") {
18397
+ nodes.push((0, _kubb_ast.createConst)({
18398
+ name: props["name"],
18399
+ type: props["type"],
18400
+ export: props["export"],
18401
+ asConst: props["asConst"],
18402
+ JSDoc: props["JSDoc"],
18403
+ nodes: collectCodeNodes(props)
18404
+ }));
18405
+ return;
18406
+ }
18407
+ if (type === "kubb-type") {
18408
+ nodes.push((0, _kubb_ast.createType)({
18409
+ name: props["name"],
18410
+ export: props["export"],
18411
+ JSDoc: props["JSDoc"],
18412
+ nodes: collectCodeNodes(props)
18413
+ }));
18414
+ return;
18415
+ }
18416
+ }
18417
+ function collectFileChildren(element) {
18418
+ const sources = [];
18419
+ const exports = [];
18420
+ const imports = [];
18421
+ walkElement(element, (text) => {
18422
+ if (text.trim()) throw new Error(`[react] '${text}' should be part of <File.Source> component when using the <File/> component`);
18423
+ }, (type, props) => {
18424
+ if (type === "kubb-source") {
18425
+ sources.push((0, _kubb_ast.createSource)({
18426
+ name: props["name"]?.toString(),
18427
+ isTypeOnly: toBool(props["isTypeOnly"]),
18428
+ isExportable: toBool(props["isExportable"]),
18429
+ isIndexable: toBool(props["isIndexable"]),
18430
+ nodes: collectCodeNodes(props)
18431
+ }));
18432
+ return;
18433
+ }
18434
+ if (type === "kubb-export") {
18435
+ exports.push((0, _kubb_ast.createExport)({
18436
+ name: props["name"],
18437
+ path: props["path"],
18438
+ isTypeOnly: toBool(props["isTypeOnly"]),
18439
+ asAlias: toBool(props["asAlias"])
18440
+ }));
18441
+ return;
18442
+ }
18443
+ if (type === "kubb-import") {
18444
+ imports.push((0, _kubb_ast.createImport)({
18445
+ name: props["name"],
18446
+ path: props["path"],
18447
+ root: props["root"],
18448
+ isTypeOnly: toBool(props["isTypeOnly"]),
18449
+ isNameSpace: toBool(props["isNameSpace"])
18450
+ }));
18451
+ return;
18452
+ }
18453
+ const nested = collectFileChildren(props["children"]);
18454
+ sources.push(...nested.sources);
18455
+ exports.push(...nested.exports);
18456
+ imports.push(...nested.imports);
18457
+ });
18458
+ return {
18459
+ sources,
18460
+ exports,
18461
+ imports
18462
+ };
18463
+ }
18464
+ function* walkFiles(element) {
18465
+ if (element == null || typeof element === "boolean") return;
18466
+ if (typeof element === "string" || typeof element === "number" || typeof element === "bigint") return;
18467
+ if (Array.isArray(element)) {
18468
+ for (const child of element) yield* walkFiles(child);
18469
+ return;
18470
+ }
18471
+ if (typeof element === "object" && "$$typeof" in element) {
18472
+ const el = element;
18473
+ const { type } = el;
18474
+ const props = el.props;
18475
+ if (type === import_react.Fragment) {
18476
+ yield* walkFiles(props["children"]);
18477
+ return;
18478
+ }
18479
+ if (typeof type === "function") {
18480
+ yield* walkFiles(type(props));
18481
+ return;
18482
+ }
18483
+ if (typeof type === "string") if (type === "kubb-file" && props["baseName"] !== void 0 && props["path"] !== void 0) {
18484
+ const { sources, exports, imports } = collectFileChildren(props["children"]);
18485
+ yield {
18486
+ baseName: props["baseName"],
18487
+ path: props["path"],
18488
+ meta: props["meta"] || {},
18489
+ footer: props["footer"],
18490
+ banner: props["banner"],
18491
+ sources,
18492
+ exports,
18493
+ imports
18494
+ };
18495
+ } else yield* walkFiles(props["children"]);
18496
+ }
18497
+ }
18498
+ /**
18499
+ * Synchronous JSX renderer that walks the element tree in a single pass,
18500
+ * producing {@link FileNode} objects directly without an intermediate virtual
18501
+ * DOM. No React fiber, scheduler, or work loop is involved.
18502
+ *
18503
+ * All components must be pure functions; hooks and class components are not
18504
+ * supported. Produces identical output to the React-backed {@link Runtime} at
18505
+ * approximately 2–4× the speed and a fraction of the allocations.
18506
+ */
18507
+ var SyncRuntime = class {
18508
+ /**
18509
+ * Accumulated {@link FileNode} results from every {@link render} call.
18510
+ */
18511
+ nodes = [];
18512
+ /**
18513
+ * Walks `element` synchronously, converts every `<kubb-file>` subtree into
18514
+ * a {@link FileNode} with no intermediate virtual DOM, and appends the results
18515
+ * to {@link nodes}.
18516
+ */
18517
+ render(element) {
18518
+ for (const file of walkFiles(element)) this.nodes.push(file);
18519
+ }
18520
+ /**
18521
+ * Walks `element` synchronously and yields each {@link FileNode} as it is
18522
+ * produced, without buffering into an intermediate array first. Callers can
18523
+ * begin processing each file before the rest of the element tree is traversed.
18524
+ *
18525
+ * @example
18526
+ * ```ts
18527
+ * for (const file of runtime.stream(element)) {
18528
+ * await writeFile(file)
18529
+ * }
18530
+ * ```
18531
+ */
18532
+ *stream(element) {
18533
+ yield* walkFiles(element);
18158
18534
  }
18159
18535
  };
18160
18536
  //#endregion
18161
18537
  //#region src/createRenderer.tsx
18162
18538
  /**
18163
- * Create a Kubb JSX renderer.
18539
+ * Renderer factory that turns the JSX produced by a generator into
18540
+ * `FileNode`s using React's reconciler under the hood. Pass as the `renderer`
18541
+ * property on `defineGenerator`. Kubb core stays generic, with no hard
18542
+ * dependency on `@kubb/renderer-jsx`.
18164
18543
  *
18165
- * The renderer converts a React JSX element tree built from the components in this
18166
- * package into an array of {@link FileNode} entries representing the generated files.
18544
+ * Use this when generators rely on React features (hooks, suspense, context).
18545
+ * For pure-function components, see {@link jsxRendererSync} for ~2-4× faster
18546
+ * rendering.
18167
18547
  *
18168
- * @example Basic usage
18169
- * ```ts
18170
- * import { createRenderer, File } from '@kubb/renderer-jsx'
18548
+ * @example Wire up a JSX generator
18549
+ * ```tsx
18550
+ * import { defineGenerator } from '@kubb/core'
18551
+ * import { jsxRenderer } from '@kubb/renderer-jsx'
18171
18552
  *
18172
- * const renderer = createRenderer()
18173
- * await renderer.render(
18174
- * <File baseName="pet.ts" path="src/models/pet.ts">
18175
- * <File.Source name="Pet" isExportable isIndexable>
18176
- * {`export type Pet = { id: number; name: string }`}
18177
- * </File.Source>
18178
- * </File>
18179
- * )
18180
- * console.log(renderer.files) // [FileNode]
18181
- * renderer.unmount()
18553
+ * export const myGenerator = defineGenerator<PluginTs>({
18554
+ * name: 'types',
18555
+ * renderer: jsxRenderer,
18556
+ * schema(node, ctx) {
18557
+ * return (
18558
+ * <File baseName="output.ts" path={`${ctx.root}/output.ts`}>
18559
+ * <Type node={node} resolver={ctx.resolver} />
18560
+ * </File>
18561
+ * )
18562
+ * },
18563
+ * })
18182
18564
  * ```
18183
18565
  */
18184
- function createRenderer(options = {}) {
18185
- const runtime = new Runtime(options);
18566
+ const jsxRenderer = () => {
18567
+ const runtime = new Runtime();
18186
18568
  return {
18187
- async render(Element) {
18188
- await runtime.render(Element);
18569
+ async render(element) {
18570
+ await runtime.render(element);
18189
18571
  },
18190
18572
  get files() {
18191
18573
  return runtime.nodes;
18192
18574
  },
18575
+ dispose() {
18576
+ runtime.unmount();
18577
+ },
18193
18578
  unmount(error) {
18194
18579
  runtime.unmount(error);
18580
+ },
18581
+ [Symbol.dispose]() {
18582
+ this.dispose();
18195
18583
  }
18196
18584
  };
18197
- }
18585
+ };
18198
18586
  /**
18199
- * A renderer factory for generators that produce JSX output.
18587
+ * Lightweight renderer that walks the JSX tree in a single recursive pass —
18588
+ * no React reconciler, no scheduler. Drop-in replacement for
18589
+ * {@link jsxRenderer} at roughly 2–4× the throughput.
18200
18590
  *
18201
- * Pass this as the `renderer` property of a `defineGenerator` call so that
18202
- * core can render the JSX element tree returned by your generator methods
18203
- * without a hard dependency on `@kubb/renderer-jsx`.
18591
+ * Constraints: every component must be a pure function. Hooks, suspense, and
18592
+ * class components are not supported.
18204
18593
  *
18205
- * @example
18206
- * ```ts
18207
- * import { jsxRenderer } from '@kubb/renderer-jsx'
18594
+ * Use this for generators that produce large amounts of output and do not need
18595
+ * React's runtime features. It also exposes `stream()` for incremental file
18596
+ * emission.
18597
+ *
18598
+ * @example Drop-in faster renderer
18599
+ * ```tsx
18208
18600
  * import { defineGenerator } from '@kubb/core'
18601
+ * import { jsxRendererSync } from '@kubb/renderer-jsx'
18209
18602
  *
18210
18603
  * export const myGenerator = defineGenerator<PluginTs>({
18211
- * name: 'my-generator',
18212
- * renderer: jsxRenderer,
18213
- * schema(node, options) {
18214
- * return <File baseName="output.ts" path="src/output.ts">...</File>
18604
+ * name: 'types',
18605
+ * renderer: jsxRendererSync,
18606
+ * schema(node, ctx) {
18607
+ * return (
18608
+ * <File baseName="output.ts" path={`${ctx.root}/output.ts`}>
18609
+ * <Type node={node} resolver={ctx.resolver} />
18610
+ * </File>
18611
+ * )
18215
18612
  * },
18216
18613
  * })
18217
18614
  * ```
18615
+ *
18616
+ * @example Stream files as they are produced
18617
+ * ```tsx
18618
+ * const renderer = jsxRendererSync()
18619
+ * for (const file of renderer.stream(element)) {
18620
+ * await writeFile(file.path, file.sources[0])
18621
+ * }
18622
+ * ```
18218
18623
  */
18219
- const jsxRenderer = () => createRenderer();
18624
+ const jsxRendererSync = () => {
18625
+ const runtime = new SyncRuntime();
18626
+ return {
18627
+ async render(element) {
18628
+ runtime.render(element);
18629
+ },
18630
+ get files() {
18631
+ return runtime.nodes;
18632
+ },
18633
+ stream(element) {
18634
+ return runtime.stream(element);
18635
+ },
18636
+ dispose() {},
18637
+ unmount(_error) {},
18638
+ [Symbol.dispose]() {
18639
+ this.dispose();
18640
+ }
18641
+ };
18642
+ };
18220
18643
  //#endregion
18644
+ exports.Callout = Callout;
18645
+ exports.CodeBlock = CodeBlock;
18221
18646
  exports.Const = Const;
18222
18647
  exports.File = File;
18648
+ exports.Frontmatter = Frontmatter;
18223
18649
  exports.Function = Function$1;
18650
+ exports.Heading = Heading;
18224
18651
  exports.Jsx = Jsx;
18652
+ exports.List = List;
18653
+ exports.Paragraph = Paragraph;
18225
18654
  exports.Root = Root;
18226
18655
  exports.Type = Type;
18227
18656
  exports.createContext = createContext;
18228
- exports.createRenderer = createRenderer;
18229
18657
  exports.inject = inject;
18230
18658
  exports.jsxRenderer = jsxRenderer;
18659
+ exports.jsxRendererSync = jsxRendererSync;
18231
18660
  exports.provide = provide;
18232
18661
  exports.unprovide = unprovide;
18233
18662