@pyreon/runtime-dom 0.24.5 → 0.24.6

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 (53) hide show
  1. package/package.json +5 -9
  2. package/src/delegate.ts +0 -98
  3. package/src/devtools.ts +0 -339
  4. package/src/env.d.ts +0 -6
  5. package/src/hydrate.ts +0 -450
  6. package/src/hydration-debug.ts +0 -129
  7. package/src/index.ts +0 -83
  8. package/src/keep-alive-entry.ts +0 -3
  9. package/src/keep-alive.ts +0 -83
  10. package/src/manifest.ts +0 -236
  11. package/src/mount.ts +0 -597
  12. package/src/nodes.ts +0 -896
  13. package/src/props.ts +0 -474
  14. package/src/template.ts +0 -523
  15. package/src/tests/callback-ref-unmount.browser.test.ts +0 -62
  16. package/src/tests/callback-ref-unmount.test.ts +0 -52
  17. package/src/tests/compiler-integration.test.tsx +0 -508
  18. package/src/tests/coverage-gaps.test.ts +0 -3183
  19. package/src/tests/coverage.test.ts +0 -1140
  20. package/src/tests/ctx-stack-growth-repro.test.tsx +0 -158
  21. package/src/tests/dev-gate-pattern.test.ts +0 -46
  22. package/src/tests/dev-gate-treeshake.test.ts +0 -256
  23. package/src/tests/error-boundary-stack-leak-repro.test.tsx +0 -133
  24. package/src/tests/fanout-repro.test.tsx +0 -219
  25. package/src/tests/hydration-integration.test.tsx +0 -540
  26. package/src/tests/keyed-array-in-for-batched-toggle.browser.test.ts +0 -140
  27. package/src/tests/lifecycle-integration.test.tsx +0 -342
  28. package/src/tests/lis-prepend.browser.test.ts +0 -99
  29. package/src/tests/manifest-snapshot.test.ts +0 -85
  30. package/src/tests/mount.test.ts +0 -3529
  31. package/src/tests/native-markers.test.ts +0 -19
  32. package/src/tests/props.test.ts +0 -581
  33. package/src/tests/reactive-props.test.ts +0 -270
  34. package/src/tests/real-world-integration.test.tsx +0 -714
  35. package/src/tests/rs-collapse-dyn-h.browser.test.ts +0 -303
  36. package/src/tests/rs-collapse-dyn.browser.test.ts +0 -316
  37. package/src/tests/rs-collapse-h.browser.test.ts +0 -152
  38. package/src/tests/rs-collapse-h.test.ts +0 -237
  39. package/src/tests/rs-collapse.browser.test.ts +0 -128
  40. package/src/tests/runtime-dom.browser.test.ts +0 -409
  41. package/src/tests/setup.ts +0 -3
  42. package/src/tests/show-context.test.ts +0 -270
  43. package/src/tests/show-of-for-batched-toggle.browser.test.ts +0 -122
  44. package/src/tests/ssr-xss-round-trip.browser.test.ts +0 -93
  45. package/src/tests/style-key-removal.browser.test.ts +0 -54
  46. package/src/tests/style-key-removal.test.ts +0 -88
  47. package/src/tests/template.test.ts +0 -383
  48. package/src/tests/transition-timeout-leak.test.ts +0 -126
  49. package/src/tests/transition.test.ts +0 -568
  50. package/src/tests/verified-correct-probes.test.ts +0 -56
  51. package/src/transition-entry.ts +0 -7
  52. package/src/transition-group.ts +0 -350
  53. package/src/transition.ts +0 -245
