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.
Files changed (74) hide show
  1. package/dist/cjs/node/plugin/plugins/buildConfig.js +1 -1
  2. package/dist/cjs/node/plugin/plugins/config/index.js +3 -3
  3. package/dist/cjs/node/plugin/plugins/devConfig/determineOptimizeDeps.js +1 -1
  4. package/dist/cjs/node/plugin/plugins/importUserCode/getVirtualFileImportUserCode.js +1 -1
  5. package/dist/cjs/node/plugin/plugins/importUserCode/index.js +1 -1
  6. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +1 -1
  7. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +5 -2
  8. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.js +4 -2
  9. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +18 -6
  10. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/transpileAndExecuteFile.js +2 -2
  11. package/dist/cjs/node/prerender/runPrerender.js +17 -8
  12. package/dist/cjs/node/runtime/renderPage/log404/index.js +2 -1
  13. package/dist/cjs/node/shared/getConfigVike.js +4 -1
  14. package/dist/cjs/shared/page-configs/serialize/parseConfigValuesImported.js +8 -5
  15. package/dist/cjs/shared/route/noRouteMatch.js +4 -0
  16. package/dist/cjs/utils/isExternalLink.js +7 -0
  17. package/dist/cjs/utils/onPageVisibilityChange.js +19 -0
  18. package/dist/cjs/utils/projectInfo.js +1 -1
  19. package/dist/esm/client/client-routing-runtime/createPageContext.js +0 -1
  20. package/dist/esm/client/client-routing-runtime/getBaseServer.d.ts +2 -1
  21. package/dist/esm/client/client-routing-runtime/getBaseServer.js +2 -1
  22. package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.d.ts +39 -0
  23. package/dist/esm/client/client-routing-runtime/{getPageContext.js → getPageContextFromHooks.js} +48 -74
  24. package/dist/esm/client/client-routing-runtime/history.js +9 -5
  25. package/dist/esm/client/client-routing-runtime/installClientRouter.d.ts +0 -19
  26. package/dist/esm/client/client-routing-runtime/installClientRouter.js +11 -488
  27. package/dist/esm/client/client-routing-runtime/isClientSideRoutable.d.ts +3 -3
  28. package/dist/esm/client/client-routing-runtime/isClientSideRoutable.js +4 -7
  29. package/dist/esm/client/client-routing-runtime/navigate.js +2 -3
  30. package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.d.ts +4 -0
  31. package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.js +63 -0
  32. package/dist/esm/client/client-routing-runtime/onLinkClick.d.ts +2 -0
  33. package/dist/esm/client/client-routing-runtime/onLinkClick.js +40 -0
  34. package/dist/esm/client/client-routing-runtime/prefetch.js +7 -8
  35. package/dist/esm/client/client-routing-runtime/renderPageClientSide.d.ts +19 -0
  36. package/dist/esm/client/client-routing-runtime/renderPageClientSide.js +347 -0
  37. package/dist/esm/client/client-routing-runtime/scrollRestoration.d.ts +6 -0
  38. package/dist/esm/client/client-routing-runtime/scrollRestoration.js +25 -0
  39. package/dist/esm/client/client-routing-runtime/setScrollPosition.d.ts +7 -0
  40. package/dist/esm/client/client-routing-runtime/setScrollPosition.js +77 -0
  41. package/dist/esm/client/client-routing-runtime/skipLink.js +9 -4
  42. package/dist/esm/client/client-routing-runtime/utils.d.ts +2 -0
  43. package/dist/esm/client/client-routing-runtime/utils.js +2 -0
  44. package/dist/esm/node/plugin/plugins/buildConfig.js +2 -2
  45. package/dist/esm/node/plugin/plugins/config/index.js +4 -4
  46. package/dist/esm/node/plugin/plugins/devConfig/determineOptimizeDeps.js +2 -2
  47. package/dist/esm/node/plugin/plugins/importUserCode/getVirtualFileImportUserCode.js +1 -1
  48. package/dist/esm/node/plugin/plugins/importUserCode/index.js +1 -1
  49. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +1 -1
  50. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.d.ts +2 -1
  51. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +6 -3
  52. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.d.ts +2 -2
  53. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.js +4 -2
  54. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.d.ts +2 -2
  55. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +18 -6
  56. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/transpileAndExecuteFile.js +3 -3
  57. package/dist/esm/node/prerender/runPrerender.js +17 -8
  58. package/dist/esm/node/runtime/renderPage/log404/index.js +2 -1
  59. package/dist/esm/node/shared/getConfigVike.d.ts +2 -1
  60. package/dist/esm/node/shared/getConfigVike.js +4 -1
  61. package/dist/esm/shared/page-configs/serialize/parseConfigValuesImported.js +9 -3
  62. package/dist/esm/shared/route/noRouteMatch.d.ts +1 -0
  63. package/dist/esm/shared/route/noRouteMatch.js +1 -0
  64. package/dist/esm/utils/onPageVisibilityChange.d.ts +4 -0
  65. package/dist/esm/utils/onPageVisibilityChange.js +16 -0
  66. package/dist/esm/utils/projectInfo.d.ts +1 -1
  67. package/dist/esm/utils/projectInfo.js +1 -1
  68. package/node/cli/bin-entry.js +1 -1
  69. package/package.json +1 -1
  70. package/dist/esm/client/client-routing-runtime/getPageContext.d.ts +0 -28
  71. package/dist/esm/client/client-routing-runtime/navigationState.d.ts +0 -5
  72. package/dist/esm/client/client-routing-runtime/navigationState.js +0 -14
  73. /package/dist/esm/{client/client-routing-runtime → utils}/isExternalLink.d.ts +0 -0
  74. /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, objectAssign } from './utils.js';
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 './installClientRouter.js';
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} doesn't match the route of any of your pages`, {
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
- objectAssign(pageContext, pageContextFromRoute);
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(pageContext._pageId, pageContext);
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 './isExternalLink.js';
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 are now deprecated)
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, getOutDirs } from '../utils.js';
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.root, getOutDirs(config).outDirRoot, false, configVike.extensions);
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 { getOutDirs, isDev2 } from '../../utils.js';
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 = resolveConfig(vikeConfig, config);
15
+ const promise = getConfigVikPromise(vikeConfig, config);
16
16
  config.configVikePromise = promise;
17
17
  await promise;
18
18
  }
19
19
  };
20
20
  }
21
- async function resolveConfig(vikeConfig, config) {
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.root, getOutDirs(config).outDirRoot, isDev2(config), extensions);
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 ?