@pyreon/runtime-dom 0.12.15 → 0.13.1
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/package.json +7 -6
- package/src/manifest.ts +218 -0
- package/src/tests/manifest-snapshot.test.ts +84 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/runtime-dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "DOM renderer for Pyreon",
|
|
5
5
|
"homepage": "https://github.com/pyreon/pyreon/tree/main/packages/runtime-dom#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -43,14 +43,15 @@
|
|
|
43
43
|
"prepublishOnly": "bun run build"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@pyreon/core": "^0.
|
|
47
|
-
"@pyreon/reactivity": "^0.
|
|
46
|
+
"@pyreon/core": "^0.13.1",
|
|
47
|
+
"@pyreon/reactivity": "^0.13.1"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@happy-dom/global-registrator": "^20.8.9",
|
|
51
|
-
"@pyreon/compiler": "^0.
|
|
52
|
-
"@pyreon/
|
|
53
|
-
"@pyreon/
|
|
51
|
+
"@pyreon/compiler": "^0.13.1",
|
|
52
|
+
"@pyreon/manifest": "0.13.1",
|
|
53
|
+
"@pyreon/runtime-server": "^0.13.1",
|
|
54
|
+
"@pyreon/test-utils": "^0.13.1",
|
|
54
55
|
"@vitest/browser-playwright": "^4.1.4",
|
|
55
56
|
"esbuild": "^0.28.0",
|
|
56
57
|
"happy-dom": "^20.8.3",
|
package/src/manifest.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { defineManifest } from '@pyreon/manifest'
|
|
2
|
+
|
|
3
|
+
export default defineManifest({
|
|
4
|
+
name: '@pyreon/runtime-dom',
|
|
5
|
+
title: 'DOM Renderer',
|
|
6
|
+
tagline:
|
|
7
|
+
'DOM renderer, mount, hydrateRoot, Transition, TransitionGroup, KeepAlive, SVG/MathML namespace, custom elements',
|
|
8
|
+
description:
|
|
9
|
+
'Surgical signal-to-DOM renderer with zero virtual DOM overhead. The compiler emits `_tpl()` (cloneNode-based template instantiation) + `_bind()` (per-node reactive bindings) calls that mount directly to the DOM without VNode diffing. Reactive text uses `TextNode.data` assignment (not `.textContent`) for minimal DOM mutation. Supports SVG/MathML namespace auto-detection (67 tags), custom elements (props as properties), CSS transitions via `<Transition>` / `<TransitionGroup>`, and component caching via `<KeepAlive>`. Dev-mode warnings use `import.meta.env.DEV` (not `typeof process`) so they tree-shake to zero bytes in production Vite builds.',
|
|
10
|
+
category: 'browser',
|
|
11
|
+
longExample: `import { mount, hydrateRoot, Transition, TransitionGroup, KeepAlive } from "@pyreon/runtime-dom"
|
|
12
|
+
import { signal } from "@pyreon/reactivity"
|
|
13
|
+
import { Show, For } from "@pyreon/core"
|
|
14
|
+
|
|
15
|
+
// Mount — clears container, returns unmount function
|
|
16
|
+
const unmount = mount(<App />, document.getElementById("app")!)
|
|
17
|
+
|
|
18
|
+
// Hydrate SSR-rendered HTML (preserves existing DOM)
|
|
19
|
+
hydrateRoot(<App />, document.getElementById("app")!)
|
|
20
|
+
|
|
21
|
+
// Transition — CSS-based enter/leave animations
|
|
22
|
+
const visible = signal(true)
|
|
23
|
+
const FadeExample = () => (
|
|
24
|
+
<Transition name="fade" mode="out-in">
|
|
25
|
+
<Show when={visible()}>
|
|
26
|
+
<div>Content</div>
|
|
27
|
+
</Show>
|
|
28
|
+
</Transition>
|
|
29
|
+
)
|
|
30
|
+
// CSS: .fade-enter-active, .fade-leave-active { transition: opacity 0.3s }
|
|
31
|
+
// .fade-enter-from, .fade-leave-to { opacity: 0 }
|
|
32
|
+
|
|
33
|
+
// TransitionGroup — animate list items entering/leaving
|
|
34
|
+
const items = signal([1, 2, 3])
|
|
35
|
+
const ListExample = () => (
|
|
36
|
+
<TransitionGroup name="list">
|
|
37
|
+
<For each={items()} by={i => i}>
|
|
38
|
+
{item => <div>{item}</div>}
|
|
39
|
+
</For>
|
|
40
|
+
</TransitionGroup>
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// KeepAlive — cache component state across mount/unmount cycles
|
|
44
|
+
const tab = signal<"a" | "b">("a")
|
|
45
|
+
const TabExample = () => (
|
|
46
|
+
<KeepAlive>
|
|
47
|
+
<Show when={tab() === "a"}><ExpensiveA /></Show>
|
|
48
|
+
<Show when={tab() === "b"}><ExpensiveB /></Show>
|
|
49
|
+
</KeepAlive>
|
|
50
|
+
)`,
|
|
51
|
+
features: [
|
|
52
|
+
'mount() — mount VNode tree into container, returns unmount function',
|
|
53
|
+
'hydrateRoot() — hydrate SSR-rendered HTML, preserving existing DOM',
|
|
54
|
+
'Transition — CSS-based enter/leave animations with mode support',
|
|
55
|
+
'TransitionGroup — animate list item additions and removals',
|
|
56
|
+
'KeepAlive — cache and restore component state across mount/unmount cycles',
|
|
57
|
+
'_tpl() + _bind() — compiler-driven template instantiation with zero VNode overhead',
|
|
58
|
+
'SVG/MathML — 67 tags auto-detected, correct namespace URI, setAttribute-only',
|
|
59
|
+
'Custom elements — props set as properties on hyphenated tag names',
|
|
60
|
+
'Event delegation — synthetic event system for performance',
|
|
61
|
+
'Dev-mode warnings — container validation, output validation, duplicate keys',
|
|
62
|
+
],
|
|
63
|
+
api: [
|
|
64
|
+
{
|
|
65
|
+
name: 'mount',
|
|
66
|
+
kind: 'function',
|
|
67
|
+
signature: 'mount(root: VNodeChild, container: Element): () => void',
|
|
68
|
+
summary:
|
|
69
|
+
'Mount a VNode tree into a container element. Clears the container first, sets up event delegation, then mounts the given child. Returns an `unmount` function that removes everything and disposes all effects. In dev mode, throws if `container` is null/undefined with an actionable error message.',
|
|
70
|
+
example: `import { mount } from "@pyreon/runtime-dom"
|
|
71
|
+
|
|
72
|
+
const dispose = mount(<App />, document.getElementById("app")!)
|
|
73
|
+
|
|
74
|
+
// To unmount:
|
|
75
|
+
dispose()`,
|
|
76
|
+
mistakes: [
|
|
77
|
+
'`createRoot(container).render(<App />)` — Pyreon uses a single function call: `mount(<App />, container)`',
|
|
78
|
+
'`mount(<App />, document.getElementById("app"))` without `!` — getElementById returns `Element | null`. The runtime throws in dev if null, but TypeScript needs the assertion',
|
|
79
|
+
'`mount(<App />, document.body)` — mounting directly to body is discouraged; use a dedicated container element',
|
|
80
|
+
'Forgetting to call the returned unmount function — leaks event listeners and effects. Store and call it on cleanup',
|
|
81
|
+
],
|
|
82
|
+
seeAlso: ['hydrateRoot', 'render'],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'render',
|
|
86
|
+
kind: 'function',
|
|
87
|
+
signature: 'render(root: VNodeChild, container: Element): () => void',
|
|
88
|
+
summary:
|
|
89
|
+
'Alias for `mount`. Provided for API familiarity — both names point to the same function.',
|
|
90
|
+
example: `import { render } from "@pyreon/runtime-dom"
|
|
91
|
+
render(<App />, document.getElementById("app")!)`,
|
|
92
|
+
seeAlso: ['mount'],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'hydrateRoot',
|
|
96
|
+
kind: 'function',
|
|
97
|
+
signature: 'hydrateRoot(root: VNodeChild, container: Element): () => void',
|
|
98
|
+
summary:
|
|
99
|
+
'Hydrate server-rendered HTML. Walks the existing DOM and attaches reactive bindings without recreating elements. Expects the DOM to match the VNode tree structure — mismatches emit dev-mode warnings. Returns an unmount function.',
|
|
100
|
+
example: `import { hydrateRoot } from "@pyreon/runtime-dom"
|
|
101
|
+
|
|
102
|
+
// Hydrate SSR-rendered HTML:
|
|
103
|
+
hydrateRoot(<App />, document.getElementById("app")!)`,
|
|
104
|
+
seeAlso: ['mount', '@pyreon/runtime-server'],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'Transition',
|
|
108
|
+
kind: 'component',
|
|
109
|
+
signature: '<Transition name={name} mode={mode} onEnter={fn} onLeave={fn}>{children}</Transition>',
|
|
110
|
+
summary:
|
|
111
|
+
'CSS-based enter/leave animation wrapper. Applies `{name}-enter-from`, `{name}-enter-active`, `{name}-enter-to` classes on enter and the corresponding `-leave-*` classes on leave. `mode` controls sequencing: `"out-in"` waits for leave to complete before entering, `"in-out"` enters first. Has a 5-second safety timeout — if `transitionend`/`animationend` never fires, the transition completes automatically.',
|
|
112
|
+
example: `<Transition name="fade" mode="out-in">
|
|
113
|
+
<Show when={visible()}>
|
|
114
|
+
<div>Content</div>
|
|
115
|
+
</Show>
|
|
116
|
+
</Transition>
|
|
117
|
+
|
|
118
|
+
/* CSS:
|
|
119
|
+
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s }
|
|
120
|
+
.fade-enter-from, .fade-leave-to { opacity: 0 }
|
|
121
|
+
*/`,
|
|
122
|
+
mistakes: [
|
|
123
|
+
'Missing CSS classes — `<Transition name="fade">` does nothing without `.fade-enter-active` / `.fade-leave-active` CSS',
|
|
124
|
+
'Wrapping multiple root elements — Transition expects a single child (or null). Multiple children cause undefined behavior',
|
|
125
|
+
'Using `mode="in-out"` when you want sequential — `"out-in"` is almost always what you want (old leaves, then new enters)',
|
|
126
|
+
],
|
|
127
|
+
seeAlso: ['TransitionGroup', '@pyreon/kinetic'],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'TransitionGroup',
|
|
131
|
+
kind: 'component',
|
|
132
|
+
signature: '<TransitionGroup name={name} tag={tag}>{children}</TransitionGroup>',
|
|
133
|
+
summary:
|
|
134
|
+
'Animate list item additions and removals with CSS transitions. Each item gets enter/leave classes on mount/unmount. The `tag` prop controls the wrapper element (defaults to a fragment). Works with `<For>` for reactive lists. Also applies `-move` classes for FLIP-animated reordering.',
|
|
135
|
+
example: `<TransitionGroup name="list" tag="ul">
|
|
136
|
+
<For each={items()} by={i => i.id}>
|
|
137
|
+
{item => <li>{item.name}</li>}
|
|
138
|
+
</For>
|
|
139
|
+
</TransitionGroup>
|
|
140
|
+
|
|
141
|
+
/* CSS:
|
|
142
|
+
.list-enter-active, .list-leave-active { transition: all 0.3s }
|
|
143
|
+
.list-enter-from, .list-leave-to { opacity: 0; transform: translateY(10px) }
|
|
144
|
+
.list-move { transition: transform 0.3s }
|
|
145
|
+
*/`,
|
|
146
|
+
seeAlso: ['Transition', 'For'],
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'KeepAlive',
|
|
150
|
+
kind: 'component',
|
|
151
|
+
signature: '<KeepAlive include={pattern} exclude={pattern} max={number}>{children}</KeepAlive>',
|
|
152
|
+
summary:
|
|
153
|
+
'Cache component instances across mount/unmount cycles so their state (signals, scroll position, form inputs) is preserved when they are toggled out and back in. `include`/`exclude` filter by component name. `max` limits cache size (LRU eviction). Useful for tab panels and multi-step forms.',
|
|
154
|
+
example: `const tab = signal<"a" | "b">("a")
|
|
155
|
+
|
|
156
|
+
<KeepAlive>
|
|
157
|
+
<Show when={tab() === "a"}><ExpensiveFormA /></Show>
|
|
158
|
+
<Show when={tab() === "b"}><ExpensiveFormB /></Show>
|
|
159
|
+
</KeepAlive>`,
|
|
160
|
+
seeAlso: ['Transition', 'Show'],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: '_tpl',
|
|
164
|
+
kind: 'function',
|
|
165
|
+
signature: '_tpl(html: string): () => DocumentFragment',
|
|
166
|
+
summary:
|
|
167
|
+
'Compiler-internal: create a template factory from an HTML string. First call parses the HTML into a `<template>` element; subsequent calls use `cloneNode(true)` for zero-parse instantiation. Not intended for direct use — the JSX compiler emits `_tpl()` calls automatically.',
|
|
168
|
+
example: `// Compiler output (not hand-written):
|
|
169
|
+
const _$t0 = _tpl("<div class=\\"container\\"><span></span></div>")`,
|
|
170
|
+
seeAlso: ['_bindText', '_bindDirect'],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: '_bindText',
|
|
174
|
+
kind: 'function',
|
|
175
|
+
signature: '_bindText(fn: () => string, node: Text): void',
|
|
176
|
+
summary:
|
|
177
|
+
'Compiler-internal: bind a reactive expression to a text node via `TextNode.data` assignment. Creates a `renderEffect` that re-runs when tracked signals change. Each text node gets its own independent binding for fine-grained reactivity.',
|
|
178
|
+
example: `// Compiler output for <div>{count()}</div>:
|
|
179
|
+
const _$t = _tpl("<div> </div>")
|
|
180
|
+
const _$n = _$t()
|
|
181
|
+
_bindText(() => count(), _$n.firstChild)`,
|
|
182
|
+
seeAlso: ['_tpl', '_bindDirect'],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: 'sanitizeHtml',
|
|
186
|
+
kind: 'function',
|
|
187
|
+
signature: 'sanitizeHtml(html: string): string',
|
|
188
|
+
summary:
|
|
189
|
+
'Sanitize an HTML string using the registered sanitizer (set via `setSanitizer()`). Falls back to the identity function if no sanitizer is registered. Used by the runtime when setting `innerHTML` on elements.',
|
|
190
|
+
example: `import { setSanitizer, sanitizeHtml } from "@pyreon/runtime-dom"
|
|
191
|
+
setSanitizer(DOMPurify.sanitize)
|
|
192
|
+
const clean = sanitizeHtml(userInput)`,
|
|
193
|
+
seeAlso: ['setSanitizer'],
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
gotchas: [
|
|
197
|
+
{
|
|
198
|
+
label: 'SVG/MathML uses setAttribute only',
|
|
199
|
+
note: 'SVG and MathML elements ALWAYS use `setAttribute()` for prop forwarding, never property assignment. Many SVG properties (`markerWidth`, `refX`, etc.) are read-only `SVGAnimatedLength` getters — `el[key] = value` crashes. Detected by `el.namespaceURI !== "http://www.w3.org/1999/xhtml"`.',
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
label: 'Custom elements use property assignment',
|
|
203
|
+
note: 'Elements with a hyphen in their tag name (custom elements) get props set as JS properties, not HTML attributes. This matches the web components spec — attributes are strings, properties can be any type.',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
label: 'Transition 5s safety timeout',
|
|
207
|
+
note: 'If `transitionend` or `animationend` never fires (missing CSS, display:none, zero-duration), the transition completes automatically after 5 seconds to prevent stuck UI.',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
label: 'Dev warnings use import.meta.env.DEV',
|
|
211
|
+
note: 'All dev-mode warnings (`mount()` null container, duplicate keys, raw signal children) use `import.meta.env.DEV` — NOT `typeof process`. Vite/Rolldown literal-replaces it at build time; production bundles contain zero warning bytes. Tests run in vitest which sets DEV=true automatically.',
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
label: 'Event delegation',
|
|
215
|
+
note: '`setupDelegation(container)` is called by `mount()` — common events are delegated to the container root for performance. Direct event binding (non-delegated) is used for events that do not bubble (focus, blur, scroll, etc.).',
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {
|
|
2
|
+
renderApiReferenceEntries,
|
|
3
|
+
renderLlmsFullSection,
|
|
4
|
+
renderLlmsTxtLine,
|
|
5
|
+
} from '@pyreon/manifest'
|
|
6
|
+
import runtimeDomManifest from '../manifest'
|
|
7
|
+
|
|
8
|
+
describe('gen-docs — runtime-dom snapshot', () => {
|
|
9
|
+
it('renders @pyreon/runtime-dom to its expected llms.txt bullet', () => {
|
|
10
|
+
expect(renderLlmsTxtLine(runtimeDomManifest)).toMatchInlineSnapshot(`"- @pyreon/runtime-dom — DOM renderer, mount, hydrateRoot, Transition, TransitionGroup, KeepAlive, SVG/MathML namespace, custom elements. SVG and MathML elements ALWAYS use \`setAttribute()\` for prop forwarding, never property assignment. Many SVG properties (\`markerWidth\`, \`refX\`, etc.) are read-only \`SVGAnimatedLength\` getters — \`el[key] = value\` crashes. Detected by \`el.namespaceURI !== "http://www.w3.org/1999/xhtml"\`."`)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('renders @pyreon/runtime-dom to its expected llms-full.txt section — full body snapshot', () => {
|
|
14
|
+
expect(renderLlmsFullSection(runtimeDomManifest)).toMatchInlineSnapshot(`
|
|
15
|
+
"## @pyreon/runtime-dom — DOM Renderer
|
|
16
|
+
|
|
17
|
+
Surgical signal-to-DOM renderer with zero virtual DOM overhead. The compiler emits \`_tpl()\` (cloneNode-based template instantiation) + \`_bind()\` (per-node reactive bindings) calls that mount directly to the DOM without VNode diffing. Reactive text uses \`TextNode.data\` assignment (not \`.textContent\`) for minimal DOM mutation. Supports SVG/MathML namespace auto-detection (67 tags), custom elements (props as properties), CSS transitions via \`<Transition>\` / \`<TransitionGroup>\`, and component caching via \`<KeepAlive>\`. Dev-mode warnings use \`import.meta.env.DEV\` (not \`typeof process\`) so they tree-shake to zero bytes in production Vite builds.
|
|
18
|
+
|
|
19
|
+
\`\`\`typescript
|
|
20
|
+
import { mount, hydrateRoot, Transition, TransitionGroup, KeepAlive } from "@pyreon/runtime-dom"
|
|
21
|
+
import { signal } from "@pyreon/reactivity"
|
|
22
|
+
import { Show, For } from "@pyreon/core"
|
|
23
|
+
|
|
24
|
+
// Mount — clears container, returns unmount function
|
|
25
|
+
const unmount = mount(<App />, document.getElementById("app")!)
|
|
26
|
+
|
|
27
|
+
// Hydrate SSR-rendered HTML (preserves existing DOM)
|
|
28
|
+
hydrateRoot(<App />, document.getElementById("app")!)
|
|
29
|
+
|
|
30
|
+
// Transition — CSS-based enter/leave animations
|
|
31
|
+
const visible = signal(true)
|
|
32
|
+
const FadeExample = () => (
|
|
33
|
+
<Transition name="fade" mode="out-in">
|
|
34
|
+
<Show when={visible()}>
|
|
35
|
+
<div>Content</div>
|
|
36
|
+
</Show>
|
|
37
|
+
</Transition>
|
|
38
|
+
)
|
|
39
|
+
// CSS: .fade-enter-active, .fade-leave-active { transition: opacity 0.3s }
|
|
40
|
+
// .fade-enter-from, .fade-leave-to { opacity: 0 }
|
|
41
|
+
|
|
42
|
+
// TransitionGroup — animate list items entering/leaving
|
|
43
|
+
const items = signal([1, 2, 3])
|
|
44
|
+
const ListExample = () => (
|
|
45
|
+
<TransitionGroup name="list">
|
|
46
|
+
<For each={items()} by={i => i}>
|
|
47
|
+
{item => <div>{item}</div>}
|
|
48
|
+
</For>
|
|
49
|
+
</TransitionGroup>
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
// KeepAlive — cache component state across mount/unmount cycles
|
|
53
|
+
const tab = signal<"a" | "b">("a")
|
|
54
|
+
const TabExample = () => (
|
|
55
|
+
<KeepAlive>
|
|
56
|
+
<Show when={tab() === "a"}><ExpensiveA /></Show>
|
|
57
|
+
<Show when={tab() === "b"}><ExpensiveB /></Show>
|
|
58
|
+
</KeepAlive>
|
|
59
|
+
)
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
> **SVG/MathML uses setAttribute only**: SVG and MathML elements ALWAYS use \`setAttribute()\` for prop forwarding, never property assignment. Many SVG properties (\`markerWidth\`, \`refX\`, etc.) are read-only \`SVGAnimatedLength\` getters — \`el[key] = value\` crashes. Detected by \`el.namespaceURI !== "http://www.w3.org/1999/xhtml"\`.
|
|
63
|
+
>
|
|
64
|
+
> **Custom elements use property assignment**: Elements with a hyphen in their tag name (custom elements) get props set as JS properties, not HTML attributes. This matches the web components spec — attributes are strings, properties can be any type.
|
|
65
|
+
>
|
|
66
|
+
> **Transition 5s safety timeout**: If \`transitionend\` or \`animationend\` never fires (missing CSS, display:none, zero-duration), the transition completes automatically after 5 seconds to prevent stuck UI.
|
|
67
|
+
>
|
|
68
|
+
> **Dev warnings use import.meta.env.DEV**: All dev-mode warnings (\`mount()\` null container, duplicate keys, raw signal children) use \`import.meta.env.DEV\` — NOT \`typeof process\`. Vite/Rolldown literal-replaces it at build time; production bundles contain zero warning bytes. Tests run in vitest which sets DEV=true automatically.
|
|
69
|
+
>
|
|
70
|
+
> **Event delegation**: \`setupDelegation(container)\` is called by \`mount()\` — common events are delegated to the container root for performance. Direct event binding (non-delegated) is used for events that do not bubble (focus, blur, scroll, etc.).
|
|
71
|
+
"
|
|
72
|
+
`)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('renders @pyreon/runtime-dom to MCP api-reference entries — one per api[] item', () => {
|
|
76
|
+
const record = renderApiReferenceEntries(runtimeDomManifest)
|
|
77
|
+
expect(Object.keys(record).length).toBe(9)
|
|
78
|
+
expect(Object.keys(record)).toContain('runtime-dom/mount')
|
|
79
|
+
// Spot-check the flagship API — mount is the primary entry point
|
|
80
|
+
const mount = record['runtime-dom/mount']!
|
|
81
|
+
expect(mount.notes).toContain('container')
|
|
82
|
+
expect(mount.mistakes?.split('\n').length).toBeGreaterThan(2)
|
|
83
|
+
})
|
|
84
|
+
})
|