@pyreon/core 0.15.0 → 0.18.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/README.md +1 -0
- package/lib/analysis/index.js.html +1 -1
- package/lib/analysis/jsx-dev-runtime.js.html +1 -1
- package/lib/analysis/jsx-runtime.js.html +1 -1
- package/lib/index.js +198 -16
- package/lib/jsx-dev-runtime.js +45 -11
- package/lib/jsx-runtime.js +45 -11
- package/lib/types/index.d.ts +176 -6
- package/lib/types/jsx-dev-runtime.d.ts +16 -2
- package/lib/types/jsx-runtime.d.ts +16 -2
- package/package.json +2 -2
- package/src/defer.ts +241 -0
- package/src/for.ts +13 -1
- package/src/h.ts +16 -2
- package/src/index.ts +11 -1
- package/src/jsx-runtime.ts +46 -8
- package/src/manifest.ts +12 -4
- package/src/props.ts +59 -0
- package/src/tests/core.test.ts +1 -1
- package/src/tests/defer.test.ts +359 -0
- package/src/tests/extract-props-overloads.types.test.ts +135 -0
- package/src/tests/for.test.ts +23 -0
- package/src/tests/h.test.ts +21 -0
- package/src/tests/reactive-props.test.ts +71 -1
- package/src/types.ts +43 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { makeReactiveProps, REACTIVE_PROP, _rp } from '../props'
|
|
2
|
+
import { makeReactiveProps, REACTIVE_PROP, _rp, _wrapSpread } from '../props'
|
|
3
3
|
|
|
4
4
|
describe('makeReactiveProps', () => {
|
|
5
5
|
it('returns raw object when no reactive props exist (fast path)', () => {
|
|
@@ -85,3 +85,73 @@ describe('_rp', () => {
|
|
|
85
85
|
expect(branded()).toBe('hello')
|
|
86
86
|
})
|
|
87
87
|
})
|
|
88
|
+
|
|
89
|
+
describe('_wrapSpread', () => {
|
|
90
|
+
it('returns null/undefined unchanged (primitive guard)', () => {
|
|
91
|
+
expect(_wrapSpread(null)).toBe(null)
|
|
92
|
+
expect(_wrapSpread(undefined)).toBe(undefined)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('returns source unchanged when no getter descriptors exist (fast path)', () => {
|
|
96
|
+
const source = { a: 1, b: 'x', c: true }
|
|
97
|
+
expect(_wrapSpread(source)).toBe(source)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('returns source unchanged for empty objects', () => {
|
|
101
|
+
const source = {}
|
|
102
|
+
expect(_wrapSpread(source)).toBe(source)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('wraps getter-shaped reactive props as _rp-branded thunks', () => {
|
|
106
|
+
let liveValue = 'a'
|
|
107
|
+
const source = {} as Record<string, unknown>
|
|
108
|
+
Object.defineProperty(source, 'x', {
|
|
109
|
+
get: () => liveValue,
|
|
110
|
+
enumerable: true,
|
|
111
|
+
configurable: true,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const result = _wrapSpread(source) as Record<string, unknown>
|
|
115
|
+
expect(result).not.toBe(source) // new object allocated
|
|
116
|
+
|
|
117
|
+
const wrappedX = result.x as () => unknown
|
|
118
|
+
expect(typeof wrappedX).toBe('function')
|
|
119
|
+
expect((wrappedX as unknown as Record<symbol, unknown>)[REACTIVE_PROP]).toBe(true)
|
|
120
|
+
|
|
121
|
+
// Lazy read — each call reads the current source[x] getter value
|
|
122
|
+
expect(wrappedX()).toBe('a')
|
|
123
|
+
liveValue = 'b'
|
|
124
|
+
expect(wrappedX()).toBe('b') // live re-read, not captured
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('preserves data properties as-is when mixed with getters', () => {
|
|
128
|
+
const source = { plain: 'data' } as Record<string, unknown>
|
|
129
|
+
Object.defineProperty(source, 'reactive', {
|
|
130
|
+
get: () => 'live',
|
|
131
|
+
enumerable: true,
|
|
132
|
+
configurable: true,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const result = _wrapSpread(source) as Record<string, unknown>
|
|
136
|
+
expect(result.plain).toBe('data') // copied through
|
|
137
|
+
expect(typeof result.reactive).toBe('function') // wrapped as thunk
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('preserves Reflect.ownKeys symbol-keyed properties', () => {
|
|
141
|
+
const sym = Symbol('marker')
|
|
142
|
+
const source = { regular: 'x' } as Record<string | symbol, unknown>
|
|
143
|
+
Object.defineProperty(source, 'reactive', {
|
|
144
|
+
get: () => 'live',
|
|
145
|
+
enumerable: true,
|
|
146
|
+
configurable: true,
|
|
147
|
+
})
|
|
148
|
+
source[sym] = 'symbol-value'
|
|
149
|
+
|
|
150
|
+
const result = _wrapSpread(source) as Record<string | symbol, unknown>
|
|
151
|
+
expect(result.regular).toBe('x')
|
|
152
|
+
// Note: symbol keys go through Reflect.ownKeys; the wrap path indexes
|
|
153
|
+
// via `key as string` for type narrowing but the runtime carries them
|
|
154
|
+
// forward as data properties.
|
|
155
|
+
expect(typeof result.reactive).toBe('function')
|
|
156
|
+
})
|
|
157
|
+
})
|
package/src/types.ts
CHANGED
|
@@ -30,8 +30,49 @@ export type ComponentFn<P extends Props = Props> = (props: P) => VNodeChild
|
|
|
30
30
|
|
|
31
31
|
// ─── Utility types ───────────────────────────────────────────────────────────
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Extract the props type from a component function, or pass through if already
|
|
35
|
+
* a props type. **Multi-overload aware** — matches up to 4 call signatures and
|
|
36
|
+
* produces the UNION of their first-argument types. A single-overload function
|
|
37
|
+
* still works (the union of 4 copies of the same props type dedupes back to
|
|
38
|
+
* the single shape).
|
|
39
|
+
*
|
|
40
|
+
* **Why this shape**. `T extends (props: infer P) => any ? P : never` only
|
|
41
|
+
* captures the LAST overload of a multi-overload function — TS's overload-
|
|
42
|
+
* resolution-against-conditional-types semantics. Multi-overload primitives
|
|
43
|
+
* (Iterator, List, Element, etc.) need the union of every overload's props
|
|
44
|
+
* to survive HOC wrapping (`rocketstyle()`, `attrs()`) without silently
|
|
45
|
+
* downgrading the public prop surface to the loosest overload. Mirrors
|
|
46
|
+
* vitus-labs PR #222.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* function Iterator<T extends SimpleValue>(p: { data: T[]; valueName?: string }): VNodeChild
|
|
50
|
+
* function Iterator<T extends ObjectValue>(p: { data: T[]; component: ComponentFn<T> }): VNodeChild
|
|
51
|
+
* type Props = ExtractProps<typeof Iterator>
|
|
52
|
+
* // → { data: SimpleValue[]; valueName?: string }
|
|
53
|
+
* // | { data: ObjectValue[]; component: ComponentFn<ObjectValue> }
|
|
54
|
+
*/
|
|
55
|
+
export type ExtractProps<T> = T extends {
|
|
56
|
+
(props: infer P1, ...args: any): any
|
|
57
|
+
(props: infer P2, ...args: any): any
|
|
58
|
+
(props: infer P3, ...args: any): any
|
|
59
|
+
(props: infer P4, ...args: any): any
|
|
60
|
+
}
|
|
61
|
+
? P1 | P2 | P3 | P4
|
|
62
|
+
: T extends {
|
|
63
|
+
(props: infer P1, ...args: any): any
|
|
64
|
+
(props: infer P2, ...args: any): any
|
|
65
|
+
(props: infer P3, ...args: any): any
|
|
66
|
+
}
|
|
67
|
+
? P1 | P2 | P3
|
|
68
|
+
: T extends {
|
|
69
|
+
(props: infer P1, ...args: any): any
|
|
70
|
+
(props: infer P2, ...args: any): any
|
|
71
|
+
}
|
|
72
|
+
? P1 | P2
|
|
73
|
+
: T extends ComponentFn<infer P>
|
|
74
|
+
? P
|
|
75
|
+
: T
|
|
35
76
|
|
|
36
77
|
/** A higher-order component that wraps a component, optionally transforming its props. */
|
|
37
78
|
export type HigherOrderComponent<HOP extends Props, P extends Props | undefined = undefined> = (
|