vike 0.4.196-commit-df033dd → 0.4.197-commit-9db6c02

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 (120) hide show
  1. package/dist/cjs/client/client-routing-runtime/prefetch/PrefetchSetting.js +2 -0
  2. package/dist/cjs/client/server-routing-runtime/onLoad.js +7 -0
  3. package/dist/cjs/client/server-routing-runtime/utils.js +34 -0
  4. package/dist/cjs/client/shared/getPageContextProxyForUser.js +79 -0
  5. package/dist/cjs/client/shared/preparePageContextForUserConsumptionClientSide.js +46 -0
  6. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +17 -1
  7. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/loadFileAtConfigTime.js +2 -2
  8. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/resolvePointerImport.js +26 -23
  9. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/{transformFileImports.js → transformPointerImports.js} +3 -4
  10. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/transpileAndExecuteFile.js +44 -47
  11. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +1 -1
  12. package/dist/cjs/node/prerender/runPrerender.js +5 -5
  13. package/dist/cjs/node/runtime/html/injectAssets/getHtmlTags.js +1 -1
  14. package/dist/cjs/node/runtime/html/injectAssets/injectAssets__public.js +1 -1
  15. package/dist/cjs/node/runtime/html/serializePageContextClientSide.js +2 -2
  16. package/dist/cjs/node/runtime/renderPage/createHttpResponse.js +3 -3
  17. package/dist/cjs/node/runtime/renderPage/debugPageFiles.js +1 -1
  18. package/dist/cjs/node/runtime/renderPage/handleErrorWithoutErrorPage.js +1 -1
  19. package/dist/cjs/node/runtime/renderPage/loadUserFilesServerSide.js +4 -4
  20. package/dist/cjs/node/runtime/renderPage/preparePageContextForUserConsumptionServerSide.js +2 -7
  21. package/dist/cjs/node/runtime/renderPage/renderPageAlreadyRouted.js +4 -4
  22. package/dist/cjs/node/runtime/renderPage.js +8 -8
  23. package/dist/cjs/shared/addIs404ToPageProps.js +1 -1
  24. package/dist/cjs/shared/assertPageContextProvidedByUser.js +1 -1
  25. package/dist/cjs/shared/page-configs/serialize/serializeConfigValues.js +2 -2
  26. package/dist/cjs/shared/preparePageContextForUserConsumption.js +34 -0
  27. package/dist/cjs/shared/route/executeGuardHook.js +1 -1
  28. package/dist/cjs/shared/route/executeOnBeforeRouteHook.js +6 -6
  29. package/dist/cjs/shared/route/index.js +3 -3
  30. package/dist/cjs/shared/route/resolveRouteString.js +10 -1
  31. package/dist/cjs/utils/PROJECT_VERSION.js +1 -1
  32. package/dist/cjs/utils/isNpmPackage.js +4 -0
  33. package/dist/cjs/utils/isScriptFile.js +3 -3
  34. package/dist/esm/client/client-routing-runtime/entry.js +2 -2
  35. package/dist/esm/client/client-routing-runtime/getPageContextCurrent.d.ts +10 -0
  36. package/dist/esm/client/client-routing-runtime/getPageContextCurrent.js +25 -0
  37. package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.d.ts +28 -18
  38. package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.js +39 -32
  39. package/dist/esm/client/client-routing-runtime/history.js +1 -1
  40. package/dist/esm/client/client-routing-runtime/initClientRouter.d.ts +2 -0
  41. package/dist/esm/client/client-routing-runtime/{installClientRouter.js → initClientRouter.js} +11 -8
  42. package/dist/esm/client/client-routing-runtime/initOnLinkClick.d.ts +2 -0
  43. package/dist/esm/client/client-routing-runtime/{onLinkClick.js → initOnLinkClick.js} +2 -2
  44. package/dist/esm/client/client-routing-runtime/isClientSideRoutable.js +1 -0
  45. package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.js +1 -1
  46. package/dist/esm/client/client-routing-runtime/prefetch/PrefetchSetting.d.ts +7 -0
  47. package/dist/esm/client/client-routing-runtime/prefetch/PrefetchSetting.js +1 -0
  48. package/dist/esm/client/client-routing-runtime/prefetch/getPrefetchSettings.d.ts +8 -7
  49. package/dist/esm/client/client-routing-runtime/prefetch/getPrefetchSettings.js +74 -67
  50. package/dist/esm/client/client-routing-runtime/prefetch.d.ts +16 -5
  51. package/dist/esm/client/client-routing-runtime/prefetch.js +167 -64
  52. package/dist/esm/client/client-routing-runtime/renderPageClientSide.js +115 -28
  53. package/dist/esm/client/server-routing-runtime/getPageContext.d.ts +1 -1
  54. package/dist/esm/client/server-routing-runtime/getPageContext.js +1 -1
  55. package/dist/esm/client/shared/executeOnRenderClientHook.d.ts +1 -1
  56. package/dist/esm/client/shared/getPageContextSerializedInHtml.d.ts +1 -1
  57. package/dist/esm/client/shared/getPageContextSerializedInHtml.js +1 -1
  58. package/dist/esm/client/shared/preparePageContextForUserConsumptionClientSide.d.ts +1 -1
  59. package/dist/esm/client/shared/preparePageContextForUserConsumptionClientSide.js +20 -29
  60. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +17 -1
  61. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/loadFileAtConfigTime.js +1 -1
  62. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/resolvePointerImport.d.ts +1 -1
  63. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/resolvePointerImport.js +25 -22
  64. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/{transformFileImports.d.ts → transformPointerImports.d.ts} +2 -2
  65. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/{transformFileImports.js → transformPointerImports.js} +3 -4
  66. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/transpileAndExecuteFile.js +45 -48
  67. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +1 -1
  68. package/dist/esm/node/prerender/runPrerender.js +5 -5
  69. package/dist/esm/node/runtime/html/injectAssets/getHtmlTags.js +1 -1
  70. package/dist/esm/node/runtime/html/injectAssets/injectAssets__public.js +1 -1
  71. package/dist/esm/node/runtime/html/injectAssets.d.ts +1 -1
  72. package/dist/esm/node/runtime/html/serializePageContextClientSide.d.ts +1 -1
  73. package/dist/esm/node/runtime/html/serializePageContextClientSide.js +2 -2
  74. package/dist/esm/node/runtime/renderPage/createHttpResponse.d.ts +1 -1
  75. package/dist/esm/node/runtime/renderPage/createHttpResponse.js +3 -3
  76. package/dist/esm/node/runtime/renderPage/debugPageFiles.d.ts +1 -1
  77. package/dist/esm/node/runtime/renderPage/debugPageFiles.js +1 -1
  78. package/dist/esm/node/runtime/renderPage/executeOnBeforeRenderAndDataHooks.d.ts +1 -1
  79. package/dist/esm/node/runtime/renderPage/executeOnRenderHtmlHook.d.ts +1 -1
  80. package/dist/esm/node/runtime/renderPage/handleErrorWithoutErrorPage.d.ts +1 -1
  81. package/dist/esm/node/runtime/renderPage/handleErrorWithoutErrorPage.js +1 -1
  82. package/dist/esm/node/runtime/renderPage/loadUserFilesServerSide.d.ts +1 -1
  83. package/dist/esm/node/runtime/renderPage/loadUserFilesServerSide.js +4 -4
  84. package/dist/esm/node/runtime/renderPage/preparePageContextForUserConsumptionServerSide.d.ts +1 -1
  85. package/dist/esm/node/runtime/renderPage/preparePageContextForUserConsumptionServerSide.js +3 -8
  86. package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.d.ts +6 -6
  87. package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.js +4 -4
  88. package/dist/esm/node/runtime/renderPage.js +8 -8
  89. package/dist/esm/shared/addIs404ToPageProps.d.ts +1 -1
  90. package/dist/esm/shared/addIs404ToPageProps.js +1 -1
  91. package/dist/esm/shared/assertPageContextProvidedByUser.js +1 -1
  92. package/dist/esm/shared/page-configs/Config.d.ts +21 -1
  93. package/dist/esm/shared/page-configs/serialize/serializeConfigValues.js +1 -1
  94. package/dist/esm/shared/preparePageContextForUserConsumption.d.ts +5 -0
  95. package/dist/esm/shared/preparePageContextForUserConsumption.js +32 -0
  96. package/dist/esm/shared/route/executeGuardHook.d.ts +1 -1
  97. package/dist/esm/shared/route/executeGuardHook.js +1 -1
  98. package/dist/esm/shared/route/executeOnBeforeRouteHook.js +6 -6
  99. package/dist/esm/shared/route/index.d.ts +1 -1
  100. package/dist/esm/shared/route/index.js +3 -3
  101. package/dist/esm/shared/route/resolveRouteString.d.ts +2 -15
  102. package/dist/esm/shared/route/resolveRouteString.js +10 -1
  103. package/dist/esm/shared/types.d.ts +6 -2
  104. package/dist/esm/types/index.d.ts +1 -1
  105. package/dist/esm/utils/PROJECT_VERSION.d.ts +1 -1
  106. package/dist/esm/utils/PROJECT_VERSION.js +1 -1
  107. package/dist/esm/utils/isNpmPackage.d.ts +2 -0
  108. package/dist/esm/utils/isNpmPackage.js +4 -0
  109. package/dist/esm/utils/isScriptFile.d.ts +2 -2
  110. package/dist/esm/utils/isScriptFile.js +3 -3
  111. package/dist/esm/utils/projectInfo.d.ts +1 -1
  112. package/package.json +1 -1
  113. package/dist/cjs/shared/sortPageContext.js +0 -12
  114. package/dist/esm/client/client-routing-runtime/installClientRouter.d.ts +0 -2
  115. package/dist/esm/client/client-routing-runtime/onLinkClick.d.ts +0 -2
  116. package/dist/esm/client/client-routing-runtime/prefetch/alreadyPrefetched.d.ts +0 -4
  117. package/dist/esm/client/client-routing-runtime/prefetch/alreadyPrefetched.js +0 -16
  118. package/dist/esm/shared/sortPageContext.d.ts +0 -2
  119. package/dist/esm/shared/sortPageContext.js +0 -10
  120. /package/{readme.md → README.md} +0 -0
