kiru 0.50.7 → 0.51.0-preview.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/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/reconciler.d.ts.map +1 -1
- package/dist/reconciler.js +4 -12
- package/dist/reconciler.js.map +1 -1
- package/dist/router/client/index.d.ts +10 -0
- package/dist/router/client/index.d.ts.map +1 -0
- package/dist/router/client/index.js +73 -0
- package/dist/router/client/index.js.map +1 -0
- package/dist/router/dev/index.d.ts +2 -0
- package/dist/router/dev/index.d.ts.map +1 -0
- package/dist/router/dev/index.js +46 -0
- package/dist/router/dev/index.js.map +1 -0
- package/dist/router/fileRouter.d.ts +1 -1
- package/dist/router/fileRouter.d.ts.map +1 -1
- package/dist/router/fileRouter.js +10 -2
- package/dist/router/fileRouter.js.map +1 -1
- package/dist/router/fileRouterController.d.ts +4 -4
- package/dist/router/fileRouterController.d.ts.map +1 -1
- package/dist/router/fileRouterController.js +118 -47
- 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/head.d.ts +38 -0
- package/dist/router/head.d.ts.map +1 -0
- package/dist/router/head.js +63 -0
- package/dist/router/head.js.map +1 -0
- package/dist/router/index.d.ts +5 -0
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +5 -0
- package/dist/router/index.js.map +1 -1
- package/dist/router/pageConfig.d.ts +1 -1
- package/dist/router/pageConfig.d.ts.map +1 -1
- package/dist/router/pageConfig.js +1 -1
- package/dist/router/pageConfig.js.map +1 -1
- package/dist/router/server/index.d.ts +17 -0
- package/dist/router/server/index.d.ts.map +1 -0
- package/dist/router/server/index.js +160 -0
- package/dist/router/server/index.js.map +1 -0
- package/dist/router/types.d.ts +35 -11
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.internal.d.ts +6 -2
- package/dist/router/types.internal.d.ts.map +1 -1
- package/dist/router/utils/index.d.ts +4 -4
- package/dist/router/utils/index.d.ts.map +1 -1
- package/dist/router/utils/index.js +18 -5
- package/dist/router/utils/index.js.map +1 -1
- package/dist/types.utils.d.ts +3 -0
- package/dist/types.utils.d.ts.map +1 -1
- package/dist/utils/vdom.d.ts +2 -1
- package/dist/utils/vdom.d.ts.map +1 -1
- package/dist/utils/vdom.js +6 -1
- package/dist/utils/vdom.js.map +1 -1
- package/package.json +17 -1
- package/src/constants.ts +1 -0
- package/src/reconciler.ts +9 -19
- package/src/router/client/index.ts +97 -0
- package/src/router/dev/index.ts +63 -0
- package/src/router/fileRouter.ts +12 -3
- package/src/router/fileRouterController.ts +185 -73
- package/src/router/globals.ts +4 -0
- package/src/router/head.ts +66 -0
- package/src/router/index.ts +7 -0
- package/src/router/pageConfig.ts +2 -2
- package/src/router/server/index.ts +214 -0
- package/src/router/types.internal.ts +6 -2
- package/src/router/types.ts +43 -20
- package/src/router/utils/index.ts +224 -206
- package/src/types.utils.ts +4 -0
- package/src/utils/vdom.ts +9 -0
|
@@ -1,206 +1,224 @@
|
|
|
1
|
-
import { createElement } from "../../element.js"
|
|
2
|
-
import { __DEV__ } from "../../env.js"
|
|
3
|
-
import type {
|
|
4
|
-
FormattedViteImportMap,
|
|
5
|
-
RouteMatch,
|
|
6
|
-
ViteImportMap,
|
|
7
|
-
} from "../types.internal"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
matchLayouts,
|
|
14
|
-
normalizePrefixPath,
|
|
15
|
-
parseQuery,
|
|
16
|
-
wrapWithLayouts,
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function formatViteImportMap(
|
|
20
|
-
map: ViteImportMap,
|
|
21
|
-
dir: string,
|
|
22
|
-
baseUrl: string
|
|
23
|
-
): FormattedViteImportMap {
|
|
24
|
-
return Object.keys(map).reduce<FormattedViteImportMap>((acc, key) => {
|
|
25
|
-
const dirIndex = key.indexOf(dir)
|
|
26
|
-
if (dirIndex === -1) {
|
|
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
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
i
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
1
|
+
import { createElement } from "../../element.js"
|
|
2
|
+
import { __DEV__ } from "../../env.js"
|
|
3
|
+
import type {
|
|
4
|
+
FormattedViteImportMap,
|
|
5
|
+
RouteMatch,
|
|
6
|
+
ViteImportMap,
|
|
7
|
+
} from "../types.internal"
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
formatViteImportMap,
|
|
11
|
+
matchRoute,
|
|
12
|
+
match404Route,
|
|
13
|
+
matchLayouts,
|
|
14
|
+
normalizePrefixPath,
|
|
15
|
+
parseQuery,
|
|
16
|
+
wrapWithLayouts,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function formatViteImportMap(
|
|
20
|
+
map: ViteImportMap,
|
|
21
|
+
dir: string,
|
|
22
|
+
baseUrl: string
|
|
23
|
+
): FormattedViteImportMap {
|
|
24
|
+
return Object.keys(map).reduce<FormattedViteImportMap>((acc, key) => {
|
|
25
|
+
const dirIndex = key.indexOf(dir)
|
|
26
|
+
if (dirIndex === -1) {
|
|
27
|
+
if (__DEV__) {
|
|
28
|
+
console.warn(
|
|
29
|
+
`[kiru/router]: File "${key}" does not start with "${dir}".`
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
return acc
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let specificity = 0
|
|
36
|
+
let k = key.slice(dirIndex + dir.length)
|
|
37
|
+
while (k.startsWith("/")) {
|
|
38
|
+
k = k.slice(1)
|
|
39
|
+
}
|
|
40
|
+
const segments: string[] = []
|
|
41
|
+
const parts = k.split("/").slice(0, -1)
|
|
42
|
+
|
|
43
|
+
for (let i = 0; i < parts.length; i++) {
|
|
44
|
+
const part = parts[i]
|
|
45
|
+
if (part.startsWith("[...") && part.endsWith("]")) {
|
|
46
|
+
if (i !== parts.length - 1) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`[kiru/router]: Catchall must be the folder name. Got "${key}"`
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
segments.push(`:${part.slice(4, -1)}*`)
|
|
52
|
+
specificity += 1
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
if (part.startsWith("[") && part.endsWith("]")) {
|
|
56
|
+
segments.push(`:${part.slice(1, -1)}`)
|
|
57
|
+
specificity += 10
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
specificity += 100
|
|
61
|
+
segments.push(part)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const value: FormattedViteImportMap[string] = {
|
|
65
|
+
load: map[key],
|
|
66
|
+
specificity,
|
|
67
|
+
segments,
|
|
68
|
+
filePath: key,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
...acc,
|
|
73
|
+
[baseUrl + segments.join("/")]: value,
|
|
74
|
+
}
|
|
75
|
+
}, {})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function matchRoute(
|
|
79
|
+
pages: FormattedViteImportMap,
|
|
80
|
+
pathSegments: string[]
|
|
81
|
+
): RouteMatch | null {
|
|
82
|
+
const matches: RouteMatch[] = []
|
|
83
|
+
outer: for (const [route, pageEntry] of Object.entries(pages)) {
|
|
84
|
+
const routeSegments = pageEntry.segments
|
|
85
|
+
const pathMatchingSegments = routeSegments.filter(
|
|
86
|
+
(seg) => !seg.startsWith("(") && !seg.endsWith(")")
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const params: Record<string, string> = {}
|
|
90
|
+
let hasCatchall = false
|
|
91
|
+
|
|
92
|
+
// Check if route matches
|
|
93
|
+
for (
|
|
94
|
+
let i = 0;
|
|
95
|
+
i < pathMatchingSegments.length && i < pathSegments.length;
|
|
96
|
+
i++
|
|
97
|
+
) {
|
|
98
|
+
const routeSeg = pathMatchingSegments[i]
|
|
99
|
+
|
|
100
|
+
if (routeSeg.startsWith(":")) {
|
|
101
|
+
const key = routeSeg.slice(1)
|
|
102
|
+
|
|
103
|
+
if (routeSeg.endsWith("*")) {
|
|
104
|
+
// Catchall route - matches remaining segments
|
|
105
|
+
hasCatchall = true
|
|
106
|
+
const catchallKey = key.slice(0, -1) // Remove the *
|
|
107
|
+
params[catchallKey] = pathSegments.slice(i).join("/")
|
|
108
|
+
break
|
|
109
|
+
} else {
|
|
110
|
+
// Regular dynamic segment
|
|
111
|
+
if (i >= pathSegments.length) {
|
|
112
|
+
continue outer
|
|
113
|
+
}
|
|
114
|
+
params[key] = pathSegments[i]
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
// Static segment
|
|
118
|
+
if (routeSeg !== pathSegments[i]) {
|
|
119
|
+
continue outer
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// For non-catchall routes, ensure exact length match
|
|
125
|
+
if (!hasCatchall && pathMatchingSegments.length !== pathSegments.length) {
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
matches.push({
|
|
130
|
+
route,
|
|
131
|
+
pageEntry,
|
|
132
|
+
params,
|
|
133
|
+
routeSegments,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Sort by specificity (highest first) and return the best match
|
|
138
|
+
if (matches.length === 0) {
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
matches.sort((a, b) => b.pageEntry.specificity - a.pageEntry.specificity)
|
|
143
|
+
return matches[0] || null
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function match404Route(
|
|
147
|
+
pages: FormattedViteImportMap,
|
|
148
|
+
pathSegments: string[]
|
|
149
|
+
): RouteMatch | null {
|
|
150
|
+
// Try to find a 404 page at each parent directory level
|
|
151
|
+
// Start from the deepest level and work up to root
|
|
152
|
+
for (let i = pathSegments.length; i >= 0; i--) {
|
|
153
|
+
const parentSegments = pathSegments.slice(0, i)
|
|
154
|
+
const fourOhFourSegments = [...parentSegments, "404"]
|
|
155
|
+
const match = matchRoute(pages, fourOhFourSegments)
|
|
156
|
+
if (match) {
|
|
157
|
+
return match
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function matchLayouts(
|
|
164
|
+
layouts: FormattedViteImportMap,
|
|
165
|
+
routeSegments: string[]
|
|
166
|
+
) {
|
|
167
|
+
return ["/", ...routeSegments].reduce((acc, _, i) => {
|
|
168
|
+
const layoutPath = "/" + routeSegments.slice(0, i).join("/")
|
|
169
|
+
const layout = layouts[layoutPath]
|
|
170
|
+
|
|
171
|
+
if (!layout) {
|
|
172
|
+
return acc
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return [...acc, layout]
|
|
176
|
+
}, [] as FormattedViteImportMap[string][])
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function normalizePrefixPath(path: string) {
|
|
180
|
+
while (path.startsWith(".")) {
|
|
181
|
+
path = path.slice(1)
|
|
182
|
+
}
|
|
183
|
+
path = `/${path}/`
|
|
184
|
+
while (path.startsWith("//")) {
|
|
185
|
+
path = path.slice(1)
|
|
186
|
+
}
|
|
187
|
+
while (path.endsWith("//")) {
|
|
188
|
+
path = path.slice(0, -1)
|
|
189
|
+
}
|
|
190
|
+
return path
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function parseQuery(
|
|
194
|
+
search: string
|
|
195
|
+
): Record<string, string | string[] | undefined> {
|
|
196
|
+
const params = new URLSearchParams(search)
|
|
197
|
+
const query: Record<string, string | string[] | undefined> = {}
|
|
198
|
+
|
|
199
|
+
for (const [key, value] of params.entries()) {
|
|
200
|
+
if (query[key]) {
|
|
201
|
+
// Convert to array if multiple values
|
|
202
|
+
if (Array.isArray(query[key])) {
|
|
203
|
+
;(query[key] as string[]).push(value)
|
|
204
|
+
} else {
|
|
205
|
+
query[key] = [query[key] as string, value]
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
query[key] = value
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return query
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function wrapWithLayouts(
|
|
216
|
+
layouts: Kiru.FC[],
|
|
217
|
+
page: Kiru.FC,
|
|
218
|
+
props: Record<string, unknown>
|
|
219
|
+
) {
|
|
220
|
+
return layouts.reduceRight(
|
|
221
|
+
(children, Layout) => createElement(Layout, { children }),
|
|
222
|
+
createElement(page, props)
|
|
223
|
+
)
|
|
224
|
+
}
|
package/src/types.utils.ts
CHANGED
package/src/utils/vdom.ts
CHANGED
|
@@ -17,6 +17,7 @@ export {
|
|
|
17
17
|
cloneVNode,
|
|
18
18
|
isVNodeDeleted,
|
|
19
19
|
isVNode,
|
|
20
|
+
isValidTextChild,
|
|
20
21
|
isExoticType,
|
|
21
22
|
isFragment,
|
|
22
23
|
isLazy,
|
|
@@ -54,6 +55,14 @@ function isVNode(thing: unknown): thing is Kiru.VNode {
|
|
|
54
55
|
return typeof thing === "object" && thing !== null && "type" in thing
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
function isValidTextChild(thing: unknown): thing is string | number | bigint {
|
|
59
|
+
return (
|
|
60
|
+
(typeof thing === "string" && thing !== "") ||
|
|
61
|
+
typeof thing === "number" ||
|
|
62
|
+
typeof thing === "bigint"
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
function isExoticType(type: Kiru.VNode["type"]): type is Kiru.ExoticSymbol {
|
|
58
67
|
return (
|
|
59
68
|
type === $FRAGMENT || type === $CONTEXT_PROVIDER || type === $ERROR_BOUNDARY
|