vike 0.4.144-commit-de18325 → 0.4.145-commit-2520555
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/cjs/node/plugin/plugins/buildConfig.js +1 -1
- package/dist/cjs/node/plugin/plugins/config/index.js +3 -3
- package/dist/cjs/node/plugin/plugins/devConfig/determineOptimizeDeps.js +1 -1
- package/dist/cjs/node/plugin/plugins/importUserCode/getVirtualFileImportUserCode.js +1 -1
- package/dist/cjs/node/plugin/plugins/importUserCode/index.js +1 -1
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +1 -1
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +5 -2
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.js +4 -2
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +18 -6
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/transpileAndExecuteFile.js +2 -2
- package/dist/cjs/node/prerender/runPrerender.js +17 -8
- package/dist/cjs/node/runtime/renderPage/log404/index.js +2 -1
- package/dist/cjs/node/shared/getConfigVike.js +4 -1
- package/dist/cjs/shared/page-configs/serialize/parseConfigValuesImported.js +8 -5
- package/dist/cjs/shared/route/noRouteMatch.js +4 -0
- package/dist/cjs/utils/isExternalLink.js +7 -0
- package/dist/cjs/utils/onPageVisibilityChange.js +19 -0
- package/dist/cjs/utils/projectInfo.js +1 -1
- package/dist/esm/client/client-routing-runtime/createPageContext.js +0 -1
- package/dist/esm/client/client-routing-runtime/getBaseServer.d.ts +2 -1
- package/dist/esm/client/client-routing-runtime/getBaseServer.js +2 -1
- package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.d.ts +39 -0
- package/dist/esm/client/client-routing-runtime/{getPageContext.js → getPageContextFromHooks.js} +48 -74
- package/dist/esm/client/client-routing-runtime/history.js +9 -5
- package/dist/esm/client/client-routing-runtime/installClientRouter.d.ts +0 -19
- package/dist/esm/client/client-routing-runtime/installClientRouter.js +11 -488
- package/dist/esm/client/client-routing-runtime/isClientSideRoutable.d.ts +3 -3
- package/dist/esm/client/client-routing-runtime/isClientSideRoutable.js +4 -7
- package/dist/esm/client/client-routing-runtime/navigate.js +2 -3
- package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.d.ts +4 -0
- package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.js +63 -0
- package/dist/esm/client/client-routing-runtime/onLinkClick.d.ts +2 -0
- package/dist/esm/client/client-routing-runtime/onLinkClick.js +40 -0
- package/dist/esm/client/client-routing-runtime/prefetch.js +7 -8
- package/dist/esm/client/client-routing-runtime/renderPageClientSide.d.ts +19 -0
- package/dist/esm/client/client-routing-runtime/renderPageClientSide.js +347 -0
- package/dist/esm/client/client-routing-runtime/scrollRestoration.d.ts +6 -0
- package/dist/esm/client/client-routing-runtime/scrollRestoration.js +25 -0
- package/dist/esm/client/client-routing-runtime/setScrollPosition.d.ts +7 -0
- package/dist/esm/client/client-routing-runtime/setScrollPosition.js +77 -0
- package/dist/esm/client/client-routing-runtime/skipLink.js +9 -4
- package/dist/esm/client/client-routing-runtime/utils.d.ts +2 -0
- package/dist/esm/client/client-routing-runtime/utils.js +2 -0
- package/dist/esm/node/plugin/plugins/buildConfig.js +2 -2
- package/dist/esm/node/plugin/plugins/config/index.js +4 -4
- package/dist/esm/node/plugin/plugins/devConfig/determineOptimizeDeps.js +2 -2
- package/dist/esm/node/plugin/plugins/importUserCode/getVirtualFileImportUserCode.js +1 -1
- package/dist/esm/node/plugin/plugins/importUserCode/index.js +1 -1
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +1 -1
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.d.ts +2 -1
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +6 -3
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.d.ts +2 -2
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.js +4 -2
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.d.ts +2 -2
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +18 -6
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/transpileAndExecuteFile.js +3 -3
- package/dist/esm/node/prerender/runPrerender.js +17 -8
- package/dist/esm/node/runtime/renderPage/log404/index.js +2 -1
- package/dist/esm/node/shared/getConfigVike.d.ts +2 -1
- package/dist/esm/node/shared/getConfigVike.js +4 -1
- package/dist/esm/shared/page-configs/serialize/parseConfigValuesImported.js +9 -3
- package/dist/esm/shared/route/noRouteMatch.d.ts +1 -0
- package/dist/esm/shared/route/noRouteMatch.js +1 -0
- package/dist/esm/utils/onPageVisibilityChange.d.ts +4 -0
- package/dist/esm/utils/onPageVisibilityChange.js +16 -0
- package/dist/esm/utils/projectInfo.d.ts +1 -1
- package/dist/esm/utils/projectInfo.js +1 -1
- package/node/cli/bin-entry.js +1 -1
- package/package.json +1 -1
- package/dist/esm/client/client-routing-runtime/getPageContext.d.ts +0 -28
- package/dist/esm/client/client-routing-runtime/navigationState.d.ts +0 -5
- package/dist/esm/client/client-routing-runtime/navigationState.js +0 -14
- /package/dist/esm/{client/client-routing-runtime → utils}/isExternalLink.d.ts +0 -0
- /package/dist/esm/{client/client-routing-runtime → utils}/isExternalLink.js +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Code adapted from https://github.com/HenrikJoreteg/internal-nav-helper/blob/5199ec5448d0b0db7ec63cf76d88fa6cad878b7d/src/index.js#L11-L29
|
|
2
|
+
export { onLinkClick };
|
|
3
|
+
import { assert } from './utils.js';
|
|
4
|
+
import { skipLink } from './skipLink.js';
|
|
5
|
+
import { renderPageClientSide } from './renderPageClientSide.js';
|
|
6
|
+
function onLinkClick() {
|
|
7
|
+
document.addEventListener('click', handler);
|
|
8
|
+
}
|
|
9
|
+
function handler(ev) {
|
|
10
|
+
if (!isNormalLeftClick(ev))
|
|
11
|
+
return;
|
|
12
|
+
const linkTag = findLinkTag(ev.target);
|
|
13
|
+
if (!linkTag)
|
|
14
|
+
return;
|
|
15
|
+
const url = linkTag.getAttribute('href');
|
|
16
|
+
if (skipLink(linkTag))
|
|
17
|
+
return;
|
|
18
|
+
assert(url);
|
|
19
|
+
ev.preventDefault();
|
|
20
|
+
const keepScrollPosition = ![null, 'false'].includes(linkTag.getAttribute('keep-scroll-position'));
|
|
21
|
+
const scrollTarget = keepScrollPosition ? 'preserve-scroll' : 'scroll-to-top-or-hash';
|
|
22
|
+
renderPageClientSide({
|
|
23
|
+
scrollTarget,
|
|
24
|
+
urlOriginal: url,
|
|
25
|
+
isBackwardNavigation: false
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function isNormalLeftClick(ev) {
|
|
29
|
+
return ev.button === 0 && !ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey;
|
|
30
|
+
}
|
|
31
|
+
function findLinkTag(target) {
|
|
32
|
+
while (target.tagName !== 'A') {
|
|
33
|
+
const { parentNode } = target;
|
|
34
|
+
if (!parentNode) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
target = parentNode;
|
|
38
|
+
}
|
|
39
|
+
return target;
|
|
40
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export { prefetch };
|
|
2
2
|
export { addLinkPrefetchHandlers };
|
|
3
|
-
import { assert, assertClientRouting, assertUsage, assertWarning, checkIfClientRouting, getGlobalObject,
|
|
3
|
+
import { assert, assertClientRouting, assertUsage, assertWarning, checkIfClientRouting, getGlobalObject, isExternalLink } from './utils.js';
|
|
4
4
|
import { isErrorFetchingStaticAssets, loadPageFilesClientSide } from '../shared/loadPageFilesClientSide.js';
|
|
5
5
|
import { skipLink } from './skipLink.js';
|
|
6
6
|
import { getPrefetchSettings } from './prefetch/getPrefetchSettings.js';
|
|
7
7
|
import { isAlreadyPrefetched, markAsAlreadyPrefetched } from './prefetch/alreadyPrefetched.js';
|
|
8
|
-
import { disableClientRouting } from './
|
|
9
|
-
import { isExternalLink } from './isExternalLink.js';
|
|
8
|
+
import { disableClientRouting } from './renderPageClientSide.js';
|
|
10
9
|
import { isClientSideRoutable } from './isClientSideRoutable.js';
|
|
11
10
|
import { createPageContext } from './createPageContext.js';
|
|
12
11
|
import { route } from '../../shared/route/index.js';
|
|
12
|
+
import { noRouteMatch } from '../../shared/route/noRouteMatch.js';
|
|
13
13
|
assertClientRouting();
|
|
14
14
|
const globalObject = getGlobalObject('prefetch.ts', { linkPrefetchHandlerAdded: new Map() });
|
|
15
15
|
async function prefetchAssets(pageId, pageContext) {
|
|
@@ -52,7 +52,7 @@ async function prefetch(url) {
|
|
|
52
52
|
}
|
|
53
53
|
const pageId = pageContextFromRoute._pageId;
|
|
54
54
|
if (!pageId) {
|
|
55
|
-
assertWarning(false, `${errPrefix}
|
|
55
|
+
assertWarning(false, `${errPrefix} ${noRouteMatch}`, {
|
|
56
56
|
showStackTrace: true,
|
|
57
57
|
onlyOnce: false
|
|
58
58
|
});
|
|
@@ -108,10 +108,9 @@ async function prefetchIfPossible(url) {
|
|
|
108
108
|
// If a route() hook has a bug or `throw render()` / `throw redirect()`
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
|
-
|
|
112
|
-
if (!pageContext._pageId)
|
|
111
|
+
if (!pageContextFromRoute?._pageId)
|
|
113
112
|
return;
|
|
114
|
-
if (!(await isClientSideRoutable(pageContext)))
|
|
113
|
+
if (!(await isClientSideRoutable(pageContextFromRoute._pageId, pageContext)))
|
|
115
114
|
return;
|
|
116
|
-
await prefetchAssets(
|
|
115
|
+
await prefetchAssets(pageContextFromRoute._pageId, pageContext);
|
|
117
116
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { renderPageClientSide };
|
|
2
|
+
export { getRenderCount };
|
|
3
|
+
export { disableClientRouting };
|
|
4
|
+
import { PageContextFromRewrite } from '../../shared/route/abort.js';
|
|
5
|
+
import { type ScrollTarget } from './setScrollPosition.js';
|
|
6
|
+
type RenderArgs = {
|
|
7
|
+
scrollTarget: ScrollTarget;
|
|
8
|
+
isBackwardNavigation: boolean | null;
|
|
9
|
+
urlOriginal?: string;
|
|
10
|
+
overwriteLastHistoryEntry?: boolean;
|
|
11
|
+
pageContextsFromRewrite?: PageContextFromRewrite[];
|
|
12
|
+
redirectCount?: number;
|
|
13
|
+
/** Whether the navigation was triggered by the user land calling `history.pushState()` */
|
|
14
|
+
isUserLandPushStateNavigation?: boolean;
|
|
15
|
+
isClientSideNavigation?: boolean;
|
|
16
|
+
};
|
|
17
|
+
declare function renderPageClientSide(renderArgs: RenderArgs): Promise<void>;
|
|
18
|
+
declare function disableClientRouting(err: unknown, log: boolean): void;
|
|
19
|
+
declare function getRenderCount(): number;
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
export { renderPageClientSide };
|
|
2
|
+
export { getRenderCount };
|
|
3
|
+
export { disableClientRouting };
|
|
4
|
+
import { assert, getCurrentUrl, isEquivalentError, objectAssign, serverSideRouteTo, getGlobalObject, executeHook, hasProp } from './utils.js';
|
|
5
|
+
import { getPageContextFromHooks_errorPage, getPageContextFromHooks_firstRender, getPageContextFromHooks_uponNavigation, isAlreadyServerSideRouted } from './getPageContextFromHooks.js';
|
|
6
|
+
import { createPageContext } from './createPageContext.js';
|
|
7
|
+
import { addLinkPrefetchHandlers } from './prefetch.js';
|
|
8
|
+
import { assertInfo, assertWarning, isReact } from './utils.js';
|
|
9
|
+
import { executeOnRenderClientHook } from '../shared/executeOnRenderClientHook.js';
|
|
10
|
+
import { assertHook } from '../../shared/hooks/getHook.js';
|
|
11
|
+
import { isErrorFetchingStaticAssets } from '../shared/loadPageFilesClientSide.js';
|
|
12
|
+
import { pushHistory } from './history.js';
|
|
13
|
+
import { assertNoInfiniteAbortLoop, getPageContextFromAllRewrites, isAbortError, logAbortErrorHandled } from '../../shared/route/abort.js';
|
|
14
|
+
import { route } from '../../shared/route/index.js';
|
|
15
|
+
import { isClientSideRoutable } from './isClientSideRoutable.js';
|
|
16
|
+
import { setScrollPosition } from './setScrollPosition.js';
|
|
17
|
+
import { updateState } from './onBrowserHistoryNavigation.js';
|
|
18
|
+
import { browserNativeScrollRestoration_disable, setInitialRenderIsDone } from './scrollRestoration.js';
|
|
19
|
+
const globalObject = getGlobalObject('renderPageClientSide.ts', { renderCounter: 0 });
|
|
20
|
+
async function renderPageClientSide(renderArgs) {
|
|
21
|
+
const { scrollTarget, urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, pageContextsFromRewrite = [], redirectCount = 0, isUserLandPushStateNavigation, isClientSideNavigation = true } = renderArgs;
|
|
22
|
+
const { abortRender, setHydrationCanBeAborted, isFirstRender } = getAbortRender();
|
|
23
|
+
assert(isClientSideNavigation === !isFirstRender);
|
|
24
|
+
assertNoInfiniteAbortLoop(pageContextsFromRewrite.length, redirectCount);
|
|
25
|
+
if (globalObject.clientRoutingIsDisabled) {
|
|
26
|
+
serverSideRouteTo(urlOriginal);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const pageContext = await createPageContext(urlOriginal);
|
|
30
|
+
if (abortRender())
|
|
31
|
+
return;
|
|
32
|
+
objectAssign(pageContext, {
|
|
33
|
+
isBackwardNavigation,
|
|
34
|
+
isClientSideNavigation
|
|
35
|
+
});
|
|
36
|
+
{
|
|
37
|
+
const pageContextFromAllRewrites = getPageContextFromAllRewrites(pageContextsFromRewrite);
|
|
38
|
+
objectAssign(pageContext, pageContextFromAllRewrites);
|
|
39
|
+
}
|
|
40
|
+
let renderState = {};
|
|
41
|
+
const onError = (err) => {
|
|
42
|
+
assert(err);
|
|
43
|
+
assert(!('err' in renderState));
|
|
44
|
+
assert(!('errorWhileRendering' in pageContext));
|
|
45
|
+
renderState.err = err;
|
|
46
|
+
pageContext.errorWhileRendering = err;
|
|
47
|
+
};
|
|
48
|
+
if (!isFirstRender) {
|
|
49
|
+
// Route
|
|
50
|
+
try {
|
|
51
|
+
renderState = { pageContextFromRoute: await route(pageContext) };
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
onError(err);
|
|
55
|
+
}
|
|
56
|
+
if (abortRender())
|
|
57
|
+
return;
|
|
58
|
+
// Check whether rendering should be skipped
|
|
59
|
+
if (renderState.pageContextFromRoute) {
|
|
60
|
+
const { pageContextFromRoute } = renderState;
|
|
61
|
+
objectAssign(pageContext, pageContextFromRoute);
|
|
62
|
+
let isClientRoutable;
|
|
63
|
+
if (!pageContextFromRoute._pageId) {
|
|
64
|
+
isClientRoutable = false;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
isClientRoutable = await isClientSideRoutable(pageContextFromRoute._pageId, pageContext);
|
|
68
|
+
if (abortRender())
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (!isClientRoutable) {
|
|
72
|
+
serverSideRouteTo(urlOriginal);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const isSamePage = pageContextFromRoute._pageId &&
|
|
76
|
+
globalObject.previousPageContext?._pageId &&
|
|
77
|
+
pageContextFromRoute._pageId === globalObject.previousPageContext._pageId;
|
|
78
|
+
if (isUserLandPushStateNavigation && isSamePage) {
|
|
79
|
+
// Skip's Vike's rendering; let the user handle the navigation
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// onPageTransitionStart()
|
|
85
|
+
const callTransitionHooks = !isFirstRender;
|
|
86
|
+
if (callTransitionHooks) {
|
|
87
|
+
if (!globalObject.isTransitioning) {
|
|
88
|
+
await globalObject.onPageTransitionStart?.(pageContext);
|
|
89
|
+
globalObject.isTransitioning = true;
|
|
90
|
+
if (abortRender())
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (isFirstRender) {
|
|
95
|
+
assert(!renderState.pageContextFromRoute);
|
|
96
|
+
assert(!renderState.err);
|
|
97
|
+
try {
|
|
98
|
+
renderState.pageContextFromHooks = await getPageContextFromHooks_firstRender(pageContext);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
onError(err);
|
|
102
|
+
}
|
|
103
|
+
if (abortRender())
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
if (!renderState.err) {
|
|
108
|
+
const { pageContextFromRoute } = renderState;
|
|
109
|
+
assert(pageContextFromRoute);
|
|
110
|
+
assert(pageContextFromRoute._pageId);
|
|
111
|
+
assert(hasProp(pageContextFromRoute, '_pageId', 'string')); // Help TS
|
|
112
|
+
objectAssign(pageContext, pageContextFromRoute);
|
|
113
|
+
try {
|
|
114
|
+
renderState.pageContextFromHooks = await getPageContextFromHooks_uponNavigation(pageContext);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
onError(err);
|
|
118
|
+
}
|
|
119
|
+
if (abortRender())
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if ('err' in renderState) {
|
|
124
|
+
const { err } = renderState;
|
|
125
|
+
if (!isAbortError(err)) {
|
|
126
|
+
// We don't swallow 404 errors:
|
|
127
|
+
// - On the server-side, Vike swallows / doesn't show any 404 error log because it's expected that a user may go to some random non-existent URL. (We don't want to flood the app's error tracking with 404 logs.)
|
|
128
|
+
// - On the client-side, if the user navigates to a 404 then it means that the UI has a broken link. (It isn't expected that users can go to some random URL using the client-side router, as it would require, for example, the user to manually change the URL of a link by manually manipulating the DOM which highly unlikely.)
|
|
129
|
+
console.error(err);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// We swallow throw redirect()/render() called by client-side hooks onBeforeRender() and guard()
|
|
133
|
+
// We handle the abort error down below.
|
|
134
|
+
}
|
|
135
|
+
if (shouldSwallowAndInterrupt(err, pageContext, isFirstRender))
|
|
136
|
+
return;
|
|
137
|
+
if (isAbortError(err)) {
|
|
138
|
+
const errAbort = err;
|
|
139
|
+
logAbortErrorHandled(err, pageContext._isProduction, pageContext);
|
|
140
|
+
const pageContextAbort = errAbort._pageContextAbort;
|
|
141
|
+
// throw render('/some-url')
|
|
142
|
+
if (pageContextAbort._urlRewrite) {
|
|
143
|
+
await renderPageClientSide({
|
|
144
|
+
...renderArgs,
|
|
145
|
+
scrollTarget: 'scroll-to-top-or-hash',
|
|
146
|
+
pageContextsFromRewrite: [...pageContextsFromRewrite, pageContextAbort]
|
|
147
|
+
});
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// throw redirect('/some-url')
|
|
151
|
+
if (pageContextAbort._urlRedirect) {
|
|
152
|
+
const urlRedirect = pageContextAbort._urlRedirect.url;
|
|
153
|
+
if (urlRedirect.startsWith('http')) {
|
|
154
|
+
// External redirection
|
|
155
|
+
window.location.href = urlRedirect;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
await renderPageClientSide({
|
|
160
|
+
...renderArgs,
|
|
161
|
+
scrollTarget: 'scroll-to-top-or-hash',
|
|
162
|
+
urlOriginal: urlRedirect,
|
|
163
|
+
overwriteLastHistoryEntry: false,
|
|
164
|
+
isBackwardNavigation: false,
|
|
165
|
+
redirectCount: redirectCount + 1
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
// throw render(statusCode)
|
|
171
|
+
assert(pageContextAbort.abortStatusCode);
|
|
172
|
+
objectAssign(pageContext, pageContextAbort);
|
|
173
|
+
if (pageContextAbort.abortStatusCode === 404) {
|
|
174
|
+
objectAssign(pageContext, { is404: true });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
objectAssign(pageContext, { is404: false });
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
renderState.pageContextFromHooks = await getPageContextFromHooks_errorPage(pageContext);
|
|
182
|
+
}
|
|
183
|
+
catch (err2) {
|
|
184
|
+
// - When user hasn't defined a `_error.page.js` file
|
|
185
|
+
// - Some unpexected vike internal error
|
|
186
|
+
if (shouldSwallowAndInterrupt(err2, pageContext, isFirstRender))
|
|
187
|
+
return;
|
|
188
|
+
if (!isFirstRender) {
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
// We let the server show the 404 page
|
|
191
|
+
window.location.pathname = urlOriginal;
|
|
192
|
+
}, 0);
|
|
193
|
+
}
|
|
194
|
+
if (!isEquivalentError(err, err2)) {
|
|
195
|
+
throw err2;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
// Abort
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (abortRender())
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const { pageContextFromHooks } = renderState;
|
|
206
|
+
assert(pageContextFromHooks);
|
|
207
|
+
objectAssign(pageContext, pageContextFromHooks);
|
|
208
|
+
// Set global onPageTransitionStart()
|
|
209
|
+
assertHook(pageContext, 'onPageTransitionStart');
|
|
210
|
+
globalObject.onPageTransitionStart = pageContext.exports.onPageTransitionStart;
|
|
211
|
+
// Set global hydrationCanBeAborted
|
|
212
|
+
if (pageContext.exports.hydrationCanBeAborted) {
|
|
213
|
+
setHydrationCanBeAborted();
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
assertWarning(!isReact(), 'You seem to be using React; we recommend setting hydrationCanBeAborted to true, see https://vike.dev/clientRouting', { onlyOnce: true });
|
|
217
|
+
}
|
|
218
|
+
// There wasn't any `await` but result may change because we just called setHydrationCanBeAborted()
|
|
219
|
+
if (abortRender())
|
|
220
|
+
return;
|
|
221
|
+
// We use globalObject.renderPromise in order to ensure that there is never two concurrent onRenderClient() calls
|
|
222
|
+
if (globalObject.renderPromise) {
|
|
223
|
+
// Make sure that the previous render has finished
|
|
224
|
+
await globalObject.renderPromise;
|
|
225
|
+
assert(globalObject.renderPromise === undefined);
|
|
226
|
+
if (abortRender())
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
changeUrl(urlOriginal, overwriteLastHistoryEntry);
|
|
230
|
+
globalObject.previousPageContext = pageContext;
|
|
231
|
+
assert(globalObject.renderPromise === undefined);
|
|
232
|
+
globalObject.renderPromise = (async () => {
|
|
233
|
+
await executeOnRenderClientHook(pageContext, true);
|
|
234
|
+
addLinkPrefetchHandlers(pageContext);
|
|
235
|
+
globalObject.renderPromise = undefined;
|
|
236
|
+
})();
|
|
237
|
+
await globalObject.renderPromise;
|
|
238
|
+
assert(globalObject.renderPromise === undefined);
|
|
239
|
+
/* We don't abort in order to ensure that onHydrationEnd() is called: we abort only after onHydrationEnd() is called.
|
|
240
|
+
if (abortRender(true)) return
|
|
241
|
+
*/
|
|
242
|
+
// onHydrationEnd()
|
|
243
|
+
if (isFirstRender) {
|
|
244
|
+
assertHook(pageContext, 'onHydrationEnd');
|
|
245
|
+
const { onHydrationEnd } = pageContext.exports;
|
|
246
|
+
if (onHydrationEnd) {
|
|
247
|
+
const hookFilePath = pageContext.exportsAll.onHydrationEnd[0].exportSource;
|
|
248
|
+
assert(hookFilePath);
|
|
249
|
+
await executeHook(() => onHydrationEnd(pageContext), 'onHydrationEnd', hookFilePath);
|
|
250
|
+
if (abortRender(true))
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// We abort only after onHydrationEnd() is called
|
|
255
|
+
if (abortRender(true))
|
|
256
|
+
return;
|
|
257
|
+
// onPageTransitionEnd()
|
|
258
|
+
if (callTransitionHooks) {
|
|
259
|
+
if (pageContext.exports.onPageTransitionEnd) {
|
|
260
|
+
assertHook(pageContext, 'onPageTransitionEnd');
|
|
261
|
+
await pageContext.exports.onPageTransitionEnd(pageContext);
|
|
262
|
+
if (abortRender(true))
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
globalObject.isTransitioning = undefined;
|
|
266
|
+
}
|
|
267
|
+
// Page scrolling
|
|
268
|
+
setScrollPosition(scrollTarget);
|
|
269
|
+
browserNativeScrollRestoration_disable();
|
|
270
|
+
setInitialRenderIsDone();
|
|
271
|
+
}
|
|
272
|
+
function changeUrl(url, overwriteLastHistoryEntry) {
|
|
273
|
+
if (getCurrentUrl() === url)
|
|
274
|
+
return;
|
|
275
|
+
browserNativeScrollRestoration_disable();
|
|
276
|
+
pushHistory(url, overwriteLastHistoryEntry);
|
|
277
|
+
updateState();
|
|
278
|
+
}
|
|
279
|
+
function shouldSwallowAndInterrupt(err, pageContext, isFirstRender) {
|
|
280
|
+
if (isAlreadyServerSideRouted(err))
|
|
281
|
+
return true;
|
|
282
|
+
if (handleErrorFetchingStaticAssets(err, pageContext, isFirstRender))
|
|
283
|
+
return true;
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
function handleErrorFetchingStaticAssets(err, pageContext, isFirstRender) {
|
|
287
|
+
if (!isErrorFetchingStaticAssets(err)) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
if (isFirstRender) {
|
|
291
|
+
disableClientRouting(err, false);
|
|
292
|
+
// This may happen if the frontend was newly deployed during hydration.
|
|
293
|
+
// Ideally: re-try a couple of times by reloading the page (not entirely trivial to implement since `localStorage` is needed.)
|
|
294
|
+
throw err;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
disableClientRouting(err, true);
|
|
298
|
+
}
|
|
299
|
+
serverSideRouteTo(pageContext.urlOriginal);
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
function disableClientRouting(err, log) {
|
|
303
|
+
assert(isErrorFetchingStaticAssets(err));
|
|
304
|
+
globalObject.clientRoutingIsDisabled = true;
|
|
305
|
+
if (log) {
|
|
306
|
+
// We don't use console.error() to avoid flooding error trackers such as Sentry
|
|
307
|
+
console.log(err);
|
|
308
|
+
}
|
|
309
|
+
// @ts-ignore Since dist/cjs/client/ is never used, we can ignore this error.
|
|
310
|
+
const isProd = import.meta.env.PROD;
|
|
311
|
+
assertInfo(false, [
|
|
312
|
+
'Failed to fetch static asset.',
|
|
313
|
+
isProd ? 'This usually happens when a new frontend is deployed.' : null,
|
|
314
|
+
'Falling back to Server Routing.',
|
|
315
|
+
'(The next page navigation will use Server Routing instead of Client Routing.)'
|
|
316
|
+
]
|
|
317
|
+
.filter(Boolean)
|
|
318
|
+
.join(' '), { onlyOnce: true });
|
|
319
|
+
}
|
|
320
|
+
function getAbortRender() {
|
|
321
|
+
const renderNumber = ++globalObject.renderCounter;
|
|
322
|
+
assert(renderNumber >= 1);
|
|
323
|
+
let hydrationCanBeAborted = false;
|
|
324
|
+
const setHydrationCanBeAborted = () => {
|
|
325
|
+
hydrationCanBeAborted = true;
|
|
326
|
+
};
|
|
327
|
+
/** Whether the rendering should be aborted because a new rendering has started. We should call this after each `await`. */
|
|
328
|
+
const abortRender = (isRenderCleanup) => {
|
|
329
|
+
// Never abort hydration if `hydrationCanBeAborted` isn't `true`
|
|
330
|
+
if (!isRenderCleanup) {
|
|
331
|
+
const isHydration = renderNumber === 1;
|
|
332
|
+
if (isHydration && !hydrationCanBeAborted) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// If there is a newer rendering, we should abort all previous renderings
|
|
337
|
+
return renderNumber !== globalObject.renderCounter;
|
|
338
|
+
};
|
|
339
|
+
return {
|
|
340
|
+
abortRender,
|
|
341
|
+
setHydrationCanBeAborted,
|
|
342
|
+
isFirstRender: renderNumber === 1
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function getRenderCount() {
|
|
346
|
+
return globalObject.renderCounter;
|
|
347
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { browserNativeScrollRestoration_disable };
|
|
2
|
+
export { setupNativeScrollRestoration };
|
|
3
|
+
export { setInitialRenderIsDone };
|
|
4
|
+
declare function setupNativeScrollRestoration(): void;
|
|
5
|
+
declare function setInitialRenderIsDone(): void;
|
|
6
|
+
declare function browserNativeScrollRestoration_disable(): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Handle the browser's native scroll restoration mechanism
|
|
2
|
+
export { browserNativeScrollRestoration_disable };
|
|
3
|
+
export { setupNativeScrollRestoration };
|
|
4
|
+
export { setInitialRenderIsDone };
|
|
5
|
+
import { getGlobalObject, onPageHide, onPageShow } from './utils.js';
|
|
6
|
+
const globalObject = getGlobalObject('scrollRestoration.ts', {});
|
|
7
|
+
// We use the browser's native scroll restoration mechanism only for the first render
|
|
8
|
+
function setupNativeScrollRestoration() {
|
|
9
|
+
browserNativeScrollRestoration_enable();
|
|
10
|
+
onPageHide(browserNativeScrollRestoration_enable);
|
|
11
|
+
onPageShow(() => globalObject.initialRenderIsDone && browserNativeScrollRestoration_disable());
|
|
12
|
+
}
|
|
13
|
+
function setInitialRenderIsDone() {
|
|
14
|
+
globalObject.initialRenderIsDone = true;
|
|
15
|
+
}
|
|
16
|
+
function browserNativeScrollRestoration_disable() {
|
|
17
|
+
if ('scrollRestoration' in window.history) {
|
|
18
|
+
window.history.scrollRestoration = 'manual';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function browserNativeScrollRestoration_enable() {
|
|
22
|
+
if ('scrollRestoration' in window.history) {
|
|
23
|
+
window.history.scrollRestoration = 'auto';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { setScrollPosition };
|
|
2
|
+
export { autoSaveScrollPosition };
|
|
3
|
+
export type { ScrollTarget };
|
|
4
|
+
import { type ScrollPosition } from './history.js';
|
|
5
|
+
type ScrollTarget = ScrollPosition | 'scroll-to-top-or-hash' | 'preserve-scroll';
|
|
6
|
+
declare function setScrollPosition(scrollTarget: ScrollTarget): void;
|
|
7
|
+
declare function autoSaveScrollPosition(): void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export { setScrollPosition };
|
|
2
|
+
export { autoSaveScrollPosition };
|
|
3
|
+
import { assert, onPageHide, sleep, throttle } from './utils.js';
|
|
4
|
+
import { saveScrollPosition } from './history.js';
|
|
5
|
+
function setScrollPosition(scrollTarget) {
|
|
6
|
+
if (scrollTarget === 'preserve-scroll') {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
let scrollPosition;
|
|
10
|
+
if (scrollTarget === 'scroll-to-top-or-hash') {
|
|
11
|
+
const hash = getUrlHash();
|
|
12
|
+
// We replicate the browser's native behavior
|
|
13
|
+
if (hash && hash !== 'top') {
|
|
14
|
+
const hashTarget = document.getElementById(hash) || document.getElementsByName(hash)[0];
|
|
15
|
+
if (hashTarget) {
|
|
16
|
+
hashTarget.scrollIntoView();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
scrollPosition = { x: 0, y: 0 };
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
assert('x' in scrollTarget && 'y' in scrollTarget);
|
|
24
|
+
scrollPosition = scrollTarget;
|
|
25
|
+
}
|
|
26
|
+
setScroll(scrollPosition);
|
|
27
|
+
}
|
|
28
|
+
/** Change the browser's scoll position, in a way that works during a repaint. */
|
|
29
|
+
function setScroll(scrollPosition) {
|
|
30
|
+
const scroll = () => window.scrollTo(scrollPosition.x, scrollPosition.y);
|
|
31
|
+
const done = () => window.scrollX === scrollPosition.x && window.scrollY === scrollPosition.y;
|
|
32
|
+
// In principle, this `done()` call should force the repaint to be finished. But that doesn't seem to be the case with `Firefox 97.0.1`.
|
|
33
|
+
if (done())
|
|
34
|
+
return;
|
|
35
|
+
scroll();
|
|
36
|
+
// Because `done()` doesn't seem to always force the repaint to be finished, we potentially need to retry again.
|
|
37
|
+
if (done())
|
|
38
|
+
return;
|
|
39
|
+
requestAnimationFrame(() => {
|
|
40
|
+
scroll();
|
|
41
|
+
if (done())
|
|
42
|
+
return;
|
|
43
|
+
setTimeout(async () => {
|
|
44
|
+
scroll();
|
|
45
|
+
if (done())
|
|
46
|
+
return;
|
|
47
|
+
// In principle, `requestAnimationFrame() -> setTimeout(, 0)` should be enough.
|
|
48
|
+
// - https://stackoverflow.com/questions/61281139/waiting-for-repaint-in-javascript
|
|
49
|
+
// - But it's not enough for `Firefox 97.0.1`.
|
|
50
|
+
// - The following strategy is very agressive. It doesn't need to be that aggressive for Firefox. But we do it to be safe.
|
|
51
|
+
const start = new Date().getTime();
|
|
52
|
+
while (true) {
|
|
53
|
+
await sleep(10);
|
|
54
|
+
scroll();
|
|
55
|
+
if (done())
|
|
56
|
+
return;
|
|
57
|
+
const millisecondsElapsed = new Date().getTime() - start;
|
|
58
|
+
if (millisecondsElapsed > 100)
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}, 0);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function getUrlHash() {
|
|
65
|
+
let { hash } = window.location;
|
|
66
|
+
if (hash === '')
|
|
67
|
+
return null;
|
|
68
|
+
assert(hash.startsWith('#'));
|
|
69
|
+
hash = hash.slice(1);
|
|
70
|
+
return hash;
|
|
71
|
+
}
|
|
72
|
+
// Save scroll position (needed for back-/forward navigation)
|
|
73
|
+
function autoSaveScrollPosition() {
|
|
74
|
+
// Safari cannot handle more than 100 `history.replaceState()` calls within 30 seconds (https://github.com/vikejs/vike/issues/46)
|
|
75
|
+
window.addEventListener('scroll', throttle(saveScrollPosition, Math.ceil(1000 / 3)), { passive: true });
|
|
76
|
+
onPageHide(saveScrollPosition);
|
|
77
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export { skipLink };
|
|
2
2
|
import { getBaseServer } from './getBaseServer.js';
|
|
3
|
-
import { isExternalLink } from './
|
|
4
|
-
import { assert, parseUrl, isBaseServer, isParsable } from './utils.js';
|
|
5
|
-
import { isDisableAutomaticLinkInterception } from './installClientRouter.js';
|
|
3
|
+
import { assert, parseUrl, isBaseServer, isParsable, isExternalLink } from './utils.js';
|
|
6
4
|
function skipLink(linkTag) {
|
|
7
5
|
const url = linkTag.getAttribute('href');
|
|
8
6
|
if (url === null)
|
|
@@ -26,7 +24,7 @@ function skipLink(linkTag) {
|
|
|
26
24
|
return true;
|
|
27
25
|
return false;
|
|
28
26
|
}
|
|
29
|
-
// TODO/v1-release: remove this in favor of synchronously checking whether URL matches the route of a page (possible since Async Route Functions
|
|
27
|
+
// TODO/v1-release: remove this in favor of synchronously checking whether URL matches the route of a page (possible since Async Route Functions will be deprecated)
|
|
30
28
|
function isVikeLink(linkTag) {
|
|
31
29
|
const disableAutomaticLinkInterception = isDisableAutomaticLinkInterception();
|
|
32
30
|
if (!disableAutomaticLinkInterception) {
|
|
@@ -58,3 +56,10 @@ function hasBaseServer(url) {
|
|
|
58
56
|
const { hasBaseServer } = parseUrl(url, baseServer);
|
|
59
57
|
return hasBaseServer;
|
|
60
58
|
}
|
|
59
|
+
function isDisableAutomaticLinkInterception() {
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
return !!window._disableAutomaticLinkInterception;
|
|
62
|
+
/* globalObject should be used if we want to make disableAutomaticLinkInterception a page-by-page setting
|
|
63
|
+
return globalObject.disableAutomaticLinkInterception ?? false
|
|
64
|
+
*/
|
|
65
|
+
}
|
|
@@ -20,3 +20,5 @@ export * from '../../utils/sleep.js';
|
|
|
20
20
|
export * from '../../utils/slice.js';
|
|
21
21
|
export * from '../../utils/throttle.js';
|
|
22
22
|
export * from '../../utils/assertRoutingType.js';
|
|
23
|
+
export * from '../../utils/onPageVisibilityChange.js';
|
|
24
|
+
export * from '../../utils/isExternalLink.js';
|
|
@@ -26,3 +26,5 @@ export * from '../../utils/sleep.js';
|
|
|
26
26
|
export * from '../../utils/slice.js';
|
|
27
27
|
export * from '../../utils/throttle.js';
|
|
28
28
|
export * from '../../utils/assertRoutingType.js';
|
|
29
|
+
export * from '../../utils/onPageVisibilityChange.js';
|
|
30
|
+
export * from '../../utils/isExternalLink.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { buildConfig };
|
|
2
2
|
export { assertRollupInput };
|
|
3
3
|
export { analyzeClientEntries };
|
|
4
|
-
import { assert, resolveOutDir, isObject, viteIsSSR, getFilePathAbsolute, addOnBeforeLogHook, removeFileExtention, unique, assertPosixPath, assertUsage
|
|
4
|
+
import { assert, resolveOutDir, isObject, viteIsSSR, getFilePathAbsolute, addOnBeforeLogHook, removeFileExtention, unique, assertPosixPath, assertUsage } from '../utils.js';
|
|
5
5
|
import { virtualFileIdImportUserCodeServer } from '../../shared/virtual-files/virtualFileImportUserCode.js';
|
|
6
6
|
import { getVikeConfig } from './importUserCode/v1-design/getVikeConfig.js';
|
|
7
7
|
import { getConfigValue } from '../../../shared/page-configs/helpers.js';
|
|
@@ -69,7 +69,7 @@ function buildConfig() {
|
|
|
69
69
|
async function getEntries(config) {
|
|
70
70
|
const configVike = await getConfigVike(config);
|
|
71
71
|
const pageFileEntries = await getPageFileEntries(config, configVike.includeAssetsImportedByServer); // TODO/v1-release: remove
|
|
72
|
-
const { pageConfigs } = await getVikeConfig(config
|
|
72
|
+
const { pageConfigs } = await getVikeConfig(config, false);
|
|
73
73
|
assertUsage(Object.keys(pageFileEntries).length !== 0 || pageConfigs.length !== 0, 'At least one page should be defined, see https://vike.dev/add');
|
|
74
74
|
if (viteIsSSR(config)) {
|
|
75
75
|
const serverEntries = analyzeServerEntries(pageConfigs);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { resolveVikeConfig };
|
|
2
2
|
import { assertVikeConfig } from './assertVikeConfig.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isDev2 } from '../../utils.js';
|
|
4
4
|
import { findConfigVikeFromStemPackages } from './findConfigVikeFromStemPackages.js';
|
|
5
5
|
import { pickFirst } from './pickFirst.js';
|
|
6
6
|
import { resolveExtensions } from './resolveExtensions.js';
|
|
@@ -12,19 +12,19 @@ function resolveVikeConfig(vikeConfig) {
|
|
|
12
12
|
name: 'vike:resolveVikeConfig',
|
|
13
13
|
enforce: 'pre',
|
|
14
14
|
async configResolved(config) {
|
|
15
|
-
const promise =
|
|
15
|
+
const promise = getConfigVikPromise(vikeConfig, config);
|
|
16
16
|
config.configVikePromise = promise;
|
|
17
17
|
await promise;
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
async function
|
|
21
|
+
async function getConfigVikPromise(vikeConfig, config) {
|
|
22
22
|
const fromPluginOptions = (vikeConfig ?? {});
|
|
23
23
|
const fromViteConfig = (config.vike ?? {});
|
|
24
24
|
const fromStemPackages = await findConfigVikeFromStemPackages(config.root);
|
|
25
25
|
const configs = [fromPluginOptions, ...fromStemPackages, fromViteConfig];
|
|
26
26
|
const extensions = resolveExtensions(configs, config);
|
|
27
|
-
const { globalVikeConfig: fromPlusConfigFile } = await getVikeConfig(config
|
|
27
|
+
const { globalVikeConfig: fromPlusConfigFile } = await getVikeConfig(config, isDev2(config), false, extensions);
|
|
28
28
|
configs.push(fromPlusConfigFile);
|
|
29
29
|
assertVikeConfig(fromPlusConfigFile, ({ prop, errMsg }) => {
|
|
30
30
|
// TODO: add config file path ?
|