flamefront 0.1.0-alpha.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -620
- package/package.json +45 -22
- package/src/cli.ts +16 -1
- package/src/entry.ts +49 -0
- package/src/fetch.ts +280 -0
- package/src/fragment-client.ts +127 -87
- package/src/fragment-hydration-client.tsx +81 -0
- package/src/fragment-protocol.ts +3 -3
- package/src/fragment.tsx +656 -0
- package/src/glob.ts +294 -0
- package/src/identifier-prefix.ts +5 -0
- package/src/index.ts +569 -49
- package/src/lifecycle.ts +7 -6
- package/src/octane-client-core.ts +10 -3
- package/src/octane-client.ts +12 -6
- package/src/octane-compiler.d.ts +19 -0
- package/src/octane-default-renderer.tsx +124 -0
- package/src/octane-router-document.ts +19 -2
- package/src/{octane.ts → octane.tsx} +152 -145
- package/src/output.ts +48 -0
- package/src/remix-route-data.ts +28 -20
- package/src/remix-router.ts +10 -4
- package/src/route-data-client.ts +22 -12
- package/src/route-prefetch.ts +19 -12
- package/src/server.ts +122 -24
- package/src/srvx.ts +43 -183
- package/src/typegen.ts +207 -0
- package/src/virtual-remix-routes.d.ts +16 -1
- package/src/vite.ts +364 -27
- package/src/fragment.ts +0 -260
package/src/fragment.ts
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createContext,
|
|
3
|
-
createElement,
|
|
4
|
-
useContext,
|
|
5
|
-
useLayoutEffect,
|
|
6
|
-
useRef,
|
|
7
|
-
} from "octane"
|
|
8
|
-
import {
|
|
9
|
-
UNSAFE_DataRouterContext,
|
|
10
|
-
UNSAFE_DataRouterStateContext,
|
|
11
|
-
UNSAFE_FetchersContext,
|
|
12
|
-
UNSAFE_LocationContext,
|
|
13
|
-
UNSAFE_NavigationContext,
|
|
14
|
-
UNSAFE_RouteContext,
|
|
15
|
-
UNSAFE_ViewTransitionContext,
|
|
16
|
-
useLocation as useRouterLocation,
|
|
17
|
-
} from "@octanejs/remix-router"
|
|
18
|
-
import type { GeneratedRouteMetadata } from "./index.ts"
|
|
19
|
-
import { isStaticFragmentRequest } from "./fragment-protocol.ts"
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
getStaticFragment,
|
|
23
|
-
shouldHydrateStaticFragment,
|
|
24
|
-
type StaticFragmentArtifact,
|
|
25
|
-
type StaticFragmentRoutingOptions,
|
|
26
|
-
} from "./fragment-client.ts"
|
|
27
|
-
|
|
28
|
-
export {
|
|
29
|
-
assertStaticFragmentArtifact,
|
|
30
|
-
getStaticFragment,
|
|
31
|
-
isStaticFragmentArtifact,
|
|
32
|
-
loadStaticFragment,
|
|
33
|
-
prefetchStaticFragment,
|
|
34
|
-
shouldHydrateStaticFragment,
|
|
35
|
-
staticFragmentProtocol,
|
|
36
|
-
} from "./fragment-client.ts"
|
|
37
|
-
export type {
|
|
38
|
-
StaticFragmentArtifact,
|
|
39
|
-
StaticFragmentBoundary,
|
|
40
|
-
StaticFragmentLoadOptions,
|
|
41
|
-
StaticFragmentRoutingOptions,
|
|
42
|
-
} from "./fragment-client.ts"
|
|
43
|
-
|
|
44
|
-
export interface StaticFragmentRouteOptions {
|
|
45
|
-
readonly metadata: Pick<
|
|
46
|
-
GeneratedRouteMetadata,
|
|
47
|
-
"id" | "boundary" | "kind" | "parent" | "path" | "hydration"
|
|
48
|
-
>
|
|
49
|
-
readonly routing: StaticFragmentRoutingOptions
|
|
50
|
-
/** Used only for initial document hydration and post-insertion hydration. */
|
|
51
|
-
readonly fallbackComponent: unknown
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const rootSlot = Symbol.for("flamefront:static-fragment:root")
|
|
55
|
-
const hydrationSlot = Symbol.for("flamefront:static-fragment:hydrate")
|
|
56
|
-
const hostSlot = Symbol.for("flamefront:static-fragment:host")
|
|
57
|
-
|
|
58
|
-
/** Server fragment renders omit the selected boundary's outer host element. */
|
|
59
|
-
export const staticFragmentBoundaryTarget = createContext<string | null>(null)
|
|
60
|
-
|
|
61
|
-
function boundaryProps(
|
|
62
|
-
metadata: Pick<GeneratedRouteMetadata, "boundary" | "kind">,
|
|
63
|
-
): Record<string, unknown> {
|
|
64
|
-
return {
|
|
65
|
-
"data-flamefront-boundary": metadata.boundary,
|
|
66
|
-
"data-flamefront-boundary-kind": metadata.kind,
|
|
67
|
-
style: "display:contents",
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Add a stable DOM boundary around every generated shell/layout/route node. */
|
|
72
|
-
export function createRouteBoundary(
|
|
73
|
-
Component: unknown,
|
|
74
|
-
metadata: Pick<GeneratedRouteMetadata, "boundary" | "kind">,
|
|
75
|
-
): (props: Record<string, unknown>) => unknown {
|
|
76
|
-
return (props) => {
|
|
77
|
-
const target = useContext(staticFragmentBoundaryTarget)
|
|
78
|
-
const children = createElement(Component as never, props)
|
|
79
|
-
|
|
80
|
-
return target === metadata.boundary
|
|
81
|
-
? children
|
|
82
|
-
: createElement("div", boundaryProps(metadata), children)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function createContextBridge(
|
|
87
|
-
Component: unknown,
|
|
88
|
-
contexts: {
|
|
89
|
-
readonly dataRouter: unknown
|
|
90
|
-
readonly dataRouterState: unknown
|
|
91
|
-
readonly fetchers: unknown
|
|
92
|
-
readonly location: unknown
|
|
93
|
-
readonly navigation: unknown
|
|
94
|
-
readonly route: unknown
|
|
95
|
-
readonly viewTransition: unknown
|
|
96
|
-
},
|
|
97
|
-
): (props: Record<string, unknown>) => unknown {
|
|
98
|
-
return () =>
|
|
99
|
-
createElement(UNSAFE_DataRouterContext.Provider as never, {
|
|
100
|
-
value: contexts.dataRouter,
|
|
101
|
-
children: createElement(UNSAFE_DataRouterStateContext.Provider as never, {
|
|
102
|
-
value: contexts.dataRouterState,
|
|
103
|
-
children: createElement(UNSAFE_FetchersContext.Provider as never, {
|
|
104
|
-
value: contexts.fetchers,
|
|
105
|
-
children: createElement(UNSAFE_LocationContext.Provider as never, {
|
|
106
|
-
value: contexts.location,
|
|
107
|
-
children: createElement(
|
|
108
|
-
UNSAFE_NavigationContext.Provider as never,
|
|
109
|
-
{
|
|
110
|
-
value: contexts.navigation,
|
|
111
|
-
children: createElement(UNSAFE_RouteContext.Provider as never, {
|
|
112
|
-
value: contexts.route,
|
|
113
|
-
children: createElement(
|
|
114
|
-
UNSAFE_ViewTransitionContext.Provider as never,
|
|
115
|
-
{
|
|
116
|
-
value: contexts.viewTransition,
|
|
117
|
-
children: createElement(Component as never, {}),
|
|
118
|
-
},
|
|
119
|
-
),
|
|
120
|
-
}),
|
|
121
|
-
},
|
|
122
|
-
),
|
|
123
|
-
}),
|
|
124
|
-
}),
|
|
125
|
-
}),
|
|
126
|
-
})
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function routeLocationUrl(): string {
|
|
130
|
-
const location = useRouterLocation()
|
|
131
|
-
|
|
132
|
-
return `${location.pathname}${location.search}${location.hash}`
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function unmountNestedRoot(root: { unmount(): void } | null): void {
|
|
136
|
-
root?.unmount()
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function hasServerBoundary(boundary: string): boolean {
|
|
140
|
-
if (typeof document === "undefined") {
|
|
141
|
-
return false
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return Boolean(
|
|
145
|
-
document.querySelector(`[data-flamefront-boundary="${boundary}"]`),
|
|
146
|
-
)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Route component used by generated browser routes. Navigation renders the
|
|
151
|
-
* fetched artifact into a boundary first; only the later layout effect may
|
|
152
|
-
* hydrate that boundary. The fallback component is used for direct-document
|
|
153
|
-
* hydration when no fragment was fetched by the browser router.
|
|
154
|
-
*/
|
|
155
|
-
export function createStaticFragmentRoute(
|
|
156
|
-
options: StaticFragmentRouteOptions,
|
|
157
|
-
): (props: Record<string, unknown>) => unknown {
|
|
158
|
-
const fallbackComponent = options.fallbackComponent
|
|
159
|
-
const fallbackBoundary = createRouteBoundary(
|
|
160
|
-
fallbackComponent,
|
|
161
|
-
options.metadata,
|
|
162
|
-
)
|
|
163
|
-
const basename = options.routing.basename ?? "/"
|
|
164
|
-
|
|
165
|
-
return function StaticFragmentRoute(props) {
|
|
166
|
-
const routeUrl = routeLocationUrl()
|
|
167
|
-
const artifact = getStaticFragment(routeUrl, { basename })
|
|
168
|
-
const hostRef = useRef<Element | null>(null, hostSlot)
|
|
169
|
-
const nestedRootRef = useRef<{ unmount(): void } | null>(null, rootSlot)
|
|
170
|
-
const dataRouter = useContext(UNSAFE_DataRouterContext)
|
|
171
|
-
const dataRouterState = useContext(UNSAFE_DataRouterStateContext)
|
|
172
|
-
const fetchers = useContext(UNSAFE_FetchersContext)
|
|
173
|
-
const location = useContext(UNSAFE_LocationContext)
|
|
174
|
-
const navigation = useContext(UNSAFE_NavigationContext)
|
|
175
|
-
const route = useContext(UNSAFE_RouteContext)
|
|
176
|
-
const viewTransition = useContext(UNSAFE_ViewTransitionContext)
|
|
177
|
-
|
|
178
|
-
useLayoutEffect(
|
|
179
|
-
() => {
|
|
180
|
-
if (
|
|
181
|
-
!artifact ||
|
|
182
|
-
!shouldHydrateStaticFragment(options.metadata.hydration)
|
|
183
|
-
) {
|
|
184
|
-
return
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const host = hostRef.current
|
|
188
|
-
|
|
189
|
-
if (!host) {
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
let active = true
|
|
194
|
-
const hydratedComponent = fallbackComponent
|
|
195
|
-
const bridge = createContextBridge(hydratedComponent, {
|
|
196
|
-
dataRouter,
|
|
197
|
-
dataRouterState,
|
|
198
|
-
fetchers,
|
|
199
|
-
location,
|
|
200
|
-
navigation,
|
|
201
|
-
route,
|
|
202
|
-
viewTransition,
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
unmountNestedRoot(nestedRootRef.current)
|
|
206
|
-
nestedRootRef.current = null
|
|
207
|
-
void import("octane").then(
|
|
208
|
-
({ hydrateRoot, setDangerouslySetInnerHTML, setHTML }) => {
|
|
209
|
-
if (!active || hostRef.current !== host) {
|
|
210
|
-
return
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// The outer document root used dangerouslySetInnerHTML to adopt the
|
|
214
|
-
// static fragment. Release that ownership before the nested root
|
|
215
|
-
// starts reconciling the same children.
|
|
216
|
-
const html = host.innerHTML
|
|
217
|
-
|
|
218
|
-
setDangerouslySetInnerHTML(host, null)
|
|
219
|
-
setHTML(host, html)
|
|
220
|
-
nestedRootRef.current = hydrateRoot(host, createElement(bridge, {}))
|
|
221
|
-
},
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
return () => {
|
|
225
|
-
active = false
|
|
226
|
-
unmountNestedRoot(nestedRootRef.current)
|
|
227
|
-
nestedRootRef.current = null
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
[
|
|
231
|
-
artifact,
|
|
232
|
-
dataRouter,
|
|
233
|
-
dataRouterState,
|
|
234
|
-
fetchers,
|
|
235
|
-
location,
|
|
236
|
-
navigation,
|
|
237
|
-
route,
|
|
238
|
-
viewTransition,
|
|
239
|
-
options.metadata.hydration,
|
|
240
|
-
],
|
|
241
|
-
hydrationSlot,
|
|
242
|
-
)
|
|
243
|
-
|
|
244
|
-
if (!artifact && hasServerBoundary(options.metadata.boundary)) {
|
|
245
|
-
return fallbackBoundary(props)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return createElement("div", {
|
|
249
|
-
...boundaryProps(options.metadata),
|
|
250
|
-
...(artifact
|
|
251
|
-
? {
|
|
252
|
-
ref: hostRef,
|
|
253
|
-
dangerouslySetInnerHTML: { __html: artifact.html },
|
|
254
|
-
}
|
|
255
|
-
: {}),
|
|
256
|
-
})
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export { isStaticFragmentRequest }
|