kiru 0.54.0-preview.0 → 0.54.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/memo.d.ts +1 -3
- package/dist/components/memo.d.ts.map +1 -1
- package/dist/components/memo.js +2 -2
- package/dist/components/memo.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -23
- package/dist/context.js.map +1 -1
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +109 -72
- package/dist/dom.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +2 -4
- package/dist/error.js.map +1 -1
- package/dist/form/index.d.ts.map +1 -1
- package/dist/form/index.js +6 -10
- package/dist/form/index.js.map +1 -1
- package/dist/globals.d.ts +1 -1
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js.map +1 -1
- package/dist/hmr.d.ts +1 -0
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +11 -3
- package/dist/hmr.js.map +1 -1
- package/dist/hooks/useEffectEvent.d.ts.map +1 -1
- package/dist/hooks/useEffectEvent.js.map +1 -1
- package/dist/hooks/usePromise.d.ts.map +1 -1
- package/dist/hooks/usePromise.js.map +1 -1
- package/dist/hooks/utils.d.ts.map +1 -1
- package/dist/hooks/utils.js +10 -10
- package/dist/hooks/utils.js.map +1 -1
- package/dist/hydration.d.ts +6 -13
- package/dist/hydration.d.ts.map +1 -1
- package/dist/hydration.js +20 -50
- package/dist/hydration.js.map +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +3 -6
- package/dist/reconciler.js.map +1 -1
- package/dist/recursiveRender.d.ts.map +1 -1
- package/dist/recursiveRender.js +9 -8
- package/dist/recursiveRender.js.map +1 -1
- package/dist/renderToString.d.ts.map +1 -1
- package/dist/renderToString.js.map +1 -1
- package/dist/router/client/index.d.ts +2 -4
- package/dist/router/client/index.d.ts.map +1 -1
- package/dist/router/client/index.js +11 -49
- package/dist/router/client/index.js.map +1 -1
- package/dist/router/context.d.ts +5 -2
- package/dist/router/context.d.ts.map +1 -1
- package/dist/router/context.js +1 -5
- package/dist/router/context.js.map +1 -1
- package/dist/router/fileRouter.d.ts.map +1 -1
- package/dist/router/fileRouter.js +2 -4
- package/dist/router/fileRouter.js.map +1 -1
- package/dist/router/fileRouterController.d.ts +2 -2
- package/dist/router/fileRouterController.d.ts.map +1 -1
- package/dist/router/fileRouterController.js +39 -101
- package/dist/router/fileRouterController.js.map +1 -1
- package/dist/router/globals.d.ts +0 -3
- package/dist/router/globals.d.ts.map +1 -1
- package/dist/router/globals.js +0 -3
- package/dist/router/globals.js.map +1 -1
- package/dist/router/head.d.ts.map +1 -1
- package/dist/router/head.js +7 -5
- package/dist/router/head.js.map +1 -1
- package/dist/router/index.d.ts +1 -2
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +1 -2
- package/dist/router/index.js.map +1 -1
- package/dist/router/link.js +3 -3
- package/dist/router/link.js.map +1 -1
- package/dist/router/{ssg → server}/index.d.ts +3 -4
- package/dist/router/server/index.d.ts.map +1 -0
- package/dist/router/{ssg → server}/index.js +5 -8
- package/dist/router/server/index.js.map +1 -0
- package/dist/router/types.d.ts +4 -37
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.internal.d.ts +0 -4
- package/dist/router/types.internal.d.ts.map +1 -1
- package/dist/router/utils/index.d.ts +3 -8
- package/dist/router/utils/index.d.ts.map +1 -1
- package/dist/router/utils/index.js +8 -40
- package/dist/router/utils/index.js.map +1 -1
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +60 -53
- package/dist/scheduler.js.map +1 -1
- package/dist/signals/base.d.ts +0 -2
- package/dist/signals/base.d.ts.map +1 -1
- package/dist/signals/base.js +0 -6
- package/dist/signals/base.js.map +1 -1
- package/dist/signals/computed.d.ts +3 -0
- package/dist/signals/computed.d.ts.map +1 -1
- package/dist/signals/computed.js +29 -20
- package/dist/signals/computed.js.map +1 -1
- package/dist/signals/for.d.ts +3 -3
- package/dist/signals/for.d.ts.map +1 -1
- package/dist/signals/for.js +2 -1
- package/dist/signals/for.js.map +1 -1
- package/dist/signals/utils.d.ts.map +1 -1
- package/dist/signals/utils.js +2 -1
- package/dist/signals/utils.js.map +1 -1
- package/dist/signals/watch.d.ts.map +1 -1
- package/dist/signals/watch.js +18 -22
- package/dist/signals/watch.js.map +1 -1
- package/dist/ssr/client.d.ts +1 -1
- package/dist/ssr/client.d.ts.map +1 -1
- package/dist/ssr/client.js +0 -2
- package/dist/ssr/client.js.map +1 -1
- package/dist/ssr/server.d.ts +2 -1
- package/dist/ssr/server.d.ts.map +1 -1
- package/dist/ssr/server.js +19 -16
- package/dist/ssr/server.js.map +1 -1
- package/dist/types.d.ts +0 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.dom.d.ts +3 -3
- package/dist/types.dom.d.ts.map +1 -1
- package/dist/utils/format.d.ts +1 -2
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +1 -4
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/runtime.d.ts +3 -2
- package/dist/utils/runtime.d.ts.map +1 -1
- package/dist/utils/runtime.js +5 -2
- package/dist/utils/runtime.js.map +1 -1
- package/dist/utils/vdom.d.ts.map +1 -1
- package/dist/utils/vdom.js +2 -2
- package/dist/utils/vdom.js.map +1 -1
- package/package.json +4 -8
- package/src/components/memo.ts +3 -11
- package/src/context.ts +1 -24
- package/src/dom.ts +145 -96
- package/src/error.ts +2 -4
- package/src/form/index.ts +6 -9
- package/src/globals.ts +1 -1
- package/src/hmr.ts +14 -5
- package/src/hooks/useEffectEvent.ts +0 -1
- package/src/hooks/usePromise.ts +0 -1
- package/src/hooks/utils.ts +12 -12
- package/src/hydration.ts +21 -57
- package/src/reconciler.ts +2 -6
- package/src/recursiveRender.ts +10 -9
- package/src/renderToString.ts +0 -1
- package/src/router/client/index.ts +14 -100
- package/src/router/context.ts +6 -7
- package/src/router/fileRouter.ts +2 -6
- package/src/router/fileRouterController.ts +39 -159
- package/src/router/globals.ts +0 -4
- package/src/router/head.ts +7 -5
- package/src/router/index.ts +1 -12
- package/src/router/link.ts +3 -3
- package/src/router/{ssg → server}/index.ts +10 -17
- package/src/router/types.internal.ts +0 -5
- package/src/router/types.ts +4 -48
- package/src/router/utils/index.ts +16 -79
- package/src/scheduler.ts +83 -70
- package/src/signals/base.ts +0 -8
- package/src/signals/computed.ts +30 -18
- package/src/signals/for.ts +15 -10
- package/src/signals/utils.ts +2 -1
- package/src/signals/watch.ts +27 -22
- package/src/ssr/client.ts +1 -4
- package/src/ssr/server.ts +21 -20
- package/src/types.dom.ts +4 -5
- package/src/types.ts +0 -10
- package/src/utils/format.ts +0 -5
- package/src/utils/runtime.ts +6 -2
- package/src/utils/vdom.ts +2 -7
- package/dist/router/guard.d.ts +0 -17
- package/dist/router/guard.d.ts.map +0 -1
- package/dist/router/guard.js +0 -45
- package/dist/router/guard.js.map +0 -1
- package/dist/router/ssg/index.d.ts.map +0 -1
- package/dist/router/ssg/index.js.map +0 -1
- package/dist/router/ssr/index.d.ts +0 -20
- package/dist/router/ssr/index.d.ts.map +0 -1
- package/dist/router/ssr/index.js +0 -160
- package/dist/router/ssr/index.js.map +0 -1
- package/src/router/guard.ts +0 -72
- package/src/router/ssr/index.ts +0 -247
package/src/components/memo.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { $MEMO } from "../constants.js"
|
|
2
2
|
import { createElement } from "../element.js"
|
|
3
|
-
import { __DEV__ } from "../env.js"
|
|
4
3
|
|
|
5
4
|
function _arePropsEqual<T extends Record<string, unknown>>(
|
|
6
5
|
prevProps: T,
|
|
@@ -17,12 +16,7 @@ function _arePropsEqual<T extends Record<string, unknown>>(
|
|
|
17
16
|
|
|
18
17
|
export interface MemoFn<T extends Record<string, unknown> = {}> {
|
|
19
18
|
(props: T): JSX.Element
|
|
20
|
-
[$MEMO]:
|
|
21
|
-
arePropsEqual: (
|
|
22
|
-
prevProps: Record<string, unknown>,
|
|
23
|
-
nextProps: Record<string, unknown>
|
|
24
|
-
) => boolean
|
|
25
|
-
}
|
|
19
|
+
[$MEMO]: (prevProps: T, nextProps: T) => boolean
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
export function memo<T extends Record<string, unknown> = {}>(
|
|
@@ -34,14 +28,12 @@ export function memo<T extends Record<string, unknown> = {}>(
|
|
|
34
28
|
return createElement(fn, props)
|
|
35
29
|
},
|
|
36
30
|
{
|
|
37
|
-
[$MEMO]:
|
|
31
|
+
[$MEMO]: arePropsEqual,
|
|
38
32
|
displayName: "Kiru.memo",
|
|
39
33
|
}
|
|
40
34
|
)
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
export function isMemoFn(fn: Function & { [$MEMO]?: any }): fn is MemoFn {
|
|
44
|
-
return
|
|
45
|
-
typeof fn === "function" && typeof fn[$MEMO]?.arePropsEqual === "function"
|
|
46
|
-
)
|
|
38
|
+
return typeof fn[$MEMO] === "function"
|
|
47
39
|
}
|
package/src/context.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import { $CONTEXT, $CONTEXT_PROVIDER
|
|
1
|
+
import { $CONTEXT, $CONTEXT_PROVIDER } from "./constants.js"
|
|
2
2
|
import { createElement } from "./element.js"
|
|
3
|
-
import { __DEV__ } from "./env.js"
|
|
4
|
-
import { GenericHMRAcceptor } from "./hmr.js"
|
|
5
3
|
import { useState } from "./hooks/useState.js"
|
|
6
|
-
import { requestUpdate } from "./scheduler.js"
|
|
7
|
-
import { traverseApply } from "./utils/index.js"
|
|
8
4
|
|
|
9
5
|
export function createContext<T>(defaultValue: T): Kiru.Context<T> {
|
|
10
6
|
const ctx: Kiru.Context<T> = {
|
|
@@ -25,25 +21,6 @@ export function createContext<T>(defaultValue: T): Kiru.Context<T> {
|
|
|
25
21
|
return this.Provider.displayName || "Anonymous Context"
|
|
26
22
|
},
|
|
27
23
|
}
|
|
28
|
-
if (__DEV__) {
|
|
29
|
-
const asHmrAcceptor = ctx as any as GenericHMRAcceptor<Kiru.Context<T>>
|
|
30
|
-
asHmrAcceptor[$HMR_ACCEPT] = {
|
|
31
|
-
inject: (prev) => {
|
|
32
|
-
const newProvider = ctx.Provider
|
|
33
|
-
window.__kiru.apps.forEach((ctx) => {
|
|
34
|
-
traverseApply(ctx.rootNode, (vNode) => {
|
|
35
|
-
if (vNode.type === prev.Provider) {
|
|
36
|
-
vNode.type = newProvider
|
|
37
|
-
vNode.hmrUpdated = true
|
|
38
|
-
requestUpdate(vNode)
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
},
|
|
43
|
-
destroy: () => {},
|
|
44
|
-
provide: () => ctx,
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
24
|
|
|
48
25
|
return ctx
|
|
49
26
|
}
|
package/src/dom.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
propToHtmlAttr,
|
|
6
6
|
getVNodeAppContext,
|
|
7
7
|
setRef,
|
|
8
|
+
isValidTextChild,
|
|
8
9
|
} from "./utils/index.js"
|
|
9
10
|
import {
|
|
10
11
|
booleanAttributes,
|
|
@@ -87,43 +88,35 @@ function createDom(vNode: DomVNode): SomeDom {
|
|
|
87
88
|
}
|
|
88
89
|
function createTextNode(vNode: VNode): Text {
|
|
89
90
|
const { nodeValue } = vNode.props
|
|
90
|
-
if (
|
|
91
|
-
return
|
|
91
|
+
if (Signal.isSignal(nodeValue)) {
|
|
92
|
+
return createSignalTextNode(vNode, nodeValue)
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
return document.createTextNode(nodeValue)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function createSignalTextNode(vNode: VNode, nodeValue: Signal<string>): Text {
|
|
94
99
|
const value = nodeValue.peek() ?? ""
|
|
95
100
|
const textNode = document.createTextNode(value)
|
|
96
101
|
subTextNode(vNode, textNode, nodeValue)
|
|
97
102
|
return textNode
|
|
98
103
|
}
|
|
99
104
|
|
|
100
|
-
function wrapFocusEventHandler(
|
|
101
|
-
|
|
102
|
-
evtName: "focus" | "blur",
|
|
103
|
-
callback: (event: FocusEvent) => void
|
|
104
|
-
) {
|
|
105
|
-
const wrappedHandlers = vNodeToWrappedFocusEventHandlersMap.get(vNode) ?? {}
|
|
106
|
-
const handler = (wrappedHandlers[evtName] = (event: FocusEvent) => {
|
|
105
|
+
function wrapFocusEventHandler(callback: (event: FocusEvent) => void) {
|
|
106
|
+
return (event: FocusEvent) => {
|
|
107
107
|
if (persistingFocus) {
|
|
108
108
|
event.preventDefault()
|
|
109
109
|
event.stopPropagation()
|
|
110
110
|
return
|
|
111
111
|
}
|
|
112
112
|
callback(event)
|
|
113
|
-
}
|
|
114
|
-
vNodeToWrappedFocusEventHandlersMap.set(vNode, wrappedHandlers)
|
|
115
|
-
return handler
|
|
113
|
+
}
|
|
116
114
|
}
|
|
117
115
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
blur?: (event: FocusEvent) => void
|
|
116
|
+
interface VNodeEventListenerObjects {
|
|
117
|
+
[key: string]: EventListenerObject
|
|
121
118
|
}
|
|
122
|
-
|
|
123
|
-
const vNodeToWrappedFocusEventHandlersMap = new WeakMap<
|
|
124
|
-
VNode,
|
|
125
|
-
WrappedFocusEventMap
|
|
126
|
-
>()
|
|
119
|
+
const eventListenerObjects = new WeakMap<VNode, VNodeEventListenerObjects>()
|
|
127
120
|
|
|
128
121
|
function updateDom(vNode: DomVNode) {
|
|
129
122
|
const { dom, prev, props, cleanups } = vNode
|
|
@@ -146,30 +139,39 @@ function updateDom(vNode: DomVNode) {
|
|
|
146
139
|
if (!(k in prevProps)) keys.push(k)
|
|
147
140
|
}
|
|
148
141
|
|
|
142
|
+
let events: VNodeEventListenerObjects | undefined
|
|
149
143
|
for (let i = 0; i < keys.length; i++) {
|
|
150
144
|
const key = keys[i]
|
|
151
145
|
const prevVal = prevProps[key]
|
|
152
146
|
const nextVal = nextProps[key]
|
|
153
147
|
|
|
154
148
|
if (propFilters.isEvent(key)) {
|
|
149
|
+
events ??= eventListenerObjects.get(vNode)
|
|
150
|
+
if (!events) eventListenerObjects.set(vNode, (events = {}))
|
|
151
|
+
|
|
155
152
|
if (prevVal !== nextVal || isHydration) {
|
|
156
153
|
const evtName = key.replace(EVENT_PREFIX_REGEX, "")
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
evtName
|
|
163
|
-
|
|
164
|
-
|
|
154
|
+
const evtListenerObj = events[evtName]
|
|
155
|
+
|
|
156
|
+
if (!nextVal) {
|
|
157
|
+
if (evtListenerObj) {
|
|
158
|
+
dom.removeEventListener(evtName, evtListenerObj)
|
|
159
|
+
delete events[evtName]
|
|
160
|
+
}
|
|
161
|
+
continue
|
|
165
162
|
}
|
|
166
163
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
isFocus ? wrapFocusEventHandler(vNode, evtName, nextVal) : nextVal
|
|
171
|
-
)
|
|
164
|
+
let handleEvent = nextVal.bind(void 0)
|
|
165
|
+
if (evtName === "focus" || evtName === "blur") {
|
|
166
|
+
handleEvent = wrapFocusEventHandler(handleEvent)
|
|
172
167
|
}
|
|
168
|
+
|
|
169
|
+
if (evtListenerObj) {
|
|
170
|
+
evtListenerObj.handleEvent = handleEvent
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
dom.addEventListener(evtName, (events[evtName] = { handleEvent }))
|
|
173
175
|
}
|
|
174
176
|
continue
|
|
175
177
|
}
|
|
@@ -206,7 +208,7 @@ function updateDom(vNode: DomVNode) {
|
|
|
206
208
|
}
|
|
207
209
|
}
|
|
208
210
|
|
|
209
|
-
function
|
|
211
|
+
function getSelectElementValue(dom: HTMLSelectElement) {
|
|
210
212
|
if (dom.multiple) {
|
|
211
213
|
return Array.from(dom.selectedOptions).map((option) => option.value)
|
|
212
214
|
}
|
|
@@ -231,7 +233,7 @@ const bindAttrToEventMap: Record<string, string> = {
|
|
|
231
233
|
playbackRate: "ratechange",
|
|
232
234
|
currentTime: "timeupdate",
|
|
233
235
|
}
|
|
234
|
-
const
|
|
236
|
+
const numericValueInputTypes = new Set(["progress", "meter", "number", "range"])
|
|
235
237
|
|
|
236
238
|
function setSignalProp(
|
|
237
239
|
vNode: VNode,
|
|
@@ -240,71 +242,80 @@ function setSignalProp(
|
|
|
240
242
|
signal: Signal<any>,
|
|
241
243
|
prevValue: unknown
|
|
242
244
|
) {
|
|
243
|
-
const cleanups = (vNode.cleanups ??= {})
|
|
244
245
|
const [modifier, attr] = key.split(":")
|
|
246
|
+
const cleanups = (vNode.cleanups ??= {})
|
|
245
247
|
if (modifier !== "bind") {
|
|
246
248
|
cleanups[key] = signal.subscribe((value, prev) => {
|
|
247
249
|
if (value === prev) return
|
|
248
250
|
setProp(dom, key, value, prev)
|
|
249
251
|
if (__DEV__) {
|
|
250
|
-
|
|
251
|
-
"signalAttrUpdate",
|
|
252
|
-
getVNodeAppContext(vNode)!
|
|
253
|
-
)
|
|
252
|
+
emitSignalAttrUpdate(vNode)
|
|
254
253
|
}
|
|
255
254
|
})
|
|
255
|
+
} else {
|
|
256
|
+
const evtName = bindAttrToEventMap[attr]
|
|
257
|
+
if (!evtName) {
|
|
258
|
+
if (__DEV__) {
|
|
259
|
+
console.error(
|
|
260
|
+
`[kiru]: ${attr} is not a valid element binding attribute.`
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
cleanups[key] = bindElementProp(vNode, dom, attr, evtName, signal)
|
|
266
|
+
}
|
|
256
267
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
setProp(dom,
|
|
261
|
-
return
|
|
268
|
+
const value = signal.peek()
|
|
269
|
+
const prev = unwrap(prevValue)
|
|
270
|
+
if (value !== prev) {
|
|
271
|
+
setProp(dom, attr ?? modifier, value, prev)
|
|
262
272
|
}
|
|
273
|
+
}
|
|
263
274
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return
|
|
275
|
+
function createElementValueReader(
|
|
276
|
+
dom: Exclude<SomeDom, Text>,
|
|
277
|
+
signal: Signal<any>
|
|
278
|
+
) {
|
|
279
|
+
if (dom instanceof HTMLInputElement) {
|
|
280
|
+
return createInputValueReader(dom, signal)
|
|
281
|
+
}
|
|
282
|
+
if (dom instanceof HTMLSelectElement) {
|
|
283
|
+
return () => getSelectElementValue(dom)
|
|
284
|
+
}
|
|
285
|
+
return () => (dom as any).value
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function bindElementProp(
|
|
289
|
+
vNode: VNode,
|
|
290
|
+
dom: Exclude<SomeDom, Text>,
|
|
291
|
+
attr: string,
|
|
292
|
+
evtName: string,
|
|
293
|
+
signal: Signal<any>
|
|
294
|
+
): () => void {
|
|
295
|
+
const writeToSignal = (val: any) => {
|
|
296
|
+
signal.sneak(val)
|
|
297
|
+
signal.notify((sub) => sub !== updateFromSignal)
|
|
270
298
|
}
|
|
271
299
|
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
300
|
+
const writeToElement =
|
|
301
|
+
dom instanceof HTMLSelectElement && attr === "value"
|
|
302
|
+
? (value: any) => setSelectElementValue(dom, value)
|
|
303
|
+
: (value: any) => ((dom as any)[attr] = value)
|
|
276
304
|
|
|
277
|
-
const
|
|
278
|
-
|
|
305
|
+
const updateFromSignal = (value: any) => {
|
|
306
|
+
writeToElement(value)
|
|
279
307
|
if (__DEV__) {
|
|
280
|
-
|
|
281
|
-
"signalAttrUpdate",
|
|
282
|
-
getVNodeAppContext(vNode)!
|
|
283
|
-
)
|
|
308
|
+
emitSignalAttrUpdate(vNode)
|
|
284
309
|
}
|
|
285
310
|
}
|
|
286
311
|
|
|
287
|
-
|
|
288
|
-
signal.sneak(val)
|
|
289
|
-
signal.notify((sub) => sub !== signalUpdateCallback)
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
let evtHandler: (evt: Event) => void
|
|
312
|
+
let evtHandler: EventListener
|
|
293
313
|
if (attr === "value") {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
evtHandler = () => {
|
|
297
|
-
let val: any = (dom as HTMLInputElement | HTMLSelectElement).value
|
|
298
|
-
if (isSelect) {
|
|
299
|
-
val = deriveSelectElementValue(dom)
|
|
300
|
-
} else if (typeof signal.peek() === "number" && useNumericValue) {
|
|
301
|
-
val = (dom as HTMLInputElement).valueAsNumber
|
|
302
|
-
}
|
|
303
|
-
setSigFromElement(val)
|
|
304
|
-
}
|
|
314
|
+
const readValue = createElementValueReader(dom, signal)
|
|
315
|
+
evtHandler = () => writeToSignal(readValue())
|
|
305
316
|
} else {
|
|
306
|
-
evtHandler = (
|
|
307
|
-
const val = (
|
|
317
|
+
evtHandler = () => {
|
|
318
|
+
const val = (dom as any)[attr]
|
|
308
319
|
/**
|
|
309
320
|
* the 'timeupdate' event is fired when the currentTime property is
|
|
310
321
|
* set (from code OR playback), so we need to prevent unnecessary
|
|
@@ -312,22 +323,42 @@ function setSignalProp(
|
|
|
312
323
|
* elements with the same signal bound to 'currentTime'
|
|
313
324
|
*/
|
|
314
325
|
if (attr === "currentTime" && signal.peek() === val) return
|
|
315
|
-
|
|
326
|
+
writeToSignal(val)
|
|
316
327
|
}
|
|
317
328
|
}
|
|
318
329
|
|
|
319
330
|
dom.addEventListener(evtName, evtHandler)
|
|
320
|
-
const unsub = signal.subscribe(
|
|
331
|
+
const unsub = signal.subscribe(updateFromSignal)
|
|
321
332
|
|
|
322
|
-
|
|
333
|
+
return () => {
|
|
323
334
|
dom.removeEventListener(evtName, evtHandler)
|
|
324
335
|
unsub()
|
|
325
336
|
}
|
|
337
|
+
}
|
|
326
338
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
339
|
+
function createInputValueReader(
|
|
340
|
+
dom: HTMLInputElement,
|
|
341
|
+
signal: Signal<any>
|
|
342
|
+
): () => any {
|
|
343
|
+
const t = dom.type
|
|
344
|
+
const v = signal.peek()
|
|
345
|
+
|
|
346
|
+
if (t === "date" && v instanceof Date) {
|
|
347
|
+
return () => dom.valueAsDate
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (numericValueInputTypes.has(t) && typeof v === "number") {
|
|
351
|
+
return () => dom.valueAsNumber
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return () => dom.value
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function emitSignalAttrUpdate(vNode: VNode) {
|
|
358
|
+
window.__kiru.profilingContext?.emit(
|
|
359
|
+
"signalAttrUpdate",
|
|
360
|
+
getVNodeAppContext(vNode)!
|
|
361
|
+
)
|
|
331
362
|
}
|
|
332
363
|
|
|
333
364
|
function subTextNode(vNode: VNode, textNode: Text, signal: Signal<string>) {
|
|
@@ -343,21 +374,39 @@ function subTextNode(vNode: VNode, textNode: Text, signal: Signal<string>) {
|
|
|
343
374
|
})
|
|
344
375
|
}
|
|
345
376
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
377
|
+
/**
|
|
378
|
+
* Creates and inserts an empty signal-bound text node into
|
|
379
|
+
* the dom tree if the signal value is null or undefined.
|
|
380
|
+
*/
|
|
381
|
+
function getOrCreateTextNode(vNode: VNode): MaybeDom {
|
|
382
|
+
const sig = vNode.props.nodeValue
|
|
383
|
+
if (!Signal.isSignal(sig)) {
|
|
384
|
+
return hydrationStack.getCurrentChild()
|
|
349
385
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
386
|
+
|
|
387
|
+
const value = sig.peek()
|
|
388
|
+
if (isValidTextChild(value)) {
|
|
389
|
+
return hydrationStack.getCurrentChild()
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const dom = createSignalTextNode(vNode, sig)
|
|
393
|
+
const currentChild = hydrationStack.getCurrentChild()
|
|
394
|
+
|
|
395
|
+
if (!currentChild) {
|
|
396
|
+
return hydrationStack.getCurrentParent().appendChild(dom)
|
|
353
397
|
}
|
|
354
|
-
|
|
355
|
-
|
|
398
|
+
|
|
399
|
+
currentChild.before(dom)
|
|
356
400
|
return dom
|
|
357
401
|
}
|
|
358
402
|
|
|
359
403
|
function hydrateDom(vNode: VNode) {
|
|
360
|
-
const dom =
|
|
404
|
+
const dom =
|
|
405
|
+
vNode.type === "#text"
|
|
406
|
+
? getOrCreateTextNode(vNode)
|
|
407
|
+
: hydrationStack.getCurrentChild()
|
|
408
|
+
|
|
409
|
+
hydrationStack.bumpChildIndex()
|
|
361
410
|
|
|
362
411
|
if (!dom) {
|
|
363
412
|
throw new KiruError({
|
package/src/error.ts
CHANGED
|
@@ -25,10 +25,8 @@ export class KiruError extends Error {
|
|
|
25
25
|
: optionsOrMessage.message
|
|
26
26
|
super(message)
|
|
27
27
|
if (typeof optionsOrMessage !== "string") {
|
|
28
|
-
if (__DEV__) {
|
|
29
|
-
|
|
30
|
-
this.customNodeStack = captureErrorStack(optionsOrMessage.vNode)
|
|
31
|
-
}
|
|
28
|
+
if (__DEV__ && optionsOrMessage?.vNode) {
|
|
29
|
+
this.customNodeStack = captureErrorStack(optionsOrMessage.vNode)
|
|
32
30
|
}
|
|
33
31
|
this.fatal = optionsOrMessage?.fatal
|
|
34
32
|
}
|
package/src/form/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
safeStringify,
|
|
5
5
|
shallowCompare,
|
|
6
6
|
generateRandomID,
|
|
7
|
+
call,
|
|
7
8
|
} from "../utils/index.js"
|
|
8
9
|
import { useEffect } from "../hooks/useEffect.js"
|
|
9
10
|
import { useMemo } from "../hooks/useMemo.js"
|
|
@@ -260,7 +261,7 @@ function createFormController<T extends Record<string, unknown>>(
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
|
|
263
|
-
formFieldUpdaters.get(name)?.forEach(
|
|
264
|
+
formFieldUpdaters.get(name)?.forEach(call)
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
const setFieldValue = <K extends RecordKey<T>>(
|
|
@@ -410,9 +411,7 @@ function createFormController<T extends Record<string, unknown>>(
|
|
|
410
411
|
delete formFieldErrors[fieldName as RecordKey<T>]
|
|
411
412
|
}
|
|
412
413
|
updateSubscribers()
|
|
413
|
-
formFieldUpdaters.forEach((updaters) =>
|
|
414
|
-
updaters.forEach((update) => update())
|
|
415
|
-
})
|
|
414
|
+
formFieldUpdaters.forEach((updaters) => updaters.forEach(call))
|
|
416
415
|
}
|
|
417
416
|
|
|
418
417
|
const validateForm = async () => {
|
|
@@ -634,11 +633,9 @@ export function useForm<T extends Record<string, unknown> = {}>(
|
|
|
634
633
|
"useFormSubscription",
|
|
635
634
|
{ sub: null! as FormStateSubscriber<T> },
|
|
636
635
|
({ hook, isInit, isHMR, update }) => {
|
|
637
|
-
if (__DEV__) {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
hook.cleanup?.()
|
|
641
|
-
}
|
|
636
|
+
if (__DEV__ && isHMR) {
|
|
637
|
+
isInit = true
|
|
638
|
+
hook.cleanup?.()
|
|
642
639
|
}
|
|
643
640
|
if (isInit) {
|
|
644
641
|
hook.sub = {
|
package/src/globals.ts
CHANGED
package/src/hmr.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { $HMR_ACCEPT, $DEV_FILE_LINK } from "./constants.js"
|
|
2
|
-
import { __DEV__ } from "./env.js"
|
|
3
2
|
import { traverseApply } from "./utils/index.js"
|
|
4
|
-
import { requestUpdate } from "./scheduler.js"
|
|
3
|
+
import { flushSync, requestUpdate } from "./scheduler.js"
|
|
5
4
|
import { Signal } from "./signals/base.js"
|
|
6
5
|
import type { WatchEffect } from "./signals/watch.js"
|
|
7
6
|
import type { Store } from "./store.js"
|
|
@@ -25,6 +24,11 @@ type HotVarDesc = {
|
|
|
25
24
|
link: string
|
|
26
25
|
}
|
|
27
26
|
|
|
27
|
+
let _isHmrUpdate = false
|
|
28
|
+
export function isHmrUpdate() {
|
|
29
|
+
return _isHmrUpdate
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
export function isGenericHmrAcceptor(
|
|
29
33
|
thing: unknown
|
|
30
34
|
): thing is GenericHMRAcceptor<any> {
|
|
@@ -128,15 +132,20 @@ export function createHMRContext() {
|
|
|
128
132
|
if (vNode.type === oldEntry.value) {
|
|
129
133
|
vNode.type = newEntry.value as any
|
|
130
134
|
dirtiedApps.add(ctx)
|
|
131
|
-
vNode.hmrUpdated = true
|
|
132
135
|
}
|
|
133
136
|
})
|
|
134
137
|
})
|
|
135
138
|
}
|
|
136
139
|
}
|
|
137
|
-
dirtiedApps.forEach((ctx) => ctx.rootNode && requestUpdate(ctx.rootNode))
|
|
138
|
-
isModuleReplacementExecution = false
|
|
139
140
|
|
|
141
|
+
if (dirtiedApps.size) {
|
|
142
|
+
_isHmrUpdate = true
|
|
143
|
+
dirtiedApps.forEach((ctx) => requestUpdate(ctx.rootNode))
|
|
144
|
+
flushSync()
|
|
145
|
+
_isHmrUpdate = false
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
isModuleReplacementExecution = false
|
|
140
149
|
currentModuleMemory = null
|
|
141
150
|
currentModuleFilePath = null
|
|
142
151
|
}
|
package/src/hooks/usePromise.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { STREAMED_DATA_EVENT } from "../constants.js"
|
|
2
|
-
import { __DEV__ } from "../env.js"
|
|
3
2
|
import { hydrationMode, renderMode } from "../globals.js"
|
|
4
3
|
import { Signal, useSignal } from "../signals/base.js"
|
|
5
4
|
import { cleanupHook, depsRequireChange, useHook } from "./utils.js"
|
package/src/hooks/utils.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { __DEV__ } from "../env.js"
|
|
|
3
3
|
import { hookIndex, node } from "../globals.js"
|
|
4
4
|
import { noop } from "../utils/index.js"
|
|
5
5
|
import { requestUpdate } from "../scheduler.js"
|
|
6
|
+
import { isHmrUpdate } from "../hmr.js"
|
|
6
7
|
export {
|
|
7
8
|
cleanupHook,
|
|
8
9
|
depsRequireChange,
|
|
@@ -115,17 +116,16 @@ function useHook<
|
|
|
115
116
|
): ReturnType<U> {
|
|
116
117
|
const vNode = getVNodeOrError(hookName)
|
|
117
118
|
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
119
|
+
if (
|
|
120
|
+
__DEV__ &&
|
|
121
|
+
currentHookName !== null &&
|
|
122
|
+
!nestedHookWarnings.has(hookName + currentHookName)
|
|
123
|
+
) {
|
|
124
|
+
nestedHookWarnings.add(hookName + currentHookName)
|
|
125
|
+
throw new KiruError({
|
|
126
|
+
message: `Nested primitive "useHook" calls are not supported. "${hookName}" was called inside "${currentHookName}". Strange will most certainly happen.`,
|
|
127
|
+
vNode,
|
|
128
|
+
})
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
const queueEffect = (callback: Function, opts?: { immediate?: boolean }) => {
|
|
@@ -177,7 +177,7 @@ function useHook<
|
|
|
177
177
|
const res = (callback as HookCallback<T>)({
|
|
178
178
|
hook,
|
|
179
179
|
isInit: !oldHook,
|
|
180
|
-
isHMR:
|
|
180
|
+
isHMR: isHmrUpdate(),
|
|
181
181
|
update: () => requestUpdate(vNode),
|
|
182
182
|
queueEffect,
|
|
183
183
|
vNode,
|