-
-
Notifications
You must be signed in to change notification settings - Fork 68
/
routes.js
127 lines (119 loc) · 3.5 KB
/
routes.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* global $paramPattern */
const routeModules = import.meta.glob('$globPattern')
export default getRoutes()
function getRoutes () {
if (import.meta.env.SSR) {
return createRoutes(routeModules)
} else {
return hydrateRoutes(routeModules)
}
}
async function createRoutes (from, { param } = { param: $paramPattern }) {
// Otherwise we get a ReferenceError, but since
// this function is only ran once, there's no overhead
class Routes extends Array {
toJSON () {
return this.map((route) => {
return {
id: route.id,
path: route.path,
layout: route.layout,
getData: !!route.getData,
getMeta: !!route.getMeta,
onEnter: !!route.onEnter,
}
})
}
}
const importPaths = Object.keys(from)
const promises = []
if (Array.isArray(from)) {
for (const routeDef of from) {
promises.push(
getRouteModule(routeDef.path, routeDef.component)
.then((routeModule) => {
return {
id: routeDef.path,
path: routeDef.path ?? routeModule.path,
...routeModule,
}
}),
)
}
} else {
// Ensure that static routes have precedence over the dynamic ones
for (const path of importPaths.sort((a, b) => a > b ? -1 : 1)) {
promises.push(
getRouteModule(path, from[path])
.then((routeModule) => {
return {
id: path,
layout: routeModule.layout,
path: routeModule.path ?? path
// Remove /pages and .jsx extension
.slice(6, -4)
// Replace [id] with :id
.replace(param, (_, m) => `:${m}`)
// Replace '/index' with '/'
.replace(/\/index$/, '/')
// Remove trailing slashs
.replace(/(.+)\/+$/, (...m) => m[1]),
...routeModule,
}
}),
)
}
}
return new Routes(...await Promise.all(promises))
}
async function hydrateRoutes (from) {
if (Array.isArray(from)) {
from = Object.fromEntries(
from.map((route) => [route.path, route]),
)
}
return window.routes.map((route) => {
route.loader = memoImport(from[route.id])
route.component = () => route.loader()
return route
})
}
function getRouteModuleExports (routeModule) {
return {
// The Route component (default export)
component: routeModule.default,
// The Layout Route component
layout: routeModule.layout,
// Route-level hooks
getData: routeModule.getData,
getMeta: routeModule.getMeta,
onEnter: routeModule.onEnter,
// Other Route-level settings
streaming: routeModule.streaming,
clientOnly: routeModule.clientOnly,
serverOnly: routeModule.serverOnly,
}
}
async function getRouteModule (path, routeModule) {
// const isServer = typeof process !== 'undefined'
if (typeof routeModule === 'function') {
routeModule = await routeModule()
return getRouteModuleExports(routeModule)
} else {
return getRouteModuleExports(routeModule)
}
}
function memoImport (func) {
// Otherwise we get a ReferenceError, but since this function
// is only ran once for each route, there's no overhead
const kFuncExecuted = Symbol('kFuncExecuted')
const kFuncValue = Symbol('kFuncValue')
func[kFuncExecuted] = false
return async function () {
if (!func[kFuncExecuted]) {
func[kFuncValue] = await func()
func[kFuncExecuted] = true
}
return func[kFuncValue]
}
}