kiru 1.4.1 → 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 +11 -28
- 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 +17 -31
- 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/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 {
|
package/src/resource.ts
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { $HMR_ACCEPT, STREAMED_DATA_EVENT } from "./constants.js"
|
|
2
2
|
import { hydrationMode, node, renderMode } from "./globals.js"
|
|
3
3
|
import { Signal, signal } from "./signals/base.js"
|
|
4
|
+
import { executeWithTracking } from "./signals/tracking.js"
|
|
4
5
|
import { createVNodeId, registerVNodeCleanup } from "./utils/vdom.js"
|
|
5
6
|
import { generateRandomID } from "./utils/generateId.js"
|
|
6
7
|
import { __DEV__, isBrowser } from "./env.js"
|
|
7
8
|
import { GenericHMRAcceptor, performHmrAccept } from "./hmr.js"
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
* Returns true if the value is a {@link Resource}
|
|
11
|
-
*/
|
|
12
|
-
export function isResource(thing: unknown): thing is Resource<unknown> {
|
|
13
|
-
return (
|
|
14
|
-
Signal.isSignal(thing) &&
|
|
15
|
-
"promise" in thing &&
|
|
16
|
-
thing["promise"] instanceof Promise
|
|
17
|
-
)
|
|
18
|
-
}
|
|
10
|
+
export type ResourceSource = Record<string, Signal<unknown>> | Signal<unknown>
|
|
19
11
|
|
|
20
|
-
|
|
12
|
+
type InnerOf<T> = T extends Kiru.Signal<infer V> ? V : never
|
|
13
|
+
|
|
14
|
+
type UnwrapResourceSource<T extends ResourceSource> =
|
|
15
|
+
T extends Kiru.Signal<unknown>
|
|
16
|
+
? InnerOf<T>
|
|
17
|
+
: { [K in keyof T]: InnerOf<T[K]> }
|
|
21
18
|
|
|
22
19
|
interface ResourceState<T> {
|
|
23
20
|
error: Signal<Error | null>
|
|
@@ -32,14 +29,31 @@ export interface ResourceLoaderContext {
|
|
|
32
29
|
signal: AbortSignal
|
|
33
30
|
}
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
const resourceMeta = new WeakMap<Kiru.VNode, { id: string; index: number }>()
|
|
33
|
+
|
|
34
|
+
export function resource<T>(
|
|
35
|
+
callback: (ctx: ResourceLoaderContext) => Promise<T>
|
|
36
|
+
): Resource<T>
|
|
37
|
+
export function resource<T, Source extends ResourceSource>(
|
|
38
|
+
source: Source,
|
|
39
|
+
callback: (
|
|
40
|
+
source: UnwrapResourceSource<Source>,
|
|
41
|
+
ctx: ResourceLoaderContext
|
|
42
|
+
) => Promise<T>
|
|
43
|
+
): Resource<T>
|
|
44
|
+
export function resource<T, Source extends ResourceSource>(
|
|
45
|
+
callbackOrSource: Source | ((ctx: ResourceLoaderContext) => Promise<T>),
|
|
46
|
+
callback?: (
|
|
47
|
+
source: UnwrapResourceSource<Source>,
|
|
48
|
+
ctx: ResourceLoaderContext
|
|
49
|
+
) => Promise<T>
|
|
38
50
|
): Resource<T> {
|
|
39
51
|
const data = signal(void 0 as T)
|
|
40
52
|
const error = signal<Error | null>(null)
|
|
41
53
|
const isPending = signal(true)
|
|
42
54
|
|
|
55
|
+
let controller = new AbortController()
|
|
56
|
+
|
|
43
57
|
let promiseId = ""
|
|
44
58
|
const vNode = node.current
|
|
45
59
|
if (!vNode) {
|
|
@@ -62,17 +76,34 @@ export function resource<T, Source>(
|
|
|
62
76
|
promiseId = generateRandomID()
|
|
63
77
|
}
|
|
64
78
|
|
|
65
|
-
const
|
|
66
|
-
resource.promise = createPromise(
|
|
79
|
+
const updateResource = () => {
|
|
80
|
+
resource.promise = createPromise()
|
|
67
81
|
resource.notify()
|
|
68
|
-
}
|
|
82
|
+
}
|
|
69
83
|
|
|
70
|
-
let
|
|
84
|
+
let unsubFromSource: (() => void) | undefined
|
|
85
|
+
if (typeof callbackOrSource === "object") {
|
|
86
|
+
if (Signal.isSignal(callbackOrSource)) {
|
|
87
|
+
unsubFromSource = callbackOrSource.subscribe(updateResource)
|
|
88
|
+
} else {
|
|
89
|
+
const unsubs: (() => void)[] = []
|
|
90
|
+
for (const key in callbackOrSource) {
|
|
91
|
+
if (!Signal.isSignal(callbackOrSource[key])) continue
|
|
92
|
+
unsubs.push(callbackOrSource[key].subscribe(updateResource))
|
|
93
|
+
}
|
|
94
|
+
unsubFromSource = () => {
|
|
95
|
+
unsubs.forEach((unsub) => unsub())
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const observedSignalUnsubs = new Map<string, () => void>()
|
|
71
101
|
const dispose = () => {
|
|
72
102
|
if (!controller.signal.aborted) controller.abort()
|
|
73
103
|
Signal.dispose(data)
|
|
74
104
|
Signal.dispose(isPending)
|
|
75
|
-
unsub()
|
|
105
|
+
observedSignalUnsubs.forEach((unsub) => unsub())
|
|
106
|
+
unsubFromSource?.()
|
|
76
107
|
}
|
|
77
108
|
|
|
78
109
|
if (vNode) {
|
|
@@ -85,7 +116,7 @@ export function resource<T, Source>(
|
|
|
85
116
|
promise: undefined as unknown as Kiru.StatefulPromise<T>,
|
|
86
117
|
refetch() {
|
|
87
118
|
data.value = void 0 as T
|
|
88
|
-
this.promise = createPromise(
|
|
119
|
+
this.promise = createPromise()
|
|
89
120
|
},
|
|
90
121
|
dispose,
|
|
91
122
|
})
|
|
@@ -111,33 +142,38 @@ export function resource<T, Source>(
|
|
|
111
142
|
}
|
|
112
143
|
}
|
|
113
144
|
|
|
114
|
-
|
|
115
|
-
queueMicrotask(() => {
|
|
116
|
-
resource.promise = createPromise(source.peek())
|
|
117
|
-
})
|
|
118
|
-
} else {
|
|
119
|
-
resource.promise = createPromise(source.peek())
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function createPromise(source: Source): Kiru.StatefulPromise<T> {
|
|
145
|
+
function createPromise(): Kiru.StatefulPromise<T> {
|
|
123
146
|
controller.abort()
|
|
124
147
|
const ctrl = (controller = new AbortController())
|
|
125
148
|
isPending.value = true
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
149
|
+
const newPromise = executeWithTracking({
|
|
150
|
+
fn: () => {
|
|
151
|
+
let promise: Promise<T>
|
|
152
|
+
if (renderMode.current === "string") {
|
|
153
|
+
// if we're rendering to a string, there's no need to fire the callback
|
|
154
|
+
promise = Promise.resolve() as Promise<T>
|
|
155
|
+
} else if (
|
|
156
|
+
renderMode.current === "hydrate" &&
|
|
157
|
+
hydrationMode.current === "dynamic"
|
|
158
|
+
) {
|
|
159
|
+
// if we're hydrating and the hydration mode is not static,
|
|
160
|
+
// we need to resolve the promise from cache/event
|
|
161
|
+
promise = resolveDeferredPromise<T>(promiseId, ctrl.signal)
|
|
162
|
+
} else {
|
|
163
|
+
// stream / dom / (hydrate + static)
|
|
164
|
+
if (typeof callbackOrSource === "function") {
|
|
165
|
+
promise = callbackOrSource({ signal: ctrl.signal })
|
|
166
|
+
} else {
|
|
167
|
+
const source = unwrapResourceSource(callbackOrSource)
|
|
168
|
+
promise = callback!(source, { signal: ctrl.signal })
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return promise
|
|
172
|
+
},
|
|
173
|
+
id: Signal.id(resource),
|
|
174
|
+
onDepChanged: updateResource,
|
|
175
|
+
subs: observedSignalUnsubs,
|
|
176
|
+
})
|
|
141
177
|
|
|
142
178
|
const statefulPromise: Kiru.StatefulPromise<T> = Object.assign(newPromise, {
|
|
143
179
|
id: promiseId,
|
|
@@ -162,6 +198,12 @@ export function resource<T, Source>(
|
|
|
162
198
|
return statefulPromise
|
|
163
199
|
}
|
|
164
200
|
|
|
201
|
+
if (__DEV__ && isBrowser && window.__kiru.HMRContext?.isReplacement()) {
|
|
202
|
+
queueMicrotask(() => (resource.promise = createPromise()))
|
|
203
|
+
} else {
|
|
204
|
+
resource.promise = createPromise()
|
|
205
|
+
}
|
|
206
|
+
|
|
165
207
|
return resource
|
|
166
208
|
}
|
|
167
209
|
|
|
@@ -205,3 +247,29 @@ function resolveDeferredPromise<T>(
|
|
|
205
247
|
})
|
|
206
248
|
})
|
|
207
249
|
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Returns true if the value is a {@link Resource}
|
|
253
|
+
*/
|
|
254
|
+
export function isResource(thing: unknown): thing is Resource<unknown> {
|
|
255
|
+
return (
|
|
256
|
+
Signal.isSignal(thing) &&
|
|
257
|
+
"promise" in thing &&
|
|
258
|
+
thing["promise"] instanceof Promise
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function unwrapResourceSource<T extends ResourceSource>(
|
|
263
|
+
source: T
|
|
264
|
+
): UnwrapResourceSource<T> {
|
|
265
|
+
if (Signal.isSignal(source)) {
|
|
266
|
+
return source.peek() as UnwrapResourceSource<T>
|
|
267
|
+
}
|
|
268
|
+
const out: Record<string, unknown> = {}
|
|
269
|
+
for (const key in source) {
|
|
270
|
+
if (Signal.isSignal(source[key])) {
|
|
271
|
+
out[key] = source[key].peek()
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return out as UnwrapResourceSource<T>
|
|
275
|
+
}
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
match404Route,
|
|
10
10
|
parseQuery,
|
|
11
11
|
} from "../utils/index.js"
|
|
12
|
-
import type { FormattedViteImportMap, PageModule } from "../types.internal"
|
|
13
|
-
import type { FileRouterConfig, FileRouterPreloadConfig } from "../types"
|
|
12
|
+
import type { FormattedViteImportMap, PageModule } from "../types.internal.js"
|
|
13
|
+
import type { FileRouterConfig, FileRouterPreloadConfig } from "../types.js"
|
|
14
14
|
import { fileRouterInstance, fileRouterRoute, routerCache } from "../globals.js"
|
|
15
15
|
import { FileRouterController } from "../fileRouterController.js"
|
|
16
16
|
import { FileRouterDataLoadError } from "../errors.js"
|
package/src/router/globals.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { RouterCache } from "./cache"
|
|
2
|
-
import type { FileRouterController } from "./fileRouterController"
|
|
1
|
+
import type { RouterCache } from "./cache.js"
|
|
2
|
+
import type { FileRouterController } from "./fileRouterController.js"
|
|
3
3
|
|
|
4
4
|
export const fileRouterInstance = {
|
|
5
5
|
current: null as FileRouterController | null,
|
package/src/router/link.ts
CHANGED
package/src/router/pageConfig.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __DEV__, isBrowser } from "../env.js"
|
|
2
2
|
import { fileRouterInstance } from "./globals.js"
|
|
3
|
-
import type { PageConfig } from "./types"
|
|
3
|
+
import type { PageConfig } from "./types.js"
|
|
4
4
|
|
|
5
5
|
export function definePageConfig<T>(config: PageConfig<T>): PageConfig<T> {
|
|
6
6
|
if (__DEV__ && isBrowser) {
|
package/src/router/types.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { AsyncTaskState } from "../types.utils"
|
|
2
|
-
import type { FileRouterDataLoadError } from "./errors"
|
|
1
|
+
import type { AsyncTaskState } from "../types.utils.js"
|
|
2
|
+
import type { FileRouterDataLoadError } from "./errors.js"
|
|
3
3
|
import type {
|
|
4
4
|
DefaultComponentModule,
|
|
5
5
|
FormattedViteImportMap,
|
|
6
6
|
PageModule,
|
|
7
|
-
} from "./types.internal"
|
|
7
|
+
} from "./types.internal.js"
|
|
8
8
|
|
|
9
9
|
export interface FileRouterPreloadConfig {
|
|
10
10
|
pages: FormattedViteImportMap
|
package/src/scheduler.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
DomVNode,
|
|
3
|
+
ErrorBoundaryNode,
|
|
4
|
+
FunctionVNode,
|
|
5
|
+
InlineFnNode,
|
|
6
|
+
} from "./types.utils.js"
|
|
2
7
|
import {
|
|
3
8
|
$ERROR_BOUNDARY,
|
|
9
|
+
$INLINE_FN,
|
|
4
10
|
CONSECUTIVE_DIRTY_LIMIT,
|
|
5
11
|
FLAG_DELETION,
|
|
6
12
|
FLAG_DIRTY,
|
|
@@ -30,7 +36,7 @@ import { node, postEffectCleanups, renderMode, setups } from "./globals.js"
|
|
|
30
36
|
import { hydrationStack } from "./hydration.js"
|
|
31
37
|
import { reconcileChildren } from "./reconciler.js"
|
|
32
38
|
import { isHmrUpdate } from "./hmr.js"
|
|
33
|
-
import type { AppHandle } from "./appHandle"
|
|
39
|
+
import type { AppHandle } from "./appHandle.js"
|
|
34
40
|
|
|
35
41
|
type VNode = Kiru.VNode
|
|
36
42
|
|
|
@@ -298,7 +304,18 @@ function updateExoticComponent(vNode: VNode): VNode | null {
|
|
|
298
304
|
const { props, type } = vNode
|
|
299
305
|
let children = props.children
|
|
300
306
|
|
|
301
|
-
if (type === $
|
|
307
|
+
if (type === $INLINE_FN) {
|
|
308
|
+
node.current = vNode
|
|
309
|
+
let render = (props as InlineFnNode["props"]).expr
|
|
310
|
+
if (__DEV__) {
|
|
311
|
+
render = latest(render)
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
children = render()
|
|
315
|
+
} finally {
|
|
316
|
+
node.current = null
|
|
317
|
+
}
|
|
318
|
+
} else if (type === $ERROR_BOUNDARY) {
|
|
302
319
|
const n = vNode as ErrorBoundaryNode
|
|
303
320
|
const { error } = n
|
|
304
321
|
if (error) {
|
package/src/signals/base.ts
CHANGED
|
@@ -5,8 +5,14 @@ import {
|
|
|
5
5
|
generateRandomID,
|
|
6
6
|
registerVNodeCleanup,
|
|
7
7
|
} from "../utils/index.js"
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
$DEV_FILE_LINK,
|
|
10
|
+
$HMR_ACCEPT,
|
|
11
|
+
$INLINE_FN,
|
|
12
|
+
$SIGNAL,
|
|
13
|
+
} from "../constants.js"
|
|
9
14
|
import { __DEV__, isBrowser } from "../env.js"
|
|
15
|
+
import { KiruError } from "../error.js"
|
|
10
16
|
import { node } from "../globals.js"
|
|
11
17
|
import { requestUpdate } from "../scheduler.js"
|
|
12
18
|
import { tracking } from "./tracking.js"
|
|
@@ -58,7 +64,15 @@ export class Signal<T> {
|
|
|
58
64
|
|
|
59
65
|
const n = node.current
|
|
60
66
|
if (n) {
|
|
61
|
-
|
|
67
|
+
if (__DEV__ && n.type === $INLINE_FN) {
|
|
68
|
+
throw new KiruError({
|
|
69
|
+
message: "Signals cannot be created inside inline functions",
|
|
70
|
+
vNode: n,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
if (sideEffectsEnabled()) {
|
|
74
|
+
registerVNodeCleanup(n, this.$id, Signal.dispose.bind(null, this))
|
|
75
|
+
}
|
|
62
76
|
}
|
|
63
77
|
}
|
|
64
78
|
|
|
@@ -151,6 +165,10 @@ export class Signal<T> {
|
|
|
151
165
|
return typeof x === "object" && !!x && $SIGNAL in x
|
|
152
166
|
}
|
|
153
167
|
|
|
168
|
+
static id(signal: Signal<any>) {
|
|
169
|
+
return signal.$id
|
|
170
|
+
}
|
|
171
|
+
|
|
154
172
|
static subscribers(signal: Signal<any>) {
|
|
155
173
|
return signal.$subs
|
|
156
174
|
}
|
package/src/signals/effect.ts
CHANGED
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
import type { Signal } from "./base.js"
|
|
12
12
|
import type { SignalValues } from "./types.js"
|
|
13
13
|
import { node } from "../globals.js"
|
|
14
|
+
import { $INLINE_FN } from "../constants.js"
|
|
15
|
+
import { KiruError } from "../error.js"
|
|
14
16
|
|
|
15
17
|
type EffectCallbackReturn = (() => void) | void
|
|
16
18
|
|
|
@@ -37,6 +39,12 @@ export class Effect<const Deps extends readonly Signal<unknown>[] = []> {
|
|
|
37
39
|
}
|
|
38
40
|
const n = node.current
|
|
39
41
|
if (n) {
|
|
42
|
+
if (__DEV__ && n.type === $INLINE_FN) {
|
|
43
|
+
throw new KiruError({
|
|
44
|
+
message: "Effects cannot be created inside inline functions",
|
|
45
|
+
vNode: n,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
40
48
|
if (!sideEffectsEnabled()) return // prevent side effects in non-browser environments
|
|
41
49
|
registerVNodeCleanup(n, this.id, this.stop.bind(this))
|
|
42
50
|
}
|
package/src/signals/tracking.ts
CHANGED
|
@@ -32,25 +32,19 @@ export function executeWithTracking<T, Deps extends readonly Signal<unknown>[]>(
|
|
|
32
32
|
ctx: TrackedExecutionContext<T, Deps>
|
|
33
33
|
): T {
|
|
34
34
|
const { id, subs, fn, deps = [], onDepChanged } = ctx
|
|
35
|
-
let observations:
|
|
35
|
+
let observations: TrackingStackObservations | undefined
|
|
36
36
|
|
|
37
37
|
effectQueue.delete(id)
|
|
38
|
-
const isServer = !!node.current && !sideEffectsEnabled()
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
// Prevent side effects in non-browser environments while rendering
|
|
40
|
+
if (!node.current || sideEffectsEnabled()) {
|
|
41
|
+
observations = new Map()
|
|
42
42
|
tracking.stack.push(observations)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const result = fn(...(deps.map((s) => s.value) as SignalValues<Deps>))
|
|
46
46
|
|
|
47
|
-
if (
|
|
48
|
-
for (const [id, unsub] of subs) {
|
|
49
|
-
if (observations!.has(id)) continue
|
|
50
|
-
unsub()
|
|
51
|
-
subs.delete(id)
|
|
52
|
-
}
|
|
53
|
-
|
|
47
|
+
if (observations) {
|
|
54
48
|
const effect = () => {
|
|
55
49
|
if (!effectQueue.size) {
|
|
56
50
|
queueMicrotask(tick)
|
|
@@ -58,11 +52,19 @@ export function executeWithTracking<T, Deps extends readonly Signal<unknown>[]>(
|
|
|
58
52
|
effectQueue.set(id, onDepChanged)
|
|
59
53
|
}
|
|
60
54
|
|
|
61
|
-
for (const [id,
|
|
62
|
-
if (subs.has(id))
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
for (const [id, signal] of observations) {
|
|
56
|
+
if (!subs.has(id)) {
|
|
57
|
+
subs.set(id, signal.subscribe(effect))
|
|
58
|
+
}
|
|
65
59
|
}
|
|
60
|
+
|
|
61
|
+
for (const [id, unsub] of subs) {
|
|
62
|
+
if (!observations.has(id)) {
|
|
63
|
+
unsub()
|
|
64
|
+
subs.delete(id)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
66
68
|
tracking.stack.pop()
|
|
67
69
|
}
|
|
68
70
|
|
package/src/ssr/client.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AppHandle, AppHandleOptions } from "../appHandle"
|
|
1
|
+
import type { AppHandle, AppHandleOptions } from "../appHandle.js"
|
|
2
2
|
import { hydrationStack } from "../hydration.js"
|
|
3
3
|
import { hydrationMode, renderMode } from "../globals.js"
|
|
4
4
|
import { mount } from "../index.js"
|