vike 0.4.196 → 0.4.197-commit-f4d8658

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