vike 0.4.197 → 0.4.198-commit-05a4973

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 (63) hide show
  1. package/dist/cjs/client/client-routing-runtime/prefetch/PrefetchSetting.js +2 -0
  2. package/dist/cjs/node/plugin/plugins/fileEnv.js +3 -0
  3. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +14 -1
  4. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/loadFileAtConfigTime.js +2 -2
  5. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/resolvePointerImport.js +26 -23
  6. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/{transformFileImports.js → transformPointerImports.js} +3 -4
  7. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/transpileAndExecuteFile.js +44 -47
  8. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +16 -7
  9. package/dist/cjs/node/plugin/shared/loggerNotProd.js +1 -1
  10. package/dist/cjs/shared/page-configs/serialize/serializeConfigValues.js +2 -2
  11. package/dist/cjs/utils/PROJECT_VERSION.js +1 -1
  12. package/dist/cjs/utils/isNpmPackage.js +4 -0
  13. package/dist/cjs/utils/isScriptFile.js +3 -3
  14. package/dist/cjs/utils/parseUrl.js +2 -10
  15. package/dist/esm/client/client-routing-runtime/entry.js +2 -2
  16. package/dist/esm/client/client-routing-runtime/getPageContextCurrent.d.ts +8 -0
  17. package/dist/esm/client/client-routing-runtime/getPageContextCurrent.js +13 -0
  18. package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.d.ts +26 -16
  19. package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.js +34 -30
  20. package/dist/esm/client/client-routing-runtime/history.js +1 -1
  21. package/dist/esm/client/client-routing-runtime/initClientRouter.d.ts +2 -0
  22. package/dist/esm/client/client-routing-runtime/{installClientRouter.js → initClientRouter.js} +11 -8
  23. package/dist/esm/client/client-routing-runtime/initOnLinkClick.d.ts +2 -0
  24. package/dist/esm/client/client-routing-runtime/{onLinkClick.js → initOnLinkClick.js} +2 -2
  25. package/dist/esm/client/client-routing-runtime/isClientSideRoutable.js +1 -0
  26. package/dist/esm/client/client-routing-runtime/navigate.js +3 -2
  27. package/dist/esm/client/client-routing-runtime/normalizeUrlArgument.d.ts +2 -0
  28. package/dist/esm/client/client-routing-runtime/normalizeUrlArgument.js +14 -0
  29. package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.js +1 -1
  30. package/dist/esm/client/client-routing-runtime/prefetch/PrefetchSetting.d.ts +7 -0
  31. package/dist/esm/client/client-routing-runtime/prefetch/PrefetchSetting.js +1 -0
  32. package/dist/esm/client/client-routing-runtime/prefetch/getPrefetchSettings.d.ts +8 -7
  33. package/dist/esm/client/client-routing-runtime/prefetch/getPrefetchSettings.js +75 -67
  34. package/dist/esm/client/client-routing-runtime/prefetch.d.ts +29 -5
  35. package/dist/esm/client/client-routing-runtime/prefetch.js +196 -68
  36. package/dist/esm/client/client-routing-runtime/renderPageClientSide.js +71 -28
  37. package/dist/esm/node/plugin/plugins/fileEnv.js +3 -0
  38. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +14 -1
  39. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/loadFileAtConfigTime.js +1 -1
  40. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/resolvePointerImport.d.ts +1 -1
  41. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/resolvePointerImport.js +25 -22
  42. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/{transformFileImports.d.ts → transformPointerImports.d.ts} +2 -2
  43. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/{transformFileImports.js → transformPointerImports.js} +3 -4
  44. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/transpileAndExecuteFile.js +45 -48
  45. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +16 -7
  46. package/dist/esm/node/plugin/shared/loggerNotProd.js +1 -1
  47. package/dist/esm/shared/page-configs/Config.d.ts +10 -1
  48. package/dist/esm/shared/page-configs/serialize/serializeConfigValues.js +1 -1
  49. package/dist/esm/shared/types.d.ts +1 -1
  50. package/dist/esm/utils/PROJECT_VERSION.d.ts +1 -1
  51. package/dist/esm/utils/PROJECT_VERSION.js +1 -1
  52. package/dist/esm/utils/isNpmPackage.d.ts +2 -0
  53. package/dist/esm/utils/isNpmPackage.js +4 -0
  54. package/dist/esm/utils/isScriptFile.d.ts +2 -2
  55. package/dist/esm/utils/isScriptFile.js +3 -3
  56. package/dist/esm/utils/parseUrl.d.ts +2 -2
  57. package/dist/esm/utils/parseUrl.js +2 -10
  58. package/dist/esm/utils/projectInfo.d.ts +1 -1
  59. package/package.json +1 -1
  60. package/dist/esm/client/client-routing-runtime/installClientRouter.d.ts +0 -2
  61. package/dist/esm/client/client-routing-runtime/onLinkClick.d.ts +0 -2
  62. package/dist/esm/client/client-routing-runtime/prefetch/alreadyPrefetched.d.ts +0 -4
  63. package/dist/esm/client/client-routing-runtime/prefetch/alreadyPrefetched.js +0 -16
