dtwo 3.0.0 → 3.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 +1 -1
- package/README.md +6 -2
- package/bin/dtwo.mjs +1 -1
- package/config.d.ts +1 -1
- package/dist/app/compat/idle-callback.d.ts +2 -0
- package/dist/app/compat/idle-callback.mjs +13 -0
- package/dist/app/components/dtwo-island.d.ts +33 -0
- package/dist/app/components/dtwo-island.mjs +62 -0
- package/dist/app/components/dtwo-link.d.ts +3 -3
- package/dist/app/components/dtwo-link.mjs +26 -28
- package/dist/app/components/dtwo-loading-indicator.d.ts +3 -3
- package/dist/app/components/dtwo-loading-indicator.mjs +8 -7
- package/dist/app/components/dtwo-root.kdu +10 -2
- package/dist/app/components/island-renderer.d.ts +20 -0
- package/dist/app/components/island-renderer.mjs +26 -0
- package/dist/app/components/layout.d.ts +1 -1
- package/dist/app/components/layout.mjs +12 -3
- package/dist/app/components/utils.mjs +1 -0
- package/dist/app/composables/asyncData.d.ts +4 -1
- package/dist/app/composables/asyncData.mjs +15 -2
- package/dist/app/composables/component.d.ts +1 -1
- package/dist/app/composables/cookie.d.ts +5 -4
- package/dist/app/composables/cookie.mjs +8 -2
- package/dist/app/composables/error.d.ts +1 -1
- package/dist/app/composables/fetch.d.ts +6 -6
- package/dist/app/composables/fetch.mjs +2 -1
- package/dist/app/composables/index.d.ts +2 -1
- package/dist/app/composables/index.mjs +2 -1
- package/dist/app/composables/ready.d.ts +1 -0
- package/dist/app/composables/ready.mjs +12 -0
- package/dist/app/composables/router.d.ts +1 -1
- package/dist/app/composables/router.mjs +5 -0
- package/dist/app/composables/ssr.d.ts +1 -1
- package/dist/app/config.mjs +1 -0
- package/dist/app/dtwo.d.ts +5 -4
- package/dist/app/plugins/cross-origin-prefetch.client.mjs +19 -14
- package/dist/app/plugins/router.mjs +13 -5
- package/dist/app/types/augments.d.ts +1 -1
- package/dist/components/runtime/server-component.d.ts +3 -0
- package/dist/components/runtime/server-component.mjs +87 -0
- package/dist/core/runtime/hole/renderer.d.ts +19 -0
- package/dist/core/runtime/hole/renderer.mjs +71 -9
- package/dist/head/runtime/components.d.ts +14 -14
- package/dist/head/runtime/components.mjs +12 -1
- package/dist/head/runtime/composables.d.ts +17 -0
- package/dist/head/runtime/composables.mjs +9 -0
- package/dist/head/runtime/lib/kduse-head.plugin.mjs +2 -1
- package/dist/index.mjs +298 -77
- package/dist/pages/runtime/composables.d.ts +1 -1
- package/dist/pages/runtime/page.d.ts +5 -1
- package/dist/pages/runtime/page.mjs +6 -3
- package/dist/pages/runtime/plugins/prefetch.client.mjs +36 -0
- package/dist/pages/runtime/{router.d.ts → plugins/router.d.ts} +1 -1
- package/dist/pages/runtime/{router.mjs → plugins/router.mjs} +11 -8
- package/dist/pages/runtime/utils.d.ts +1 -1
- package/dist/pages/runtime/utils.mjs +1 -1
- package/dist/pages/runtime/validate.mjs +4 -4
- package/package.json +51 -51
- package/types.d.ts +3 -0
- package/dist/head/runtime/lib/kdu-meta.plugin.mjs +0 -31
- /package/dist/{head/runtime/lib/kdu-meta.plugin.d.ts → pages/runtime/plugins/prefetch.client.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { defineDtwoComponent } from './component';
|
|
2
|
-
export { useAsyncData, useLazyAsyncData, refreshDtwoData, clearDtwoData } from './asyncData';
|
|
2
|
+
export { useAsyncData, useLazyAsyncData, useDtwoData, refreshDtwoData, clearDtwoData } from './asyncData';
|
|
3
3
|
export type { AsyncDataOptions, AsyncData } from './asyncData';
|
|
4
4
|
export { useHydration } from './hydrate';
|
|
5
5
|
export { useState } from './state';
|
|
@@ -10,6 +10,7 @@ export type { FetchResult, UseFetchOptions } from './fetch';
|
|
|
10
10
|
export { useCookie } from './cookie';
|
|
11
11
|
export type { CookieOptions, CookieRef } from './cookie';
|
|
12
12
|
export { useRequestHeaders, useRequestEvent, setResponseStatus } from './ssr';
|
|
13
|
+
export { onDtwoReady } from './ready';
|
|
13
14
|
export { abortNavigation, addRouteMiddleware, defineDtwoRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from './router';
|
|
14
15
|
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router';
|
|
15
16
|
export { preloadComponents, prefetchComponents, preloadRouteComponents } from './preload';
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export { defineDtwoComponent } from "./component.mjs";
|
|
2
|
-
export { useAsyncData, useLazyAsyncData, refreshDtwoData, clearDtwoData } from "./asyncData.mjs";
|
|
2
|
+
export { useAsyncData, useLazyAsyncData, useDtwoData, refreshDtwoData, clearDtwoData } from "./asyncData.mjs";
|
|
3
3
|
export { useHydration } from "./hydrate.mjs";
|
|
4
4
|
export { useState } from "./state.mjs";
|
|
5
5
|
export { clearError, createError, isDtwoError, showError, useError } from "./error.mjs";
|
|
6
6
|
export { useFetch, useLazyFetch } from "./fetch.mjs";
|
|
7
7
|
export { useCookie } from "./cookie.mjs";
|
|
8
8
|
export { useRequestHeaders, useRequestEvent, setResponseStatus } from "./ssr.mjs";
|
|
9
|
+
export { onDtwoReady } from "./ready.mjs";
|
|
9
10
|
export { abortNavigation, addRouteMiddleware, defineDtwoRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from "./router.mjs";
|
|
10
11
|
export { preloadComponents, prefetchComponents, preloadRouteComponents } from "./preload.mjs";
|
|
11
12
|
export { isPrerendered, loadPayload, preloadPayload } from "./payload.mjs";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const onDtwoReady: (callback: () => any) => void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useDtwoApp } from "../dtwo.mjs";
|
|
2
|
+
import { requestIdleCallback } from "../compat/idle-callback.mjs";
|
|
3
|
+
export const onDtwoReady = (callback) => {
|
|
4
|
+
const dtwoApp = useDtwoApp();
|
|
5
|
+
if (dtwoApp.isHydrating) {
|
|
6
|
+
dtwoApp.hooks.hookOnce("app:suspense:resolve", () => {
|
|
7
|
+
requestIdleCallback(callback);
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
requestIdleCallback(callback);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NavigationGuard, RouteLocationNormalized, RouteLocationRaw, NavigationFailure } from 'kdu-router';
|
|
2
|
-
import { DtwoError } from './error';
|
|
2
|
+
import type { DtwoError } from './error';
|
|
3
3
|
export declare const useRouter: () => Router;
|
|
4
4
|
export declare const useRoute: () => RouteLocationNormalizedLoaded;
|
|
5
5
|
export declare const onBeforeRouteLeave: (guard: NavigationGuard) => void;
|
|
@@ -4,6 +4,7 @@ import { hasProtocol, joinURL, parseURL } from "ufo";
|
|
|
4
4
|
import { useDtwoApp, useRuntimeConfig } from "../dtwo.mjs";
|
|
5
5
|
import { createError } from "./error.mjs";
|
|
6
6
|
import { useState } from "./state.mjs";
|
|
7
|
+
import { setResponseStatus } from "./ssr.mjs";
|
|
7
8
|
export const useRouter = () => {
|
|
8
9
|
return useDtwoApp()?.$router;
|
|
9
10
|
};
|
|
@@ -64,6 +65,10 @@ export const navigateTo = (to, options) => {
|
|
|
64
65
|
if (process.server) {
|
|
65
66
|
const dtwoApp = useDtwoApp();
|
|
66
67
|
if (dtwoApp.ssrContext && dtwoApp.ssrContext.event) {
|
|
68
|
+
if (isProcessingMiddleware() && !isExternal) {
|
|
69
|
+
setResponseStatus(options?.redirectCode || 302);
|
|
70
|
+
return to;
|
|
71
|
+
}
|
|
67
72
|
const redirectLocation = isExternal ? toPath : joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).fullPath || "/");
|
|
68
73
|
return dtwoApp.callHook("app:redirected").then(() => sendRedirect(dtwoApp.ssrContext.event, redirectLocation, options?.redirectCode || 302));
|
|
69
74
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { H3Event } from 'h3';
|
|
2
|
-
import { DtwoApp } from '../dtwo';
|
|
2
|
+
import type { DtwoApp } from '../dtwo';
|
|
3
3
|
export declare function useRequestHeaders<K extends string = string>(include: K[]): Record<Lowercase<K>, string | undefined>;
|
|
4
4
|
export declare function useRequestHeaders(): Readonly<Record<string, string | undefined>>;
|
|
5
5
|
export declare function useRequestEvent(dtwoApp?: DtwoApp): H3Event;
|
package/dist/app/config.mjs
CHANGED
package/dist/app/dtwo.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Ref } from 'kdu';
|
|
2
|
-
import type {
|
|
3
|
-
import { Hookable } from 'hookable';
|
|
1
|
+
import type { App, onErrorCaptured, KNode, Ref } from 'kdu';
|
|
2
|
+
import type { Hookable } from 'hookable';
|
|
4
3
|
import type { RuntimeConfig, AppConfigInput } from '@dtwo/schema';
|
|
5
4
|
import type { SSRContext } from 'kdu-bundle-renderer/runtime';
|
|
6
5
|
import type { H3Event } from 'h3';
|
|
6
|
+
import type { DtwoIslandContext } from '../core/runtime/hole/renderer';
|
|
7
7
|
type DtwoMeta = {
|
|
8
8
|
htmlAttrs?: string;
|
|
9
9
|
headAttrs?: string;
|
|
@@ -46,6 +46,7 @@ export interface DtwoSSRContext extends SSRContext {
|
|
|
46
46
|
payload: _DtwoApp['payload'];
|
|
47
47
|
teleports?: Record<string, string>;
|
|
48
48
|
renderMeta?: () => Promise<DtwoMeta> | DtwoMeta;
|
|
49
|
+
islandContext?: DtwoIslandContext;
|
|
49
50
|
}
|
|
50
51
|
interface _DtwoApp {
|
|
51
52
|
kduApp: App<Element>;
|
|
@@ -112,7 +113,7 @@ export declare function isDtwoPlugin(plugin: unknown): boolean;
|
|
|
112
113
|
* @param dtwo A Dtwo instance
|
|
113
114
|
* @param setup The function to call
|
|
114
115
|
*/
|
|
115
|
-
export declare function callWithDtwo<T extends (...args: any[]) => any>(dtwo: DtwoApp | _DtwoApp, setup: T, args?: Parameters<T>):
|
|
116
|
+
export declare function callWithDtwo<T extends (...args: any[]) => any>(dtwo: DtwoApp | _DtwoApp, setup: T, args?: Parameters<T>): ReturnType<T> | Promise<ReturnType<T>>;
|
|
116
117
|
/**
|
|
117
118
|
* Returns the current Dtwo instance.
|
|
118
119
|
*/
|
|
@@ -3,26 +3,31 @@ import { parseURL } from "ufo";
|
|
|
3
3
|
import { defineDtwoPlugin, useHead } from "#app";
|
|
4
4
|
export default defineDtwoPlugin((dtwoApp) => {
|
|
5
5
|
const externalURLs = ref(/* @__PURE__ */ new Set());
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
})
|
|
6
|
+
function generateRules() {
|
|
7
|
+
return {
|
|
8
|
+
type: "speculationrules",
|
|
9
|
+
key: "speculationrules",
|
|
10
|
+
innerHTML: JSON.stringify({
|
|
11
|
+
prefetch: [
|
|
12
|
+
{
|
|
13
|
+
source: "list",
|
|
14
|
+
urls: [...externalURLs.value],
|
|
15
|
+
requires: ["anonymous-client-ip-when-cross-origin"]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
19
18
|
})
|
|
20
|
-
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const head = useHead({
|
|
22
|
+
script: [generateRules()]
|
|
21
23
|
});
|
|
22
24
|
dtwoApp.hook("link:prefetch", (url) => {
|
|
23
25
|
const { protocol } = parseURL(url);
|
|
24
26
|
if (protocol && ["http:", "https:"].includes(protocol)) {
|
|
25
27
|
externalURLs.value.add(url);
|
|
28
|
+
head?.patch({
|
|
29
|
+
script: [generateRules()]
|
|
30
|
+
});
|
|
26
31
|
}
|
|
27
32
|
});
|
|
28
33
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { reactive, h } from "kdu";
|
|
1
|
+
import { reactive, h, isReadonly } from "kdu";
|
|
2
2
|
import { parseURL, stringifyParsedURL, parseQuery, stringifyQuery, withoutBase, isEqual, joinURL } from "ufo";
|
|
3
3
|
import { createError } from "h3";
|
|
4
|
-
import { defineDtwoPlugin, clearError, navigateTo, showError, useRuntimeConfig, useState } from "../index.mjs";
|
|
4
|
+
import { defineDtwoPlugin, clearError, navigateTo, showError, useRuntimeConfig, useState, useRequestEvent } from "../index.mjs";
|
|
5
5
|
import { callWithDtwo } from "../dtwo.mjs";
|
|
6
6
|
import { globalMiddleware } from "#build/middleware";
|
|
7
7
|
function getRouteFromPath(fullPath) {
|
|
@@ -18,6 +18,7 @@ function getRouteFromPath(fullPath) {
|
|
|
18
18
|
fullPath,
|
|
19
19
|
query: parseQuery(url.search),
|
|
20
20
|
hash: url.hash,
|
|
21
|
+
// stub properties for compat with kdu-router
|
|
21
22
|
params: {},
|
|
22
23
|
name: void 0,
|
|
23
24
|
matched: [],
|
|
@@ -78,17 +79,21 @@ export default defineDtwoPlugin((dtwoApp) => {
|
|
|
78
79
|
const router = {
|
|
79
80
|
currentRoute: route,
|
|
80
81
|
isReady: () => Promise.resolve(),
|
|
82
|
+
// These options provide a similar API to kdu-router but have no effect
|
|
81
83
|
options: {},
|
|
82
84
|
install: () => Promise.resolve(),
|
|
85
|
+
// Navigation
|
|
83
86
|
push: (url) => handleNavigation(url, false),
|
|
84
87
|
replace: (url) => handleNavigation(url, true),
|
|
85
88
|
back: () => window.history.go(-1),
|
|
86
89
|
go: (delta) => window.history.go(delta),
|
|
87
90
|
forward: () => window.history.go(1),
|
|
91
|
+
// Guards
|
|
88
92
|
beforeResolve: (guard) => registerHook("resolve:before", guard),
|
|
89
93
|
beforeEach: (guard) => registerHook("navigate:before", guard),
|
|
90
94
|
afterEach: (guard) => registerHook("navigate:after", guard),
|
|
91
95
|
onError: (handler) => registerHook("error", handler),
|
|
96
|
+
// Routes
|
|
92
97
|
resolve: getRouteFromPath,
|
|
93
98
|
addRoute: (parentName, route2) => {
|
|
94
99
|
routes.push(route2);
|
|
@@ -108,6 +113,7 @@ export default defineDtwoPlugin((dtwoApp) => {
|
|
|
108
113
|
to: String,
|
|
109
114
|
custom: Boolean,
|
|
110
115
|
replace: Boolean,
|
|
116
|
+
// Not implemented
|
|
111
117
|
activeClass: String,
|
|
112
118
|
exactActiveClass: String,
|
|
113
119
|
ariaCurrentValue: String
|
|
@@ -138,8 +144,8 @@ export default defineDtwoPlugin((dtwoApp) => {
|
|
|
138
144
|
dtwoApp.hooks.hookOnce("app:created", async () => {
|
|
139
145
|
router.beforeEach(async (to, from) => {
|
|
140
146
|
to.meta = reactive(to.meta || {});
|
|
141
|
-
if (dtwoApp.isHydrating) {
|
|
142
|
-
to.meta.layout = initialLayout.value
|
|
147
|
+
if (dtwoApp.isHydrating && initialLayout.value && !isReadonly(to.meta.layout)) {
|
|
148
|
+
to.meta.layout = initialLayout.value;
|
|
143
149
|
}
|
|
144
150
|
dtwoApp._processingMiddleware = true;
|
|
145
151
|
const middlewareEntries = /* @__PURE__ */ new Set([...globalMiddleware, ...dtwoApp._middleware.global]);
|
|
@@ -164,7 +170,9 @@ export default defineDtwoPlugin((dtwoApp) => {
|
|
|
164
170
|
});
|
|
165
171
|
await router.replace(initialURL);
|
|
166
172
|
if (!isEqual(route.fullPath, initialURL)) {
|
|
167
|
-
await callWithDtwo(dtwoApp,
|
|
173
|
+
const event = await callWithDtwo(dtwoApp, useRequestEvent);
|
|
174
|
+
const options = { redirectCode: event.node.res.statusCode !== 200 ? event.node.res.statusCode || 302 : 302 };
|
|
175
|
+
await callWithDtwo(dtwoApp, navigateTo, [route.fullPath, options]);
|
|
168
176
|
}
|
|
169
177
|
});
|
|
170
178
|
return {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const createServerComponent: (name: string) => import("kdu").DefineComponent<{}, () => import("kdu").KNode<import("kdu").RendererNode, import("kdu").RendererElement, {
|
|
2
|
+
[key: string]: any;
|
|
3
|
+
}>, {}, {}, {}, import("kdu").ComponentOptionsMixin, import("kdu").ComponentOptionsMixin, {}, string, import("kdu").KNodeProps & import("kdu").AllowedComponentProps & import("kdu").ComponentCustomProps, Readonly<import("kdu").ExtractPropTypes<{}>>, {}>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { defineComponent, createStaticKNode, computed, h, watch } from "kdu";
|
|
2
|
+
import { debounce } from "perfect-debounce";
|
|
3
|
+
import { hash } from "ohash";
|
|
4
|
+
import { appendHeader } from "h3";
|
|
5
|
+
import { useAsyncData, useHead, useDtwoApp, useRequestEvent } from "#app";
|
|
6
|
+
const pKey = "_islandPromises";
|
|
7
|
+
export const createServerComponent = (name) => {
|
|
8
|
+
return defineComponent({
|
|
9
|
+
name,
|
|
10
|
+
inheritAttrs: false,
|
|
11
|
+
setup(_props, { attrs }) {
|
|
12
|
+
return () => h(DtwoServerComponent, {
|
|
13
|
+
name,
|
|
14
|
+
props: attrs
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
const DtwoServerComponent = defineComponent({
|
|
20
|
+
name: "DtwoServerComponent",
|
|
21
|
+
props: {
|
|
22
|
+
name: {
|
|
23
|
+
type: String,
|
|
24
|
+
required: true
|
|
25
|
+
},
|
|
26
|
+
props: {
|
|
27
|
+
type: Object,
|
|
28
|
+
default: () => void 0
|
|
29
|
+
},
|
|
30
|
+
context: {
|
|
31
|
+
type: Object,
|
|
32
|
+
default: () => ({})
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
async setup(props) {
|
|
36
|
+
const dtwoApp = useDtwoApp();
|
|
37
|
+
const hashId = computed(() => hash([props.name, props.props, props.context]));
|
|
38
|
+
const event = useRequestEvent();
|
|
39
|
+
function _fetchComponent() {
|
|
40
|
+
const url = `/__dtwo_island/${props.name}:${hashId.value}`;
|
|
41
|
+
if (process.server && process.env.prerender) {
|
|
42
|
+
appendHeader(event, "x-hole-prerender", url);
|
|
43
|
+
}
|
|
44
|
+
return $fetch(url, {
|
|
45
|
+
params: {
|
|
46
|
+
...props.context,
|
|
47
|
+
props: props.props ? JSON.stringify(props.props) : void 0
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const res = useAsyncData(
|
|
52
|
+
`${props.name}:${hashId.value}`,
|
|
53
|
+
async () => {
|
|
54
|
+
dtwoApp[pKey] = dtwoApp[pKey] || {};
|
|
55
|
+
if (!dtwoApp[pKey][hashId.value]) {
|
|
56
|
+
dtwoApp[pKey][hashId.value] = _fetchComponent().finally(() => {
|
|
57
|
+
delete dtwoApp[pKey][hashId.value];
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const res2 = await dtwoApp[pKey][hashId.value];
|
|
61
|
+
return {
|
|
62
|
+
html: res2.html,
|
|
63
|
+
head: {
|
|
64
|
+
link: res2.head.link,
|
|
65
|
+
style: res2.head.style
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
immediate: process.server || !dtwoApp.isHydrating,
|
|
71
|
+
default: () => ({
|
|
72
|
+
html: "",
|
|
73
|
+
head: {
|
|
74
|
+
link: [],
|
|
75
|
+
style: []
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
useHead(() => res.data.value.head);
|
|
81
|
+
if (process.client) {
|
|
82
|
+
watch(props, debounce(() => res.execute(), 100));
|
|
83
|
+
}
|
|
84
|
+
await res;
|
|
85
|
+
return () => createStaticKNode(res.data.value.html, 1);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export interface DtwoRenderHTMLContext {
|
|
2
|
+
island?: boolean;
|
|
2
3
|
htmlAttrs: string[];
|
|
3
4
|
head: string[];
|
|
4
5
|
bodyAttrs: string[];
|
|
@@ -6,6 +7,24 @@ export interface DtwoRenderHTMLContext {
|
|
|
6
7
|
body: string[];
|
|
7
8
|
bodyAppend: string[];
|
|
8
9
|
}
|
|
10
|
+
export interface DtwoIslandContext {
|
|
11
|
+
id?: string;
|
|
12
|
+
name: string;
|
|
13
|
+
props?: Record<string, any>;
|
|
14
|
+
url?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface DtwoIslandResponse {
|
|
17
|
+
id?: string;
|
|
18
|
+
html: string;
|
|
19
|
+
state: Record<string, any>;
|
|
20
|
+
head: {
|
|
21
|
+
link: (Record<string, string>)[];
|
|
22
|
+
style: ({
|
|
23
|
+
innerHTML: string;
|
|
24
|
+
key: string;
|
|
25
|
+
})[];
|
|
26
|
+
};
|
|
27
|
+
}
|
|
9
28
|
export interface DtwoRenderResponse {
|
|
10
29
|
body: string;
|
|
11
30
|
statusCode: number;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createRenderer, renderResourceHeaders } from "kdu-bundle-renderer/runtime";
|
|
2
|
-
import { appendHeader,
|
|
2
|
+
import { appendHeader, getQuery, writeEarlyHints, readBody, createError } from "h3";
|
|
3
3
|
import devalue from "@d2js/devalue";
|
|
4
|
+
import destr from "destr";
|
|
4
5
|
import { joinURL } from "ufo";
|
|
5
6
|
import { renderToString as _renderToString } from "kdu/server-renderer";
|
|
6
7
|
import { useRuntimeConfig, useHoleApp, defineRenderHandler, getRouteRules } from "#internal/hole";
|
|
8
|
+
import { hash } from "ohash";
|
|
7
9
|
import { appRootId, appRootTag } from "#internal/dtwo.config.mjs";
|
|
8
10
|
import { buildAssetsURL, publicAssetsURL } from "#paths";
|
|
9
11
|
globalThis.__buildAssetsURL = buildAssetsURL;
|
|
@@ -65,15 +67,30 @@ const getSPARenderer = lazyCachedFunction(async () => {
|
|
|
65
67
|
renderToString
|
|
66
68
|
};
|
|
67
69
|
});
|
|
70
|
+
async function getIslandContext(event) {
|
|
71
|
+
const url = event.node.req.url?.substring("/__dtwo_island".length + 1) || "";
|
|
72
|
+
const [componentName, hashId] = url.split("?")[0].split(":");
|
|
73
|
+
const context = event.node.req.method === "GET" ? getQuery(event) : await readBody(event);
|
|
74
|
+
const ctx = {
|
|
75
|
+
url: "/",
|
|
76
|
+
...context,
|
|
77
|
+
id: hashId,
|
|
78
|
+
name: componentName,
|
|
79
|
+
props: destr(context.props) || {}
|
|
80
|
+
};
|
|
81
|
+
return ctx;
|
|
82
|
+
}
|
|
68
83
|
const PAYLOAD_CACHE = process.env.DTWO_PAYLOAD_EXTRACTION && process.env.prerender ? /* @__PURE__ */ new Map() : null;
|
|
69
84
|
const PAYLOAD_URL_RE = /\/_payload(\.[a-zA-Z0-9]+)?.js(\?.*)?$/;
|
|
70
85
|
const PRERENDER_NO_SSR_ROUTES = /* @__PURE__ */ new Set(["/index.html", "/200.html", "/404.html"]);
|
|
71
86
|
export default defineRenderHandler(async (event) => {
|
|
87
|
+
const holeApp = useHoleApp();
|
|
72
88
|
const ssrError = event.node.req.url?.startsWith("/__dtwo_error") ? getQuery(event) : null;
|
|
73
89
|
if (ssrError && event.node.req.socket.readyState !== "readOnly") {
|
|
74
90
|
throw createError("Cannot directly render error page!");
|
|
75
91
|
}
|
|
76
|
-
|
|
92
|
+
const islandContext = process.env.DTWO_COMPONENT_ISLANDS && event.node.req.url?.startsWith("/__dtwo_island") ? await getIslandContext(event) : void 0;
|
|
93
|
+
let url = ssrError?.url || islandContext?.url || event.node.req.url;
|
|
77
94
|
const isRenderingPayload = PAYLOAD_URL_RE.test(url);
|
|
78
95
|
if (isRenderingPayload) {
|
|
79
96
|
url = url.substring(0, url.lastIndexOf("/")) || "/";
|
|
@@ -90,7 +107,9 @@ export default defineRenderHandler(async (event) => {
|
|
|
90
107
|
noSSR: !!process.env.DTWO_NO_SSR || !!event.node.req.headers["x-dtwo-no-ssr"] || routeOptions.ssr === false || (process.env.prerender ? PRERENDER_NO_SSR_ROUTES.has(url) : false),
|
|
91
108
|
error: !!ssrError,
|
|
92
109
|
dtwo: void 0,
|
|
93
|
-
|
|
110
|
+
/* DtwoApp */
|
|
111
|
+
payload: ssrError ? { error: ssrError } : {},
|
|
112
|
+
islandContext
|
|
94
113
|
};
|
|
95
114
|
const _PAYLOAD_EXTRACTION = process.env.prerender && process.env.DTWO_PAYLOAD_EXTRACTION && !ssrContext.noSSR;
|
|
96
115
|
const payloadURL = _PAYLOAD_EXTRACTION ? joinURL(useRuntimeConfig().app.baseURL, url, "_payload.js") : void 0;
|
|
@@ -123,6 +142,7 @@ export default defineRenderHandler(async (event) => {
|
|
|
123
142
|
const renderedMeta = await ssrContext.renderMeta?.() ?? {};
|
|
124
143
|
const inlinedStyles = process.env.DTWO_INLINE_STYLES ? await renderInlineStyles(ssrContext.modules ?? ssrContext._registeredComponents ?? []) : "";
|
|
125
144
|
const htmlContext = {
|
|
145
|
+
island: Boolean(islandContext),
|
|
126
146
|
htmlAttrs: normalizeChunks([renderedMeta.htmlAttrs]),
|
|
127
147
|
head: normalizeChunks([
|
|
128
148
|
renderedMeta.headTags,
|
|
@@ -137,24 +157,52 @@ export default defineRenderHandler(async (event) => {
|
|
|
137
157
|
renderedMeta.bodyScriptsPrepend,
|
|
138
158
|
ssrContext.teleports?.body
|
|
139
159
|
]),
|
|
140
|
-
body: [
|
|
141
|
-
_rendered.html
|
|
142
|
-
],
|
|
160
|
+
body: process.env.DTWO_COMPONENT_ISLANDS && islandContext ? [] : [_rendered.html],
|
|
143
161
|
bodyAppend: normalizeChunks([
|
|
144
162
|
process.env.DTWO_NO_SCRIPTS ? void 0 : _PAYLOAD_EXTRACTION ? `<script type="module">import p from "${payloadURL}";window.__DTWO__={...p,...(${devalue(splitPayload(ssrContext).initial)})}<\/script>` : `<script>window.__DTWO__=${devalue(ssrContext.payload)}<\/script>`,
|
|
145
163
|
_rendered.renderScripts(),
|
|
164
|
+
// Note: bodyScripts may contain tags other than <script>
|
|
146
165
|
renderedMeta.bodyScripts
|
|
147
166
|
])
|
|
148
167
|
};
|
|
149
|
-
const holeApp = useHoleApp();
|
|
150
168
|
await holeApp.hooks.callHook("render:html", htmlContext, { event });
|
|
169
|
+
if (process.env.DTWO_COMPONENT_ISLANDS && islandContext) {
|
|
170
|
+
const _tags = htmlContext.head.flatMap((head2) => extractHTMLTags(head2));
|
|
171
|
+
const head = {
|
|
172
|
+
link: _tags.filter((tag) => tag.tagName === "link" && tag.attrs.rel === "stylesheet" && tag.attrs.href.includes("scoped") && !tag.attrs.href.includes("pages/")).map((tag) => ({
|
|
173
|
+
key: "island-link-" + hash(tag.attrs.href),
|
|
174
|
+
...tag.attrs
|
|
175
|
+
})),
|
|
176
|
+
style: _tags.filter((tag) => tag.tagName === "style" && tag.innerHTML).map((tag) => ({
|
|
177
|
+
key: "island-style-" + hash(tag.innerHTML),
|
|
178
|
+
innerHTML: tag.innerHTML
|
|
179
|
+
}))
|
|
180
|
+
};
|
|
181
|
+
const islandResponse = {
|
|
182
|
+
id: islandContext.id,
|
|
183
|
+
head,
|
|
184
|
+
html: ssrContext.teleports["dtwo-island"].replace(/<!--.*-->/g, ""),
|
|
185
|
+
state: ssrContext.payload.state
|
|
186
|
+
};
|
|
187
|
+
await holeApp.hooks.callHook("render:island", islandResponse, { event, islandContext });
|
|
188
|
+
const response2 = {
|
|
189
|
+
body: JSON.stringify(islandResponse, null, 2),
|
|
190
|
+
statusCode: event.node.res.statusCode,
|
|
191
|
+
statusMessage: event.node.res.statusMessage,
|
|
192
|
+
headers: {
|
|
193
|
+
"content-type": "application/json;charset=utf-8",
|
|
194
|
+
"x-powered-by": "Dtwo"
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
return response2;
|
|
198
|
+
}
|
|
151
199
|
const response = {
|
|
152
200
|
body: renderHTMLDocument(htmlContext),
|
|
153
201
|
statusCode: event.node.res.statusCode,
|
|
154
202
|
statusMessage: event.node.res.statusMessage,
|
|
155
203
|
headers: {
|
|
156
|
-
"
|
|
157
|
-
"
|
|
204
|
+
"content-type": "text/html;charset=utf-8",
|
|
205
|
+
"x-powered-by": "Dtwo"
|
|
158
206
|
}
|
|
159
207
|
};
|
|
160
208
|
return response;
|
|
@@ -187,6 +235,20 @@ function renderHTMLDocument(html) {
|
|
|
187
235
|
<body ${joinAttrs(html.bodyAttrs)}>${joinTags(html.bodyPrepend)}${joinTags(html.body)}${joinTags(html.bodyAppend)}</body>
|
|
188
236
|
</html>`;
|
|
189
237
|
}
|
|
238
|
+
const HTML_TAG_RE = /<(?<tag>[a-z]+)(?<rawAttrs> [^>]*)?>(?:(?<innerHTML>[\s\S]*?)<\/\k<tag>)?/g;
|
|
239
|
+
const HTML_TAG_ATTR_RE = /(?<name>[a-z]+)="(?<value>[^"]*)"/g;
|
|
240
|
+
function extractHTMLTags(html) {
|
|
241
|
+
const tags = [];
|
|
242
|
+
for (const tagMatch of html.matchAll(HTML_TAG_RE)) {
|
|
243
|
+
const attrs = {};
|
|
244
|
+
for (const attrMatch of tagMatch.groups.rawAttrs?.matchAll(HTML_TAG_ATTR_RE) || []) {
|
|
245
|
+
attrs[attrMatch.groups.name] = attrMatch.groups.value;
|
|
246
|
+
}
|
|
247
|
+
const innerHTML = tagMatch.groups.innerHTML || "";
|
|
248
|
+
tags.push({ tagName: tagMatch.groups.tag, attrs, innerHTML });
|
|
249
|
+
}
|
|
250
|
+
return tags;
|
|
251
|
+
}
|
|
190
252
|
async function renderInlineStyles(usedModules) {
|
|
191
253
|
const styleMap = await getSSRStyles();
|
|
192
254
|
const inlinedStyles = /* @__PURE__ */ new Set();
|
|
@@ -10,7 +10,7 @@ export declare const NoScript: import("kdu").DefineComponent<{
|
|
|
10
10
|
type: BooleanConstructor;
|
|
11
11
|
default: undefined;
|
|
12
12
|
};
|
|
13
|
-
class: StringConstructor;
|
|
13
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
14
14
|
contenteditable: {
|
|
15
15
|
type: BooleanConstructor;
|
|
16
16
|
default: undefined;
|
|
@@ -58,7 +58,7 @@ export declare const NoScript: import("kdu").DefineComponent<{
|
|
|
58
58
|
type: BooleanConstructor;
|
|
59
59
|
default: undefined;
|
|
60
60
|
};
|
|
61
|
-
class: StringConstructor;
|
|
61
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
62
62
|
contenteditable: {
|
|
63
63
|
type: BooleanConstructor;
|
|
64
64
|
default: undefined;
|
|
@@ -134,7 +134,7 @@ export declare const Link: import("kdu").DefineComponent<{
|
|
|
134
134
|
type: BooleanConstructor;
|
|
135
135
|
default: undefined;
|
|
136
136
|
};
|
|
137
|
-
class: StringConstructor;
|
|
137
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
138
138
|
contenteditable: {
|
|
139
139
|
type: BooleanConstructor;
|
|
140
140
|
default: undefined;
|
|
@@ -204,7 +204,7 @@ export declare const Link: import("kdu").DefineComponent<{
|
|
|
204
204
|
type: BooleanConstructor;
|
|
205
205
|
default: undefined;
|
|
206
206
|
};
|
|
207
|
-
class: StringConstructor;
|
|
207
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
208
208
|
contenteditable: {
|
|
209
209
|
type: BooleanConstructor;
|
|
210
210
|
default: undefined;
|
|
@@ -259,7 +259,7 @@ export declare const Base: import("kdu").DefineComponent<{
|
|
|
259
259
|
type: BooleanConstructor;
|
|
260
260
|
default: undefined;
|
|
261
261
|
};
|
|
262
|
-
class: StringConstructor;
|
|
262
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
263
263
|
contenteditable: {
|
|
264
264
|
type: BooleanConstructor;
|
|
265
265
|
default: undefined;
|
|
@@ -307,7 +307,7 @@ export declare const Base: import("kdu").DefineComponent<{
|
|
|
307
307
|
type: BooleanConstructor;
|
|
308
308
|
default: undefined;
|
|
309
309
|
};
|
|
310
|
-
class: StringConstructor;
|
|
310
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
311
311
|
contenteditable: {
|
|
312
312
|
type: BooleanConstructor;
|
|
313
313
|
default: undefined;
|
|
@@ -373,7 +373,7 @@ export declare const Meta: import("kdu").DefineComponent<{
|
|
|
373
373
|
type: BooleanConstructor;
|
|
374
374
|
default: undefined;
|
|
375
375
|
};
|
|
376
|
-
class: StringConstructor;
|
|
376
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
377
377
|
contenteditable: {
|
|
378
378
|
type: BooleanConstructor;
|
|
379
379
|
default: undefined;
|
|
@@ -425,7 +425,7 @@ export declare const Meta: import("kdu").DefineComponent<{
|
|
|
425
425
|
type: BooleanConstructor;
|
|
426
426
|
default: undefined;
|
|
427
427
|
};
|
|
428
|
-
class: StringConstructor;
|
|
428
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
429
429
|
contenteditable: {
|
|
430
430
|
type: BooleanConstructor;
|
|
431
431
|
default: undefined;
|
|
@@ -488,7 +488,7 @@ export declare const Style: import("kdu").DefineComponent<{
|
|
|
488
488
|
type: BooleanConstructor;
|
|
489
489
|
default: undefined;
|
|
490
490
|
};
|
|
491
|
-
class: StringConstructor;
|
|
491
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
492
492
|
contenteditable: {
|
|
493
493
|
type: BooleanConstructor;
|
|
494
494
|
default: undefined;
|
|
@@ -543,7 +543,7 @@ export declare const Style: import("kdu").DefineComponent<{
|
|
|
543
543
|
type: BooleanConstructor;
|
|
544
544
|
default: undefined;
|
|
545
545
|
};
|
|
546
|
-
class: StringConstructor;
|
|
546
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
547
547
|
contenteditable: {
|
|
548
548
|
type: BooleanConstructor;
|
|
549
549
|
default: undefined;
|
|
@@ -601,7 +601,7 @@ export declare const Html: import("kdu").DefineComponent<{
|
|
|
601
601
|
type: BooleanConstructor;
|
|
602
602
|
default: undefined;
|
|
603
603
|
};
|
|
604
|
-
class: StringConstructor;
|
|
604
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
605
605
|
contenteditable: {
|
|
606
606
|
type: BooleanConstructor;
|
|
607
607
|
default: undefined;
|
|
@@ -651,7 +651,7 @@ export declare const Html: import("kdu").DefineComponent<{
|
|
|
651
651
|
type: BooleanConstructor;
|
|
652
652
|
default: undefined;
|
|
653
653
|
};
|
|
654
|
-
class: StringConstructor;
|
|
654
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
655
655
|
contenteditable: {
|
|
656
656
|
type: BooleanConstructor;
|
|
657
657
|
default: undefined;
|
|
@@ -703,7 +703,7 @@ export declare const Body: import("kdu").DefineComponent<{
|
|
|
703
703
|
type: BooleanConstructor;
|
|
704
704
|
default: undefined;
|
|
705
705
|
};
|
|
706
|
-
class: StringConstructor;
|
|
706
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
707
707
|
contenteditable: {
|
|
708
708
|
type: BooleanConstructor;
|
|
709
709
|
default: undefined;
|
|
@@ -750,7 +750,7 @@ export declare const Body: import("kdu").DefineComponent<{
|
|
|
750
750
|
type: BooleanConstructor;
|
|
751
751
|
default: undefined;
|
|
752
752
|
};
|
|
753
|
-
class: StringConstructor;
|
|
753
|
+
class: (StringConstructor | ObjectConstructor | ArrayConstructor)[];
|
|
754
754
|
contenteditable: {
|
|
755
755
|
type: BooleanConstructor;
|
|
756
756
|
default: undefined;
|