@pyreon/vue-compat 0.13.0 → 0.14.0
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/lib/analysis/index.js.html +1 -1
- package/lib/analysis/jsx-runtime.js.html +1 -1
- package/lib/index.js +408 -28
- package/lib/index.js.map +1 -1
- package/lib/jsx-runtime.js +9 -0
- package/lib/jsx-runtime.js.map +1 -1
- package/lib/types/index.d.ts +168 -8
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/jsx-runtime.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +622 -21
- package/src/jsx-runtime.ts +15 -0
- package/src/tests/jsx-runtime-wrapper.test.ts +87 -0
- package/src/tests/new-apis.test.ts +1303 -0
package/src/jsx-runtime.ts
CHANGED
|
@@ -165,6 +165,21 @@ export function jsx(
|
|
|
165
165
|
return h(wrapped, componentProps)
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
// DOM element: convert Vue ref ({ value }) to callback ref for Pyreon's runtime-dom
|
|
169
|
+
if (typeof type === 'string' && propsWithKey.ref != null) {
|
|
170
|
+
const r = propsWithKey.ref
|
|
171
|
+
if (
|
|
172
|
+
typeof r === 'object' &&
|
|
173
|
+
r !== null &&
|
|
174
|
+
(r as Record<symbol, unknown>)[Symbol.for('__v_isRef')] === true
|
|
175
|
+
) {
|
|
176
|
+
const vueRef = r as { value: unknown }
|
|
177
|
+
propsWithKey.ref = (el: Element | null) => {
|
|
178
|
+
vueRef.value = el
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
168
183
|
// DOM element or symbol (Fragment): children go in vnode.children
|
|
169
184
|
const childArray = children === undefined ? [] : Array.isArray(children) ? children : [children]
|
|
170
185
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { mount } from '@pyreon/runtime-dom'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { onMounted, onUnmounted } from '../index'
|
|
4
|
+
import { jsx } from '../jsx-runtime'
|
|
5
|
+
|
|
6
|
+
// Coverage gap closed in PR #323. Exercises the Vue-compat wrapper
|
|
7
|
+
// (`wrapCompatComponent`) end-to-end: JSX → mount → lifecycle hooks
|
|
8
|
+
// → unmount cleanup. Pins the wrapper's setup-and-teardown contract;
|
|
9
|
+
// fine-grained reactivity behavior is covered by the broader
|
|
10
|
+
// vue-compat.test.ts integration suite.
|
|
11
|
+
|
|
12
|
+
function container(): HTMLElement {
|
|
13
|
+
const el = document.createElement('div')
|
|
14
|
+
document.body.appendChild(el)
|
|
15
|
+
return el
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const tick = () => new Promise<void>((r) => queueMicrotask(() => r()))
|
|
19
|
+
|
|
20
|
+
describe('vue-compat — wrapCompatComponent (jsx-runtime)', () => {
|
|
21
|
+
it('caches the wrapper per source-fn identity (same wrapper on repeat jsx calls)', () => {
|
|
22
|
+
const Comp = () => jsx('div', { children: 'hi' })
|
|
23
|
+
const a = jsx(Comp, {})
|
|
24
|
+
const b = jsx(Comp, {})
|
|
25
|
+
// Both vnodes should reference the same wrapped component (the
|
|
26
|
+
// _wrapperCache WeakMap reuses by source-fn identity)
|
|
27
|
+
expect(a.type).toBe(b.type)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('produces distinct wrappers for distinct source functions', () => {
|
|
31
|
+
const A = () => jsx('div', { children: 'a' })
|
|
32
|
+
const B = () => jsx('div', { children: 'b' })
|
|
33
|
+
expect(jsx(A, {}).type).not.toBe(jsx(B, {}).type)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('mounts a Vue-style component and renders into the container', () => {
|
|
37
|
+
const Comp = () => jsx('div', { children: 'mounted-via-vue-compat' })
|
|
38
|
+
const c = container()
|
|
39
|
+
mount(jsx(Comp, {}), c)
|
|
40
|
+
expect(c.textContent).toContain('mounted-via-vue-compat')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('runs onMounted callback after first render', async () => {
|
|
44
|
+
const mountedSpy = vi.fn()
|
|
45
|
+
const Comp = () => {
|
|
46
|
+
onMounted(mountedSpy)
|
|
47
|
+
return jsx('div', { children: 'hi' })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const c = container()
|
|
51
|
+
mount(jsx(Comp, {}), c)
|
|
52
|
+
await tick()
|
|
53
|
+
await tick()
|
|
54
|
+
expect(mountedSpy).toHaveBeenCalledTimes(1)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('runs onUnmounted callback after disposal', async () => {
|
|
58
|
+
const unmountedSpy = vi.fn()
|
|
59
|
+
const Comp = () => {
|
|
60
|
+
onUnmounted(unmountedSpy)
|
|
61
|
+
return jsx('div', { children: 'hi' })
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const c = container()
|
|
65
|
+
const dispose = mount(jsx(Comp, {}), c)
|
|
66
|
+
await tick()
|
|
67
|
+
expect(unmountedSpy).not.toHaveBeenCalled()
|
|
68
|
+
|
|
69
|
+
dispose()
|
|
70
|
+
expect(unmountedSpy).toHaveBeenCalledTimes(1)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('handles components with children prop (passes children through to wrapped fn)', () => {
|
|
74
|
+
const Wrapper = (props: { children?: string }) =>
|
|
75
|
+
jsx('section', { children: props.children ?? '' })
|
|
76
|
+
const c = container()
|
|
77
|
+
mount(jsx(Wrapper, { children: 'inner' }), c)
|
|
78
|
+
expect(c.textContent).toContain('inner')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('handles components with no props', () => {
|
|
82
|
+
const Comp = () => jsx('div', { children: 'noprops' })
|
|
83
|
+
const c = container()
|
|
84
|
+
mount(jsx(Comp, {}), c)
|
|
85
|
+
expect(c.textContent).toContain('noprops')
|
|
86
|
+
})
|
|
87
|
+
})
|