@@ -1,21 +1,43 @@
1
1
  export { prefetch };
2
+ export { getPageContextPrefetched };
3
+ export { initLinkPrefetchHandlers };
2
4
  export { addLinkPrefetchHandlers };
3
- import { assert, assertClientRouting, assertUsage, assertUsageUrlPathname, assertWarning, checkIfClientRouting, getGlobalObject } from './utils.js';
5
+ export { addLinkPrefetchHandlers_watch };
6
+ export { addLinkPrefetchHandlers_unwatch };
7
+ import { assert, assertClientRouting, assertUsage, assertUsageUrlPathname, assertWarning, checkIfClientRouting, getGlobalObject, hasProp, objectAssign } from './utils.js';
4
8
  import { isErrorFetchingStaticAssets, loadUserFilesClientSide } from '../shared/loadUserFilesClientSide.js';
5
9
  import { skipLink } from './skipLink.js';
6
- import { getPrefetchSettings } from './prefetch/getPrefetchSettings.js';
7
- import { isAlreadyPrefetched, markAsAlreadyPrefetched } from './prefetch/alreadyPrefetched.js';
8
10
  import { disableClientRouting } from './renderPageClientSide.js';
9
11
  import { isClientSideRoutable } from './isClientSideRoutable.js';
10
12
  import { createPageContext } from './createPageContext.js';
