vike 0.4.197 → 0.4.198-commit-081c85f

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 -24
  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,6 +33,7 @@ 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
38
  // Note that pageContext.isHydration isn't equivalent to isFirstRender
37
39
  // - Thus pageContext.isHydration isn't equivalent to !pageContext.isClientSideNavigation
@@ -51,7 +53,7 @@ async function renderPageClientSide(renderArgs) {
51
53
  const onError = async (err) => {
52
54
  await renderErrorPage({ err });
53
55
  };
54
- const pageContext = await getPageContextBegin();
56
+ const pageContext = await getPageContextBegin(false);
55
57
  if (isRenderOutdated())
56
58
  return;
57
59
  // onPageTransitionStart()
@@ -79,10 +81,13 @@ async function renderPageClientSide(renderArgs) {
79
81
  }
80
82
  }
81
83
  // Route
82
- let pageContextRouted;
83
84
  if (isFirstRender) {
84
85
  const pageContextSerialized = getPageContextFromHooks_serialized();
85
- pageContextRouted = pageContextSerialized;
86
+ // TODO/eventually: create helper assertPageContextFromHook()
87
+ assert(!('urlOriginal' in pageContextSerialized));
88
+ objectAssign(pageContext, pageContextSerialized);
89
+ // TODO/pageContext-prefetch: remove or change, because this only makes sense for a pre-rendered page
90
+ populatePageContextPrefetchCache(pageContext, { pageContextFromServerHooks: pageContextSerialized });
86
91
  }
87
92
  else {
88
93
  let pageContextFromRoute;
@@ -123,10 +128,10 @@ async function renderPageClientSide(renderArgs) {
123
128
  // Skip's Vike's rendering; let the user handle the navigation
124
129
  return;
125
130
  }
126
- pageContextRouted = pageContextFromRoute;
131
+ // TODO/eventually: create helper assertPageContextFromHook()
132
+ assert(!('urlOriginal' in pageContextFromRoute));
133
+ objectAssign(pageContext, pageContextFromRoute);
127
134
  }
128
- assert(!('urlOriginal' in pageContextRouted));
129
- objectAssign(pageContext, pageContextRouted);
130
135
  try {
131
136
  objectAssign(pageContext, await loadUserFilesClientSide(pageContext.pageId, pageContext._pageFilesAll, pageContext._pageConfigs));
132
137
  }
@@ -142,6 +147,7 @@ async function renderPageClientSide(renderArgs) {
142
147
  }
143
148
  if (isRenderOutdated())
144
149
  return;
150
+ setPageContextCurrent(pageContext);
145
151
  // Set global hydrationCanBeAborted
