vike 0.4.144-commit-6aef8a6 → 0.4.144-commit-7f5e99a
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/__internal/index.js +6 -2
- package/dist/cjs/node/plugin/plugins/buildConfig.js +2 -2
- package/dist/cjs/node/plugin/plugins/commonConfig.js +0 -3
- package/dist/cjs/node/plugin/plugins/devConfig/determineOptimizeDeps.js +5 -5
- package/dist/cjs/node/plugin/plugins/devConfig/index.js +1 -0
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +4 -3
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +29 -42
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.js +2 -2
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +14 -5
- package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/helpers.js +1 -14
- package/dist/cjs/node/plugin/plugins/previewConfig.js +11 -2
- package/dist/cjs/node/prerender/runPrerender.js +16 -17
- package/dist/cjs/node/prerender/utils.js +1 -1
- package/dist/cjs/node/runtime/html/serializePageContextClientSide.js +20 -6
- package/dist/cjs/node/runtime/renderPage/renderPageAlreadyRouted.js +2 -2
- package/dist/cjs/node/runtime/renderPage.js +2 -2
- package/dist/cjs/node/runtime/utils.js +1 -1
- package/dist/cjs/node/shared/getClientEntryFilePath.js +2 -2
- package/dist/cjs/shared/addUrlComputedProps.js +24 -12
- package/dist/cjs/shared/getPageFiles/analyzeClientSide.js +4 -6
- package/dist/cjs/shared/getPageFiles/getExports.js +3 -3
- package/dist/cjs/shared/hooks/getHook.js +1 -1
- package/dist/cjs/shared/page-configs/helpers/getConfigDefinedAtString.js +43 -0
- package/dist/cjs/shared/page-configs/helpers/getConfigValue.js +44 -0
- package/dist/cjs/shared/page-configs/helpers.js +33 -0
- package/dist/cjs/shared/page-configs/serialize/parseConfigValuesImported.js +6 -8
- package/dist/cjs/shared/page-configs/serialize/parsePageConfigs.js +2 -2
- package/dist/cjs/shared/page-configs/serialize/serializeConfigValue.js +2 -2
- package/dist/cjs/shared/route/executeOnBeforeRouteHook.js +11 -13
- package/dist/cjs/shared/route/index.js +3 -3
- package/dist/cjs/shared/route/loadPageRoutes.js +11 -10
- package/dist/cjs/shared/route/resolveRouteFunction.js +1 -1
- package/dist/cjs/shared/utils.js +1 -1
- package/dist/cjs/utils/{hasPropertyGetter.js → isPropertyGetter.js} +3 -3
- package/dist/cjs/utils/projectInfo.js +1 -1
- package/dist/esm/__internal/index.d.ts +6 -3
- package/dist/esm/__internal/index.js +8 -3
- package/dist/esm/client/client-routing-runtime/createPageContext.d.ts +2 -3
- package/dist/esm/client/client-routing-runtime/createPageContext.js +3 -3
- package/dist/esm/client/client-routing-runtime/entry.js +2 -2
- package/dist/esm/client/client-routing-runtime/getPageContext.d.ts +0 -1
- package/dist/esm/client/client-routing-runtime/getPageContext.js +4 -7
- package/dist/esm/client/client-routing-runtime/getPageId.d.ts +1 -1
- package/dist/esm/client/client-routing-runtime/getPageId.js +4 -7
- package/dist/esm/client/client-routing-runtime/history.d.ts +3 -1
- package/dist/esm/client/client-routing-runtime/history.js +26 -8
- package/dist/esm/client/client-routing-runtime/installClientRouter.d.ts +21 -0
- package/dist/esm/client/client-routing-runtime/{useClientRouter.js → installClientRouter.js} +248 -242
- package/dist/esm/client/client-routing-runtime/isClientSideRoutable.d.ts +8 -0
- package/dist/esm/client/client-routing-runtime/isClientSideRoutable.js +15 -0
- package/dist/esm/client/client-routing-runtime/navigate.d.ts +0 -2
- package/dist/esm/client/client-routing-runtime/navigate.js +10 -8
- package/dist/esm/client/client-routing-runtime/prefetch.js +12 -5
- package/dist/esm/client/client-routing-runtime/skipLink.d.ts +0 -1
- package/dist/esm/client/client-routing-runtime/skipLink.js +1 -2
- package/dist/esm/client/shared/executeOnRenderClientHook.js +6 -5
- package/dist/esm/client/shared/getPageContextProxyForUser.js +13 -7
- package/dist/esm/node/plugin/plugins/buildConfig.js +1 -1
- package/dist/esm/node/plugin/plugins/commonConfig.js +0 -3
- package/dist/esm/node/plugin/plugins/devConfig/determineOptimizeDeps.js +5 -5
- package/dist/esm/node/plugin/plugins/devConfig/index.js +1 -0
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.d.ts +2 -2
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +4 -3
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +29 -42
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.js +1 -1
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +15 -6
- package/dist/esm/node/plugin/plugins/importUserCode/v1-design/helpers.js +1 -14
- package/dist/esm/node/plugin/plugins/previewConfig.js +11 -2
- package/dist/esm/node/prerender/runPrerender.js +11 -12
- package/dist/esm/node/prerender/utils.d.ts +1 -1
- package/dist/esm/node/prerender/utils.js +1 -1
- package/dist/esm/node/runtime/html/serializePageContextClientSide.js +21 -7
- package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.js +1 -1
- package/dist/esm/node/runtime/renderPage.js +2 -2
- package/dist/esm/node/runtime/utils.d.ts +1 -1
- package/dist/esm/node/runtime/utils.js +1 -1
- package/dist/esm/node/shared/getClientEntryFilePath.js +1 -1
- package/dist/esm/shared/addUrlComputedProps.d.ts +1 -0
- package/dist/esm/shared/addUrlComputedProps.js +25 -13
- package/dist/esm/shared/getPageFiles/analyzeClientSide.js +2 -4
- package/dist/esm/shared/getPageFiles/getExports.js +2 -2
- package/dist/esm/shared/hooks/getHook.js +1 -1
- package/dist/esm/shared/page-configs/PageConfig.d.ts +4 -12
- package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.d.ts +7 -0
- package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.js +37 -0
- package/dist/esm/shared/page-configs/helpers/getConfigValue.d.ts +14 -0
- package/dist/esm/shared/page-configs/helpers/getConfigValue.js +38 -0
- package/dist/esm/shared/page-configs/helpers.d.ts +13 -0
- package/dist/esm/shared/page-configs/helpers.js +27 -0
- package/dist/esm/shared/page-configs/serialize/parseConfigValuesImported.js +6 -8
- package/dist/esm/shared/page-configs/serialize/parsePageConfigs.js +2 -2
- package/dist/esm/shared/page-configs/serialize/serializeConfigValue.js +2 -2
- package/dist/esm/shared/route/executeOnBeforeRouteHook.d.ts +1 -1
- package/dist/esm/shared/route/executeOnBeforeRouteHook.js +11 -13
- package/dist/esm/shared/route/index.d.ts +11 -9
- package/dist/esm/shared/route/index.js +3 -3
- package/dist/esm/shared/route/loadPageRoutes.js +7 -6
- package/dist/esm/shared/route/resolveRouteFunction.js +1 -1
- package/dist/esm/shared/utils.d.ts +1 -1
- package/dist/esm/shared/utils.js +1 -1
- package/dist/esm/utils/isPropertyGetter.d.ts +1 -0
- package/dist/esm/utils/{hasPropertyGetter.js → isPropertyGetter.js} +1 -1
- package/dist/esm/utils/projectInfo.d.ts +1 -1
- package/dist/esm/utils/projectInfo.js +1 -1
- package/package.json +2 -2
- package/dist/cjs/shared/page-configs/utils.js +0 -96
- package/dist/esm/client/client-routing-runtime/skipLink/isClientSideRoutable.d.ts +0 -2
- package/dist/esm/client/client-routing-runtime/skipLink/isClientSideRoutable.js +0 -15
- package/dist/esm/client/client-routing-runtime/useClientRouter.d.ts +0 -6
- package/dist/esm/shared/page-configs/utils.d.ts +0 -35
- package/dist/esm/shared/page-configs/utils.js +0 -90
- package/dist/esm/utils/hasPropertyGetter.d.ts +0 -1
package/dist/esm/client/client-routing-runtime/{useClientRouter.js → installClientRouter.js}
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { installClientRouter };
|
|
2
2
|
export { disableClientRouting };
|
|
3
3
|
export { isDisableAutomaticLinkInterception };
|
|
4
|
+
export { renderPageClientSide };
|
|
4
5
|
import { assert, getCurrentUrl, isEquivalentError, objectAssign, serverSideRouteTo, throttle, sleep, getGlobalObject, executeHook } from './utils.js';
|
|
5
6
|
import { navigationState } from './navigationState.js';
|
|
6
7
|
import { checkIf404, getPageContext, getPageContextErrorPage, isAlreadyServerSideRouted } from './getPageContext.js';
|
|
@@ -9,130 +10,81 @@ import { addLinkPrefetchHandlers } from './prefetch.js';
|
|
|
9
10
|
import { assertInfo, assertWarning, isReact } from './utils.js';
|
|
10
11
|
import { executeOnRenderClientHook } from '../shared/executeOnRenderClientHook.js';
|
|
11
12
|
import { assertHook } from '../../shared/hooks/getHook.js';
|
|
12
|
-
import {
|
|
13
|
+
import { skipLink } from './skipLink.js';
|
|
13
14
|
import { isErrorFetchingStaticAssets } from '../shared/loadPageFilesClientSide.js';
|
|
14
|
-
import { initHistoryState, getHistoryState, pushHistory, saveScrollPosition } from './history.js';
|
|
15
|
-
import { defineNavigate } from './navigate.js';
|
|
15
|
+
import { initHistoryState, getHistoryState, pushHistory, saveScrollPosition, monkeyPatchHistoryPushState } from './history.js';
|
|
16
16
|
import { assertNoInfiniteAbortLoop, getPageContextFromAllRewrites, isAbortError, logAbortErrorHandled } from '../../shared/route/abort.js';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (log) {
|
|
24
|
-
// We don't use console.error() to avoid flooding error trackers such as Sentry
|
|
25
|
-
console.log(err);
|
|
26
|
-
}
|
|
27
|
-
// @ts-ignore Since dist/cjs/client/ is never used, we can ignore this error.
|
|
28
|
-
const isProd = import.meta.env.PROD;
|
|
29
|
-
assertInfo(false, [
|
|
30
|
-
'Failed to fetch static asset.',
|
|
31
|
-
isProd ? 'This usually happens when a new frontend is deployed.' : null,
|
|
32
|
-
'Falling back to Server Routing.',
|
|
33
|
-
'(The next page navigation will use Server Routing instead of Client Routing.)'
|
|
34
|
-
]
|
|
35
|
-
.filter(Boolean)
|
|
36
|
-
.join(' '), { onlyOnce: true });
|
|
37
|
-
}
|
|
38
|
-
function useClientRouter() {
|
|
17
|
+
import { route } from '../../shared/route/index.js';
|
|
18
|
+
import { isClientSideRoutable } from './isClientSideRoutable.js';
|
|
19
|
+
const globalObject = getGlobalObject('installClientRouter.ts', { previousState: getState(), renderCounter: 0 });
|
|
20
|
+
function installClientRouter() {
|
|
21
|
+
setupNativeScrollRestoration();
|
|
22
|
+
initHistoryState();
|
|
39
23
|
autoSaveScrollPosition();
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
isBackwardNavigation: false,
|
|
59
|
-
checkClientSideRenderable: true
|
|
60
|
-
});
|
|
24
|
+
monkeyPatchHistoryPushState();
|
|
25
|
+
// Intercept <a> links
|
|
26
|
+
onLinkClick();
|
|
27
|
+
// Handle back-/forward navigation
|
|
28
|
+
onBrowserHistoryNavigation();
|
|
29
|
+
// Intial render
|
|
30
|
+
renderPageClientSide({ scrollTarget: 'preserve-scroll', isBackwardNavigation: null });
|
|
31
|
+
}
|
|
32
|
+
async function renderPageClientSide(renderArgs) {
|
|
33
|
+
const { scrollTarget, urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, checkIfClientSideRenderable, pageContextsFromRewrite = [], redirectCount = 0, isUserLandNavigation } = renderArgs;
|
|
34
|
+
assertNoInfiniteAbortLoop(pageContextsFromRewrite.length, redirectCount);
|
|
35
|
+
if (globalObject.clientRoutingIsDisabled) {
|
|
36
|
+
serverSideRouteTo(urlOriginal);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const pageContext = await createPageContext(urlOriginal);
|
|
40
|
+
objectAssign(pageContext, {
|
|
41
|
+
isBackwardNavigation
|
|
61
42
|
});
|
|
62
|
-
|
|
63
|
-
let renderPromise;
|
|
64
|
-
let isTransitioning = false;
|
|
65
|
-
fetchAndRender({ scrollTarget: 'preserve-scroll', isBackwardNavigation: null });
|
|
66
|
-
return;
|
|
67
|
-
async function fetchAndRender({ scrollTarget, urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, checkClientSideRenderable, pageContextsFromRewrite = [], redirectCount = 0 }) {
|
|
68
|
-
assertNoInfiniteAbortLoop(pageContextsFromRewrite.length, redirectCount);
|
|
69
|
-
if (globalObject.clientRoutingIsDisabled) {
|
|
70
|
-
serverSideRouteTo(urlOriginal);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
43
|
+
{
|
|
73
44
|
const pageContextFromAllRewrites = getPageContextFromAllRewrites(pageContextsFromRewrite);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// If a route() hook has a bug
|
|
83
|
-
throw err;
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
// If the user's route() hook throw redirect() / throw render()
|
|
87
|
-
// We handle the abort error down below: the user's route() hook is called again in getPageContext()
|
|
88
|
-
isClientRoutable = true;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (!isClientRoutable) {
|
|
92
|
-
serverSideRouteTo(urlOriginal);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
45
|
+
objectAssign(pageContext, pageContextFromAllRewrites);
|
|
46
|
+
}
|
|
47
|
+
let hasError = false;
|
|
48
|
+
let err;
|
|
49
|
+
{
|
|
50
|
+
let pageContextFromRoute;
|
|
51
|
+
try {
|
|
52
|
+
pageContextFromRoute = await route(pageContext);
|
|
95
53
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
...pageContextFromAllRewrites
|
|
100
|
-
};
|
|
101
|
-
const renderingNumber = ++renderingCounter;
|
|
102
|
-
assert(renderingNumber >= 1);
|
|
103
|
-
// Start transition before any await's
|
|
104
|
-
if (renderingNumber > 1) {
|
|
105
|
-
if (isTransitioning === false) {
|
|
106
|
-
await globalObject.onPageTransitionStart?.(pageContextBase);
|
|
107
|
-
isTransitioning = true;
|
|
108
|
-
}
|
|
54
|
+
catch (err_) {
|
|
55
|
+
hasError = true;
|
|
56
|
+
err = err_;
|
|
109
57
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return
|
|
58
|
+
if (pageContextFromRoute) {
|
|
59
|
+
objectAssign(pageContext, pageContextFromRoute);
|
|
60
|
+
if (checkIfClientSideRenderable) {
|
|
61
|
+
const isClientRoutable = await isClientSideRoutable(pageContext);
|
|
62
|
+
if (!isClientRoutable) {
|
|
63
|
+
serverSideRouteTo(urlOriginal);
|
|
64
|
+
return;
|
|
117
65
|
}
|
|
118
66
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return
|
|
67
|
+
if (isUserLandNavigation && pageContextFromRoute._pageId === globalObject.previousPageContext?._pageId) {
|
|
68
|
+
// Skip's Vike's rendering; let the user handle the navigation
|
|
69
|
+
return;
|
|
122
70
|
}
|
|
123
|
-
return false;
|
|
124
|
-
};
|
|
125
|
-
const pageContext = await createPageContext(pageContextBase);
|
|
126
|
-
if (shouldAbort()) {
|
|
127
|
-
return;
|
|
128
71
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
72
|
+
}
|
|
73
|
+
const { renderNumber, shouldAbort, setHydrationCanBeAborted } = getRenderNumber();
|
|
74
|
+
const isFirstRenderAttempt = renderNumber === 1;
|
|
75
|
+
objectAssign(pageContext, { _isFirstRenderAttempt: isFirstRenderAttempt });
|
|
76
|
+
// Start transition before any await's
|
|
77
|
+
if (renderNumber > 1) {
|
|
78
|
+
if (!globalObject.isTransitioning) {
|
|
79
|
+
await globalObject.onPageTransitionStart?.(pageContext);
|
|
80
|
+
globalObject.isTransitioning = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (shouldAbort()) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let pageContextAddendum;
|
|
87
|
+
if (!hasError) {
|
|
136
88
|
try {
|
|
137
89
|
pageContextAddendum = await getPageContext(pageContext);
|
|
138
90
|
}
|
|
@@ -140,141 +92,140 @@ function useClientRouter() {
|
|
|
140
92
|
hasError = true;
|
|
141
93
|
err = err_;
|
|
142
94
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
95
|
+
}
|
|
96
|
+
if (hasError) {
|
|
97
|
+
if (!isAbortError(err)) {
|
|
98
|
+
// We don't swallow 404 errors:
|
|
99
|
+
// - 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.)
|
|
100
|
+
// - 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.)
|
|
101
|
+
console.error(err);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// We swallow throw redirect()/render() called by client-side hooks onBeforeRender() and guard()
|
|
105
|
+
// We handle the abort error down below.
|
|
106
|
+
}
|
|
107
|
+
if (shouldSwallowAndInterrupt(err, pageContext))
|
|
108
|
+
return;
|
|
109
|
+
if (isAbortError(err)) {
|
|
110
|
+
const errAbort = err;
|
|
111
|
+
logAbortErrorHandled(err, pageContext._isProduction, pageContext);
|
|
112
|
+
const pageContextAbort = errAbort._pageContextAbort;
|
|
113
|
+
// throw render('/some-url')
|
|
114
|
+
if (pageContextAbort._urlRewrite) {
|
|
115
|
+
await renderPageClientSide({
|
|
116
|
+
...renderArgs,
|
|
117
|
+
scrollTarget: 'scroll-to-top-or-hash',
|
|
118
|
+
checkIfClientSideRenderable: true,
|
|
119
|
+
pageContextsFromRewrite: [...pageContextsFromRewrite, pageContextAbort]
|
|
120
|
+
});
|
|
155
121
|
return;
|
|
156
|
-
if (isAbortError(err)) {
|
|
157
|
-
const errAbort = err;
|
|
158
|
-
logAbortErrorHandled(err, pageContext._isProduction, pageContext);
|
|
159
|
-
const pageContextAbort = errAbort._pageContextAbort;
|
|
160
|
-
// throw render('/some-url')
|
|
161
|
-
if (pageContextAbort._urlRewrite) {
|
|
162
|
-
await fetchAndRender({
|
|
163
|
-
scrollTarget,
|
|
164
|
-
urlOriginal,
|
|
165
|
-
overwriteLastHistoryEntry,
|
|
166
|
-
isBackwardNavigation,
|
|
167
|
-
pageContextsFromRewrite: [...pageContextsFromRewrite, pageContextAbort],
|
|
168
|
-
redirectCount
|
|
169
|
-
});
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
// throw redirect('/some-url')
|
|
173
|
-
if (pageContextAbort._urlRedirect) {
|
|
174
|
-
const urlRedirect = pageContextAbort._urlRedirect.url;
|
|
175
|
-
if (urlRedirect.startsWith('http')) {
|
|
176
|
-
// External redirection
|
|
177
|
-
window.location.href = urlRedirect;
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
await fetchAndRender({
|
|
182
|
-
scrollTarget: 'scroll-to-top-or-hash',
|
|
183
|
-
urlOriginal: urlRedirect,
|
|
184
|
-
overwriteLastHistoryEntry: false,
|
|
185
|
-
isBackwardNavigation: false,
|
|
186
|
-
checkClientSideRenderable: true,
|
|
187
|
-
pageContextsFromRewrite,
|
|
188
|
-
redirectCount: redirectCount++
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
// throw render(statusCode)
|
|
194
|
-
assert(pageContextAbort.abortStatusCode);
|
|
195
|
-
objectAssign(pageContext, pageContextAbort);
|
|
196
|
-
if (pageContextAbort.abortStatusCode === 404) {
|
|
197
|
-
objectAssign(pageContext, { is404: true });
|
|
198
|
-
}
|
|
199
122
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
catch (err2) {
|
|
207
|
-
// - When user hasn't defined a `_error.page.js` file
|
|
208
|
-
// - Some unpexected vike internal error
|
|
209
|
-
if (shouldSwallowAndInterrupt(err2, pageContext))
|
|
123
|
+
// throw redirect('/some-url')
|
|
124
|
+
if (pageContextAbort._urlRedirect) {
|
|
125
|
+
const urlRedirect = pageContextAbort._urlRedirect.url;
|
|
126
|
+
if (urlRedirect.startsWith('http')) {
|
|
127
|
+
// External redirection
|
|
128
|
+
window.location.href = urlRedirect;
|
|
210
129
|
return;
|
|
211
|
-
if (!isFirstRenderAttempt) {
|
|
212
|
-
setTimeout(() => {
|
|
213
|
-
// We let the server show the 404 page
|
|
214
|
-
window.location.pathname = urlOriginal;
|
|
215
|
-
}, 0);
|
|
216
|
-
}
|
|
217
|
-
if (!isEquivalentError(err, err2)) {
|
|
218
|
-
throw err2;
|
|
219
130
|
}
|
|
220
131
|
else {
|
|
221
|
-
|
|
222
|
-
|
|
132
|
+
await renderPageClientSide({
|
|
133
|
+
...renderArgs,
|
|
134
|
+
scrollTarget: 'scroll-to-top-or-hash',
|
|
135
|
+
urlOriginal: urlRedirect,
|
|
136
|
+
overwriteLastHistoryEntry: false,
|
|
137
|
+
isBackwardNavigation: false,
|
|
138
|
+
checkIfClientSideRenderable: true,
|
|
139
|
+
redirectCount: redirectCount + 1
|
|
140
|
+
});
|
|
223
141
|
}
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// throw render(statusCode)
|
|
145
|
+
assert(pageContextAbort.abortStatusCode);
|
|
146
|
+
objectAssign(pageContext, pageContextAbort);
|
|
147
|
+
if (pageContextAbort.abortStatusCode === 404) {
|
|
148
|
+
objectAssign(pageContext, { is404: true });
|
|
224
149
|
}
|
|
225
|
-
}
|
|
226
|
-
assert(pageContextAddendum);
|
|
227
|
-
objectAssign(pageContext, pageContextAddendum);
|
|
228
|
-
assertHook(pageContext, 'onPageTransitionStart');
|
|
229
|
-
globalObject.onPageTransitionStart = pageContext.exports.onPageTransitionStart;
|
|
230
|
-
if (pageContext.exports.hydrationCanBeAborted) {
|
|
231
|
-
hydrationCanBeAborted = true;
|
|
232
150
|
}
|
|
233
151
|
else {
|
|
234
|
-
|
|
152
|
+
objectAssign(pageContext, { is404: checkIf404(err) });
|
|
235
153
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
if (renderPromise) {
|
|
240
|
-
// Always make sure that the previous render has finished,
|
|
241
|
-
// otherwise that previous render may finish after this one.
|
|
242
|
-
await renderPromise;
|
|
243
|
-
}
|
|
244
|
-
if (shouldAbort()) {
|
|
245
|
-
return;
|
|
154
|
+
try {
|
|
155
|
+
pageContextAddendum = await getPageContextErrorPage(pageContext);
|
|
246
156
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
assertHook(pageContext, 'onHydrationEnd');
|
|
258
|
-
const { onHydrationEnd } = pageContext.exports;
|
|
259
|
-
if (onHydrationEnd) {
|
|
260
|
-
const hookFilePath = pageContext.exportsAll.onHydrationEnd[0].exportSource;
|
|
261
|
-
assert(hookFilePath);
|
|
262
|
-
await executeHook(() => onHydrationEnd(pageContext), 'onHydrationEnd', hookFilePath);
|
|
157
|
+
catch (err2) {
|
|
158
|
+
// - When user hasn't defined a `_error.page.js` file
|
|
159
|
+
// - Some unpexected vike internal error
|
|
160
|
+
if (shouldSwallowAndInterrupt(err2, pageContext))
|
|
161
|
+
return;
|
|
162
|
+
if (!isFirstRenderAttempt) {
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
// We let the server show the 404 page
|
|
165
|
+
window.location.pathname = urlOriginal;
|
|
166
|
+
}, 0);
|
|
263
167
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (pageContext.exports.onPageTransitionEnd) {
|
|
267
|
-
assertHook(pageContext, 'onPageTransitionEnd');
|
|
268
|
-
await pageContext.exports.onPageTransitionEnd(pageContext);
|
|
168
|
+
if (!isEquivalentError(err, err2)) {
|
|
169
|
+
throw err2;
|
|
269
170
|
}
|
|
270
|
-
|
|
171
|
+
else {
|
|
172
|
+
// Abort
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
assert(pageContextAddendum);
|
|
178
|
+
objectAssign(pageContext, pageContextAddendum);
|
|
179
|
+
assertHook(pageContext, 'onPageTransitionStart');
|
|
180
|
+
globalObject.onPageTransitionStart = pageContext.exports.onPageTransitionStart;
|
|
181
|
+
if (pageContext.exports.hydrationCanBeAborted) {
|
|
182
|
+
setHydrationCanBeAborted();
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
assertWarning(!isReact(), 'You seem to be using React; we recommend setting hydrationCanBeAborted to true, see https://vike.dev/clientRouting', { onlyOnce: true });
|
|
186
|
+
}
|
|
187
|
+
if (shouldAbort()) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (globalObject.renderPromise) {
|
|
191
|
+
// Always make sure that the previous render has finished,
|
|
192
|
+
// otherwise that previous render may finish after this one.
|
|
193
|
+
await globalObject.renderPromise;
|
|
194
|
+
}
|
|
195
|
+
if (shouldAbort()) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
changeUrl(urlOriginal, overwriteLastHistoryEntry);
|
|
199
|
+
navigationState.markNavigationChange();
|
|
200
|
+
globalObject.previousPageContext = pageContext;
|
|
201
|
+
assert(globalObject.renderPromise === undefined);
|
|
202
|
+
globalObject.renderPromise = (async () => {
|
|
203
|
+
await executeOnRenderClientHook(pageContext, true);
|
|
204
|
+
addLinkPrefetchHandlers(pageContext);
|
|
205
|
+
})();
|
|
206
|
+
await globalObject.renderPromise;
|
|
207
|
+
globalObject.renderPromise = undefined;
|
|
208
|
+
if (pageContext._isFirstRenderAttempt) {
|
|
209
|
+
assertHook(pageContext, 'onHydrationEnd');
|
|
210
|
+
const { onHydrationEnd } = pageContext.exports;
|
|
211
|
+
if (onHydrationEnd) {
|
|
212
|
+
const hookFilePath = pageContext.exportsAll.onHydrationEnd[0].exportSource;
|
|
213
|
+
assert(hookFilePath);
|
|
214
|
+
await executeHook(() => onHydrationEnd(pageContext), 'onHydrationEnd', hookFilePath);
|
|
271
215
|
}
|
|
272
|
-
setScrollPosition(scrollTarget);
|
|
273
|
-
browserNativeScrollRestoration_disable();
|
|
274
|
-
globalObject.initialRenderIsDone = true;
|
|
275
216
|
}
|
|
217
|
+
else if (renderNumber === globalObject.renderCounter) {
|
|
218
|
+
if (pageContext.exports.onPageTransitionEnd) {
|
|
219
|
+
assertHook(pageContext, 'onPageTransitionEnd');
|
|
220
|
+
await pageContext.exports.onPageTransitionEnd(pageContext);
|
|
221
|
+
}
|
|
222
|
+
globalObject.isTransitioning = undefined;
|
|
223
|
+
}
|
|
224
|
+
setScrollPosition(scrollTarget);
|
|
225
|
+
browserNativeScrollRestoration_disable();
|
|
226
|
+
globalObject.initialRenderIsDone = true;
|
|
276
227
|
}
|
|
277
|
-
function onLinkClick(
|
|
228
|
+
function onLinkClick() {
|
|
278
229
|
document.addEventListener('click', onClick);
|
|
279
230
|
return;
|
|
280
231
|
// Code adapted from https://github.com/HenrikJoreteg/internal-nav-helper/blob/5199ec5448d0b0db7ec63cf76d88fa6cad878b7d/src/index.js#L11-L29
|
|
@@ -290,7 +241,13 @@ function onLinkClick(callback) {
|
|
|
290
241
|
assert(url);
|
|
291
242
|
ev.preventDefault();
|
|
292
243
|
const keepScrollPosition = ![null, 'false'].includes(linkTag.getAttribute('keep-scroll-position'));
|
|
293
|
-
|
|
244
|
+
const scrollTarget = keepScrollPosition ? 'preserve-scroll' : 'scroll-to-top-or-hash';
|
|
245
|
+
renderPageClientSide({
|
|
246
|
+
scrollTarget,
|
|
247
|
+
urlOriginal: url,
|
|
248
|
+
isBackwardNavigation: false,
|
|
249
|
+
checkIfClientSideRenderable: true
|
|
250
|
+
});
|
|
294
251
|
}
|
|
295
252
|
function isNormalLeftClick(ev) {
|
|
296
253
|
return ev.button === 0 && !ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey;
|
|
@@ -306,21 +263,29 @@ function onLinkClick(callback) {
|
|
|
306
263
|
return target;
|
|
307
264
|
}
|
|
308
265
|
}
|
|
309
|
-
function onBrowserHistoryNavigation(
|
|
310
|
-
// The
|
|
311
|
-
//
|
|
266
|
+
function onBrowserHistoryNavigation() {
|
|
267
|
+
// - The popstate event is trigged upon:
|
|
268
|
+
// - Back-/forward navigation.
|
|
269
|
+
// - By user clicking on his browser's back-/forward navigation (or using a shortcut)
|
|
270
|
+
// - By JavaScript: `history.back()` / `history.forward()`
|
|
271
|
+
// - URL hash change.
|
|
272
|
+
// - By user clicking on a hash link `<a href="#some-hash" />`
|
|
273
|
+
// - The popstate event is *only* triggered if `href` starts with '#' (even if `href` is '/#some-hash' while the current URL's pathname is '/' then the popstate still isn't triggered)
|
|
274
|
+
// - By JavaScript: `location.hash = 'some-hash'`
|
|
275
|
+
// - The `event` of `window.addEventListener('popstate', (event) => /*...*/)` is useless: the History API doesn't provide the previous state (the popped state), see https://stackoverflow.com/questions/48055323/is-history-state-always-the-same-as-popstate-event-state
|
|
312
276
|
window.addEventListener('popstate', () => {
|
|
313
277
|
const currentState = getState();
|
|
314
278
|
const scrollTarget = currentState.historyState.scrollPosition || 'scroll-to-top-or-hash';
|
|
279
|
+
const isUserLandNavigation = currentState.historyState.triggedBy === 'user';
|
|
315
280
|
const isHashNavigation = currentState.urlWithoutHash === globalObject.previousState.urlWithoutHash;
|
|
316
281
|
const isBackwardNavigation = !currentState.historyState.timestamp || !globalObject.previousState.historyState.timestamp
|
|
317
282
|
? null
|
|
318
283
|
: currentState.historyState.timestamp < globalObject.previousState.historyState.timestamp;
|
|
319
284
|
globalObject.previousState = currentState;
|
|
320
|
-
if (isHashNavigation) {
|
|
285
|
+
if (isHashNavigation && !isUserLandNavigation) {
|
|
321
286
|
// - `history.state` is uninitialized (`null`) when:
|
|
322
|
-
// - The
|
|
323
|
-
// - The user clicks on an anchor link `<a href="#section">Section</a
|
|
287
|
+
// - The user's code runs `window.location.hash = '#section'`.
|
|
288
|
+
// - The user clicks on an anchor link `<a href="#section">Section</a>` (because Vike's `onLinkClick()` handler skips hash links).
|
|
324
289
|
// - `history.state` is `null` when uninitialized: https://developer.mozilla.org/en-US/docs/Web/API/History/state
|
|
325
290
|
// - Alternatively, we completely take over hash navigation and reproduce the browser's native behavior upon hash navigation.
|
|
326
291
|
// - Problem: we cannot intercept `window.location.hash = '#section'`. (Or maybe we can with the `hashchange` event?)
|
|
@@ -341,8 +306,7 @@ function onBrowserHistoryNavigation(callback) {
|
|
|
341
306
|
}
|
|
342
307
|
}
|
|
343
308
|
else {
|
|
344
|
-
|
|
345
|
-
callback(scrollTarget, isBackwardNavigation);
|
|
309
|
+
renderPageClientSide({ scrollTarget, isBackwardNavigation, isUserLandNavigation });
|
|
346
310
|
}
|
|
347
311
|
});
|
|
348
312
|
}
|
|
@@ -418,6 +382,7 @@ function setScroll(scrollPosition) {
|
|
|
418
382
|
}, 0);
|
|
419
383
|
});
|
|
420
384
|
}
|
|
385
|
+
// Save scroll position (needed for back-/forward navigation)
|
|
421
386
|
function autoSaveScrollPosition() {
|
|
422
387
|
// Safari cannot handle more than 100 `history.replaceState()` calls within 30 seconds (https://github.com/vikejs/vike/issues/46)
|
|
423
388
|
window.addEventListener('scroll', throttle(saveScrollPosition, Math.ceil(1000 / 3)), { passive: true });
|
|
@@ -491,3 +456,44 @@ function isDisableAutomaticLinkInterception() {
|
|
|
491
456
|
return globalObject.disableAutomaticLinkInterception ?? false
|
|
492
457
|
*/
|
|
493
458
|
}
|
|
459
|
+
function disableClientRouting(err, log) {
|
|
460
|
+
assert(isErrorFetchingStaticAssets(err));
|
|
461
|
+
globalObject.clientRoutingIsDisabled = true;
|
|
462
|
+
if (log) {
|
|
463
|
+
// We don't use console.error() to avoid flooding error trackers such as Sentry
|
|
464
|
+
console.log(err);
|
|
465
|
+
}
|
|
466
|
+
// @ts-ignore Since dist/cjs/client/ is never used, we can ignore this error.
|
|
467
|
+
const isProd = import.meta.env.PROD;
|
|
468
|
+
assertInfo(false, [
|
|
469
|
+
'Failed to fetch static asset.',
|
|
470
|
+
isProd ? 'This usually happens when a new frontend is deployed.' : null,
|
|
471
|
+
'Falling back to Server Routing.',
|
|
472
|
+
'(The next page navigation will use Server Routing instead of Client Routing.)'
|
|
473
|
+
]
|
|
474
|
+
.filter(Boolean)
|
|
475
|
+
.join(' '), { onlyOnce: true });
|
|
476
|
+
}
|
|
477
|
+
function getRenderNumber() {
|
|
478
|
+
const renderNumber = ++globalObject.renderCounter;
|
|
479
|
+
assert(renderNumber >= 1);
|
|
480
|
+
let hydrationCanBeAborted = false;
|
|
481
|
+
const setHydrationCanBeAborted = () => {
|
|
482
|
+
hydrationCanBeAborted = true;
|
|
483
|
+
};
|
|
484
|
+
const shouldAbort = () => {
|
|
485
|
+
{
|
|
486
|
+
// We should never abort the hydration if `hydrationCanBeAborted` isn't `true`
|
|
487
|
+
const isHydration = renderNumber === 1;
|
|
488
|
+
if (isHydration && hydrationCanBeAborted === false) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
// If there is a newer rendering, we should abort all previous renderings
|
|
493
|
+
if (renderNumber !== globalObject.renderCounter) {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
return false;
|
|
497
|
+
};
|
|
498
|
+
return { renderNumber, shouldAbort, setHydrationCanBeAborted };
|
|
499
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { isClientSideRoutable };
|
|
2
|
+
import type { PageFile } from '../../shared/getPageFiles.js';
|
|
3
|
+
import type { PageConfigRuntime } from '../../shared/page-configs/PageConfig.js';
|
|
4
|
+
declare function isClientSideRoutable(pageContext: {
|
|
5
|
+
_pageId: string | null;
|
|
6
|
+
_pageFilesAll: PageFile[];
|
|
7
|
+
_pageConfigs: PageConfigRuntime[];
|
|
8
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { isClientSideRoutable };
|
|
2
|
+
import { analyzePageClientSideInit } from '../../shared/getPageFiles/analyzePageClientSide.js';
|
|
3
|
+
import { findPageConfig } from '../../shared/page-configs/findPageConfig.js';
|
|
4
|
+
import { analyzeClientSide } from '../../shared/getPageFiles/analyzeClientSide.js';
|
|
5
|
+
async function isClientSideRoutable(pageContext) {
|
|
6
|
+
if (!pageContext._pageId) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
await analyzePageClientSideInit(pageContext._pageFilesAll, pageContext._pageId, {
|
|
10
|
+
sharedPageFilesAlreadyLoaded: false
|
|
11
|
+
});
|
|
12
|
+
const pageConfig = findPageConfig(pageContext._pageConfigs, pageContext._pageId);
|
|
13
|
+
const { isClientSideRenderable, isClientRouting } = analyzeClientSide(pageConfig, pageContext._pageFilesAll, pageContext._pageId);
|
|
14
|
+
return isClientSideRenderable && isClientRouting;
|
|
15
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { navigate };
|
|
2
2
|
export { reload };
|
|
3
|
-
export { defineNavigate };
|
|
4
3
|
/** Programmatically navigate to a new page.
|
|
5
4
|
*
|
|
6
5
|
* https://vike.dev/navigate
|
|
@@ -13,5 +12,4 @@ declare function navigate(url: string, { keepScrollPosition, overwriteLastHistor
|
|
|
13
12
|
keepScrollPosition?: boolean | undefined;
|
|
14
13
|
overwriteLastHistoryEntry?: boolean | undefined;
|
|
15
14
|
}): Promise<void>;
|
|
16
|
-
declare function defineNavigate(navigate_: typeof navigate): void;
|
|
17
15
|
declare function reload(): Promise<void>;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
export { navigate };
|
|
2
2
|
export { reload };
|
|
3
|
-
|
|
4
|
-
import { assertUsage, isBrowser,
|
|
3
|
+
import { renderPageClientSide } from './installClientRouter.js';
|
|
4
|
+
import { assertUsage, isBrowser, assertClientRouting, checkIfClientRouting, getCurrentUrl } from './utils.js';
|
|
5
5
|
assertClientRouting();
|
|
6
|
-
const globalObject = getGlobalObject('navigate.ts', {});
|
|
7
6
|
/** Programmatically navigate to a new page.
|
|
8
7
|
*
|
|
9
8
|
* https://vike.dev/navigate
|
|
@@ -15,7 +14,6 @@ const globalObject = getGlobalObject('navigate.ts', {});
|
|
|
15
14
|
async function navigate(url, { keepScrollPosition = false, overwriteLastHistoryEntry = false } = {}) {
|
|
16
15
|
assertUsage(isBrowser(), 'The navigate() function can be called only on the client-side', { showStackTrace: true });
|
|
17
16
|
const errMsg = 'navigate() works only with Client Routing, see https://vike.dev/navigate';
|
|
18
|
-
assertUsage(globalObject.navigate, errMsg, { showStackTrace: true });
|
|
19
17
|
assertUsage(checkIfClientRouting(), errMsg, { showStackTrace: true });
|
|
20
18
|
assertUsage(url, '[navigate(url)] Missing argument url', { showStackTrace: true });
|
|
21
19
|
assertUsage(typeof url === 'string', '[navigate(url)] Argument url should be a string', { showStackTrace: true });
|
|
@@ -24,10 +22,14 @@ async function navigate(url, { keepScrollPosition = false, overwriteLastHistoryE
|
|
|
24
22
|
assertUsage(url.startsWith('/'), '[navigate(url)] Argument url should start with a leading /', {
|
|
25
23
|
showStackTrace: true
|
|
26
24
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
const scrollTarget = keepScrollPosition ? 'preserve-scroll' : 'scroll-to-top-or-hash';
|
|
26
|
+
await renderPageClientSide({
|
|
27
|
+
scrollTarget,
|
|
28
|
+
urlOriginal: url,
|
|
29
|
+
overwriteLastHistoryEntry,
|
|
30
|
+
isBackwardNavigation: false,
|
|
31
|
+
checkIfClientSideRenderable: true
|
|
32
|
+
});
|
|
31
33
|
}
|
|
32
34
|
async function reload() {
|
|
33
35
|
await navigate(getCurrentUrl());
|