@@ -1,270 +0,0 @@
1
- import { h, splitProps } from '@pyreon/core'
2
- import { _rp } from '@pyreon/core'
3
- import { signal } from '@pyreon/reactivity'
4
- import { mount } from '../index'
5
-
6
- describe('reactive component props', () => {
7
- let container: HTMLDivElement
8
-
9
- beforeEach(() => {
10
- container = document.createElement('div')
11
- document.body.appendChild(container)
12
- })
13
-
14
- afterEach(() => {
15
- container.remove()
16
- })
17
-
18
- it('updates DOM when a reactive prop changes (no remount)', () => {
19
- const active = signal(false)
20
- let mountCount = 0
21
-
22
- const Inner = (props: any) => {
23
- mountCount++
24
- return h('span', { 'data-active': () => String(props.active) })
25
- }
26
-
27
- const Outer = () => {
28
- return h(Inner, { active: _rp(() => active()) })
29
- }
30
-
31
- mount(h(Outer, null), container)
32
-
33
- expect(mountCount).toBe(1)
34
- expect(container.querySelector('span')?.getAttribute('data-active')).toBe('false')
35
-
36
- active.set(true)
37
-
38
- // Inner should NOT have remounted — mountCount stays 1
39
- expect(mountCount).toBe(1)
40
- expect(container.querySelector('span')?.getAttribute('data-active')).toBe('true')
41
- })
42
-
43
- it('non-reactive props stay static', () => {
44
- let value = ''
45
- const Comp = (props: any) => {
46
- value = props.label
47
- return h('span', null, props.label)
48
- }
49
-
50
- mount(h(Comp, { label: 'hello' }), container)
51
- expect(value).toBe('hello')
52
- })
53
-
54
- it('event handlers are NOT wrapped as getters', () => {
55
- let clicked = false
56
- const Comp = (props: any) => {
57
- return h('button', { onClick: props.onClick }, 'click')
58
- }
59
-
60
- mount(h(Comp, { onClick: () => { clicked = true } }), container)
61
- container.querySelector('button')?.click()
62
- expect(clicked).toBe(true)
63
- })
64
-
65
- it('user accessor props (like Show when) stay as functions', () => {
66
- let receivedWhen: unknown
67
- const MyShow = (props: any) => {
68
- receivedWhen = props.when
69
- return props.when() ? h('div', null, 'visible') : null
70
- }
71
-
72
- const show = signal(true)
73
- mount(h(MyShow, { when: () => show() }), container)
74
-
75
- // when should be a function (NOT converted to getter)
76
- expect(typeof receivedWhen).toBe('function')
77
- })
78
-
79
- it('props.x read inside effect tracks the signal', () => {
80
- const count = signal(0)
81
- const values: number[] = []
82
-
83
- const Comp = (props: any) => {
84
- // Read props.count inside a reactive child accessor
85
- return h('div', null, () => {
86
- const v = props.count
87
- values.push(v)
88
- return String(v)
89
- })
90
- }
91
-
92
- mount(h(Comp, { count: _rp(() => count()) }), container)
93
- expect(container.textContent).toBe('0')
94
-
95
- count.set(5)
96
- expect(container.textContent).toBe('5')
97
-
98
- count.set(10)
99
- expect(container.textContent).toBe('10')
100
-
101
- // Values tracked — getter called multiple times proves reactivity
102
- expect(values.length).toBeGreaterThan(1)
103
- expect(values[values.length - 1]).toBe(10)
104
- })
105
-
106
- it('splitProps preserves getter reactivity', () => {
107
- const active = signal(false)
108
-
109
- const Comp = (props: any) => {
110
- const [own, rest] = splitProps(props, ['active'])
111
- // own.active should be a getter — reactive when read in tracked scope
112
- return h('div', {
113
- 'data-active': () => String(own.active),
114
- 'data-rest': () => JSON.stringify(Object.keys(rest)),
115
- })
116
- }
117
-
118
- mount(h(Comp, { active: _rp(() => active()), label: 'test' }), container)
119
- expect(container.querySelector('div')?.getAttribute('data-active')).toBe('false')
120
-
121
- active.set(true)
122
- expect(container.querySelector('div')?.getAttribute('data-active')).toBe('true')
123
- })
124
-
125
- it('VNode prop as single JSX element — stable, not re-created', () => {
126
- let iconMountCount = 0
127
-
128
- const Icon = (props: any) => {
129
- iconMountCount++
130
- return h('i', { 'data-name': () => props.name })
131
- }
132
-
133
- // Simulate what the compiler produces for:
134
- // <Wrapper icon={<Icon name={_rp(() => name())} />} />
135
- // The compiler does NOT wrap the outer JSX — Icon is created once
136
- // with reactive inner props.
137
- const name = signal('check')
138
- const iconVNode = h(Icon, { name: _rp(() => name()) })
139
-
140
- const Wrapper = (props: any) => {
141
- return h('div', null, props.icon)
142
- }
143
-
144
- mount(h(Wrapper, { icon: iconVNode }), container)
145
- expect(iconMountCount).toBe(1)
146
- expect(container.querySelector('i')?.getAttribute('data-name')).toBe('check')
147
-
148
- name.set('close')
149
- expect(iconMountCount).toBe(1) // NOT remounted
150
- expect(container.querySelector('i')?.getAttribute('data-name')).toBe('close')
151
- })
152
-
153
- it('multiple reactive props update independently', () => {
154
- const first = signal('Alice')
155
- const last = signal('Smith')
156
- let mountCount = 0
157
-
158
- const Comp = (props: any) => {
159
- mountCount++
160
- return h('div', null,
161
- h('span', { id: 'first' }, () => props.first),
162
- h('span', { id: 'last' }, () => props.last),
163
- )
164
- }
165
-
166
- mount(h(Comp, { first: _rp(() => first()), last: _rp(() => last()) }), container)
167
- expect(mountCount).toBe(1)
168
- expect(container.querySelector('#first')?.textContent).toBe('Alice')
169
- expect(container.querySelector('#last')?.textContent).toBe('Smith')
170
-
171
- first.set('Bob')
172
- expect(mountCount).toBe(1)
173
- expect(container.querySelector('#first')?.textContent).toBe('Bob')
174
- expect(container.querySelector('#last')?.textContent).toBe('Smith')
175
-
176
- last.set('Jones')
177
- expect(mountCount).toBe(1)
178
- expect(container.querySelector('#first')?.textContent).toBe('Bob')
179
- expect(container.querySelector('#last')?.textContent).toBe('Jones')
180
- })
181
-
182
- it('reactive and static props coexist', () => {
183
- const count = signal(0)
184
- let mountCount = 0
185
-
186
- const Comp = (props: any) => {
187
- mountCount++
188
- return h('div', null,
189
- h('span', { id: 'dynamic' }, () => String(props.count)),
190
- h('span', { id: 'static' }, props.label),
191
- )
192
- }
193
-
194
- mount(h(Comp, { count: _rp(() => count()), label: 'fixed' }), container)
195
- expect(mountCount).toBe(1)
196
- expect(container.querySelector('#dynamic')?.textContent).toBe('0')
197
- expect(container.querySelector('#static')?.textContent).toBe('fixed')
198
-
199
- count.set(42)
200
- expect(mountCount).toBe(1)
201
- expect(container.querySelector('#dynamic')?.textContent).toBe('42')
202
- expect(container.querySelector('#static')?.textContent).toBe('fixed')
203
- })
204
-
205
- it('nested components with reactive props from same signal', () => {
206
- const value = signal('hello')
207
- let outerMounts = 0
208
- let innerMounts = 0
209
-
210
- const Inner = (props: any) => {
211
- innerMounts++
212
- return h('span', { id: 'inner' }, () => props.text)
213
- }
214
-
215
- const Outer = (props: any) => {
216
- outerMounts++
217
- return h('div', { id: 'outer' },
218
- () => props.label,
219
- h(Inner, { text: _rp(() => value()) }),
220
- )
221
- }
222
-
223
- mount(h(Outer, { label: _rp(() => value()) }), container)
224
- expect(outerMounts).toBe(1)
225
- expect(innerMounts).toBe(1)
226
- expect(container.querySelector('#outer')?.textContent).toBe('hellohello')
227
-
228
- value.set('world')
229
- expect(outerMounts).toBe(1)
230
- expect(innerMounts).toBe(1)
231
- expect(container.querySelector('#outer')?.textContent).toBe('worldworld')
232
- })
233
-
234
- it('prop changing to undefined', () => {
235
- const title = signal<string | undefined>('visible')
236
-
237
- const Comp = (props: any) => {
238
- return h('div', null, () => props.title ?? 'empty')
239
- }
240
-
241
- mount(h(Comp, { title: _rp(() => title()) }), container)
242
- expect(container.textContent).toBe('visible')
243
-
244
- title.set(undefined)
245
- expect(container.textContent).toBe('empty')
246
-
247
- title.set('back')
248
- expect(container.textContent).toBe('back')
249
- })
250
-
251
- it('rapid signal updates produce correct final value', () => {
252
- const count = signal(0)
253
- let mountCount = 0
254
-
255
- const Comp = (props: any) => {
256
- mountCount++
257
- return h('div', null, () => String(props.count))
258
- }
259
-
260
- mount(h(Comp, { count: _rp(() => count()) }), container)
261
- expect(mountCount).toBe(1)
262
-
263
- for (let i = 1; i <= 100; i++) {
264
- count.set(i)
265
- }
266
-
267
- expect(mountCount).toBe(1)
268
- expect(container.textContent).toBe('100')
269
- })
270
- })