@@ -1,21 +1,51 @@
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, 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';
20
+ import { normalizeUrlArgument } from './normalizeUrlArgument.js';
14
21
  assertClientRouting();
15
- const globalObject = getGlobalObject('prefetch.ts', { linkPrefetchHandlerAdded: new WeakMap() });
16
- async function prefetchAssets(pageId, pageContext) {
22
+ const globalObject = getGlobalObject('prefetch.ts', {
23
+ linkPrefetchHandlerAdded: new WeakSet(),
24
+ addLinkPrefetchHandlers_debounce: null,
25
+ mutationObserver: new MutationObserver(addLinkPrefetchHandlers),
26
+ // `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.)
27
+ linkTags: document.getElementsByTagName('A'),
28
+ prefetchedPageContexts: {}
29
+ });
30
+ function getPageContextPrefetched(pageContext) {
31
+ const prefetchSettings = getPrefetchSettings(pageContext, null);
32
+ // TODO/pageContext-prefetch: I guess we need linkTag to make this condition work
33
+ if (!prefetchSettings.pageContext)
34
+ return null;
35
+ const key = getCacheKey(pageContext.urlPathname);
36
+ const found = globalObject.prefetchedPageContexts[key];
37
+ if (!found || found.result.is404ServerSideRouted || isExpired(found))
38
+ return null;
39
+ const pageContextPrefetched = found.result.pageContextFromServerHooks;
40
+ /* TODO/pageContext-prefetch: make it work for when resultMaxAge is Infinity.
41
+ // We discard the prefetched pageContext whenever we use it, so that the user always sees fresh data upon naivgating.
42
+ delete globalObject.prefetchedPageContexts[key]
43
+ */
44
+ return pageContextPrefetched;
45
+ }
46
+ async function prefetchAssets(pageContextLink) {
17
47
  try {
18
- await loadUserFilesClientSide(pageId, pageContext._pageFilesAll, pageContext._pageConfigs);
48
+ await loadUserFilesClientSide(pageContextLink.pageId, pageContextLink._pageFilesAll, pageContextLink._pageConfigs);
19
49
  }
20
50
  catch (err) {
21
51
  if (isErrorFetchingStaticAssets(err)) {
@@ -26,6 +56,36 @@ async function prefetchAssets(pageId, pageContext) {
26
56
  }
27
57
  }
28
58
  }
59
+ async function prefetchPageContextFromServerHooks(pageContextLink, resultMaxAge) {
60
+ const result = await getPageContextFromServerHooks(pageContextLink, false);
61
+ setPageContextPrefetchCache(pageContextLink, result, resultMaxAge);
62
+ }
63
+ function populatePageContextPrefetchCache(pageContext /*& PageContextExports*/, result) {
64
+ // TODO/pageContext-prefetch: replace with using pageContext.config.prerender instead. (For being able to do that: eager configs need to be accessible without have to use PageContextExports as it isn't available here.)
65
+ if (!isBrilloutDocpress())
66
+ return;
67
+ setPageContextPrefetchCache(pageContext, result, null);
68
+ }
69
+ function setPageContextPrefetchCache(pageContext, result, resultMaxAge) {
70
+ if (resultMaxAge === null)
71
+ resultMaxAge = getResultMaxAge();
72
+ const key = getCacheKey(pageContext.urlPathname);
73
+ assert(isBrilloutDocpress()); // Ensure this API isn't used by anyone else
74
+ globalObject.prefetchedPageContexts[key] = {
75
+ resultFetchedAt: Date.now(),
76
+ resultMaxAge,
77
+ result
78
+ };
79
+ }
80
+ function getResultMaxAge() {
81
+ const pageContextCurrent = getPageContextCurrent();
82
+ // TODO/pageContext-prefetch: remove this dirty hack used by @brillout/docpress and, instead, use Vike's default if pageContextCurrent isn't defined yet.
83
+ if (!pageContextCurrent)
84
+ return Infinity;
85
+ const prefetchSettings = getPrefetchSettings(pageContextCurrent, null);
86
+ const resultMaxAge = typeof prefetchSettings.pageContext === 'number' ? prefetchSettings.pageContext : PAGE_CONTEXT_MAX_AGE_DEFAULT;
87
+ return resultMaxAge;
88
+ }
29
89
  /**
30
90
  * Programmatically prefetch client assets.
31
91
  *
@@ -33,85 +93,153 @@ async function prefetchAssets(pageId, pageContext) {
33
93
  *
34
94
  * @param url - The URL of the page you want to prefetch.
35
95
  */
36
- async function prefetch(url) {
96
+ async function prefetch(url, options) {
37
97
  assertUsage(checkIfClientRouting(), 'prefetch() only works with Client Routing, see https://vike.dev/prefetch', {
38
98
  showStackTrace: true
39
99
  });
40
- const errPrefix = '[prefetch(url)] url';
41
- 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) {
56
- assertWarning(false, `${errPrefix} ${pc.string(url)} ${noRouteMatch}`, {
100
+ url = normalizeUrlArgument(url, 'prefetch');
101
+ const pageContextLink = await getPageContextLink(url);
102
+ if (!pageContextLink?.pageId) {
103
+ assertWarning(false, `[prefetch(url)] ${pc.string(url)} ${noRouteMatch}`, {
57
104
  showStackTrace: true,
58
105
  onlyOnce: false
59
106
  });
60
107
  return;
61
108
  }
62
- await prefetchAssets(pageId, pageContext);
109
+ assert(hasProp(pageContextLink, 'pageId', 'string')); // help TypeScript
110
+ await Promise.all([
111
+ (async () => {
112
+ if (options?.staticAssets !== false) {
113
+ await prefetchAssets(pageContextLink);
114
+ }
115
+ })(),
116
+ (async () => {
117
+ if (options?.pageContext !== false) {
118
+ const resultMaxAge = typeof options?.pageContext === 'number' ? options.pageContext : null;
119
+ await prefetchPageContextFromServerHooks(pageContextLink, resultMaxAge);
120
+ }
121
+ })()
122
+ ]);
63
123
  }
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 });
124
+ // 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
125
+ function addLinkPrefetchHandlers() {
126
+ if (globalObject.addLinkPrefetchHandlers_debounce)
127
+ clearTimeout(globalObject.addLinkPrefetchHandlers_debounce);
128
+ globalObject.addLinkPrefetchHandlers_debounce = setTimeout(() => {
129
+ // Wait for the next idle period, as it is less likely to interfere with any other work the browser is doing post-mutation.
130
+ if ('requestIdleCallback' in window) {
131
+ requestIdleCallback(addLinkPrefetchHandlers_apply, { timeout: 300 });
88
132
  }
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);
133
+ else {
134
+ // Fallback for old versions of Safari, we'll assume that things are less likely to be busy after 150ms.
135
+ setTimeout(addLinkPrefetchHandlers_apply, 150);
99
136
  }
137
+ }, 250);
138
+ }
139
+ function initLinkPrefetchHandlers() {
140
+ addLinkPrefetchHandlers();
141
+ }
142
+ function addLinkPrefetchHandlers_watch() {
143
+ // Notes about performance:
144
+ // - https://stackoverflow.com/questions/31659567/performance-of-mutationobserver-to-detect-nodes-in-entire-dom/39332340#39332340
145
+ // - https://news.ycombinator.com/item?id=15274211
146
+ // - https://github.com/kubetail-org/sentineljs
147
+ // - https://stackoverflow.com/questions/55046093/listening-for-changes-in-htmlcollection-or-achieving-a-similar-effect
148
+ globalObject.mutationObserver.observe(document.body, {
149
+ childList: true,
150
+ subtree: true
100
151
  });
101
152
  }
102
- async function prefetchIfPossible(url) {
103
- const pageContext = await createPageContext(url);
153
+ function addLinkPrefetchHandlers_unwatch() {
154
+ globalObject.mutationObserver.disconnect();
155
+ }
156
+ function addLinkPrefetchHandlers_apply() {
157
+ for (let linkTag of globalObject.linkTags) {
158
+ if (globalObject.linkPrefetchHandlerAdded.has(linkTag))
159
+ continue;
160
+ globalObject.linkPrefetchHandlerAdded.add(linkTag);
161
+ if (skipLink(linkTag))
162
+ continue;
163
+ linkTag.addEventListener('mouseover', () => {
164
+ prefetchOnEvent(linkTag, 'hover');
165
+ }, { passive: true });
166
+ linkTag.addEventListener('touchstart', () => {
167
+ prefetchOnEvent(linkTag, 'hover');
168
+ }, { passive: true });
169
+ const observer = new IntersectionObserver((entries) => {
170
+ entries.forEach((entry) => {
171
+ if (entry.isIntersecting) {
172
+ prefetchOnEvent(linkTag, 'viewport');
173
+ }
174
+ });
175
+ });
176
+ observer.observe(linkTag);
177
+ }
178
+ }
179
+ async function prefetchOnEvent(linkTag, event) {
180
+ let prefetchSettings;
181
+ const pageContextCurrent = getPageContextCurrent();
182
+ if (pageContextCurrent) {
183
+ prefetchSettings = getPrefetchSettings(pageContextCurrent, linkTag);
184
+ }
185
+ else {
186
+ // TODO/pageContext-prefetch: remove this dirty hack used by @brillout/docpress and, instead, use Vike's default if pageContextCurrent isn't defined yet.
187
+ prefetchSettings = { staticAssets: 'hover', pageContext: Infinity };
188
+ }
189
+ // Check again in case DOM was manipulated since last check
190
+ if (skipLink(linkTag))
191
+ return;
192
+ const urlOfLink = linkTag.getAttribute('href');
193
+ const pageContextLink = await getPageContextLink(urlOfLink);
194
+ if (!pageContextLink?.pageId)
195
+ return;
196
+ assert(hasProp(pageContextLink, 'pageId', 'string')); // help TypeScript
197
+ if (!(await isClientSideRoutable(pageContextLink.pageId, pageContextLink)))
198
+ return;
199
+ await Promise.all([
200
+ (async () => {
201
+ if (prefetchSettings.staticAssets === event) {
202
+ await prefetchAssets(pageContextLink);
203
+ }
204
+ })(),
205
+ (async () => {
206
+ if (event !== 'viewport' && prefetchSettings.pageContext) {
207
+ const key = getCacheKey(urlOfLink);
208
+ const found = globalObject.prefetchedPageContexts[key];
209
+ if (!found || isExpired(found)) {
210
+ // TODO/pageContext-prefetch: move this logic in getPrefetchSettings()
211
+ const resultMaxAge = prefetchSettings.pageContext;
212
+ await prefetchPageContextFromServerHooks(pageContextLink, resultMaxAge);
213
+ }
214
+ }
215
+ })()
216
+ ]);
217
+ }
218
+ function isExpired(found) {
219
+ return Date.now() - found.resultFetchedAt > found.resultMaxAge;
220
+ }
221
+ // TODO/next-major-release: make it sync
222
+ async function getPageContextLink(urlOfLink) {
223
+ const pageContextLink = await createPageContext(urlOfLink);
104
224
  let pageContextFromRoute;
105
225
  try {
106
- pageContextFromRoute = await route(pageContext);
226
+ pageContextFromRoute = await route(pageContextLink);
107
227
  }
108
228
  catch {
109
229
  // If a route() hook has a bug or `throw render()` / `throw redirect()`
110
- return;
230
+ return null;
111
231
  }
112
- if (!pageContextFromRoute?.pageId)
113
- return;
114
- if (!(await isClientSideRoutable(pageContextFromRoute.pageId, pageContext)))
115
- return;
116
- await prefetchAssets(pageContextFromRoute.pageId, pageContext);
232
+ objectAssign(pageContextLink, pageContextFromRoute);
233
+ return pageContextLink;
234
+ }
235
+ function getCacheKey(url) {
236
+ if (url.startsWith('#'))
237
+ url = '/';
238
+ assert(url.startsWith('/'), { urlPathname: url });
239
+ const key = url.split('#')[0];
240
+ return key;
241
+ }
242
+ // TODO/pageContext-prefetch: remove
243
+ function isBrilloutDocpress() {
244
+ return '_isBrilloutDocpress' in window;
117
245
  }
@@ -3,9 +3,9 @@ export { getRenderCount };
3
3
  export { disableClientRouting };
4
4
  export { firstRenderStartPromise };
5
5
  import { assert, getCurrentUrl, isSameErrorMessage, objectAssign, redirectHard, getGlobalObject, executeHook, hasProp, augmentType, genPromise, isCallable } from './utils.js';
6
- import { getPageContextFromHooks_isHydration, getPageContextFromHooks_isNotHydration, getPageContextFromHooks_serialized } from './getPageContextFromHooks.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,7 @@ 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';
21
22
  import { getRouteStringParameterList } from '../../shared/route/resolveRouteString.js';
22
23
  const globalObject = getGlobalObject('renderPageClientSide.ts', (() => {
23
24
  const { promise: firstRenderStartPromise, resolve: firstRenderStartPromiseResolve } = genPromise();
@@ -32,11 +33,8 @@ async function renderPageClientSide(renderArgs) {
32
33
  const { urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, pageContextsFromRewrite = [], redirectCount = 0, isUserLandPushStateNavigation, isClientSideNavigation = true } = renderArgs;
33
34
  let { scrollTarget } = renderArgs;
34
35
  const { previousPageContext } = globalObject;
36
+ addLinkPrefetchHandlers_unwatch();
35
37
  const { isRenderOutdated, setHydrationCanBeAborted, isFirstRender } = getIsRenderOutdated();
36
- // Note that pageContext.isHydration isn't equivalent to isFirstRender
37
- // - Thus pageContext.isHydration isn't equivalent to !pageContext.isClientSideNavigation
38
- // - `pageContext.isHydration === !isFirstRender && !isErrorPage`
39
- assert(isClientSideNavigation === !isFirstRender);
40
38
  assertNoInfiniteAbortLoop(pageContextsFromRewrite.length, redirectCount);
41
39
  if (globalObject.clientRoutingIsDisabled) {
42
40
  redirectHard(urlOriginal);
@@ -51,7 +49,7 @@ async function renderPageClientSide(renderArgs) {
51
49
  const onError = async (err) => {
52
50
  await renderErrorPage({ err });
53
51
  };
54
- const pageContext = await getPageContextBegin();
52
+ const pageContext = await getPageContextBegin(false);
55
53
  if (isRenderOutdated())
56
54
  return;
57
55
  // onPageTransitionStart()
@@ -79,10 +77,13 @@ async function renderPageClientSide(renderArgs) {
79
77
  }
80
78
  }
81
79
  // Route
82
- let pageContextRouted;
83
80
  if (isFirstRender) {
84
81
  const pageContextSerialized = getPageContextFromHooks_serialized();
85
- pageContextRouted = pageContextSerialized;
82
+ // TODO/eventually: create helper assertPageContextFromHook()
83
+ assert(!('urlOriginal' in pageContextSerialized));
84
+ objectAssign(pageContext, pageContextSerialized);
85
+ // TODO/pageContext-prefetch: remove or change, because this only makes sense for a pre-rendered page
86
+ populatePageContextPrefetchCache(pageContext, { pageContextFromServerHooks: pageContextSerialized });
86
87
  }
87
88
  else {
88
89
  let pageContextFromRoute;
@@ -123,10 +124,10 @@ async function renderPageClientSide(renderArgs) {
123
124
  // Skip's Vike's rendering; let the user handle the navigation
124
125
  return;
125
126
  }
126
- pageContextRouted = pageContextFromRoute;
127
+ // TODO/eventually: create helper assertPageContextFromHook()
128
+ assert(!('urlOriginal' in pageContextFromRoute));
129
+ objectAssign(pageContext, pageContextFromRoute);
127
130
  }
128
- assert(!('urlOriginal' in pageContextRouted));
129
- objectAssign(pageContext, pageContextRouted);
130
131
  try {
131
132
  objectAssign(pageContext, await loadUserFilesClientSide(pageContext.pageId, pageContext._pageFilesAll, pageContext._pageConfigs));
132
133
  }
@@ -142,6 +143,7 @@ async function renderPageClientSide(renderArgs) {
142
143
  }
143
144
  if (isRenderOutdated())
144
145
  return;
146
+ setPageContextCurrent(pageContext);
145
147
  // Set global hydrationCanBeAborted
146
148
  if (pageContext.exports.hydrationCanBeAborted) {
147
149
  setHydrationCanBeAborted();
@@ -149,7 +151,7 @@ async function renderPageClientSide(renderArgs) {
149
151
  else {
150
152
  assertWarning(!isReact(), 'You seem to be using React; we recommend setting hydrationCanBeAborted to true, see https://vike.dev/hydrationCanBeAborted', { onlyOnce: true });
151
153
  }
152
- // 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()
153
155
  if (isRenderOutdated())
154
156
  return;
155
157
  // Get pageContext from hooks (fetched from server, and/or directly called on the client-side)
@@ -170,9 +172,35 @@ async function renderPageClientSide(renderArgs) {
170
172
  await renderPageView(pageContext);
171
173
  }
172
174
  else {
173
- 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
+ // TODO/pageContext-prefetch: remove or change, because this only makes sense for a pre-rendered page
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;
174
202
  try {
175
- res = await getPageContextFromHooks_isNotHydration(pageContext, false);
203
+ pageContextFromClientHooks = await getPageContextFromClientHooks(pageContext, false);
176
204
  }
177
205
  catch (err) {
178
206
  await onError(err);
@@ -180,18 +208,16 @@ async function renderPageClientSide(renderArgs) {
180
208
  }
181
209
  if (isRenderOutdated())
182
210
  return;
183
- if ('is404ServerSideRouted' in res)
184
- return;
185
- augmentType(pageContext, res.pageContextAugmented);
186
- // Render page view
211
+ augmentType(pageContext, pageContextFromClientHooks);
187
212
  await renderPageView(pageContext);
188
213
  }
189
214
  }
190
- async function getPageContextBegin() {
215
+ async function getPageContextBegin(isForErrorPage) {
191
216
  const pageContext = await createPageContext(urlOriginal);
192
217
  objectAssign(pageContext, {
193
218
  isBackwardNavigation,
194
219
  isClientSideNavigation,
220
+ isHydration: isFirstRender && !isForErrorPage,
195
221
  _previousPageContext: previousPageContext
196
222
  });
197
223
  {
@@ -226,7 +252,7 @@ async function renderPageClientSide(renderArgs) {
226
252
  // We handle the abort error down below.
227
253
  }
228
254
  }
229
- const pageContext = await getPageContextBegin();
255
+ const pageContext = await getPageContextBegin(true);
230
256
  if (isRenderOutdated())
231
257
  return;
232
258
  if (args.is404)
@@ -310,21 +336,34 @@ async function renderPageClientSide(renderArgs) {
310
336
  }
311
337
  if (isRenderOutdated())
312
338
  return;
313
- let res;
339
+ setPageContextCurrent(pageContext);
340
+ let pageContextFromServerHooks;
314
341
  try {
315
- res = await getPageContextFromHooks_isNotHydration(pageContext, true);
342
+ const result = await getPageContextFromServerHooks(pageContext, true);
343
+ if (result.is404ServerSideRouted)
344
+ return;
345
+ pageContextFromServerHooks = result.pageContextFromServerHooks;
316
346
  }
317
347
  catch (err) {
318
- // - When user hasn't defined a `_error.page.js` file
319
- // - Some Vike unpexected internal error
320
348
  onError(err);
321
349
  return;
322
350
  }
323
351
  if (isRenderOutdated())
324
352
  return;
325
- if ('is404ServerSideRouted' in res)
353
+ // TODO/eventually: create helper assertPageContextFromHook()
354
+ assert(!('urlOriginal' in pageContextFromServerHooks));
355
+ objectAssign(pageContext, pageContextFromServerHooks);
356
+ let pageContextFromClientHooks;
357
+ try {
358
+ pageContextFromClientHooks = await getPageContextFromClientHooks(pageContext, true);
359
+ }
360
+ catch (err) {
361
+ onError(err);
362
+ return;
363
+ }
364
+ if (isRenderOutdated())
326
365
  return;
327
- augmentType(pageContext, res.pageContextAugmented);
366
+ augmentType(pageContext, pageContextFromClientHooks);
328
367
  objectAssign(pageContext, { routeParams: {} });
329
368
  await renderPageView(pageContext, args);
330
369
  }
@@ -372,7 +411,6 @@ async function renderPageClientSide(renderArgs) {
372
411
  /* We don't abort in order to ensure that onHydrationEnd() is called: we abort only after onHydrationEnd() is called.
373
412
  if (isRenderOutdated(true)) return
374
413
  */
375
- addLinkPrefetchHandlers(pageContext);
376
414
  // onHydrationEnd()
377
415
  if (isFirstRender && !onRenderClientError) {
378
416
  assertHook(pageContext, 'onHydrationEnd');
@@ -427,6 +465,11 @@ async function renderPageClientSide(renderArgs) {
427
465
  setScrollPosition(scrollTarget);
428
466
  browserNativeScrollRestoration_disable();
429
467
  setInitialRenderIsDone();
468
+ if (pageContext._hasPageContextFromServer)
469
+ setPageContextInitIsPassedToClient(pageContext);
470
+ // Add link prefetch handlers
471
+ addLinkPrefetchHandlers_watch();
472
+ addLinkPrefetchHandlers();
430
473
  }
431
474
  }
432
475
  function changeUrl(url, overwriteLastHistoryEntry) {
@@ -17,6 +17,9 @@ function fileEnv() {
17
17
  return;
18
18
  if (skip(id))
19
19
  return;
20
+ // For `.vue` files: https://github.com/vikejs/vike/issues/1912#issuecomment-2394981475
21
+ if (id.endsWith('?direct'))
22
+ id = id.slice(0, -1 * '?direct'.length);
20
23
  const moduleInfo = viteDevServer.moduleGraph.getModuleById(id);
21
24
  assert(moduleInfo);
22
25
  const importers = Array.from(moduleInfo.importers)
@@ -67,6 +67,11 @@ const configDefinitionsBuiltIn = {
67
67
  hydrationCanBeAborted: {
68
68
  env: { client: true }
69
69
  },
70
+ prefetch: {
71
+ env: { client: true },
72
+ eager: true
73
+ },
74
+ // TODO/v1-release: remove
70
75
  prefetchStaticAssets: {
71
76
  env: { client: true }
72
77
  },
@@ -100,10 +105,12 @@ const configDefinitionsBuiltIn = {
100
105
  },
101
106
  onBeforeRenderEnv: {
102
107
  env: { client: true },
108
+ eager: true,
103
109
  _computed: (configValueSources) => !isConfigSet(configValueSources, 'onBeforeRender') ? null : getConfigEnv(configValueSources, 'onBeforeRender')
104
110
  },
105
111
  dataEnv: {
106
112
  env: { client: true },
113
+ eager: true,
107
114
  _computed: (configValueSources) => !isConfigSet(configValueSources, 'data') ? null : getConfigEnv(configValueSources, 'data')
108
115
  },
109
116
  hooksTimeout: {
@@ -149,7 +156,13 @@ function getConfigEnv(configValueSources, configName) {
149
156
  const configValueSource = getConfigValueSource(configValueSources, configName);
150
157
  if (!configValueSource)
151
158
  return null;
152
- return configValueSource.configEnv;
159
+ const { configEnv } = configValueSource;
160
+ const env = {};
161
+ if (configEnv.client)
162
+ env.client = true;
163
+ if (configEnv.server)
164
+ env.server = true;
165
+ return env;
153
166
  }
154
167
  function isConfigSet(configValueSources, configName) {
155
168
  const configValueSource = getConfigValueSource(configValueSources, configName);
@@ -6,7 +6,7 @@ import { assert, assertUsage, hasProp, assertIsNotProductionRuntime } from '../.
6
6
  import { transpileAndExecuteFile } from './transpileAndExecuteFile.js';
7
7
  import { assertPlusFileExport } from '../../../../../../shared/page-configs/assertPlusFileExport.js';
8
8
  import pc from '@brillout/picocolors';
9
- import { parsePointerImportData } from './transformFileImports.js';
9
+ import { parsePointerImportData } from './transformPointerImports.js';
10
10
  import { getConfigFileExport } from '../getConfigFileExport.js';
11
11
  import { resolvePointerImport } from './resolvePointerImport.js';
12
12
  assertIsNotProductionRuntime();
@@ -2,7 +2,7 @@ export { resolvePointerImportOfConfig };
2
2
  export { resolvePointerImport };
3
3
  export { clearFilesEnvMap };
4
4
  import type { ConfigEnvInternal, DefinedAtFilePath } from '../../../../../../shared/page-configs/PageConfig.js';
5
- import { type PointerImportData } from './transformFileImports.js';
5
+ import { type PointerImportData } from './transformPointerImports.js';
6
6
  import type { FilePath, FilePathResolved } from '../../../../../../shared/page-configs/FilePath.js';
7
7
  type PointerImportResolved = DefinedAtFilePath & {
8
8
  fileExportName: string;