flamefront 0.0.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/LICENSE.md +110 -0
- package/README.md +58 -0
- package/bin/ff-loader.mjs +18 -0
- package/bin/ff.js +6 -0
- package/package.json +84 -8
- package/src/babel.ts +22 -0
- package/src/cli.ts +98 -0
- package/src/entry.ts +49 -0
- package/src/fetch.ts +280 -0
- package/src/fragment-client.ts +271 -0
- package/src/fragment-hydration-client.tsx +81 -0
- package/src/fragment-protocol.ts +39 -0
- package/src/fragment.tsx +656 -0
- package/src/glob.ts +294 -0
- package/src/identifier-prefix.ts +5 -0
- package/src/index.ts +1168 -0
- package/src/lifecycle.ts +487 -0
- package/src/octane-client-core.ts +141 -0
- package/src/octane-client.ts +48 -0
- package/src/octane-compiler.d.ts +19 -0
- package/src/octane-default-renderer.tsx +124 -0
- package/src/octane-router-document.ts +22 -0
- package/src/octane.tsx +661 -0
- package/src/output.ts +48 -0
- package/src/remix-route-data.ts +76 -0
- package/src/remix-router-core.ts +106 -0
- package/src/remix-router.ts +117 -0
- package/src/remove-exports.ts +148 -0
- package/src/route-data-client.ts +234 -0
- package/src/route-prefetch.ts +79 -0
- package/src/server.ts +338 -0
- package/src/srvx.ts +174 -0
- package/src/static-fragment-artifacts.ts +86 -0
- package/src/typegen.ts +207 -0
- package/src/virtual-remix-routes.d.ts +35 -0
- package/src/vite.ts +1030 -0
- package/readme.md +0 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StaticRouterProvider,
|
|
3
|
+
type RouteObject,
|
|
4
|
+
createStaticRouter,
|
|
5
|
+
type DataRouter,
|
|
6
|
+
type StaticHandlerContext,
|
|
7
|
+
} from "@octanejs/remix-router"
|
|
8
|
+
import { renderToString } from "octane/server"
|
|
9
|
+
import { routeFragmentBoundaryTarget } from "./fragment.tsx"
|
|
10
|
+
import { RouterDocument } from "./octane-router-document.ts"
|
|
11
|
+
import type { OctaneRenderer } from "./octane.tsx"
|
|
12
|
+
|
|
13
|
+
function findRoute(
|
|
14
|
+
routes: readonly RouteObject[],
|
|
15
|
+
id: string,
|
|
16
|
+
): RouteObject | undefined {
|
|
17
|
+
for (const route of routes) {
|
|
18
|
+
if (route.id === id) {
|
|
19
|
+
return route
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const match = route.children ? findRoute(route.children, id) : undefined
|
|
23
|
+
|
|
24
|
+
if (match) {
|
|
25
|
+
return match
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function errorsForBoundary(
|
|
33
|
+
context: StaticHandlerContext,
|
|
34
|
+
matches: readonly { readonly route: { readonly id: string } }[],
|
|
35
|
+
): StaticHandlerContext["errors"] {
|
|
36
|
+
if (!context.errors) {
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ids = new Set(matches.map((match) => match.route.id))
|
|
41
|
+
const errors: Record<string, unknown> = {}
|
|
42
|
+
let inheritedError: unknown = undefined
|
|
43
|
+
|
|
44
|
+
for (const [id, error] of Object.entries(context.errors)) {
|
|
45
|
+
if (ids.has(id)) {
|
|
46
|
+
errors[id] = error
|
|
47
|
+
} else if (inheritedError === undefined) {
|
|
48
|
+
inheritedError = error
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (inheritedError !== undefined && matches[0]) {
|
|
53
|
+
errors[matches[0].route.id] ??= inheritedError
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Object.keys(errors).length > 0 ? errors : null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const defaultOctaneRenderer: OctaneRenderer = {
|
|
60
|
+
createStaticRouter: (routes, context) =>
|
|
61
|
+
createStaticRouter(
|
|
62
|
+
routes as RouteObject[],
|
|
63
|
+
context as StaticHandlerContext,
|
|
64
|
+
),
|
|
65
|
+
renderToString: (component, props, options) =>
|
|
66
|
+
renderToString(
|
|
67
|
+
component as Parameters<typeof renderToString>[0],
|
|
68
|
+
props,
|
|
69
|
+
options,
|
|
70
|
+
),
|
|
71
|
+
renderRouteFragment: (router, context, boundary, options) => {
|
|
72
|
+
const dataRouter = router as DataRouter
|
|
73
|
+
const staticContext = context as StaticHandlerContext
|
|
74
|
+
const state = dataRouter.state
|
|
75
|
+
const matchIndex = state.matches.findIndex(
|
|
76
|
+
(match) => match.route?.id === boundary,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if (matchIndex < 0) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`No server router match exists for fragment boundary ${JSON.stringify(boundary)}.`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const outletRoute = findRoute(dataRouter.routes, boundary)
|
|
86
|
+
|
|
87
|
+
if (!outletRoute) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`No server route exists for fragment boundary ${JSON.stringify(boundary)}.`,
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const matches = staticContext.matches.slice(matchIndex)
|
|
94
|
+
const outletContext = {
|
|
95
|
+
...staticContext,
|
|
96
|
+
matches,
|
|
97
|
+
errors: errorsForBoundary(staticContext, matches),
|
|
98
|
+
}
|
|
99
|
+
const outletRouter = createStaticRouter([outletRoute], outletContext)
|
|
100
|
+
const FragmentBoundaryProvider = routeFragmentBoundaryTarget.Provider
|
|
101
|
+
const FragmentRoot = () => (
|
|
102
|
+
<FragmentBoundaryProvider
|
|
103
|
+
value={options?.includeBoundary ? null : boundary}
|
|
104
|
+
>
|
|
105
|
+
<StaticRouterProvider
|
|
106
|
+
router={outletRouter}
|
|
107
|
+
context={outletContext}
|
|
108
|
+
hydrate={false}
|
|
109
|
+
/>
|
|
110
|
+
</FragmentBoundaryProvider>
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return renderToString(
|
|
114
|
+
FragmentRoot,
|
|
115
|
+
{},
|
|
116
|
+
{
|
|
117
|
+
...(options?.identifierPrefix
|
|
118
|
+
? { identifierPrefix: options.identifierPrefix }
|
|
119
|
+
: {}),
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
},
|
|
123
|
+
defaultRouterDocument: RouterDocument,
|
|
124
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RouterProvider } from "@octanejs/remix-router/dom"
|
|
2
|
+
import { createElement } from "octane"
|
|
3
|
+
import { routeOutletHtmlContext } from "./fragment.tsx"
|
|
4
|
+
import type {
|
|
5
|
+
RouterDocument as RouterDocumentComponent,
|
|
6
|
+
RouterDocumentProps,
|
|
7
|
+
} from "./octane.tsx"
|
|
8
|
+
|
|
9
|
+
/** Canonical router root shared by Flamefront's Octane server and client. */
|
|
10
|
+
export const RouterDocument: RouterDocumentComponent = (
|
|
11
|
+
props: RouterDocumentProps,
|
|
12
|
+
) => {
|
|
13
|
+
const Router = RouterProvider as unknown as (props: {
|
|
14
|
+
readonly router: unknown
|
|
15
|
+
}) => unknown
|
|
16
|
+
|
|
17
|
+
return createElement(
|
|
18
|
+
routeOutletHtmlContext.Provider,
|
|
19
|
+
{ value: props.outletHtml ?? null },
|
|
20
|
+
createElement(Router, { router: props.router }),
|
|
21
|
+
)
|
|
22
|
+
}
|