kiru 1.4.0 → 1.5.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/dist/components/derive.d.ts +1 -1
- package/dist/components/derive.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/devtools.d.ts +1 -1
- package/dist/devtools.d.ts.map +1 -1
- package/dist/dom/nodes.d.ts +1 -1
- package/dist/dom/nodes.d.ts.map +1 -1
- package/dist/dom/nodes.js.map +1 -1
- package/dist/dom/props.js.map +1 -1
- package/dist/globalContext.d.ts +2 -2
- package/dist/globalContext.d.ts.map +1 -1
- package/dist/headlessRender.d.ts.map +1 -1
- package/dist/headlessRender.js +3 -0
- package/dist/headlessRender.js.map +1 -1
- package/dist/hooks/onCleanup.d.ts.map +1 -1
- package/dist/hooks/onCleanup.js +3 -1
- package/dist/hooks/onCleanup.js.map +1 -1
- package/dist/hooks/setup.d.ts.map +1 -1
- package/dist/hooks/setup.js +108 -63
- package/dist/hooks/setup.js.map +1 -1
- package/dist/hooks/utils.d.ts.map +1 -1
- package/dist/hooks/utils.js +2 -1
- package/dist/hooks/utils.js.map +1 -1
- package/dist/hydration.d.ts +1 -1
- package/dist/hydration.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/profiling.d.ts +1 -1
- package/dist/profiling.d.ts.map +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +38 -6
- package/dist/reconciler.js.map +1 -1
- package/dist/resource.d.ts +11 -5
- package/dist/resource.d.ts.map +1 -1
- package/dist/resource.js +83 -39
- package/dist/resource.js.map +1 -1
- package/dist/router/client/index.d.ts +1 -1
- package/dist/router/client/index.d.ts.map +1 -1
- package/dist/router/globals.d.ts +2 -2
- package/dist/router/globals.d.ts.map +1 -1
- package/dist/router/link.d.ts +1 -1
- package/dist/router/link.d.ts.map +1 -1
- package/dist/router/pageConfig.d.ts +1 -1
- package/dist/router/pageConfig.d.ts.map +1 -1
- package/dist/router/types.d.ts +3 -3
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.internal.d.ts +2 -2
- package/dist/router/types.internal.d.ts.map +1 -1
- package/dist/router/utils/index.d.ts +2 -2
- package/dist/router/utils/index.d.ts.map +1 -1
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +15 -2
- package/dist/scheduler.js.map +1 -1
- package/dist/signals/base.d.ts +1 -0
- package/dist/signals/base.d.ts.map +1 -1
- package/dist/signals/base.js +14 -2
- package/dist/signals/base.js.map +1 -1
- package/dist/signals/effect.d.ts.map +1 -1
- package/dist/signals/effect.js +8 -0
- package/dist/signals/effect.js.map +1 -1
- package/dist/signals/tracking.d.ts.map +1 -1
- package/dist/signals/tracking.js +13 -14
- package/dist/signals/tracking.js.map +1 -1
- package/dist/ssr/client.d.ts +1 -1
- package/dist/ssr/client.d.ts.map +1 -1
- package/dist/types.d.ts +31 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.dom.d.ts +190 -2
- package/dist/types.dom.d.ts.map +1 -1
- package/dist/types.utils.d.ts +28 -21
- package/dist/types.utils.d.ts.map +1 -1
- package/dist/utils/vdom.d.ts.map +1 -1
- package/dist/utils/vdom.js +5 -2
- package/dist/utils/vdom.js.map +1 -1
- package/package.json +1 -1
- package/src/components/derive.ts +1 -1
- package/src/constants.ts +2 -0
- package/src/devtools.ts +1 -1
- package/src/dom/commit.ts +1 -1
- package/src/dom/nodes.ts +8 -3
- package/src/dom/props.ts +6 -6
- package/src/globalContext.ts +2 -2
- package/src/headlessRender.ts +4 -1
- package/src/hooks/onCleanup.ts +3 -1
- package/src/hooks/setup.ts +133 -80
- package/src/hooks/utils.ts +2 -1
- package/src/hydration.ts +1 -1
- package/src/index.ts +1 -1
- package/src/profiling.ts +1 -1
- package/src/reconciler.ts +51 -9
- package/src/resource.ts +112 -44
- package/src/router/client/index.ts +2 -2
- package/src/router/globals.ts +2 -2
- package/src/router/link.ts +1 -1
- package/src/router/pageConfig.ts +1 -1
- package/src/router/types.internal.ts +2 -2
- package/src/router/types.ts +3 -3
- package/src/router/utils/index.ts +1 -1
- package/src/scheduler.ts +20 -3
- package/src/signals/base.ts +20 -2
- package/src/signals/effect.ts +8 -0
- package/src/signals/tracking.ts +17 -15
- package/src/ssr/client.ts +1 -1
- package/src/types.dom.ts +270 -53
- package/src/types.ts +36 -32
- package/src/types.utils.ts +56 -22
- package/src/utils/vdom.ts +7 -1
package/src/dom/props.ts
CHANGED
|
@@ -733,8 +733,8 @@ function setStyleProp(
|
|
|
733
733
|
// Avoid Set allocation for the common case where prevStyle is empty
|
|
734
734
|
if (prevKeys.length === 0) {
|
|
735
735
|
for (let i = 0; i < nextKeys.length; i++) {
|
|
736
|
-
const k = nextKeys[i]
|
|
737
|
-
const rawNext = nextStyle[k]
|
|
736
|
+
const k = nextKeys[i]
|
|
737
|
+
const rawNext = nextStyle[k as keyof StyleObject]
|
|
738
738
|
const nextVal = unwrap(rawNext)
|
|
739
739
|
if (trackSignals && Signal.isSignal(rawNext)) {
|
|
740
740
|
styleKeyToSignal.set(k, rawNext)
|
|
@@ -751,7 +751,7 @@ function setStyleProp(
|
|
|
751
751
|
// Full merge path: iterate prevKeys for removals, nextKeys for additions/changes
|
|
752
752
|
const nextStyleKeys = new Set(nextKeys)
|
|
753
753
|
for (let i = 0; i < prevKeys.length; i++) {
|
|
754
|
-
const k = prevKeys[i]
|
|
754
|
+
const k = prevKeys[i]
|
|
755
755
|
if (!nextStyleKeys.has(k)) {
|
|
756
756
|
// Property was removed
|
|
757
757
|
if ((k as string).startsWith("--")) {
|
|
@@ -763,9 +763,9 @@ function setStyleProp(
|
|
|
763
763
|
}
|
|
764
764
|
|
|
765
765
|
for (let i = 0; i < nextKeys.length; i++) {
|
|
766
|
-
const k = nextKeys[i]
|
|
767
|
-
const rawNext = nextStyle[k]
|
|
768
|
-
const prevVal = unwrap(prevStyle[k])
|
|
766
|
+
const k = nextKeys[i]
|
|
767
|
+
const rawNext = nextStyle[k as keyof StyleObject]
|
|
768
|
+
const prevVal = unwrap(prevStyle[k as keyof StyleObject])
|
|
769
769
|
const nextVal = unwrap(rawNext)
|
|
770
770
|
if (trackSignals && Signal.isSignal(rawNext)) {
|
|
771
771
|
styleKeyToSignal.set(k, rawNext)
|
package/src/globalContext.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { __DEV__ } from "./env.js"
|
|
|
2
2
|
import { createHmrContext } from "./hmr.js"
|
|
3
3
|
import { createProfilingContext } from "./profiling.js"
|
|
4
4
|
import { fileRouterInstance } from "./router/globals.js"
|
|
5
|
-
import type { FileRouterController } from "./router/fileRouterController"
|
|
6
|
-
import type { AppHandle } from "./appHandle"
|
|
5
|
+
import type { FileRouterController } from "./router/fileRouterController.js"
|
|
6
|
+
import type { AppHandle } from "./appHandle.js"
|
|
7
7
|
|
|
8
8
|
export { createKiruGlobalContext, type GlobalKiruEvent, type KiruGlobalContext }
|
|
9
9
|
|
package/src/headlessRender.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { Signal } from "./signals/base.js"
|
|
13
13
|
import { $ERROR_BOUNDARY, voidElements, $STREAM_DATA } from "./constants.js"
|
|
14
14
|
import { __DEV__ } from "./env.js"
|
|
15
|
-
import type { ErrorBoundaryNode } from "./types.utils"
|
|
15
|
+
import type { ErrorBoundaryNode } from "./types.utils.js"
|
|
16
16
|
|
|
17
17
|
export interface HeadlessRenderContext {
|
|
18
18
|
write(chunk: string): void
|
|
@@ -37,6 +37,9 @@ export function headlessRender(
|
|
|
37
37
|
if (el instanceof Array) {
|
|
38
38
|
return el.forEach((c, i) => headlessRender(ctx, c, parent, i))
|
|
39
39
|
}
|
|
40
|
+
if (typeof el === "function") {
|
|
41
|
+
return headlessRender(ctx, el(), parent, idx)
|
|
42
|
+
}
|
|
40
43
|
if (Signal.isSignal(el)) {
|
|
41
44
|
const value = el.peek()
|
|
42
45
|
if (!isPrimitiveChild(value)) {
|
package/src/hooks/onCleanup.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { $INLINE_FN } from "../constants.js"
|
|
2
|
+
import { __DEV__ } from "../env.js"
|
|
1
3
|
import { node } from "../globals.js"
|
|
2
4
|
import {
|
|
3
5
|
generateRandomID,
|
|
@@ -14,7 +16,7 @@ import {
|
|
|
14
16
|
export function onCleanup(fn: () => void): void {
|
|
15
17
|
if (!sideEffectsEnabled()) return
|
|
16
18
|
const vNode = node.current!
|
|
17
|
-
if (!vNode) {
|
|
19
|
+
if (!vNode || (__DEV__ && vNode.type === $INLINE_FN)) {
|
|
18
20
|
throw new Error("Cannot queue onCleanup effect outside of a component")
|
|
19
21
|
}
|
|
20
22
|
registerVNodeCleanup(vNode, generateRandomID(10), fn)
|
package/src/hooks/setup.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { signal, Signal } from "../signals/base.js"
|
|
2
|
-
import { createVNodeId } from "../utils/vdom.js"
|
|
2
|
+
import { createVNodeId, isVNodeDeleted } from "../utils/vdom.js"
|
|
3
|
+
import { $INLINE_FN } from "../constants.js"
|
|
3
4
|
import { __DEV__ } from "../env.js"
|
|
4
5
|
import { node, setups } from "../globals.js"
|
|
5
|
-
import {
|
|
6
|
-
tracking,
|
|
7
|
-
type TrackingStackObservations,
|
|
8
|
-
} from "../signals/tracking.js"
|
|
6
|
+
import { executeWithTracking } from "../signals/tracking.js"
|
|
9
7
|
import { registerVNodeCleanup } from "../utils/index.js"
|
|
10
8
|
|
|
9
|
+
let currentAccessedPaths: Set<string[]> | null = null
|
|
10
|
+
const OWN_KEYS = `__KEYS__`
|
|
11
|
+
|
|
11
12
|
export interface Setup<Props extends {}> {
|
|
12
13
|
readonly derive: <T>(
|
|
13
14
|
selector: (props: Props extends Kiru.FC<infer P> ? P : Props) => T
|
|
14
15
|
) => Signal<T>
|
|
15
16
|
readonly id: Signal<string>
|
|
17
|
+
// Not reactive — for use in render functions only
|
|
16
18
|
readonly props: Readonly<Props extends Kiru.FC<infer P> ? P : Props>
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -25,7 +27,7 @@ export interface Setup<Props extends {}> {
|
|
|
25
27
|
export function setup<Props extends {}>(): Setup<Props> {
|
|
26
28
|
const vNode = node.current!
|
|
27
29
|
if (__DEV__) {
|
|
28
|
-
if (!vNode) {
|
|
30
|
+
if (!vNode || vNode.type === $INLINE_FN) {
|
|
29
31
|
throw new Error("setup() must be called inside a Kiru component")
|
|
30
32
|
}
|
|
31
33
|
if (vNode.render) {
|
|
@@ -41,8 +43,10 @@ export function setup<Props extends {}>(): Setup<Props> {
|
|
|
41
43
|
setups.set(vNode, setup)
|
|
42
44
|
return setup
|
|
43
45
|
}
|
|
46
|
+
|
|
44
47
|
function createSetup<Props extends {}>(vNode: Kiru.VNode): Setup<Props> {
|
|
45
48
|
let id: Signal<string>
|
|
49
|
+
let propsProxy: InferredProps
|
|
46
50
|
|
|
47
51
|
type InferredProps = Props extends Kiru.FC<infer R> ? R : Props
|
|
48
52
|
const propSyncs = (vNode.propSyncs = []) as ((props: InferredProps) => void)[]
|
|
@@ -53,7 +57,7 @@ function createSetup<Props extends {}>(vNode: Kiru.VNode): Setup<Props> {
|
|
|
53
57
|
const currentProps = { current: { ...vNode.props } as InferredProps }
|
|
54
58
|
const deriveCleanups: Array<() => void> = []
|
|
55
59
|
|
|
56
|
-
type DeriveEntry = { run: () => void;
|
|
60
|
+
type DeriveEntry = { run: () => void; paths: Set<string[]> }
|
|
57
61
|
const deriveEntries: DeriveEntry[] = []
|
|
58
62
|
|
|
59
63
|
propSyncs.push((p) => {
|
|
@@ -61,12 +65,8 @@ function createSetup<Props extends {}>(vNode: Kiru.VNode): Setup<Props> {
|
|
|
61
65
|
const skip = new Set<DeriveEntry>()
|
|
62
66
|
for (const entry of deriveEntries) {
|
|
63
67
|
if (
|
|
64
|
-
entry.
|
|
65
|
-
propsUnchangedAtPaths(
|
|
66
|
-
old,
|
|
67
|
-
p as Record<string, unknown>,
|
|
68
|
-
entry.accessedPaths
|
|
69
|
-
)
|
|
68
|
+
entry.paths.size > 0 &&
|
|
69
|
+
propsUnchangedAtPaths(old, p as Record<string, unknown>, entry.paths)
|
|
70
70
|
) {
|
|
71
71
|
skip.add(entry)
|
|
72
72
|
}
|
|
@@ -88,41 +88,27 @@ function createSetup<Props extends {}>(vNode: Kiru.VNode): Setup<Props> {
|
|
|
88
88
|
) {
|
|
89
89
|
const resultSig = signal(undefined!) as Signal<T>
|
|
90
90
|
const unsubs = new Map<string, () => void>()
|
|
91
|
-
const accessedPaths = new Set<string>()
|
|
91
|
+
const accessedPaths = new Set<string[]>()
|
|
92
92
|
|
|
93
93
|
function sync() {
|
|
94
94
|
accessedPaths.clear()
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
currentAccessedPaths = accessedPaths
|
|
96
|
+
|
|
97
|
+
const propsProxy = createProxy(
|
|
98
|
+
currentProps.current as Record<string, unknown>
|
|
98
99
|
) as InferredProps
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
for (const [sid, unsub] of unsubs) {
|
|
108
|
-
if (!observations.has(sid)) {
|
|
109
|
-
unsub()
|
|
110
|
-
unsubs.delete(sid)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
for (const [sid, observedSig] of observations) {
|
|
114
|
-
if (!unsubs.has(sid)) {
|
|
115
|
-
try {
|
|
116
|
-
unsubs.set(sid, observedSig.subscribe(sync))
|
|
117
|
-
} catch {
|
|
118
|
-
// Signal may be disposed after HMR; skip subscribing
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
100
|
+
|
|
101
|
+
resultSig.value = executeWithTracking({
|
|
102
|
+
id: Signal.id(resultSig),
|
|
103
|
+
fn: () => selector(propsProxy),
|
|
104
|
+
onDepChanged: sync,
|
|
105
|
+
subs: unsubs,
|
|
106
|
+
})
|
|
107
|
+
currentAccessedPaths = null
|
|
122
108
|
}
|
|
123
109
|
|
|
124
110
|
sync()
|
|
125
|
-
const entry: DeriveEntry = { run: sync, accessedPaths }
|
|
111
|
+
const entry: DeriveEntry = { run: sync, paths: accessedPaths }
|
|
126
112
|
deriveEntries.push(entry)
|
|
127
113
|
deriveCleanups.push(() => {
|
|
128
114
|
unsubs.forEach((u) => u())
|
|
@@ -136,6 +122,16 @@ function createSetup<Props extends {}>(vNode: Kiru.VNode): Setup<Props> {
|
|
|
136
122
|
get id() {
|
|
137
123
|
if (!id) {
|
|
138
124
|
id = signal(createVNodeId(vNode))
|
|
125
|
+
if (isVNodeDeleted(vNode)) {
|
|
126
|
+
return id
|
|
127
|
+
}
|
|
128
|
+
if (node.current !== vNode) {
|
|
129
|
+
registerVNodeCleanup(
|
|
130
|
+
vNode,
|
|
131
|
+
Signal.id(id),
|
|
132
|
+
Signal.dispose.bind(null, id)
|
|
133
|
+
)
|
|
134
|
+
}
|
|
139
135
|
prevIndex = vNode.index
|
|
140
136
|
propSyncs.push(() => {
|
|
141
137
|
if (prevIndex !== vNode.index) {
|
|
@@ -148,61 +144,118 @@ function createSetup<Props extends {}>(vNode: Kiru.VNode): Setup<Props> {
|
|
|
148
144
|
return id
|
|
149
145
|
},
|
|
150
146
|
get props() {
|
|
151
|
-
return
|
|
147
|
+
return (propsProxy ??= new Proxy(
|
|
148
|
+
{},
|
|
149
|
+
{
|
|
150
|
+
get(_, key) {
|
|
151
|
+
if (typeof key === "symbol")
|
|
152
|
+
return Reflect.get(currentProps.current as any, key)
|
|
153
|
+
const v = (currentProps.current as any)[key]
|
|
154
|
+
if (v !== null && typeof v === "object" && !Signal.isSignal(v)) {
|
|
155
|
+
return createProxy(v)
|
|
156
|
+
}
|
|
157
|
+
return v
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
) as InferredProps)
|
|
152
161
|
},
|
|
153
162
|
}
|
|
154
163
|
return setupResult
|
|
155
164
|
}
|
|
165
|
+
|
|
156
166
|
function propsUnchangedAtPaths(
|
|
157
167
|
oldProps: Record<string, unknown>,
|
|
158
168
|
newProps: Record<string, unknown>,
|
|
159
|
-
|
|
169
|
+
accessedPaths: Set<string[]>
|
|
160
170
|
): boolean {
|
|
161
|
-
for (const path of
|
|
162
|
-
|
|
163
|
-
|
|
171
|
+
outer: for (const path of accessedPaths) {
|
|
172
|
+
let a: unknown = oldProps
|
|
173
|
+
let b: unknown = newProps
|
|
174
|
+
|
|
175
|
+
for (let i = 0; i < path.length; i++) {
|
|
176
|
+
const key = path[i]
|
|
177
|
+
|
|
178
|
+
// Sentinel: caller iterated keys of the object at this path —
|
|
179
|
+
// re-run if the key sets differ
|
|
180
|
+
if (key === OWN_KEYS) {
|
|
181
|
+
if (a === b) continue outer
|
|
182
|
+
if (
|
|
183
|
+
a == null ||
|
|
184
|
+
b == null ||
|
|
185
|
+
typeof a !== "object" ||
|
|
186
|
+
typeof b !== "object"
|
|
187
|
+
) {
|
|
188
|
+
return false
|
|
189
|
+
}
|
|
190
|
+
const aKeys = Object.keys(a)
|
|
191
|
+
const bKeys = Object.keys(b)
|
|
192
|
+
if (aKeys.length !== bKeys.length) return false
|
|
193
|
+
for (const k of aKeys) {
|
|
194
|
+
if (!(k in (b as object))) return false
|
|
195
|
+
}
|
|
196
|
+
continue outer
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (a === b) continue outer
|
|
200
|
+
if (
|
|
201
|
+
a == null ||
|
|
202
|
+
b == null ||
|
|
203
|
+
typeof a !== "object" ||
|
|
204
|
+
typeof b !== "object"
|
|
205
|
+
) {
|
|
206
|
+
if (!Object.is(a, b)) return false
|
|
207
|
+
continue outer
|
|
208
|
+
}
|
|
209
|
+
a = (a as Record<string, unknown>)[key]
|
|
210
|
+
b = (b as Record<string, unknown>)[key]
|
|
164
211
|
}
|
|
212
|
+
|
|
213
|
+
if (!Object.is(a, b)) return false
|
|
165
214
|
}
|
|
215
|
+
|
|
166
216
|
return true
|
|
167
217
|
}
|
|
168
218
|
|
|
169
|
-
|
|
170
|
-
let cur: unknown = obj
|
|
171
|
-
for (const key of path.split(".")) {
|
|
172
|
-
if (cur == null || typeof cur !== "object") return undefined
|
|
173
|
-
cur = (cur as Record<string, unknown>)[key]
|
|
174
|
-
}
|
|
175
|
-
return cur
|
|
176
|
-
}
|
|
219
|
+
const proxyCache = new WeakMap<object, any>()
|
|
177
220
|
|
|
178
221
|
/**
|
|
179
|
-
* Proxy that records paths and
|
|
180
|
-
*
|
|
181
|
-
* signal refs. Container objects (e.g. "data") are new every render and would
|
|
182
|
-
* always fail the skip.
|
|
222
|
+
* Proxy that records accessed paths and primitives into the current
|
|
223
|
+
* tracking context (currentAccessedPaths).
|
|
183
224
|
*/
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
pathPrefix?: string
|
|
225
|
+
function createProxy<P extends Record<string, unknown>>(
|
|
226
|
+
source: P,
|
|
227
|
+
path: string[] = []
|
|
188
228
|
): P {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
229
|
+
let cached = proxyCache.get(source)
|
|
230
|
+
|
|
231
|
+
if (!cached) {
|
|
232
|
+
cached = new Proxy(source, {
|
|
233
|
+
get(holder, key: string | symbol) {
|
|
234
|
+
if (typeof key === "symbol") return Reflect.get(holder, key)
|
|
235
|
+
|
|
236
|
+
const keyPath = [...path, key as string]
|
|
237
|
+
const v = holder[key as string]
|
|
238
|
+
|
|
239
|
+
if (v !== null && typeof v === "object" && !Signal.isSignal(v)) {
|
|
240
|
+
return createProxy(v as Record<string, unknown>, keyPath)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
currentAccessedPaths?.add(keyPath)
|
|
195
244
|
return v
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
245
|
+
},
|
|
246
|
+
has(holder, key: string | symbol) {
|
|
247
|
+
if (typeof key === "symbol") return Reflect.has(holder, key)
|
|
248
|
+
currentAccessedPaths?.add([...path, key as string])
|
|
249
|
+
return key in holder
|
|
250
|
+
},
|
|
251
|
+
ownKeys(holder) {
|
|
252
|
+
currentAccessedPaths?.add([...path, OWN_KEYS])
|
|
253
|
+
return Reflect.ownKeys(holder)
|
|
254
|
+
},
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
proxyCache.set(source, cached)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return cached
|
|
208
261
|
}
|
package/src/hooks/utils.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { $INLINE_FN } from "../constants.js"
|
|
1
2
|
import { node } from "../globals.js"
|
|
2
3
|
|
|
3
4
|
export function getVNodeLifecycleHooks(): null | NonNullable<
|
|
4
5
|
Kiru.VNode["hooks"]
|
|
5
6
|
> {
|
|
6
7
|
const vNode = node.current!
|
|
7
|
-
if (!vNode) return null
|
|
8
|
+
if (!vNode || vNode.type === $INLINE_FN) return null
|
|
8
9
|
|
|
9
10
|
return (vNode.hooks ??= {
|
|
10
11
|
pre: [],
|
package/src/hydration.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createKiruGlobalContext } from "./globalContext.js"
|
|
2
2
|
import { isBrowser } from "./env.js"
|
|
3
3
|
|
|
4
|
-
export type * from "./types"
|
|
4
|
+
export type * from "./types.js"
|
|
5
5
|
export * from "./signals/index.js"
|
|
6
6
|
export * from "./action.js"
|
|
7
7
|
export * from "./appHandle.js"
|
package/src/profiling.ts
CHANGED
package/src/reconciler.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
$FRAGMENT,
|
|
3
|
+
$INLINE_FN,
|
|
4
|
+
FLAG_PLACEMENT,
|
|
5
|
+
FLAG_UPDATE,
|
|
6
|
+
} from "./constants.js"
|
|
2
7
|
import {
|
|
3
8
|
getVNodeApp,
|
|
4
9
|
isElement,
|
|
@@ -189,6 +194,10 @@ function updateSlot(
|
|
|
189
194
|
}
|
|
190
195
|
return updateFragment(parent, oldChild, child)
|
|
191
196
|
}
|
|
197
|
+
if (typeof child === "function") {
|
|
198
|
+
if (key !== null) return null
|
|
199
|
+
return updateInlineFnChild(parent, oldChild, child)
|
|
200
|
+
}
|
|
192
201
|
return null
|
|
193
202
|
}
|
|
194
203
|
|
|
@@ -263,6 +272,23 @@ function updateFragment(
|
|
|
263
272
|
return oldChild
|
|
264
273
|
}
|
|
265
274
|
|
|
275
|
+
function updateInlineFnChild(
|
|
276
|
+
parent: VNode,
|
|
277
|
+
oldChild: VNode | null,
|
|
278
|
+
expr: Function
|
|
279
|
+
) {
|
|
280
|
+
if (oldChild === null || oldChild.type !== $INLINE_FN) {
|
|
281
|
+
return createVNode(parent, $INLINE_FN, { expr })
|
|
282
|
+
}
|
|
283
|
+
if (__DEV__) {
|
|
284
|
+
dev_emitUpdateNode()
|
|
285
|
+
}
|
|
286
|
+
oldChild.props = { expr }
|
|
287
|
+
oldChild.flags |= FLAG_UPDATE
|
|
288
|
+
oldChild.sibling = null
|
|
289
|
+
return oldChild
|
|
290
|
+
}
|
|
291
|
+
|
|
266
292
|
function createChild(parent: VNode, child: unknown): VNode | null {
|
|
267
293
|
if (isValidTextChild(child)) {
|
|
268
294
|
return createVNode(parent, "#text", { nodeValue: "" + child })
|
|
@@ -283,6 +309,10 @@ function createChild(parent: VNode, child: unknown): VNode | null {
|
|
|
283
309
|
return createVNode(parent, $FRAGMENT, { children: child })
|
|
284
310
|
}
|
|
285
311
|
|
|
312
|
+
if (typeof child === "function") {
|
|
313
|
+
return createVNode(parent, $INLINE_FN, { expr: child })
|
|
314
|
+
}
|
|
315
|
+
|
|
286
316
|
return null
|
|
287
317
|
}
|
|
288
318
|
|
|
@@ -316,14 +346,11 @@ function updateFromMap(
|
|
|
316
346
|
const isSig = Signal.isSignal(child)
|
|
317
347
|
if (isSig || isValidTextChild(child)) {
|
|
318
348
|
const oldChild = existingChildren.get(index)
|
|
319
|
-
if (oldChild) {
|
|
349
|
+
if (oldChild?.type === "#text") {
|
|
320
350
|
if (oldChild.props.nodeValue === child) {
|
|
321
351
|
return oldChild
|
|
322
352
|
}
|
|
323
|
-
if (
|
|
324
|
-
oldChild.type === "#text" &&
|
|
325
|
-
Signal.isSignal(oldChild.props.nodeValue)
|
|
326
|
-
) {
|
|
353
|
+
if (Signal.isSignal(oldChild.props.nodeValue)) {
|
|
327
354
|
oldChild.cleanups?.["nodeValue"]?.()
|
|
328
355
|
}
|
|
329
356
|
}
|
|
@@ -356,11 +383,11 @@ function updateFromMap(
|
|
|
356
383
|
|
|
357
384
|
if (Array.isArray(child)) {
|
|
358
385
|
const props = { children: child }
|
|
359
|
-
const oldChild = existingChildren.get(index)
|
|
360
386
|
if (__DEV__) {
|
|
361
387
|
markListChild(child)
|
|
362
388
|
}
|
|
363
|
-
|
|
389
|
+
const oldChild = existingChildren.get(index)
|
|
390
|
+
if (oldChild?.type === $FRAGMENT) {
|
|
364
391
|
if (__DEV__) {
|
|
365
392
|
dev_emitUpdateNode()
|
|
366
393
|
}
|
|
@@ -372,6 +399,21 @@ function updateFromMap(
|
|
|
372
399
|
return createVNode(parent, $FRAGMENT, props, null, index)
|
|
373
400
|
}
|
|
374
401
|
|
|
402
|
+
if (typeof child === "function") {
|
|
403
|
+
const props = { expr: child }
|
|
404
|
+
const oldChild = existingChildren.get(index)
|
|
405
|
+
if (oldChild?.type === $INLINE_FN) {
|
|
406
|
+
if (__DEV__) {
|
|
407
|
+
dev_emitUpdateNode()
|
|
408
|
+
}
|
|
409
|
+
oldChild.flags |= FLAG_UPDATE
|
|
410
|
+
oldChild.props = props
|
|
411
|
+
return oldChild
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return createVNode(parent, $INLINE_FN, props, null, index)
|
|
415
|
+
}
|
|
416
|
+
|
|
375
417
|
return null
|
|
376
418
|
}
|
|
377
419
|
|
|
@@ -472,7 +514,7 @@ function getNearestParentFcTag(vNode: VNode) {
|
|
|
472
514
|
function createVNode(
|
|
473
515
|
parent: VNode,
|
|
474
516
|
type: VNode["type"],
|
|
475
|
-
props
|
|
517
|
+
props?: VNode["props"],
|
|
476
518
|
key: VNode["key"] = null,
|
|
477
519
|
index = 0
|
|
478
520
|
): VNode {
|