146
152
  if (pageContext.exports.hydrationCanBeAborted) {
147
153
  setHydrationCanBeAborted();
@@ -149,7 +155,7 @@ async function renderPageClientSide(renderArgs) {
149
155
  else {
150
156
  assertWarning(!isReact(), 'You seem to be using React; we recommend setting hydrationCanBeAborted to true, see https://vike.dev/hydrationCanBeAborted', { onlyOnce: true });
151
157
  }
152
- // There wasn't any `await` but result may change because we just called setHydrationCanBeAborted()
158
+ // There wasn't any `await` but the isRenderOutdated() return value may have changed because we called setHydrationCanBeAborted()
153
159
  if (isRenderOutdated())
154
160
  return;
155
161
  // Get pageContext from hooks (fetched from server, and/or directly called on the client-side)
@@ -170,9 +176,35 @@ async function renderPageClientSide(renderArgs) {
170
176
  await renderPageView(pageContext);
171
177
  }
172
178
  else {
173
- let res;
179
+ // Fetch pageContext from server-side hooks
180
+ let pageContextFromServerHooks;
181
+ const pageContextPrefetched = getPageContextPrefetched(pageContext);
182
+ if (pageContextPrefetched) {
183
+ pageContextFromServerHooks = pageContextPrefetched;
184
+ }
185
+ else {
186
+ try {
187
+ const result = await getPageContextFromServerHooks(pageContext, false);
188
+ if (result.is404ServerSideRouted)
189
+ return;
190
+ pageContextFromServerHooks = result.pageContextFromServerHooks;
191
+ // TODO/pageContext-prefetch: remove or change, because this only makes sense for a pre-rendered page
192
+ populatePageContextPrefetchCache(pageContext, result);
193
+ }
194
+ catch (err) {
195
+ await onError(err);
196
+ return;
197
+ }
198
+ }
199
+ if (isRenderOutdated())
200
+ return;
201
+ // TODO/eventually: create helper assertPageContextFromHook()
202
+ assert(!('urlOriginal' in pageContextFromServerHooks));
203
+ objectAssign(pageContext, pageContextFromServerHooks);
204
+ // Get pageContext from client-side hooks
205
+ let pageContextFromClientHooks;
174
206
  try {
175
- res = await getPageContextFromHooks_isNotHydration(pageContext, false);
207
+ pageContextFromClientHooks = await getPageContextFromClientHooks(pageContext, false);
176
208
  }
177
209
  catch (err) {
178
210
  await onError(err);
@@ -180,18 +212,16 @@ async function renderPageClientSide(renderArgs) {
180
212
  }
181
213
  if (isRenderOutdated())
182
214
  return;
183
- if ('is404ServerSideRouted' in res)
184
- return;
185
- augmentType(pageContext, res.pageContextAugmented);
186
- // Render page view
215
+ augmentType(pageContext, pageContextFromClientHooks);
187
216
  await renderPageView(pageContext);
188
217
  }
189
218
  }
190
- async function getPageContextBegin() {
219
+ async function getPageContextBegin(isForErrorPage) {
191
220
  const pageContext = await createPageContext(urlOriginal);
192
221
  objectAssign(pageContext, {
193
222
  isBackwardNavigation,
194
223
  isClientSideNavigation,
224
+ isHydration: isFirstRender && !isForErrorPage,
195
225
  _previousPageContext: previousPageContext
196
226
  });
197
227
  {
@@ -226,7 +256,7 @@ async function renderPageClientSide(renderArgs) {
226
256
  // We handle the abort error down below.
227
257
  }
228
258
  }
229
- const pageContext = await getPageContextBegin();
259
+ const pageContext = await getPageContextBegin(true);
230
260
  if (isRenderOutdated())
231
261
  return;
232
262
  if (args.is404)
@@ -310,21 +340,34 @@ async function renderPageClientSide(renderArgs) {
310
340
  }
311
341
  if (isRenderOutdated())
312
342
  return;
313
- let res;
343
+ setPageContextCurrent(pageContext);
344
+ let pageContextFromServerHooks;
314
345
  try {
315
- res = await getPageContextFromHooks_isNotHydration(pageContext, true);
346
+ const result = await getPageContextFromServerHooks(pageContext, true);
347
+ if (result.is404ServerSideRouted)
348
+ return;
349
+ pageContextFromServerHooks = result.pageContextFromServerHooks;
316
350
  }
317
351
  catch (err) {
318
- // - When user hasn't defined a `_error.page.js` file
319
- // - Some Vike unpexected internal error
320
352
  onError(err);
321
353
  return;
322
354
  }
323
355
  if (isRenderOutdated())
324
356
  return;
325
- if ('is404ServerSideRouted' in res)
357
+ // TODO/eventually: create helper assertPageContextFromHook()
358
+ assert(!('urlOriginal' in pageContextFromServerHooks));
359
+ objectAssign(pageContext, pageContextFromServerHooks);
360
+ let pageContextFromClientHooks;
361
+ try {
362
+ pageContextFromClientHooks = await getPageContextFromClientHooks(pageContext, true);
363
+ }
364
+ catch (err) {
365
+ onError(err);
366
+ return;
367
+ }
368
+ if (isRenderOutdated())
326
369
  return;
327
- augmentType(pageContext, res.pageContextAugmented);
370
+ augmentType(pageContext, pageContextFromClientHooks);
328
371
  objectAssign(pageContext, { routeParams: {} });
329
372
  await renderPageView(pageContext, args);
330
373
  }
@@ -372,7 +415,6 @@ async function renderPageClientSide(renderArgs) {
372
415
  /* We don't abort in order to ensure that onHydrationEnd() is called: we abort only after onHydrationEnd() is called.
373
416
  if (isRenderOutdated(true)) return
374
417
  */
375
- addLinkPrefetchHandlers(pageContext);
376
418
  // onHydrationEnd()
377
419
  if (isFirstRender && !onRenderClientError) {
378
420
  assertHook(pageContext, 'onHydrationEnd');
@@ -427,6 +469,11 @@ async function renderPageClientSide(renderArgs) {
427
469
  setScrollPosition(scrollTarget);
428
470
  browserNativeScrollRestoration_disable();
429
471
  setInitialRenderIsDone();
472
+ if (pageContext._hasPageContextFromServer)
473
+ setPageContextInitIsPassedToClient(pageContext);
474
+ // Add link prefetch handlers
475
+ addLinkPrefetchHandlers_watch();
476
+ addLinkPrefetchHandlers();
430
477
  }
431
478
  }
432
479
  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;