kiru 0.53.0 → 0.54.0-preview.1
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/components/derive.js +3 -2
- package/dist/components/derive.js.map +1 -1
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +6 -2
- package/dist/dom.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/hooks/usePromise.d.ts +2 -1
- package/dist/hooks/usePromise.d.ts.map +1 -1
- package/dist/hooks/usePromise.js +31 -62
- package/dist/hooks/usePromise.js.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/router/client/index.d.ts +4 -2
- package/dist/router/client/index.d.ts.map +1 -1
- package/dist/router/client/index.js +59 -13
- package/dist/router/client/index.js.map +1 -1
- package/dist/router/constants.d.ts +2 -0
- package/dist/router/constants.d.ts.map +1 -0
- package/dist/router/constants.js +2 -0
- package/dist/router/constants.js.map +1 -0
- package/dist/router/context.d.ts +2 -0
- package/dist/router/context.d.ts.map +1 -1
- package/dist/router/context.js +5 -1
- package/dist/router/context.js.map +1 -1
- package/dist/router/fileRouterController.d.ts +2 -0
- package/dist/router/fileRouterController.d.ts.map +1 -1
- package/dist/router/fileRouterController.js +195 -107
- package/dist/router/fileRouterController.js.map +1 -1
- package/dist/router/globals.d.ts +3 -0
- package/dist/router/globals.d.ts.map +1 -1
- package/dist/router/globals.js +3 -0
- package/dist/router/globals.js.map +1 -1
- package/dist/router/guard.d.ts +17 -0
- package/dist/router/guard.d.ts.map +1 -0
- package/dist/router/guard.js +45 -0
- package/dist/router/guard.js.map +1 -0
- package/dist/router/head.d.ts.map +1 -1
- package/dist/router/head.js +5 -7
- package/dist/router/head.js.map +1 -1
- package/dist/router/index.d.ts +2 -1
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +2 -1
- package/dist/router/index.js.map +1 -1
- package/dist/router/{server → ssg}/index.d.ts +4 -3
- package/dist/router/ssg/index.d.ts.map +1 -0
- package/dist/router/{server → ssg}/index.js +8 -5
- package/dist/router/ssg/index.js.map +1 -0
- package/dist/router/ssr/index.d.ts +20 -0
- package/dist/router/ssr/index.d.ts.map +1 -0
- package/dist/router/ssr/index.js +163 -0
- package/dist/router/ssr/index.js.map +1 -0
- package/dist/router/types.d.ts +42 -16
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.internal.d.ts +4 -0
- package/dist/router/types.internal.d.ts.map +1 -1
- package/dist/router/utils/index.d.ts +8 -3
- package/dist/router/utils/index.d.ts.map +1 -1
- package/dist/router/utils/index.js +38 -6
- package/dist/router/utils/index.js.map +1 -1
- package/dist/scheduler.d.ts +14 -3
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +3 -4
- package/dist/scheduler.js.map +1 -1
- package/dist/ssr/client.d.ts +1 -1
- package/dist/ssr/client.d.ts.map +1 -1
- package/dist/ssr/server.d.ts +9 -3
- package/dist/ssr/server.d.ts.map +1 -1
- package/dist/ssr/server.js +37 -30
- package/dist/ssr/server.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/format.d.ts +2 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +4 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/promise.d.ts +2 -0
- package/dist/utils/promise.d.ts.map +1 -1
- package/dist/utils/promise.js +45 -1
- package/dist/utils/promise.js.map +1 -1
- package/dist/utils/runtime.d.ts +1 -1
- package/dist/utils/runtime.js +1 -1
- package/package.json +8 -4
- package/src/components/derive.ts +5 -3
- package/src/dom.ts +5 -1
- package/src/globals.ts +1 -1
- package/src/hooks/usePromise.ts +57 -77
- package/src/index.ts +1 -1
- package/src/router/client/index.ts +114 -16
- package/src/router/constants.ts +1 -0
- package/src/router/context.ts +7 -1
- package/src/router/fileRouterController.ts +304 -132
- package/src/router/globals.ts +4 -0
- package/src/router/guard.ts +72 -0
- package/src/router/head.ts +5 -7
- package/src/router/index.ts +12 -1
- package/src/router/{server → ssg}/index.ts +17 -10
- package/src/router/ssr/index.ts +252 -0
- package/src/router/types.internal.ts +5 -0
- package/src/router/types.ts +53 -16
- package/src/router/utils/index.ts +74 -8
- package/src/scheduler.ts +20 -3
- package/src/ssr/client.ts +1 -1
- package/src/ssr/server.ts +58 -34
- package/src/types.ts +3 -0
- package/src/utils/format.ts +5 -0
- package/src/utils/index.ts +1 -1
- package/src/utils/promise.ts +70 -1
- package/src/utils/runtime.ts +1 -1
- package/dist/router/server/index.d.ts.map +0 -1
- package/dist/router/server/index.js.map +0 -1
package/dist/utils/runtime.js
CHANGED
|
@@ -39,7 +39,7 @@ function setRef(ref, value) {
|
|
|
39
39
|
ref.current = value;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Returns
|
|
42
|
+
* Returns true if called during 'dom' or 'hydrate' mode.
|
|
43
43
|
*/
|
|
44
44
|
function sideEffectsEnabled() {
|
|
45
45
|
return renderMode.current === "dom" || renderMode.current === "hydrate";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kiru",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0-preview.1",
|
|
4
4
|
"description": "A batteries-included, easy-to-use rendering library with a tiny footprint",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -34,9 +34,13 @@
|
|
|
34
34
|
"types": "./dist/router/client/index.d.ts",
|
|
35
35
|
"default": "./dist/router/client/index.js"
|
|
36
36
|
},
|
|
37
|
-
"./router/
|
|
38
|
-
"types": "./dist/router/
|
|
39
|
-
"default": "./dist/router/
|
|
37
|
+
"./router/ssg": {
|
|
38
|
+
"types": "./dist/router/ssg/index.d.ts",
|
|
39
|
+
"default": "./dist/router/ssg/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./router/ssr": {
|
|
42
|
+
"types": "./dist/router/ssr/index.d.ts",
|
|
43
|
+
"default": "./dist/router/ssr/index.js"
|
|
40
44
|
},
|
|
41
45
|
"./router/utils": {
|
|
42
46
|
"types": "./dist/router/utils/index.d.ts",
|
package/src/components/derive.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { sideEffectsEnabled } from "../utils/index.js"
|
|
|
7
7
|
import type { RecordHas } from "../types.utils"
|
|
8
8
|
import { isStatefulPromise, StreamDataThrowValue } from "../utils/promise.js"
|
|
9
9
|
|
|
10
|
+
const $NO_VALUE = Symbol("no value")
|
|
11
|
+
|
|
10
12
|
export type Derivable =
|
|
11
13
|
| Kiru.Signal<unknown>
|
|
12
14
|
| Kiru.StatefulPromise<unknown>
|
|
@@ -36,7 +38,7 @@ export type DeriveFallbackMode = "swr" | "fallback"
|
|
|
36
38
|
|
|
37
39
|
export interface DeriveProps<
|
|
38
40
|
T extends Derivable,
|
|
39
|
-
Mode extends DeriveFallbackMode = "
|
|
41
|
+
Mode extends DeriveFallbackMode = "swr"
|
|
40
42
|
> {
|
|
41
43
|
from: T
|
|
42
44
|
mode?: Mode
|
|
@@ -65,7 +67,7 @@ export function Derive<
|
|
|
65
67
|
U extends DeriveFallbackMode = "swr"
|
|
66
68
|
>(props: DeriveProps<T, U>) {
|
|
67
69
|
const { from, children, fallback, mode } = props
|
|
68
|
-
const prevSuccess = useRef<UnwrapDerive<T> |
|
|
70
|
+
const prevSuccess = useRef<UnwrapDerive<T> | typeof $NO_VALUE>($NO_VALUE)
|
|
69
71
|
|
|
70
72
|
const promises = new Set<Kiru.StatefulPromise<any>>()
|
|
71
73
|
let value: UnwrapDerive<T>
|
|
@@ -106,7 +108,7 @@ export function Derive<
|
|
|
106
108
|
const nodeRef = node.current!
|
|
107
109
|
Promise.allSettled(promises).then(() => requestUpdate(nodeRef))
|
|
108
110
|
|
|
109
|
-
if (mode !== "fallback" && prevSuccess.current) {
|
|
111
|
+
if (mode !== "fallback" && prevSuccess.current !== $NO_VALUE) {
|
|
110
112
|
return (children as ChildFnWithStale<UnwrapDerive<T>>)(
|
|
111
113
|
prevSuccess.current,
|
|
112
114
|
true
|
package/src/dom.ts
CHANGED
|
@@ -699,7 +699,11 @@ function commitDeletion(vNode: VNode) {
|
|
|
699
699
|
|
|
700
700
|
subs?.forEach((unsub) => unsub())
|
|
701
701
|
if (cleanups) Object.values(cleanups).forEach((c) => c())
|
|
702
|
-
while (hooks?.length)
|
|
702
|
+
while (hooks?.length) {
|
|
703
|
+
try {
|
|
704
|
+
hooks.pop()!.cleanup?.()
|
|
705
|
+
} catch {}
|
|
706
|
+
}
|
|
703
707
|
|
|
704
708
|
if (__DEV__) {
|
|
705
709
|
window.__kiru.profilingContext?.emit("removeNode", ctx)
|
package/src/globals.ts
CHANGED
package/src/hooks/usePromise.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { STREAMED_DATA_EVENT } from "../constants.js"
|
|
2
1
|
import { __DEV__ } from "../env.js"
|
|
3
2
|
import { hydrationMode, renderMode } from "../globals.js"
|
|
4
3
|
import { Signal, useSignal } from "../signals/base.js"
|
|
5
|
-
import {
|
|
4
|
+
import { depsRequireChange, useHook } from "./utils.js"
|
|
6
5
|
import { useId } from "./useId.js"
|
|
6
|
+
import {
|
|
7
|
+
createStatefulPromise,
|
|
8
|
+
resolveStreamedPromise,
|
|
9
|
+
} from "../utils/promise.js"
|
|
7
10
|
|
|
8
11
|
export { usePromise }
|
|
9
12
|
|
|
10
13
|
const nodeToPromiseIndex = new WeakMap<Kiru.VNode, number>()
|
|
11
14
|
|
|
15
|
+
type PromiseMutator = (signal: AbortSignal) => unknown | Promise<unknown>
|
|
16
|
+
|
|
12
17
|
type UsePromiseResult<T> = Kiru.StatefulPromise<T> & {
|
|
13
|
-
refresh: () => void
|
|
18
|
+
refresh: (mutator?: PromiseMutator) => void
|
|
14
19
|
isPending: Signal<boolean>
|
|
15
20
|
}
|
|
16
21
|
|
|
@@ -18,7 +23,7 @@ function usePromise<T>(
|
|
|
18
23
|
callback: (signal: AbortSignal) => Promise<T>,
|
|
19
24
|
deps: unknown[]
|
|
20
25
|
): UsePromiseResult<T> {
|
|
21
|
-
const
|
|
26
|
+
const vNodeId = useId()
|
|
22
27
|
const isPending = useSignal(true)
|
|
23
28
|
|
|
24
29
|
return useHook(
|
|
@@ -27,24 +32,54 @@ function usePromise<T>(
|
|
|
27
32
|
promise: UsePromiseResult<T>
|
|
28
33
|
abortController?: AbortController
|
|
29
34
|
deps?: unknown[]
|
|
35
|
+
refresh: (mutator?: PromiseMutator) => void
|
|
36
|
+
promiseId: string
|
|
37
|
+
epoch: number
|
|
30
38
|
},
|
|
31
39
|
({ hook, isInit, vNode, update }) => {
|
|
32
|
-
if (isInit
|
|
33
|
-
|
|
34
|
-
hook.
|
|
35
|
-
cleanupHook(hook)
|
|
36
|
-
|
|
37
|
-
const controller = (hook.abortController = new AbortController())
|
|
38
|
-
hook.cleanup = () => controller.abort()
|
|
40
|
+
if (isInit) {
|
|
41
|
+
hook.epoch = 0
|
|
42
|
+
hook.cleanup = () => hook.abortController?.abort("aborted")
|
|
39
43
|
|
|
40
44
|
const index = nodeToPromiseIndex.get(vNode) ?? 0
|
|
41
45
|
nodeToPromiseIndex.set(vNode, index + 1)
|
|
46
|
+
const promiseId = (hook.promiseId = `${vNodeId}:data:${index}`)
|
|
47
|
+
|
|
48
|
+
const refresh = (hook.refresh = (mutator?: PromiseMutator) => {
|
|
49
|
+
if (typeof mutator !== "function") {
|
|
50
|
+
delete hook.deps
|
|
51
|
+
return update()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
hook.cleanup!()
|
|
55
|
+
const signal = (hook.abortController = new AbortController()).signal
|
|
56
|
+
const promise = Promise.try(mutator, signal).then(() =>
|
|
57
|
+
callback(signal)
|
|
58
|
+
)
|
|
59
|
+
const epoch = ++hook.epoch
|
|
60
|
+
hook.promise = createStatefulPromise(
|
|
61
|
+
promiseId,
|
|
62
|
+
promise,
|
|
63
|
+
{ isPending, refresh },
|
|
64
|
+
() => epoch === hook.epoch && (isPending.value = false)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
isPending.value = true
|
|
68
|
+
update()
|
|
69
|
+
})
|
|
70
|
+
}
|
|
42
71
|
|
|
43
|
-
|
|
72
|
+
if (isInit || depsRequireChange(deps, hook.deps)) {
|
|
73
|
+
isPending.value = true
|
|
74
|
+
hook.deps = deps
|
|
75
|
+
hook.cleanup!()
|
|
76
|
+
|
|
77
|
+
const signal = (hook.abortController = new AbortController()).signal
|
|
78
|
+
const { promiseId, refresh } = hook
|
|
44
79
|
|
|
45
80
|
let promise: Promise<T>
|
|
46
81
|
if (renderMode.current === "string") {
|
|
47
|
-
// if we're rendering to
|
|
82
|
+
// if we're rendering to string, there's no need to fire the callback
|
|
48
83
|
promise = Promise.resolve() as Promise<T>
|
|
49
84
|
} else if (
|
|
50
85
|
renderMode.current === "hydrate" &&
|
|
@@ -52,76 +87,21 @@ function usePromise<T>(
|
|
|
52
87
|
) {
|
|
53
88
|
// if we're hydrating and the hydration mode is not static,
|
|
54
89
|
// we need to resolve the promise from cache/event
|
|
55
|
-
promise =
|
|
90
|
+
promise = resolveStreamedPromise<T>(promiseId, signal)
|
|
56
91
|
} else {
|
|
57
92
|
// dom / stream / (hydrate + static)
|
|
58
|
-
promise = callback(
|
|
93
|
+
promise = callback(signal)
|
|
59
94
|
}
|
|
60
95
|
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
},
|
|
69
|
-
}))
|
|
70
|
-
|
|
71
|
-
statefulPromise
|
|
72
|
-
.then((value) => {
|
|
73
|
-
statefulPromise.state = "fulfilled"
|
|
74
|
-
statefulPromise.value = value
|
|
75
|
-
isPending.value = false
|
|
76
|
-
})
|
|
77
|
-
.catch((error) => {
|
|
78
|
-
statefulPromise.state = "rejected"
|
|
79
|
-
statefulPromise.error =
|
|
80
|
-
error instanceof Error ? error : new Error(error)
|
|
81
|
-
})
|
|
96
|
+
const epoch = ++hook.epoch
|
|
97
|
+
hook.promise = createStatefulPromise(
|
|
98
|
+
promiseId,
|
|
99
|
+
promise,
|
|
100
|
+
{ isPending, refresh },
|
|
101
|
+
() => epoch === hook.epoch && (isPending.value = false)
|
|
102
|
+
)
|
|
82
103
|
}
|
|
83
104
|
return hook.promise
|
|
84
105
|
}
|
|
85
106
|
)
|
|
86
107
|
}
|
|
87
|
-
|
|
88
|
-
interface DeferredPromiseEventDetail<T> {
|
|
89
|
-
id: string
|
|
90
|
-
data?: T
|
|
91
|
-
error?: string
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function resolveDeferredPromise<T>(
|
|
95
|
-
id: string,
|
|
96
|
-
signal: AbortSignal
|
|
97
|
-
): Promise<T> {
|
|
98
|
-
return new Promise<T>((resolve, reject) => {
|
|
99
|
-
const deferralCache: Map<string, { data?: T; error?: string }> = // @ts-ignore
|
|
100
|
-
(window[STREAMED_DATA_EVENT] ??= new Map())
|
|
101
|
-
|
|
102
|
-
const existing = deferralCache.get(id)
|
|
103
|
-
if (existing) {
|
|
104
|
-
const { data, error } = existing
|
|
105
|
-
deferralCache.delete(id)
|
|
106
|
-
if (error) return reject(error)
|
|
107
|
-
return resolve(data!)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const onDataEvent = (event: Event) => {
|
|
111
|
-
const { detail } = event as CustomEvent<DeferredPromiseEventDetail<T>>
|
|
112
|
-
if (detail.id === id) {
|
|
113
|
-
deferralCache.delete(id)
|
|
114
|
-
window.removeEventListener(STREAMED_DATA_EVENT, onDataEvent)
|
|
115
|
-
const { data, error } = detail
|
|
116
|
-
if (error) return reject(error)
|
|
117
|
-
resolve(data!)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
window.addEventListener(STREAMED_DATA_EVENT, onDataEvent)
|
|
122
|
-
signal.addEventListener("abort", () => {
|
|
123
|
-
window.removeEventListener(STREAMED_DATA_EVENT, onDataEvent)
|
|
124
|
-
reject()
|
|
125
|
-
})
|
|
126
|
-
})
|
|
127
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -1,45 +1,91 @@
|
|
|
1
|
-
// initClient({ dir, baseUrl, pages, layouts })
|
|
2
|
-
|
|
3
1
|
import { createElement } from "../../element.js"
|
|
4
2
|
import { hydrate } from "../../ssr/client.js"
|
|
5
3
|
import { FileRouter } from "../fileRouter.js"
|
|
4
|
+
import { toArray } from "../../utils/format.js"
|
|
5
|
+
import { resolveStreamedPromise } from "../../utils/promise.js"
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
matchModules,
|
|
8
8
|
matchRoute,
|
|
9
9
|
match404Route,
|
|
10
10
|
parseQuery,
|
|
11
|
+
runBeforeEachGuards,
|
|
12
|
+
runAfterEachGuards,
|
|
13
|
+
runBeforeEnterHooks,
|
|
11
14
|
} from "../utils/index.js"
|
|
12
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
FormattedViteImportMap,
|
|
17
|
+
GuardModule,
|
|
18
|
+
PageModule,
|
|
19
|
+
} from "../types.internal"
|
|
13
20
|
import type { FileRouterConfig, FileRouterPreloadConfig } from "../types"
|
|
14
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
fileRouterInstance,
|
|
23
|
+
fileRouterRoute,
|
|
24
|
+
requestContext,
|
|
25
|
+
routerCache,
|
|
26
|
+
} from "../globals.js"
|
|
15
27
|
import { FileRouterController } from "../fileRouterController.js"
|
|
16
28
|
import { FileRouterDataLoadError } from "../errors.js"
|
|
17
29
|
import { __DEV__ } from "../../env.js"
|
|
18
30
|
import { RouterCache } from "../cache.js"
|
|
31
|
+
import { RequestContext } from "../context.js"
|
|
32
|
+
import { PAGE_DATA_PROMISE_ID } from "../constants.js"
|
|
33
|
+
import { AsyncTaskState } from "../../types.js"
|
|
19
34
|
|
|
20
35
|
interface InitClientOptions {
|
|
21
36
|
dir: string
|
|
22
37
|
baseUrl: string
|
|
23
|
-
pages: FormattedViteImportMap
|
|
38
|
+
pages: FormattedViteImportMap<PageModule>
|
|
24
39
|
layouts: FormattedViteImportMap
|
|
40
|
+
guards?: FormattedViteImportMap<GuardModule>
|
|
25
41
|
transition: boolean
|
|
42
|
+
hydrationMode?: Kiru.HydrationMode
|
|
26
43
|
}
|
|
27
44
|
|
|
28
45
|
export async function initClient(options: InitClientOptions) {
|
|
29
46
|
routerCache.current = new RouterCache()
|
|
30
|
-
const {
|
|
47
|
+
const {
|
|
48
|
+
dir,
|
|
49
|
+
baseUrl,
|
|
50
|
+
pages,
|
|
51
|
+
layouts,
|
|
52
|
+
guards,
|
|
53
|
+
transition,
|
|
54
|
+
hydrationMode = "static",
|
|
55
|
+
} = options
|
|
31
56
|
|
|
57
|
+
try {
|
|
58
|
+
requestContext.current = JSON.parse(
|
|
59
|
+
document.querySelector("[k-request-context]")!.innerHTML
|
|
60
|
+
)
|
|
61
|
+
} catch {}
|
|
62
|
+
|
|
63
|
+
const preloaded = await preparePreloadConfig(
|
|
64
|
+
options,
|
|
65
|
+
false,
|
|
66
|
+
hydrationMode === "dynamic"
|
|
67
|
+
)
|
|
32
68
|
const config: FileRouterConfig = {
|
|
33
69
|
dir,
|
|
34
70
|
baseUrl,
|
|
35
71
|
pages,
|
|
36
72
|
layouts,
|
|
73
|
+
guards,
|
|
37
74
|
transition,
|
|
38
|
-
preloaded
|
|
75
|
+
preloaded,
|
|
39
76
|
}
|
|
40
77
|
|
|
41
|
-
const
|
|
42
|
-
|
|
78
|
+
const children = createElement(FileRouter, { config })
|
|
79
|
+
|
|
80
|
+
const app =
|
|
81
|
+
hydrationMode === "static"
|
|
82
|
+
? children
|
|
83
|
+
: createElement(RequestContext.Provider, {
|
|
84
|
+
value: requestContext.current,
|
|
85
|
+
children,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
hydrate(app, document.body, { hydrationMode })
|
|
43
89
|
|
|
44
90
|
if (__DEV__) {
|
|
45
91
|
onLoadedDev()
|
|
@@ -48,7 +94,8 @@ export async function initClient(options: InitClientOptions) {
|
|
|
48
94
|
|
|
49
95
|
async function preparePreloadConfig(
|
|
50
96
|
options: InitClientOptions,
|
|
51
|
-
isStatic404 = false
|
|
97
|
+
isStatic404 = false,
|
|
98
|
+
isSSR = false
|
|
52
99
|
): Promise<FileRouterPreloadConfig> {
|
|
53
100
|
let pageProps = {}
|
|
54
101
|
let cacheData: null | { value: unknown } = null
|
|
@@ -68,7 +115,25 @@ async function preparePreloadConfig(
|
|
|
68
115
|
throw new Error(`No route defined (path: ${url.pathname}).`)
|
|
69
116
|
}
|
|
70
117
|
|
|
71
|
-
const layoutEntries =
|
|
118
|
+
const layoutEntries = matchModules(options.layouts, routeMatch.routeSegments)
|
|
119
|
+
|
|
120
|
+
// Load and run guards before loading page
|
|
121
|
+
// if SSR, do we even need to do this?
|
|
122
|
+
let guardModules: GuardModule[] = []
|
|
123
|
+
if (options.guards) {
|
|
124
|
+
const guardEntries = matchModules(options.guards, routeMatch.routeSegments)
|
|
125
|
+
guardModules = await Promise.all(guardEntries.map((entry) => entry.load()))
|
|
126
|
+
|
|
127
|
+
const redirectPath = await runBeforeEachGuards(
|
|
128
|
+
guardModules,
|
|
129
|
+
{ ...requestContext.current },
|
|
130
|
+
url.pathname
|
|
131
|
+
)
|
|
132
|
+
if (redirectPath !== null) {
|
|
133
|
+
window.location.href = redirectPath
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
72
137
|
fileRouterInstance.current = new FileRouterController()
|
|
73
138
|
fileRouterRoute.current = routeMatch.route
|
|
74
139
|
const [page, ...layouts] = await Promise.all([
|
|
@@ -77,6 +142,26 @@ async function preparePreloadConfig(
|
|
|
77
142
|
])
|
|
78
143
|
fileRouterRoute.current = null
|
|
79
144
|
|
|
145
|
+
const onBeforeEnter = page.config?.hooks?.onBeforeEnter
|
|
146
|
+
if (onBeforeEnter) {
|
|
147
|
+
const redirectPath = await runBeforeEnterHooks(
|
|
148
|
+
toArray(onBeforeEnter),
|
|
149
|
+
{ ...requestContext.current },
|
|
150
|
+
url.pathname
|
|
151
|
+
)
|
|
152
|
+
if (redirectPath !== null) {
|
|
153
|
+
window.location.href = redirectPath
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const query = parseQuery(window.location.search)
|
|
160
|
+
|
|
161
|
+
let pagePropsPromise:
|
|
162
|
+
| Promise<AsyncTaskState<unknown, FileRouterDataLoadError>>
|
|
163
|
+
| undefined
|
|
164
|
+
|
|
80
165
|
// Check if page has static props pre-loaded at build time
|
|
81
166
|
if (page.__KIRU_STATIC_PROPS__) {
|
|
82
167
|
const staticProps = page.__KIRU_STATIC_PROPS__[window.location.pathname]
|
|
@@ -95,26 +180,39 @@ async function preparePreloadConfig(
|
|
|
95
180
|
pageProps = { loading: true, data: null, error: null }
|
|
96
181
|
|
|
97
182
|
const loader = page.config.loader
|
|
98
|
-
|
|
99
|
-
|
|
183
|
+
if (isSSR) {
|
|
184
|
+
pagePropsPromise = resolveStreamedPromise(PAGE_DATA_PROMISE_ID)
|
|
185
|
+
.then((data) => ({ data, error: null, loading: false } as const))
|
|
186
|
+
.catch((error) => ({ data: null, error, loading: false } as const))
|
|
187
|
+
} else if (!loader.static && loader.cache) {
|
|
100
188
|
const cacheKey = {
|
|
101
189
|
path: window.location.pathname,
|
|
102
190
|
params: routeMatch.params,
|
|
103
|
-
query
|
|
191
|
+
query,
|
|
104
192
|
}
|
|
105
193
|
|
|
106
194
|
cacheData = routerCache.current!.get(cacheKey, loader.cache)
|
|
107
195
|
}
|
|
108
196
|
}
|
|
109
197
|
|
|
198
|
+
window.__kiru.on("mount", () => {
|
|
199
|
+
runAfterEachGuards(
|
|
200
|
+
guardModules,
|
|
201
|
+
{ ...requestContext.current },
|
|
202
|
+
url.pathname
|
|
203
|
+
)
|
|
204
|
+
})
|
|
205
|
+
|
|
110
206
|
return {
|
|
111
207
|
pages: options.pages,
|
|
112
208
|
layouts: options.layouts,
|
|
209
|
+
guards: options.guards,
|
|
113
210
|
page: page,
|
|
114
211
|
pageProps: pageProps,
|
|
212
|
+
pagePropsPromise,
|
|
115
213
|
pageLayouts: layouts,
|
|
116
214
|
params: routeMatch.params,
|
|
117
|
-
query:
|
|
215
|
+
query: query,
|
|
118
216
|
route: routeMatch.route,
|
|
119
217
|
cacheData,
|
|
120
218
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PAGE_DATA_PROMISE_ID = "__KIRU_PAGE_DATA__"
|
package/src/router/context.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { useContext } from "../hooks/index.js"
|
|
1
2
|
import { createContext } from "../context.js"
|
|
2
3
|
import { __DEV__ } from "../env.js"
|
|
3
|
-
import { useContext } from "../hooks/index.js"
|
|
4
4
|
import type { RouteQuery, RouterState } from "./types.js"
|
|
5
5
|
|
|
6
6
|
export interface ReloadOptions {
|
|
@@ -74,3 +74,9 @@ if (__DEV__) {
|
|
|
74
74
|
export function useFileRouter(): FileRouterContextType {
|
|
75
75
|
return useContext(RouterContext)
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
export const RequestContext = createContext<Kiru.RequestContext>({})
|
|
79
|
+
|
|
80
|
+
export function useRequestContext() {
|
|
81
|
+
return useContext(RequestContext)
|
|
82
|
+
}
|