kiru 0.50.8 → 0.51.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/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.dom.d.ts +2 -2
- package/dist/types.dom.d.ts.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 +25 -7
- package/src/types.dom.ts +2 -5
- package/src/types.utils.ts +4 -0
- package/src/utils/vdom.ts +9 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { createElement } from "../../element.js"
|
|
2
|
+
import { renderToReadableStream } from "../../ssr/server.js"
|
|
3
|
+
import {
|
|
4
|
+
matchLayouts,
|
|
5
|
+
matchRoute,
|
|
6
|
+
match404Route,
|
|
7
|
+
parseQuery,
|
|
8
|
+
wrapWithLayouts,
|
|
9
|
+
} from "../utils/index.js"
|
|
10
|
+
import { RouterContext } from "../context.js"
|
|
11
|
+
import type { PageConfig, PageProps, RouterState } from "../types.js"
|
|
12
|
+
import type { Readable } from "node:stream"
|
|
13
|
+
import { FormattedViteImportMap, PageModule } from "../types.internal.js"
|
|
14
|
+
import { __DEV__ } from "../../env.js"
|
|
15
|
+
import { FileRouterDataLoadError } from "../errors.js"
|
|
16
|
+
|
|
17
|
+
export interface RenderContext {
|
|
18
|
+
pages: FormattedViteImportMap
|
|
19
|
+
layouts: FormattedViteImportMap
|
|
20
|
+
Document: Kiru.FC
|
|
21
|
+
registerModule: (moduleId: string) => void
|
|
22
|
+
registerPreloadedPageProps: (props: Record<string, unknown>) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface RenderResult {
|
|
26
|
+
status: number
|
|
27
|
+
immediate: string
|
|
28
|
+
stream: Readable | null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function render(
|
|
32
|
+
url: string,
|
|
33
|
+
ctx: RenderContext,
|
|
34
|
+
result?: RenderResult
|
|
35
|
+
): Promise<RenderResult> {
|
|
36
|
+
const u = new URL(url, "http://localhost")
|
|
37
|
+
const pathSegments = u.pathname.split("/").filter(Boolean)
|
|
38
|
+
let routeMatch = matchRoute(ctx.pages, pathSegments)
|
|
39
|
+
|
|
40
|
+
if (!routeMatch) {
|
|
41
|
+
// Try to find a 404 page in parent directories
|
|
42
|
+
const fourOhFourMatch = match404Route(ctx.pages, pathSegments)
|
|
43
|
+
if (fourOhFourMatch) {
|
|
44
|
+
routeMatch = fourOhFourMatch
|
|
45
|
+
} else {
|
|
46
|
+
// Fallback to root 404 or default fallback
|
|
47
|
+
if (url === "/404" && result) {
|
|
48
|
+
if (__DEV__) {
|
|
49
|
+
console.warn(
|
|
50
|
+
"[kiru/router]: No 404 route defined. Using fallback 404 page."
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
status: 404,
|
|
55
|
+
immediate:
|
|
56
|
+
"<!doctype html><html><head><title>Not Found</title></head><body><h1>404</h1></body></html>",
|
|
57
|
+
stream: null,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return render("/404", ctx, {
|
|
61
|
+
...(result ?? {}),
|
|
62
|
+
immediate: "",
|
|
63
|
+
stream: null,
|
|
64
|
+
status: 404,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { pageEntry, routeSegments, params } = routeMatch
|
|
70
|
+
const is404Route = routeMatch.routeSegments.includes("404")
|
|
71
|
+
const layoutEntries = matchLayouts(ctx.layouts, routeSegments)
|
|
72
|
+
|
|
73
|
+
if (__DEV__) {
|
|
74
|
+
;[pageEntry, ...layoutEntries].forEach((e) => {
|
|
75
|
+
ctx.registerModule(e.filePath!)
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const [page, ...layouts] = await Promise.all([
|
|
80
|
+
pageEntry.load() as unknown as Promise<PageModule>,
|
|
81
|
+
...layoutEntries.map((layoutEntry) => layoutEntry.load()),
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
const query = parseQuery(u.search)
|
|
85
|
+
|
|
86
|
+
let props = {} as PageProps<PageConfig>
|
|
87
|
+
const config = page.config ?? {}
|
|
88
|
+
const abortController = new AbortController()
|
|
89
|
+
|
|
90
|
+
if (config.loader) {
|
|
91
|
+
if (config.loader.mode !== "static" || __DEV__) {
|
|
92
|
+
props = { loading: true, data: null, error: null }
|
|
93
|
+
} else {
|
|
94
|
+
const routerState: RouterState = {
|
|
95
|
+
path: u.pathname,
|
|
96
|
+
params,
|
|
97
|
+
query,
|
|
98
|
+
signal: abortController.signal,
|
|
99
|
+
}
|
|
100
|
+
const timeout = setTimeout(() => {
|
|
101
|
+
abortController.abort(
|
|
102
|
+
"[kiru/router]: Page data loading timed out after 10 seconds"
|
|
103
|
+
)
|
|
104
|
+
}, 10000)
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const data = await config.loader.load(routerState)
|
|
108
|
+
props = {
|
|
109
|
+
data,
|
|
110
|
+
error: null,
|
|
111
|
+
loading: false,
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
props = {
|
|
115
|
+
error: new FileRouterDataLoadError(error),
|
|
116
|
+
loading: false,
|
|
117
|
+
data: null,
|
|
118
|
+
}
|
|
119
|
+
} finally {
|
|
120
|
+
clearTimeout(timeout)
|
|
121
|
+
ctx.registerPreloadedPageProps({ data: props.data, error: props.error })
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const children = wrapWithLayouts(
|
|
127
|
+
layouts
|
|
128
|
+
.map((layout) => layout.default)
|
|
129
|
+
.filter((l) => typeof l === "function"),
|
|
130
|
+
page.default,
|
|
131
|
+
props
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
const app = createElement(RouterContext.Provider, {
|
|
135
|
+
children: createElement(ctx.Document, { children }),
|
|
136
|
+
value: {
|
|
137
|
+
state: {
|
|
138
|
+
params,
|
|
139
|
+
query,
|
|
140
|
+
path: u.pathname,
|
|
141
|
+
signal: abortController.signal, // Server-side signal (not abortable)
|
|
142
|
+
} as RouterState,
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
let { immediate, stream } = renderToReadableStream(app)
|
|
147
|
+
const hasHeadOutlet = immediate.includes("<kiru-head-outlet>")
|
|
148
|
+
const hasHeadContent = immediate.includes("<kiru-head-content>")
|
|
149
|
+
|
|
150
|
+
if (hasHeadOutlet && hasHeadContent) {
|
|
151
|
+
let [preHeadContent = "", headContentInner = "", postHeadContent = ""] =
|
|
152
|
+
immediate.split(/<kiru-head-content>|<\/kiru-head-content>/)
|
|
153
|
+
|
|
154
|
+
preHeadContent = preHeadContent.replace(
|
|
155
|
+
"<kiru-head-outlet>",
|
|
156
|
+
headContentInner
|
|
157
|
+
)
|
|
158
|
+
immediate = `${preHeadContent}${postHeadContent}`
|
|
159
|
+
} else if (hasHeadContent) {
|
|
160
|
+
// remove head content element and everything within it
|
|
161
|
+
immediate = immediate.replace(
|
|
162
|
+
/<kiru-head-content>(.*?)<\/kiru-head-content>/,
|
|
163
|
+
""
|
|
164
|
+
)
|
|
165
|
+
} else if (hasHeadOutlet) {
|
|
166
|
+
// remove head outlet element and everything within it
|
|
167
|
+
immediate = immediate.replaceAll("<kiru-head-outlet>", "")
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// console.log("immediate", immediate)
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
status: is404Route ? 404 : result?.status ?? 200,
|
|
174
|
+
immediate: "<!doctype html>" + immediate,
|
|
175
|
+
stream,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function generateStaticPaths(pages: FormattedViteImportMap) {
|
|
180
|
+
const results: Record<string, string> = {}
|
|
181
|
+
const entries = Object.values(pages)
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
// Build a clean URL path excluding group segments like (articles)
|
|
184
|
+
const urlSegments = entry.segments.filter(
|
|
185
|
+
(s) => !(s.startsWith("(") && s.endsWith(")"))
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
const basePath = "/" + urlSegments.join("/")
|
|
189
|
+
// if (basePath.endsWith("/404")) continue
|
|
190
|
+
|
|
191
|
+
const hasDynamic = urlSegments.some((s) => s.startsWith(":"))
|
|
192
|
+
if (!hasDynamic) {
|
|
193
|
+
results[basePath === "" ? "/" : basePath] = entry.filePath
|
|
194
|
+
continue
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const mod: PageModule = await entry.load()
|
|
198
|
+
const gen = mod?.config?.generateStaticParams
|
|
199
|
+
if (!gen) continue
|
|
200
|
+
const paramsList = await gen()
|
|
201
|
+
if (!Array.isArray(paramsList)) continue
|
|
202
|
+
|
|
203
|
+
for (const params of paramsList) {
|
|
204
|
+
let p = basePath
|
|
205
|
+
for (const key in params) {
|
|
206
|
+
const value = params[key]
|
|
207
|
+
p = p.replace(`:${key}*`, value).replace(`:${key}`, value)
|
|
208
|
+
}
|
|
209
|
+
results[p] = entry.filePath
|
|
210
|
+
}
|
|
211
|
+
} catch {}
|
|
212
|
+
}
|
|
213
|
+
return results
|
|
214
|
+
}
|
|
@@ -5,8 +5,12 @@ export interface DefaultComponentModule {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export interface PageModule {
|
|
8
|
-
default:
|
|
8
|
+
default: Kiru.FC
|
|
9
9
|
config?: PageConfig
|
|
10
|
+
__KIRU_STATIC_PROPS__?: Record<
|
|
11
|
+
string,
|
|
12
|
+
{ data: unknown; error: string | null }
|
|
13
|
+
>
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
export interface ViteImportMap {
|
|
@@ -18,7 +22,7 @@ export interface FormattedViteImportMap {
|
|
|
18
22
|
load: () => Promise<DefaultComponentModule>
|
|
19
23
|
specificity: number
|
|
20
24
|
segments: string[]
|
|
21
|
-
filePath
|
|
25
|
+
filePath: string
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
28
|
|
package/src/router/types.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { AsyncTaskState } from "../types.utils"
|
|
2
2
|
import { FileRouterDataLoadError } from "./errors"
|
|
3
|
+
import {
|
|
4
|
+
DefaultComponentModule,
|
|
5
|
+
FormattedViteImportMap,
|
|
6
|
+
PageModule,
|
|
7
|
+
} from "./types.internal"
|
|
8
|
+
|
|
9
|
+
export interface FileRouterPreloadConfig {
|
|
10
|
+
pages: FormattedViteImportMap
|
|
11
|
+
layouts: FormattedViteImportMap
|
|
12
|
+
page: PageModule
|
|
13
|
+
pageProps: Record<string, unknown>
|
|
14
|
+
pageLayouts: DefaultComponentModule[]
|
|
15
|
+
params: RouteParams
|
|
16
|
+
query: RouteQuery
|
|
17
|
+
route: string
|
|
18
|
+
}
|
|
3
19
|
|
|
4
20
|
export interface FileRouterConfig {
|
|
5
21
|
/**
|
|
@@ -35,6 +51,12 @@ export interface FileRouterConfig {
|
|
|
35
51
|
* @default false
|
|
36
52
|
*/
|
|
37
53
|
transition?: boolean
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Used for generated entry point files
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
preloaded?: FileRouterPreloadConfig
|
|
38
60
|
}
|
|
39
61
|
|
|
40
62
|
export interface RouteParams {
|
|
@@ -69,37 +91,38 @@ export interface RouterState {
|
|
|
69
91
|
|
|
70
92
|
type PageDataLoaderContext = RouterState & {}
|
|
71
93
|
|
|
72
|
-
export
|
|
94
|
+
export type PageDataLoaderConfig<T = unknown> = {
|
|
73
95
|
/**
|
|
74
96
|
* The function to load the page data
|
|
75
97
|
*/
|
|
76
98
|
load: (context: PageDataLoaderContext) => Promise<T>
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The mode to use for the page data loader
|
|
102
|
+
* @default "client"
|
|
103
|
+
* @description
|
|
104
|
+
* - **static**: The page data is loaded at build time and never updated
|
|
105
|
+
* - **client**: The page data is loaded upon navigation and updated on subsequent navigations
|
|
106
|
+
*/
|
|
107
|
+
mode?: "static" | "client"
|
|
77
108
|
/**
|
|
78
|
-
* Enable transitions when swapping between "load", "error" and "data" states
|
|
109
|
+
* Enable transitions when swapping between "load", "error" and "data" states (only when mode is "client")
|
|
79
110
|
*/
|
|
80
111
|
transition?: boolean
|
|
81
112
|
}
|
|
82
113
|
|
|
83
|
-
export interface PageConfig {
|
|
114
|
+
export interface PageConfig<T = unknown> {
|
|
84
115
|
/**
|
|
85
116
|
* The loader configuration for this page
|
|
86
117
|
*/
|
|
87
|
-
loader?: PageDataLoaderConfig
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
118
|
+
loader?: PageDataLoaderConfig<T>
|
|
119
|
+
/**
|
|
120
|
+
* Generate static params for this page. For each params
|
|
121
|
+
* returned, a page will be generated
|
|
122
|
+
*/
|
|
123
|
+
generateStaticParams?: () => RouteParams[] | Promise<RouteParams[]>
|
|
91
124
|
}
|
|
92
125
|
|
|
93
|
-
export type PageProps<T extends PageConfig
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
Awaited<ReturnType<T["loader"]["load"]>>,
|
|
97
|
-
FileRouterDataLoadError
|
|
98
|
-
>
|
|
99
|
-
: {}
|
|
100
|
-
|
|
101
|
-
export interface ErrorPageProps {
|
|
102
|
-
source?: {
|
|
103
|
-
path: string
|
|
104
|
-
}
|
|
105
|
-
}
|
|
126
|
+
export type PageProps<T extends PageConfig<any>> = T extends PageConfig<infer U>
|
|
127
|
+
? AsyncTaskState<U, FileRouterDataLoadError>
|
|
128
|
+
: {}
|
|
@@ -5,11 +5,11 @@ import type {
|
|
|
5
5
|
RouteMatch,
|
|
6
6
|
ViteImportMap,
|
|
7
7
|
} from "../types.internal"
|
|
8
|
-
import { PageConfig, PageProps } from "../types.js"
|
|
9
8
|
|
|
10
9
|
export {
|
|
11
10
|
formatViteImportMap,
|
|
12
11
|
matchRoute,
|
|
12
|
+
match404Route,
|
|
13
13
|
matchLayouts,
|
|
14
14
|
normalizePrefixPath,
|
|
15
15
|
parseQuery,
|
|
@@ -24,7 +24,11 @@ function formatViteImportMap(
|
|
|
24
24
|
return Object.keys(map).reduce<FormattedViteImportMap>((acc, key) => {
|
|
25
25
|
const dirIndex = key.indexOf(dir)
|
|
26
26
|
if (dirIndex === -1) {
|
|
27
|
-
|
|
27
|
+
if (__DEV__) {
|
|
28
|
+
console.warn(
|
|
29
|
+
`[kiru/router]: File "${key}" does not start with "${dir}".`
|
|
30
|
+
)
|
|
31
|
+
}
|
|
28
32
|
return acc
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -61,10 +65,7 @@ function formatViteImportMap(
|
|
|
61
65
|
load: map[key],
|
|
62
66
|
specificity,
|
|
63
67
|
segments,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (__DEV__) {
|
|
67
|
-
value.filePath = key
|
|
68
|
+
filePath: key,
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
return {
|
|
@@ -142,6 +143,23 @@ function matchRoute(
|
|
|
142
143
|
return matches[0] || null
|
|
143
144
|
}
|
|
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
|
+
|
|
145
163
|
function matchLayouts(
|
|
146
164
|
layouts: FormattedViteImportMap,
|
|
147
165
|
routeSegments: string[]
|
|
@@ -197,7 +215,7 @@ function parseQuery(
|
|
|
197
215
|
function wrapWithLayouts(
|
|
198
216
|
layouts: Kiru.FC[],
|
|
199
217
|
page: Kiru.FC,
|
|
200
|
-
props:
|
|
218
|
+
props: Record<string, unknown>
|
|
201
219
|
) {
|
|
202
220
|
return layouts.reduceRight(
|
|
203
221
|
(children, Layout) => createElement(Layout, { children }),
|
package/src/types.dom.ts
CHANGED
|
@@ -365,7 +365,7 @@ declare global {
|
|
|
365
365
|
bivarianceHack(event: E): void
|
|
366
366
|
}["bivarianceHack"]
|
|
367
367
|
|
|
368
|
-
interface
|
|
368
|
+
interface BaseEventHandler<T extends Element = Element>
|
|
369
369
|
extends DOMEvent<Event, T> {}
|
|
370
370
|
|
|
371
371
|
interface AnimationEvent<T extends Element = Element>
|
|
@@ -413,10 +413,6 @@ declare global {
|
|
|
413
413
|
interface WheelEvent<T extends Element = Element>
|
|
414
414
|
extends DOMEvent<NativeWheelEvent, T> {}
|
|
415
415
|
|
|
416
|
-
type BaseEventHandler<T extends Element = Element> = EventHandler<
|
|
417
|
-
BaseEvent<T>
|
|
418
|
-
>
|
|
419
|
-
|
|
420
416
|
type ClipboardEventHandler<T extends Element = Element> = EventHandler<
|
|
421
417
|
ClipboardEvent<T>
|
|
422
418
|
>
|
|
@@ -832,6 +828,7 @@ interface HtmlElementAttributes {
|
|
|
832
828
|
content?: string
|
|
833
829
|
httpEquiv?: string
|
|
834
830
|
name?: string
|
|
831
|
+
property?: string
|
|
835
832
|
}
|
|
836
833
|
meter: {
|
|
837
834
|
value?: string | number
|
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
|