11
13
  import { route } from '../../shared/route/index.js';
12
14
  import { noRouteMatch } from '../../shared/route/noRouteMatch.js';
15
+ import { getPageContextFromServerHooks } from './getPageContextFromHooks.js';
16
+ import { getPageContextCurrent } from './getPageContextCurrent.js';
17
+ import { PAGE_CONTEXT_MAX_AGE_DEFAULT, getPrefetchSettings } from './prefetch/getPrefetchSettings.js';
13
18
  import pc from '@brillout/picocolors';
14
19
  assertClientRouting();
15
- const globalObject = getGlobalObject('prefetch.ts', { linkPrefetchHandlerAdded: new WeakMap() });
16
- async function prefetchAssets(pageId, pageContext) {
20
+ const globalObject = getGlobalObject('prefetch.ts', {
21
+ linkPrefetchHandlerAdded: new WeakSet(),
22
+ addLinkPrefetchHandlers_debounce: null,
23
+ mutationObserver: new MutationObserver(addLinkPrefetchHandlers),
24
+ // `linkTags` [is automatically updated](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection#:~:text=An%20HTMLCollection%20in%20the%20HTML%20DOM%20is%20live%3B%20it%20is%20automatically%20updated%20when%20the%20underlying%20document%20is%20changed.)
25
+ linkTags: document.getElementsByTagName('A'),
26
+ prefetchedPageContexts: {}
27
+ });
28
+ function getPageContextPrefetched(pageContext) {
29
+ const url = pageContext.urlOriginal;
30
+ const found = globalObject.prefetchedPageContexts[url];
31
+ if (!found || found.result.is404ServerSideRouted || isExpired(found))
32
+ return null;
33
+ const pageContextPrefetched = found.result.pageContextFromServerHooks;
34
+ // We discard the prefetched pageContext whenever we use it, so that the user always sees fresh data upon naivgating.
35
+ delete globalObject.prefetchedPageContexts[url];
36
+ return pageContextPrefetched;
37
+ }
38
+ async function prefetchAssets(pageContextLink) {
17
39
  try {
18
- await loadUserFilesClientSide(pageId, pageContext._pageFilesAll, pageContext._pageConfigs);
40
+ await loadUserFilesClientSide(pageContextLink.pageId, pageContextLink._pageFilesAll, pageContextLink._pageConfigs);
19
41
  }
20
42
  catch (err) {
21
43
  if (isErrorFetchingStaticAssets(err)) {
@@ -26,6 +48,15 @@ async function prefetchAssets(pageId, pageContext) {
26
48
  }
27
49
  }
28
50
  }
51
+ async function prefetchPageContextFromServerHooks(pageContextLink, resultMaxAge) {
52
+ const result = await getPageContextFromServerHooks(pageContextLink, false);
53
+ const urlOfLink = pageContextLink.urlOriginal;
54
+ globalObject.prefetchedPageContexts[urlOfLink] = {
55
+ resultFetchedAt: Date.now(),
56
+ resultMaxAge,
57
+ result
58
+ };
59
+ }
29
60
  /**
30
61
  * Programmatically prefetch client assets.
31
62
  *
@@ -33,85 +64,157 @@ async function prefetchAssets(pageId, pageContext) {
33
64
  *
34
65
  * @param url - The URL of the page you want to prefetch.
35
66
  */
36
- async function prefetch(url) {
67
+ async function prefetch(url, options) {
37
68
  assertUsage(checkIfClientRouting(), 'prefetch() only works with Client Routing, see https://vike.dev/prefetch', {
38
69
  showStackTrace: true
39
70
  });
40
71
  const errPrefix = '[prefetch(url)] url';
41
72
  assertUsageUrlPathname(url, errPrefix);
42
- if (isAlreadyPrefetched(url))
43
- return;
44
- markAsAlreadyPrefetched(url);
45
- const pageContext = await createPageContext(url);
46
- let pageContextFromRoute;
47
- try {
48
- pageContextFromRoute = await route(pageContext);
49
- }
50
- catch {
51
- // If a route() hook has a bug or `throw render()` / `throw redirect()`
52
- return;
53
- }
54
- const pageId = pageContextFromRoute._pageId;
55
- if (!pageId) {
73
+ const pageContextLink = await getPageContextLink(url);
74
+ if (!pageContextLink?.pageId) {
56
75
  assertWarning(false, `${errPrefix} ${pc.string(url)} ${noRouteMatch}`, {
57
76
  showStackTrace: true,
58
77
  onlyOnce: false
59
78
  });
60
79
  return;
61
80
  }
62
- await prefetchAssets(pageId, pageContext);
81
+ assert(hasProp(pageContextLink, 'pageId', 'string')); // help TypeScript
82
+ await Promise.all([
83
+ (async () => {
84
+ if (options?.staticAssets !== false) {
85
+ await prefetchAssets(pageContextLink);
86
+ }
87
+ })(),
88
+ (async () => {
89
+ if (options?.pageContext !== false) {
90
+ const resultMaxAge = getResultMaxAge();
91
+ await prefetchPageContextFromServerHooks(pageContextLink, resultMaxAge);
92
+ }
93
+ })()
94
+ ]);
95
+ return;
96
+ function getResultMaxAge() {
97
+ if (typeof options?.pageContext === 'number') {
98
+ return options.pageContext;
99
+ }
100
+ else {
101
+ // If user calls prefetch() before hydration finished => await the pageContext to be set
102
+ const pageContextCurrent = getPageContextCurrent();
103
+ // TODO/pageContext-prefetch: remove this dirty hack for @brillout/docpress and, instead, use Vike's default if pageContextCurrent isn't defined yet.
104
+ if (!pageContextCurrent)
105
+ return Infinity;
106
+ const prefetchSettings = getPrefetchSettings(pageContextCurrent, null);
107
+ const resultMaxAge = typeof prefetchSettings.pageContext === 'number' ? prefetchSettings.pageContext : PAGE_CONTEXT_MAX_AGE_DEFAULT;
108
+ return resultMaxAge;
109
+ }
110
+ }
111
+ }
112
+ // Lazy execution logic copied from: https://github.com/withastro/astro/blob/2594eb088d53a98181ac820243bcb1a765856ecf/packages/astro/src/runtime/client/dev-toolbar/apps/audit/index.ts#L53-L72
113
+ function addLinkPrefetchHandlers() {
114
+ if (globalObject.addLinkPrefetchHandlers_debounce)
115
+ clearTimeout(globalObject.addLinkPrefetchHandlers_debounce);
116
+ globalObject.addLinkPrefetchHandlers_debounce = setTimeout(() => {
117
+ // Wait for the next idle period, as it is less likely to interfere with any other work the browser is doing post-mutation.
118
+ if ('requestIdleCallback' in window) {
119
+ requestIdleCallback(addLinkPrefetchHandlers_apply, { timeout: 300 });
120
+ }
121
+ else {
122
+ // Fallback for old versions of Safari, we'll assume that things are less likely to be busy after 150ms.
123
+ setTimeout(addLinkPrefetchHandlers_apply, 150);
124
+ }
125
+ }, 250);
126
+ }
127
+ function initLinkPrefetchHandlers() {
128
+ addLinkPrefetchHandlers();
129
+ }
130
+ function addLinkPrefetchHandlers_watch() {
131
+ // Notes about performance:
132
+ // - https://stackoverflow.com/questions/31659567/performance-of-mutationobserver-to-detect-nodes-in-entire-dom/39332340#39332340
133
+ // - https://news.ycombinator.com/item?id=15274211
134
+ // - https://github.com/kubetail-org/sentineljs
135
+ // - https://stackoverflow.com/questions/55046093/listening-for-changes-in-htmlcollection-or-achieving-a-similar-effect
136
+ globalObject.mutationObserver.observe(document.body, {
137
+ childList: true,
138
+ subtree: true
139
+ });
140
+ }
141
+ function addLinkPrefetchHandlers_unwatch() {
142
+ globalObject.mutationObserver.disconnect();
63
143
  }
64
- function addLinkPrefetchHandlers(pageContext) {
65
- // Current URL is already prefetched
66
- markAsAlreadyPrefetched(pageContext.urlPathname);
67
- const linkTags = [...document.getElementsByTagName('A')];
68
- linkTags.forEach((linkTag) => {
144
+ function addLinkPrefetchHandlers_apply() {
145
+ for (let linkTag of globalObject.linkTags) {
69
146
  if (globalObject.linkPrefetchHandlerAdded.has(linkTag))
70
- return;
71
- globalObject.linkPrefetchHandlerAdded.set(linkTag, true);
72
- const url = linkTag.getAttribute('href');
147
+ continue;
148
+ globalObject.linkPrefetchHandlerAdded.add(linkTag);
73
149
  if (skipLink(linkTag))
74
150
  return;
75
- assert(url);
76
- if (isAlreadyPrefetched(url))
77
- return;
78
- const { prefetchStaticAssets } = getPrefetchSettings(pageContext, linkTag);
79
- if (!prefetchStaticAssets)
80
- return;
81
- if (prefetchStaticAssets === 'hover') {
82
- linkTag.addEventListener('mouseover', () => {
83
- prefetchIfPossible(url);
151
+ linkTag.addEventListener('mouseover', () => {
152
+ prefetchOnEvent(linkTag, 'hover');
153
+ }, { passive: true });
154
+ linkTag.addEventListener('touchstart', () => {
155
+ prefetchOnEvent(linkTag, 'hover');
156
+ }, { passive: true });
157
+ const observer = new IntersectionObserver((entries) => {
158
+ entries.forEach((entry) => {
159
+ if (entry.isIntersecting) {
160
+ prefetchOnEvent(linkTag, 'viewport');
161
+ observer.disconnect();
162
+ }
84
163
  });
85
- linkTag.addEventListener('touchstart', () => {
86
- prefetchIfPossible(url);
87
- }, { passive: true });
88
- }
89
- if (prefetchStaticAssets === 'viewport') {
90
- const observer = new IntersectionObserver((entries) => {
91
- entries.forEach((entry) => {
92
- if (entry.isIntersecting) {
93
- prefetchIfPossible(url);
94
- observer.disconnect();
95
- }
96
- });
97
- });
98
- observer.observe(linkTag);
99
- }
100
- });
164
+ });
165
+ observer.observe(linkTag);
166
+ }
167
+ }
168
+ async function prefetchOnEvent(linkTag, event) {
169
+ let prefetchSettings;
170
+ const pageContextCurrent = getPageContextCurrent();
171
+ if (pageContextCurrent) {
172
+ prefetchSettings = getPrefetchSettings(pageContextCurrent, linkTag);
173
+ }
174
+ else {
175
+ // TODO/pageContext-prefetch: remove this dirty hack for @brillout/docpress and, instead, use Vike's default if pageContextCurrent isn't defined yet.
176
+ prefetchSettings = { staticAssets: 'hover', pageContext: Infinity };
177
+ }
178
+ const urlOfLink = linkTag.getAttribute('href');
179
+ assert(urlOfLink);
180
+ const pageContextLink = await getPageContextLink(urlOfLink);
181
+ if (!pageContextLink?.pageId)
182
+ return;
183
+ assert(hasProp(pageContextLink, 'pageId', 'string')); // help TypeScript
184
+ if (!(await isClientSideRoutable(pageContextLink.pageId, pageContextLink)))
185
+ return;
186
+ await Promise.all([
187
+ (async () => {
188
+ if (prefetchSettings.staticAssets === event) {
189
+ await prefetchAssets(pageContextLink);
190
+ }
191
+ })(),
192
+ (async () => {
193
+ if (event !== 'viewport' && prefetchSettings.pageContext) {
194
+ const found = globalObject.prefetchedPageContexts[urlOfLink];
195
+ if (!found || isExpired(found)) {
196
+ // TODO/pageContext-prefetch: move this logic in getPrefetchSettings()
197
+ const resultMaxAge = prefetchSettings.pageContext;
198
+ await prefetchPageContextFromServerHooks(pageContextLink, resultMaxAge);
199
+ }
200
+ }
201
+ })()
202
+ ]);
203
+ }
204
+ function isExpired(found) {
205
+ return Date.now() - found.resultFetchedAt > found.resultMaxAge;
101
206
  }
102
- async function prefetchIfPossible(url) {
103
- const pageContext = await createPageContext(url);
207
+ // TODO/next-major-release: make it sync
208
+ async function getPageContextLink(urlOfLink) {
209
+ const pageContextLink = await createPageContext(urlOfLink);
104
210
  let pageContextFromRoute;
105
211
  try {
106
- pageContextFromRoute = await route(pageContext);
212
+ pageContextFromRoute = await route(pageContextLink);
107
213
  }
108
214
  catch {
109
215
  // If a route() hook has a bug or `throw render()` / `throw redirect()`
110
- return;
216
+ return null;
111
217
  }
112
- if (!pageContextFromRoute?._pageId)
113
- return;
114
- if (!(await isClientSideRoutable(pageContextFromRoute._pageId, pageContext)))
115
- return;
116
- await prefetchAssets(pageContextFromRoute._pageId, pageContext);
218
+ objectAssign(pageContextLink, pageContextFromRoute);
219
+ return pageContextLink;
117
220
  }
@@ -2,10 +2,10 @@ export { renderPageClientSide };
2
2
  export { getRenderCount };
3
3
  export { disableClientRouting };
4
4
  export { firstRenderStartPromise };
5
- import { assert, getCurrentUrl, isSameErrorMessage, objectAssign, redirectHard, getGlobalObject, executeHook, hasProp, augmentType, genPromise } from './utils.js';
6
- import { getPageContextFromHooks_isHydration, getPageContextFromHooks_isNotHydration, getPageContextFromHooks_serialized } from './getPageContextFromHooks.js';
5
+ import { assert, getCurrentUrl, isSameErrorMessage, objectAssign, redirectHard, getGlobalObject, executeHook, hasProp, augmentType, genPromise, isCallable } from './utils.js';
6
+ import { getPageContextFromClientHooks, getPageContextFromServerHooks, getPageContextFromHooks_isHydration, getPageContextFromHooks_serialized, setPageContextInitIsPassedToClient } from './getPageContextFromHooks.js';
7
7
  import { createPageContext } from './createPageContext.js';
8
- import { addLinkPrefetchHandlers } from './prefetch.js';
8
+ import { addLinkPrefetchHandlers, addLinkPrefetchHandlers_unwatch, addLinkPrefetchHandlers_watch, getPageContextPrefetched } from './prefetch.js';
9
9
  import { assertInfo, assertWarning, isReact } from './utils.js';
10
10
  import { executeOnRenderClientHook } from '../shared/executeOnRenderClientHook.js';
11
11
  import { assertHook, getHook } from '../../shared/hooks/getHook.js';
@@ -18,6 +18,8 @@ import { setScrollPosition } from './setScrollPosition.js';
18
18
  import { updateState } from './onBrowserHistoryNavigation.js';
19
19
  import { browserNativeScrollRestoration_disable, setInitialRenderIsDone } from './scrollRestoration.js';
20
20
  import { getErrorPageId } from '../../shared/error-page.js';
21
+ import { setPageContextCurrent } from './getPageContextCurrent.js';
22
+ import { getRouteStringParameterList } from '../../shared/route/resolveRouteString.js';
21
23
  const globalObject = getGlobalObject('renderPageClientSide.ts', (() => {
22
24
  const { promise: firstRenderStartPromise, resolve: firstRenderStartPromiseResolve } = genPromise();
23
25
  return {
@@ -28,8 +30,10 @@ const globalObject = getGlobalObject('renderPageClientSide.ts', (() => {
28
30
  })());
29
31
  const { firstRenderStartPromise } = globalObject;
30
32
  async function renderPageClientSide(renderArgs) {
31
- const { scrollTarget, urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, pageContextsFromRewrite = [], redirectCount = 0, isUserLandPushStateNavigation, isClientSideNavigation = true } = renderArgs;
33
+ const { urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, pageContextsFromRewrite = [], redirectCount = 0, isUserLandPushStateNavigation, isClientSideNavigation = true } = renderArgs;
34
+ let { scrollTarget } = renderArgs;
32
35
  const { previousPageContext } = globalObject;
36
+ addLinkPrefetchHandlers_unwatch();
33
37
  const { isRenderOutdated, setHydrationCanBeAborted, isFirstRender } = getIsRenderOutdated();
34
38
  // Note that pageContext.isHydration isn't equivalent to isFirstRender
35
39
  // - Thus pageContext.isHydration isn't equivalent to !pageContext.isClientSideNavigation
@@ -93,7 +97,7 @@ async function renderPageClientSide(renderArgs) {
93
97
  }
94
98
  if (isRenderOutdated())
95
99
  return;
96
- if (!pageContextFromRoute._pageId) {
100
+ if (!pageContextFromRoute.pageId) {
97
101
  /*
98
102
  // We don't use the client router to render the 404 page:
99
103
  // - So that the +redirects setting (https://vike.dev/redirects) can be applied.
@@ -106,17 +110,17 @@ async function renderPageClientSide(renderArgs) {
106
110
  redirectHard(urlOriginal);
107
111
  return;
108
112
  }
109
- assert(hasProp(pageContextFromRoute, '_pageId', 'string')); // Help TS
110
- const isClientRoutable = await isClientSideRoutable(pageContextFromRoute._pageId, pageContext);
113
+ assert(hasProp(pageContextFromRoute, 'pageId', 'string')); // Help TS
114
+ const isClientRoutable = await isClientSideRoutable(pageContextFromRoute.pageId, pageContext);
111
115
  if (isRenderOutdated())
112
116
  return;
113
117
  if (!isClientRoutable) {
114
118
  redirectHard(urlOriginal);
115
119
  return;
116
120
  }
117
- const isSamePage = pageContextFromRoute._pageId &&
118
- previousPageContext?._pageId &&
119
- pageContextFromRoute._pageId === previousPageContext._pageId;
121
+ const isSamePage = pageContextFromRoute.pageId &&
122
+ previousPageContext?.pageId &&
123
+ pageContextFromRoute.pageId === previousPageContext.pageId;
120
124
  if (isUserLandPushStateNavigation && isSamePage) {
121
125
  // Skip's Vike's rendering; let the user handle the navigation
122
126
  return;
@@ -126,7 +130,7 @@ async function renderPageClientSide(renderArgs) {
126
130
  assert(!('urlOriginal' in pageContextRouted));
127
131
  objectAssign(pageContext, pageContextRouted);
128
132
  try {
129
- objectAssign(pageContext, await loadUserFilesClientSide(pageContext._pageId, pageContext._pageFilesAll, pageContext._pageConfigs));
133
+ objectAssign(pageContext, await loadUserFilesClientSide(pageContext.pageId, pageContext._pageFilesAll, pageContext._pageConfigs));
130
134
  }
131
135
  catch (err) {
132
136
  if (handleErrorFetchingStaticAssets(err, pageContext, isFirstRender)) {
@@ -147,7 +151,7 @@ async function renderPageClientSide(renderArgs) {
147
151
  else {
148
152
  assertWarning(!isReact(), 'You seem to be using React; we recommend setting hydrationCanBeAborted to true, see https://vike.dev/hydrationCanBeAborted', { onlyOnce: true });
149
153
  }
150
- // There wasn't any `await` but result may change because we just called setHydrationCanBeAborted()
154
+ // There wasn't any `await` but the isRenderOutdated() return value may have changed because we called setHydrationCanBeAborted()
151
155
  if (isRenderOutdated())
152
156
  return;
153
157
  // Get pageContext from hooks (fetched from server, and/or directly called on the client-side)
@@ -168,9 +172,33 @@ async function renderPageClientSide(renderArgs) {
168
172
  await renderPageView(pageContext);
169
173
  }
170
174
  else {
171
- let res;
175
+ // Fetch pageContext from server-side hooks
176
+ let pageContextFromServerHooks;
177
+ const pageContextPrefetched = getPageContextPrefetched(pageContext);
178
+ if (pageContextPrefetched) {
179
+ pageContextFromServerHooks = pageContextPrefetched;
180
+ }
181
+ else {
182
+ try {
183
+ const result = await getPageContextFromServerHooks(pageContext, false);
184
+ if (result.is404ServerSideRouted)
185
+ return;
186
+ pageContextFromServerHooks = result.pageContextFromServerHooks;
187
+ }
188
+ catch (err) {
189
+ await onError(err);
190
+ return;
191
+ }
192
+ }
193
+ if (isRenderOutdated())
194
+ return;
195
+ // TODO/eventually: create helper assertPageContextFromHook()
196
+ assert(!('urlOriginal' in pageContextFromServerHooks));
197
+ objectAssign(pageContext, pageContextFromServerHooks);
198
+ // Get pageContext from client-side hooks
199
+ let pageContextFromClientHooks;
172
200
  try {
173
- res = await getPageContextFromHooks_isNotHydration(pageContext, false);
201
+ pageContextFromClientHooks = await getPageContextFromClientHooks(pageContext, false);
174
202
  }
175
203
  catch (err) {
176
204
  await onError(err);
@@ -178,10 +206,7 @@ async function renderPageClientSide(renderArgs) {
178
206
  }
179
207
  if (isRenderOutdated())
180
208
  return;
181
- if ('is404ServerSideRouted' in res)
182
- return;
183
- augmentType(pageContext, res.pageContextAugmented);
184
- // Render page view
209
+ augmentType(pageContext, pageContextFromClientHooks);
185
210
  await renderPageView(pageContext);
186
211
  }
187
212
  }
@@ -284,9 +309,9 @@ async function renderPageClientSide(renderArgs) {
284
309
  if (!errorPageId)
285
310
  throw new Error('No error page defined.');
286
311
  objectAssign(pageContext, {
287
- _pageId: errorPageId
312
+ pageId: errorPageId
288
313
  });
289
- const isClientRoutable = await isClientSideRoutable(pageContext._pageId, pageContext);
314
+ const isClientRoutable = await isClientSideRoutable(pageContext.pageId, pageContext);
290
315
  if (isRenderOutdated())
291
316
  return;
292
317
  if (!isClientRoutable) {
@@ -294,7 +319,7 @@ async function renderPageClientSide(renderArgs) {
294
319
  return;
295
320
  }
296
321
  try {
297
- objectAssign(pageContext, await loadUserFilesClientSide(pageContext._pageId, pageContext._pageFilesAll, pageContext._pageConfigs));
322
+ objectAssign(pageContext, await loadUserFilesClientSide(pageContext.pageId, pageContext._pageFilesAll, pageContext._pageConfigs));
298
323
  }
299
324
  catch (err) {
300
325
  if (handleErrorFetchingStaticAssets(err, pageContext, isFirstRender)) {
@@ -308,21 +333,34 @@ async function renderPageClientSide(renderArgs) {
308
333
  }
309
334
  if (isRenderOutdated())
310
335
  return;
311
- let res;
336
+ let pageContextFromServerHooks;
312
337
  try {
313
- res = await getPageContextFromHooks_isNotHydration(pageContext, true);
338
+ const result = await getPageContextFromServerHooks(pageContext, true);
339
+ if (result.is404ServerSideRouted)
340
+ return;
341
+ pageContextFromServerHooks = result.pageContextFromServerHooks;
314
342
  }
315
343
  catch (err) {
316
- // - When user hasn't defined a `_error.page.js` file
317
- // - Some Vike unpexected internal error
318
344
  onError(err);
319
345
  return;
320
346
  }
321
347
  if (isRenderOutdated())
322
348
  return;
323
- if ('is404ServerSideRouted' in res)
349
+ // TODO/eventually: create helper assertPageContextFromHook()
350
+ assert(!('urlOriginal' in pageContextFromServerHooks));
351
+ objectAssign(pageContext, pageContextFromServerHooks);
352
+ let pageContextFromClientHooks;
353
+ try {
354
+ pageContextFromClientHooks = await getPageContextFromClientHooks(pageContext, true);
355
+ }
356
+ catch (err) {
357
+ onError(err);
324
358
  return;
325
- augmentType(pageContext, res.pageContextAugmented);
359
+ }
360
+ if (isRenderOutdated())
361
+ return;
362
+ augmentType(pageContext, pageContextFromClientHooks);
363
+ objectAssign(pageContext, { routeParams: {} });
326
364
  await renderPageView(pageContext, args);
327
365
  }
328
366
  async function renderPageView(pageContext, isErrorPage) {
@@ -369,7 +407,7 @@ async function renderPageClientSide(renderArgs) {
369
407
  /* We don't abort in order to ensure that onHydrationEnd() is called: we abort only after onHydrationEnd() is called.
370
408
  if (isRenderOutdated(true)) return
371
409
  */
372
- addLinkPrefetchHandlers(pageContext);
410
+ setPageContextCurrent(pageContext);
373
411
  // onHydrationEnd()
374
412
  if (isFirstRender && !onRenderClientError) {
375
413
  assertHook(pageContext, 'onHydrationEnd');
@@ -411,10 +449,24 @@ async function renderPageClientSide(renderArgs) {
411
449
  return;
412
450
  }
413
451
  }
452
+ if (!scrollTarget && previousPageContext) {
453
+ const keepScrollPositionPrev = getKeepScrollPositionSetting(previousPageContext);
454
+ const keepScrollPositionNext = getKeepScrollPositionSetting(pageContext);
455
+ if (keepScrollPositionNext !== false &&
456
+ keepScrollPositionPrev !== false &&
457
+ areKeysEqual(keepScrollPositionNext, keepScrollPositionPrev)) {
458
+ scrollTarget = { preserveScroll: true };
459
+ }
460
+ }
414
461
  // Page scrolling
415
462
  setScrollPosition(scrollTarget);
416
463
  browserNativeScrollRestoration_disable();
417
464
  setInitialRenderIsDone();
465
+ if (pageContext._hasPageContextFromServer)
466
+ setPageContextInitIsPassedToClient(pageContext);
467
+ // Add link prefetch handlers
468
+ addLinkPrefetchHandlers_watch();
469
+ addLinkPrefetchHandlers();
418
470
  }
419
471
  }
420
472
  function changeUrl(url, overwriteLastHistoryEntry) {
@@ -486,3 +538,38 @@ function getIsRenderOutdated() {
486
538
  function getRenderCount() {
487
539
  return globalObject.renderCounter;
488
540
  }
541
+ function getKeepScrollPositionSetting(pageContext) {
542
+ const c = pageContext.from.configsStandard.keepScrollPosition;
543
+ if (!c)
544
+ return false;
545
+ let val = c.value;
546
+ const configDefinedAt = c.definedAt;
547
+ assert(configDefinedAt);
548
+ const routeParameterList = getRouteStringParameterList(configDefinedAt);
549
+ if (isCallable(val))
550
+ val = val(pageContext, {
551
+ configDefinedAt: c.definedAt
552
+ /* We don't pass routeParameterList because it's useless: the user knows the parameter list.
553
+ routeParameterList
554
+ */
555
+ });
556
+ if (val === true) {
557
+ return [
558
+ configDefinedAt,
559
+ ...routeParameterList.map((param) => {
560
+ const val = pageContext.routeParams[param];
561
+ assert(val);
562
+ return val;
563
+ })
564
+ ];
565
+ }
566
+ // We skip validation and type-cast instead of assertUsage() in order to save client-side KBs
567
+ return val;
568
+ }
569
+ function areKeysEqual(key1, key2) {
570
+ if (key1 === key2)
571
+ return true;
572
+ if (!Array.isArray(key1) || !Array.isArray(key2))
573
+ return false;
574
+ return key1.length === key2.length && key1.every((_, i) => key1[i] === key2[i]);
575
+ }
@@ -1,6 +1,6 @@
1
1
  export { getPageContext };
2
2
  declare function getPageContext(): Promise<{
3
- _pageId: string;
3
+ pageId: string;
4
4
  routeParams: Record<string, string>;
5
5
  } & {
6
6
  isHydration: true;
@@ -12,7 +12,7 @@ async function getPageContext() {
12
12
  _hasPageContextFromServer: true,
13
13
  _hasPageContextFromClient: false
14
14
  });
15
- objectAssign(pageContext, await loadPageUserFiles(pageContext._pageId));
15
+ objectAssign(pageContext, await loadPageUserFiles(pageContext.pageId));
16
16
  assertPristineUrl();
17
17
  return pageContext;
18
18
  }
@@ -7,7 +7,7 @@ type PageContextBeforeRenderClient = {
7
7
  _pageFilesLoaded: PageFile[];
8
8
  urlOriginal?: string;
9
9
  urlPathname?: string;
10
- _pageId: string;
10
+ pageId: string;
11
11
  _pageConfigs: PageConfigRuntime[];
12
12
  } & PageContextExports & PageContextForUserConsumptionClientSide;
13
13
  declare function executeOnRenderClientHook<PC extends PageContextBeforeRenderClient>(pageContext: PC, isClientRouting: boolean): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  export { getPageContextSerializedInHtml };
2
2
  declare function getPageContextSerializedInHtml(): {
3
- _pageId: string;
3
+ pageId: string;
4
4
  routeParams: Record<string, string>;
5
5
  };
@@ -14,7 +14,7 @@ function getPageContextSerializedInHtml() {
14
14
  const pageContextJson = elem.textContent;
15
15
  assert(pageContextJson);
16
16
  const pageContextSerializedInHtml = parse(pageContextJson);
17
- assert(hasProp(pageContextSerializedInHtml, '_pageId', 'string'));
17
+ assert(hasProp(pageContextSerializedInHtml, 'pageId', 'string'));
18
18
  assert(hasProp(pageContextSerializedInHtml, 'routeParams', 'string{}'));
19
19
  return pageContextSerializedInHtml;
20
20
  }
@@ -4,7 +4,7 @@ import type { PageContextExports } from '../../shared/getPageFiles.js';
4
4
  import type { PageConfigRuntime } from '../../shared/page-configs/PageConfig.js';
5
5
  import { PageContextForPassToClientWarning } from './getPageContextProxyForUser.js';
6
6
  type PageContextForUserConsumptionClientSide = PageContextExports & PageContextForPassToClientWarning & {
7
- _pageId: string;
7
+ pageId: string;
8
8
  _pageConfigs: PageConfigRuntime[];
9
9
  };
10
10
  declare function preparePageContextForUserConsumptionClientSide<T extends PageContextForUserConsumptionClientSide>(pageContext: T, isClientRouting: boolean): T & {