vike 0.4.144 → 0.4.145-commit-2520555

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/dist/cjs/__internal/index.js +6 -2
  2. package/dist/cjs/node/plugin/plugins/buildConfig.js +3 -3
  3. package/dist/cjs/node/plugin/plugins/commonConfig.js +0 -3
  4. package/dist/cjs/node/plugin/plugins/config/index.js +3 -3
  5. package/dist/cjs/node/plugin/plugins/devConfig/determineOptimizeDeps.js +8 -8
  6. package/dist/cjs/node/plugin/plugins/devConfig/index.js +1 -0
  7. package/dist/cjs/node/plugin/plugins/envVars.js +34 -20
  8. package/dist/cjs/node/plugin/plugins/importBuild/index.js +3 -3
  9. package/dist/cjs/node/plugin/plugins/importUserCode/getVirtualFileImportUserCode.js +1 -1
  10. package/dist/cjs/node/plugin/plugins/importUserCode/index.js +3 -3
  11. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +5 -4
  12. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/filesystemRouting.js +12 -12
  13. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +249 -228
  14. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.js +8 -6
  15. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +36 -14
  16. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/helpers.js +1 -14
  17. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/isRuntimeEnvMatch.js +18 -0
  18. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/transpileAndExecuteFile.js +15 -17
  19. package/dist/cjs/node/plugin/plugins/previewConfig.js +11 -2
  20. package/dist/cjs/node/prerender/runPrerender.js +34 -26
  21. package/dist/cjs/node/prerender/utils.js +1 -1
  22. package/dist/cjs/node/runtime/html/serializePageContextClientSide.js +20 -6
  23. package/dist/cjs/node/runtime/renderPage/debugPageFiles.js +5 -5
  24. package/dist/cjs/node/runtime/renderPage/log404/index.js +28 -17
  25. package/dist/cjs/node/runtime/renderPage/renderPageAlreadyRouted.js +3 -3
  26. package/dist/cjs/node/runtime/renderPage.js +3 -3
  27. package/dist/cjs/node/runtime/utils.js +1 -1
  28. package/dist/cjs/node/shared/getClientEntryFilePath.js +2 -2
  29. package/dist/cjs/node/shared/getConfigVike.js +4 -1
  30. package/dist/cjs/shared/addUrlComputedProps.js +24 -12
  31. package/dist/cjs/shared/getPageFiles/analyzeClientSide.js +4 -6
  32. package/dist/cjs/shared/getPageFiles/getExports.js +3 -3
  33. package/dist/cjs/shared/hooks/getHook.js +1 -1
  34. package/dist/cjs/shared/page-configs/getExportPath.js +3 -3
  35. package/dist/cjs/shared/page-configs/helpers/getConfigDefinedAtString.js +43 -0
  36. package/dist/cjs/shared/page-configs/helpers/getConfigValue.js +44 -0
  37. package/dist/cjs/shared/page-configs/helpers.js +33 -0
  38. package/dist/cjs/shared/page-configs/loadConfigValues.js +2 -2
  39. package/dist/cjs/shared/page-configs/serialize/parseConfigValuesImported.js +14 -13
  40. package/dist/cjs/shared/page-configs/serialize/parsePageConfigs.js +2 -2
  41. package/dist/cjs/shared/page-configs/serialize/serializeConfigValue.js +9 -10
  42. package/dist/cjs/shared/route/abort.js +1 -1
  43. package/dist/cjs/shared/route/executeOnBeforeRouteHook.js +41 -15
  44. package/dist/cjs/shared/route/index.js +22 -32
  45. package/dist/cjs/shared/route/loadPageRoutes.js +11 -10
  46. package/dist/cjs/shared/route/noRouteMatch.js +4 -0
  47. package/dist/cjs/shared/route/resolveRouteFunction.js +1 -1
  48. package/dist/cjs/shared/utils.js +1 -1
  49. package/dist/cjs/utils/getFilePathAbsolute.js +11 -11
  50. package/dist/cjs/utils/isExternalLink.js +7 -0
  51. package/dist/cjs/utils/{hasPropertyGetter.js → isPropertyGetter.js} +3 -3
  52. package/dist/cjs/utils/onPageVisibilityChange.js +19 -0
  53. package/dist/cjs/utils/parseUrl.js +1 -1
  54. package/dist/cjs/utils/projectInfo.js +1 -1
  55. package/dist/cjs/utils/truncateString.js +12 -7
  56. package/dist/esm/__internal/index.d.ts +6 -3
  57. package/dist/esm/__internal/index.js +8 -3
  58. package/dist/esm/client/client-routing-runtime/createPageContext.d.ts +2 -3
  59. package/dist/esm/client/client-routing-runtime/createPageContext.js +3 -4
  60. package/dist/esm/client/client-routing-runtime/entry.js +2 -2
  61. package/dist/esm/client/client-routing-runtime/getBaseServer.d.ts +2 -1
  62. package/dist/esm/client/client-routing-runtime/getBaseServer.js +2 -1
  63. package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.d.ts +39 -0
  64. package/dist/esm/client/client-routing-runtime/{getPageContext.js → getPageContextFromHooks.js} +50 -79
  65. package/dist/esm/client/client-routing-runtime/history.d.ts +3 -1
  66. package/dist/esm/client/client-routing-runtime/history.js +31 -9
  67. package/dist/esm/client/client-routing-runtime/installClientRouter.d.ts +2 -0
  68. package/dist/esm/client/client-routing-runtime/installClientRouter.js +22 -0
  69. package/dist/esm/client/client-routing-runtime/isClientSideRoutable.d.ts +8 -0
  70. package/dist/esm/client/client-routing-runtime/isClientSideRoutable.js +12 -0
  71. package/dist/esm/client/client-routing-runtime/navigate.d.ts +0 -2
  72. package/dist/esm/client/client-routing-runtime/navigate.js +9 -8
  73. package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.d.ts +4 -0
  74. package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.js +63 -0
  75. package/dist/esm/client/client-routing-runtime/onLinkClick.d.ts +2 -0
  76. package/dist/esm/client/client-routing-runtime/onLinkClick.js +40 -0
  77. package/dist/esm/client/client-routing-runtime/prefetch.js +54 -29
  78. package/dist/esm/client/client-routing-runtime/renderPageClientSide.d.ts +19 -0
  79. package/dist/esm/client/client-routing-runtime/renderPageClientSide.js +347 -0
  80. package/dist/esm/client/client-routing-runtime/scrollRestoration.d.ts +6 -0
  81. package/dist/esm/client/client-routing-runtime/scrollRestoration.js +25 -0
  82. package/dist/esm/client/client-routing-runtime/setScrollPosition.d.ts +7 -0
  83. package/dist/esm/client/client-routing-runtime/setScrollPosition.js +77 -0
  84. package/dist/esm/client/client-routing-runtime/skipLink.d.ts +0 -1
  85. package/dist/esm/client/client-routing-runtime/skipLink.js +9 -5
  86. package/dist/esm/client/client-routing-runtime/utils.d.ts +2 -0
  87. package/dist/esm/client/client-routing-runtime/utils.js +2 -0
  88. package/dist/esm/client/server-routing-runtime/getPageContext.js +1 -1
  89. package/dist/esm/client/shared/executeOnRenderClientHook.js +6 -5
  90. package/dist/esm/client/shared/getPageContextProxyForUser.js +13 -7
  91. package/dist/esm/client/shared/loadPageFilesClientSide.d.ts +8 -3
  92. package/dist/esm/client/shared/loadPageFilesClientSide.js +5 -5
  93. package/dist/esm/node/plugin/plugins/buildConfig.js +3 -3
  94. package/dist/esm/node/plugin/plugins/commonConfig.js +0 -3
  95. package/dist/esm/node/plugin/plugins/config/index.js +4 -4
  96. package/dist/esm/node/plugin/plugins/devConfig/determineOptimizeDeps.js +9 -9
  97. package/dist/esm/node/plugin/plugins/devConfig/index.js +1 -0
  98. package/dist/esm/node/plugin/plugins/envVars.d.ts +2 -0
  99. package/dist/esm/node/plugin/plugins/envVars.js +35 -20
  100. package/dist/esm/node/plugin/plugins/importBuild/index.js +3 -3
  101. package/dist/esm/node/plugin/plugins/importUserCode/getVirtualFileImportUserCode.js +1 -1
  102. package/dist/esm/node/plugin/plugins/importUserCode/index.js +3 -3
  103. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.d.ts +2 -1
  104. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +5 -4
  105. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/filesystemRouting.d.ts +1 -1
  106. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/filesystemRouting.js +12 -12
  107. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.d.ts +2 -1
  108. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +250 -229
  109. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.d.ts +2 -2
  110. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigValuesAll.js +7 -5
  111. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.d.ts +2 -2
  112. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +37 -15
  113. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/helpers.js +1 -14
  114. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/isRuntimeEnvMatch.d.ts +7 -0
  115. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/isRuntimeEnvMatch.js +15 -0
  116. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/transpileAndExecuteFile.d.ts +2 -2
  117. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/transpileAndExecuteFile.js +16 -18
  118. package/dist/esm/node/plugin/plugins/previewConfig.js +11 -2
  119. package/dist/esm/node/prerender/runPrerender.js +29 -21
  120. package/dist/esm/node/prerender/utils.d.ts +1 -1
  121. package/dist/esm/node/prerender/utils.js +1 -1
  122. package/dist/esm/node/runtime/html/serializePageContextClientSide.js +21 -7
  123. package/dist/esm/node/runtime/renderPage/debugPageFiles.d.ts +5 -5
  124. package/dist/esm/node/runtime/renderPage/debugPageFiles.js +5 -5
  125. package/dist/esm/node/runtime/renderPage/loadPageFilesServerSide.d.ts +2 -2
  126. package/dist/esm/node/runtime/renderPage/log404/index.d.ts +2 -2
  127. package/dist/esm/node/runtime/renderPage/log404/index.js +28 -16
  128. package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.js +2 -2
  129. package/dist/esm/node/runtime/renderPage.js +3 -3
  130. package/dist/esm/node/runtime/utils.d.ts +1 -1
  131. package/dist/esm/node/runtime/utils.js +1 -1
  132. package/dist/esm/node/shared/getClientEntryFilePath.js +1 -1
  133. package/dist/esm/node/shared/getConfigVike.d.ts +2 -1
  134. package/dist/esm/node/shared/getConfigVike.js +4 -1
  135. package/dist/esm/shared/addUrlComputedProps.d.ts +1 -0
  136. package/dist/esm/shared/addUrlComputedProps.js +25 -13
  137. package/dist/esm/shared/getPageFiles/analyzeClientSide.js +2 -4
  138. package/dist/esm/shared/getPageFiles/getExports.js +2 -2
  139. package/dist/esm/shared/hooks/getHook.js +1 -1
  140. package/dist/esm/shared/page-configs/PageConfig.d.ts +55 -31
  141. package/dist/esm/shared/page-configs/getExportPath.d.ts +1 -1
  142. package/dist/esm/shared/page-configs/getExportPath.js +3 -3
  143. package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.d.ts +7 -0
  144. package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.js +37 -0
  145. package/dist/esm/shared/page-configs/helpers/getConfigValue.d.ts +14 -0
  146. package/dist/esm/shared/page-configs/helpers/getConfigValue.js +38 -0
  147. package/dist/esm/shared/page-configs/helpers.d.ts +13 -0
  148. package/dist/esm/shared/page-configs/helpers.js +27 -0
  149. package/dist/esm/shared/page-configs/loadConfigValues.js +2 -2
  150. package/dist/esm/shared/page-configs/serialize/PageConfigSerialized.d.ts +4 -4
  151. package/dist/esm/shared/page-configs/serialize/parseConfigValuesImported.js +15 -11
  152. package/dist/esm/shared/page-configs/serialize/parsePageConfigs.js +2 -2
  153. package/dist/esm/shared/page-configs/serialize/serializeConfigValue.js +9 -10
  154. package/dist/esm/shared/route/abort.js +1 -1
  155. package/dist/esm/shared/route/executeOnBeforeRouteHook.d.ts +5 -8
  156. package/dist/esm/shared/route/executeOnBeforeRouteHook.js +41 -15
  157. package/dist/esm/shared/route/index.d.ts +12 -10
  158. package/dist/esm/shared/route/index.js +23 -33
  159. package/dist/esm/shared/route/loadPageRoutes.js +8 -7
  160. package/dist/esm/shared/route/noRouteMatch.d.ts +1 -0
  161. package/dist/esm/shared/route/noRouteMatch.js +1 -0
  162. package/dist/esm/shared/route/resolveRouteFunction.js +1 -1
  163. package/dist/esm/shared/utils.d.ts +1 -1
  164. package/dist/esm/shared/utils.js +1 -1
  165. package/dist/esm/utils/getFilePathAbsolute.d.ts +1 -1
  166. package/dist/esm/utils/getFilePathAbsolute.js +11 -11
  167. package/dist/esm/utils/isPropertyGetter.d.ts +1 -0
  168. package/dist/esm/utils/{hasPropertyGetter.js → isPropertyGetter.js} +1 -1
  169. package/dist/esm/utils/onPageVisibilityChange.d.ts +4 -0
  170. package/dist/esm/utils/onPageVisibilityChange.js +16 -0
  171. package/dist/esm/utils/parseUrl.js +1 -1
  172. package/dist/esm/utils/projectInfo.d.ts +1 -1
  173. package/dist/esm/utils/projectInfo.js +1 -1
  174. package/dist/esm/utils/truncateString.d.ts +2 -1
  175. package/dist/esm/utils/truncateString.js +10 -7
  176. package/node/cli/bin-entry.js +1 -1
  177. package/package.json +2 -3
  178. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getFilePathToShowToUser.js +0 -16
  179. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/isConfigEnvMatch.js +0 -13
  180. package/dist/cjs/shared/page-configs/utils.js +0 -103
  181. package/dist/esm/client/client-routing-runtime/getPageContext.d.ts +0 -29
  182. package/dist/esm/client/client-routing-runtime/getPageId.d.ts +0 -10
  183. package/dist/esm/client/client-routing-runtime/getPageId.js +0 -17
  184. package/dist/esm/client/client-routing-runtime/navigationState.d.ts +0 -5
  185. package/dist/esm/client/client-routing-runtime/navigationState.js +0 -14
  186. package/dist/esm/client/client-routing-runtime/skipLink/isClientSideRoutable.d.ts +0 -2
  187. package/dist/esm/client/client-routing-runtime/skipLink/isClientSideRoutable.js +0 -15
  188. package/dist/esm/client/client-routing-runtime/useClientRouter.d.ts +0 -6
  189. package/dist/esm/client/client-routing-runtime/useClientRouter.js +0 -493
  190. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getFilePathToShowToUser.d.ts +0 -24
  191. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getFilePathToShowToUser.js +0 -13
  192. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/isConfigEnvMatch.d.ts +0 -3
  193. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/isConfigEnvMatch.js +0 -10
  194. package/dist/esm/shared/page-configs/utils.d.ts +0 -35
  195. package/dist/esm/shared/page-configs/utils.js +0 -97
  196. package/dist/esm/utils/hasPropertyGetter.d.ts +0 -1
  197. /package/dist/esm/{client/client-routing-runtime → utils}/isExternalLink.d.ts +0 -0
  198. /package/dist/esm/{client/client-routing-runtime → utils}/isExternalLink.js +0 -0
@@ -1,15 +1,30 @@
1
1
  export { prefetch };
2
2
  export { addLinkPrefetchHandlers };
3
- import { assert, assertClientRouting, assertUsage, checkIfClientRouting } from './utils.js';
3
+ import { assert, assertClientRouting, assertUsage, assertWarning, checkIfClientRouting, getGlobalObject, isExternalLink } from './utils.js';
4
4
  import { isErrorFetchingStaticAssets, loadPageFilesClientSide } from '../shared/loadPageFilesClientSide.js';
5
- import { isClientSideRoutable, skipLink } from './skipLink.js';
6
- import { getPageId } from './getPageId.js';
5
+ import { skipLink } from './skipLink.js';
7
6
  import { getPrefetchSettings } from './prefetch/getPrefetchSettings.js';
8
7
  import { isAlreadyPrefetched, markAsAlreadyPrefetched } from './prefetch/alreadyPrefetched.js';
9
- import { disableClientRouting } from './useClientRouter.js';
10
- import { isExternalLink } from './isExternalLink.js';
8
+ import { disableClientRouting } from './renderPageClientSide.js';
9
+ import { isClientSideRoutable } from './isClientSideRoutable.js';
10
+ import { createPageContext } from './createPageContext.js';
11
+ import { route } from '../../shared/route/index.js';
12
+ import { noRouteMatch } from '../../shared/route/noRouteMatch.js';
11
13
  assertClientRouting();
12
- const linkPrefetchHandlerAdded = new Map();
14
+ const globalObject = getGlobalObject('prefetch.ts', { linkPrefetchHandlerAdded: new Map() });
15
+ async function prefetchAssets(pageId, pageContext) {
16
+ try {
17
+ await loadPageFilesClientSide(pageId, pageContext);
18
+ }
19
+ catch (err) {
20
+ if (isErrorFetchingStaticAssets(err)) {
21
+ disableClientRouting(err, true);
22
+ }
23
+ else {
24
+ throw err;
25
+ }
26
+ }
27
+ }
13
28
  /**
14
29
  * Programmatically prefetch client assets.
15
30
  *
@@ -21,33 +36,38 @@ async function prefetch(url) {
21
36
  assertUsage(checkIfClientRouting(), 'prefetch() only works with Client Routing, see https://vike.dev/prefetch', {
22
37
  showStackTrace: true
23
38
  });
24
- assertUsage(!isExternalLink(url), `You are trying to prefetch the URL ${url} of another domain which cannot be prefetched`, { showStackTrace: true });
39
+ const errPrefix = `Cannot prefetch URL ${url} because it`;
40
+ assertUsage(!isExternalLink(url), `${errPrefix} lives on another domain`, { showStackTrace: true });
25
41
  if (isAlreadyPrefetched(url))
26
42
  return;
27
43
  markAsAlreadyPrefetched(url);
28
- const { pageId, pageFilesAll, pageConfigs } = await getPageId(url);
29
- if (pageId) {
30
- try {
31
- await loadPageFilesClientSide(pageFilesAll, pageConfigs, pageId);
32
- }
33
- catch (err) {
34
- if (isErrorFetchingStaticAssets(err)) {
35
- disableClientRouting(err, true);
36
- }
37
- else {
38
- throw err;
39
- }
40
- }
44
+ const pageContext = await createPageContext(url);
45
+ let pageContextFromRoute;
46
+ try {
47
+ pageContextFromRoute = await route(pageContext);
41
48
  }
49
+ catch {
50
+ // If a route() hook has a bug or `throw render()` / `throw redirect()`
51
+ return;
52
+ }
53
+ const pageId = pageContextFromRoute._pageId;
54
+ if (!pageId) {
55
+ assertWarning(false, `${errPrefix} ${noRouteMatch}`, {
56
+ showStackTrace: true,
57
+ onlyOnce: false
58
+ });
59
+ return;
60
+ }
61
+ await prefetchAssets(pageId, pageContext);
42
62
  }
43
63
  function addLinkPrefetchHandlers(pageContext) {
44
64
  // Current URL is already prefetched
45
65
  markAsAlreadyPrefetched(pageContext.urlPathname);
46
66
  const linkTags = [...document.getElementsByTagName('A')];
47
67
  linkTags.forEach((linkTag) => {
48
- if (linkPrefetchHandlerAdded.has(linkTag))
68
+ if (globalObject.linkPrefetchHandlerAdded.has(linkTag))
49
69
  return;
50
- linkPrefetchHandlerAdded.set(linkTag, true);
70
+ globalObject.linkPrefetchHandlerAdded.set(linkTag, true);
51
71
  const url = linkTag.getAttribute('href');
52
72
  if (skipLink(linkTag))
53
73
  return;
@@ -59,17 +79,17 @@ function addLinkPrefetchHandlers(pageContext) {
59
79
  return;
60
80
  if (prefetchStaticAssets === 'hover') {
61
81
  linkTag.addEventListener('mouseover', () => {
62
- prefetchIfClientSideRoutable(url);
82
+ prefetchIfPossible(url);
63
83
  });
64
84
  linkTag.addEventListener('touchstart', () => {
65
- prefetchIfClientSideRoutable(url);
85
+ prefetchIfPossible(url);
66
86
  }, { passive: true });
67
87
  }
68
88
  if (prefetchStaticAssets === 'viewport') {
69
89
  const observer = new IntersectionObserver((entries) => {
70
90
  entries.forEach((entry) => {
71
91
  if (entry.isIntersecting) {
72
- prefetchIfClientSideRoutable(url);
92
+ prefetchIfPossible(url);
73
93
  observer.disconnect();
74
94
  }
75
95
  });
@@ -78,14 +98,19 @@ function addLinkPrefetchHandlers(pageContext) {
78
98
  }
79
99
  });
80
100
  }
81
- async function prefetchIfClientSideRoutable(url) {
101
+ async function prefetchIfPossible(url) {
102
+ const pageContext = await createPageContext(url);
103
+ let pageContextFromRoute;
82
104
  try {
83
- if (!(await isClientSideRoutable(url)))
84
- return;
105
+ pageContextFromRoute = await route(pageContext);
85
106
  }
86
107
  catch {
87
108
  // If a route() hook has a bug or `throw render()` / `throw redirect()`
88
109
  return;
89
110
  }
90
- await prefetch(url);
111
+ if (!pageContextFromRoute?._pageId)
112
+ return;
113
+ if (!(await isClientSideRoutable(pageContextFromRoute._pageId, pageContext)))
114
+ return;
115
+ await prefetchAssets(pageContextFromRoute._pageId, pageContext);
91
116
  }
@@ -0,0 +1,19 @@
1
+ export { renderPageClientSide };
2
+ export { getRenderCount };
3
+ export { disableClientRouting };
4
+ import { PageContextFromRewrite } from '../../shared/route/abort.js';
5
+ import { type ScrollTarget } from './setScrollPosition.js';
6
+ type RenderArgs = {
7
+ scrollTarget: ScrollTarget;
8
+ isBackwardNavigation: boolean | null;
9
+ urlOriginal?: string;
10
+ overwriteLastHistoryEntry?: boolean;
11
+ pageContextsFromRewrite?: PageContextFromRewrite[];
12
+ redirectCount?: number;
13
+ /** Whether the navigation was triggered by the user land calling `history.pushState()` */
14
+ isUserLandPushStateNavigation?: boolean;
15
+ isClientSideNavigation?: boolean;
16
+ };
17
+ declare function renderPageClientSide(renderArgs: RenderArgs): Promise<void>;
18
+ declare function disableClientRouting(err: unknown, log: boolean): void;
19
+ declare function getRenderCount(): number;
@@ -0,0 +1,347 @@
1
+ export { renderPageClientSide };
2
+ export { getRenderCount };
3
+ export { disableClientRouting };
4
+ import { assert, getCurrentUrl, isEquivalentError, objectAssign, serverSideRouteTo, getGlobalObject, executeHook, hasProp } from './utils.js';
5
+ import { getPageContextFromHooks_errorPage, getPageContextFromHooks_firstRender, getPageContextFromHooks_uponNavigation, isAlreadyServerSideRouted } from './getPageContextFromHooks.js';
6
+ import { createPageContext } from './createPageContext.js';
7
+ import { addLinkPrefetchHandlers } from './prefetch.js';
8
+ import { assertInfo, assertWarning, isReact } from './utils.js';
9
+ import { executeOnRenderClientHook } from '../shared/executeOnRenderClientHook.js';
10
+ import { assertHook } from '../../shared/hooks/getHook.js';
11
+ import { isErrorFetchingStaticAssets } from '../shared/loadPageFilesClientSide.js';
12
+ import { pushHistory } from './history.js';
13
+ import { assertNoInfiniteAbortLoop, getPageContextFromAllRewrites, isAbortError, logAbortErrorHandled } from '../../shared/route/abort.js';
14
+ import { route } from '../../shared/route/index.js';
15
+ import { isClientSideRoutable } from './isClientSideRoutable.js';
16
+ import { setScrollPosition } from './setScrollPosition.js';
17
+ import { updateState } from './onBrowserHistoryNavigation.js';
18
+ import { browserNativeScrollRestoration_disable, setInitialRenderIsDone } from './scrollRestoration.js';
19
+ const globalObject = getGlobalObject('renderPageClientSide.ts', { renderCounter: 0 });
20
+ async function renderPageClientSide(renderArgs) {
21
+ const { scrollTarget, urlOriginal = getCurrentUrl(), overwriteLastHistoryEntry = false, isBackwardNavigation, pageContextsFromRewrite = [], redirectCount = 0, isUserLandPushStateNavigation, isClientSideNavigation = true } = renderArgs;
22
+ const { abortRender, setHydrationCanBeAborted, isFirstRender } = getAbortRender();
23
+ assert(isClientSideNavigation === !isFirstRender);
24
+ assertNoInfiniteAbortLoop(pageContextsFromRewrite.length, redirectCount);
25
+ if (globalObject.clientRoutingIsDisabled) {
26
+ serverSideRouteTo(urlOriginal);
27
+ return;
28
+ }
29
+ const pageContext = await createPageContext(urlOriginal);
30
+ if (abortRender())
31
+ return;
32
+ objectAssign(pageContext, {
33
+ isBackwardNavigation,
34
+ isClientSideNavigation
35
+ });
36
+ {
37
+ const pageContextFromAllRewrites = getPageContextFromAllRewrites(pageContextsFromRewrite);
38
+ objectAssign(pageContext, pageContextFromAllRewrites);
39
+ }
40
+ let renderState = {};
41
+ const onError = (err) => {
42
+ assert(err);
43
+ assert(!('err' in renderState));
44
+ assert(!('errorWhileRendering' in pageContext));
45
+ renderState.err = err;
46
+ pageContext.errorWhileRendering = err;
47
+ };
48
+ if (!isFirstRender) {
49
+ // Route
50
+ try {
51
+ renderState = { pageContextFromRoute: await route(pageContext) };
52
+ }
53
+ catch (err) {
54
+ onError(err);
55
+ }
56
+ if (abortRender())
57
+ return;
58
+ // Check whether rendering should be skipped
59
+ if (renderState.pageContextFromRoute) {
60
+ const { pageContextFromRoute } = renderState;
61
+ objectAssign(pageContext, pageContextFromRoute);
62
+ let isClientRoutable;
63
+ if (!pageContextFromRoute._pageId) {
64
+ isClientRoutable = false;
65
+ }
66
+ else {
67
+ isClientRoutable = await isClientSideRoutable(pageContextFromRoute._pageId, pageContext);
68
+ if (abortRender())
69
+ return;
70
+ }
71
+ if (!isClientRoutable) {
72
+ serverSideRouteTo(urlOriginal);
73
+ return;
74
+ }
75
+ const isSamePage = pageContextFromRoute._pageId &&
76
+ globalObject.previousPageContext?._pageId &&
77
+ pageContextFromRoute._pageId === globalObject.previousPageContext._pageId;
78
+ if (isUserLandPushStateNavigation && isSamePage) {
79
+ // Skip's Vike's rendering; let the user handle the navigation
80
+ return;
81
+ }
82
+ }
83
+ }
84
+ // onPageTransitionStart()
85
+ const callTransitionHooks = !isFirstRender;
86
+ if (callTransitionHooks) {
87
+ if (!globalObject.isTransitioning) {
88
+ await globalObject.onPageTransitionStart?.(pageContext);
89
+ globalObject.isTransitioning = true;
90
+ if (abortRender())
91
+ return;
92
+ }
93
+ }
94
+ if (isFirstRender) {
95
+ assert(!renderState.pageContextFromRoute);
96
+ assert(!renderState.err);
97
+ try {
98
+ renderState.pageContextFromHooks = await getPageContextFromHooks_firstRender(pageContext);
99
+ }
100
+ catch (err) {
101
+ onError(err);
102
+ }
103
+ if (abortRender())
104
+ return;
105
+ }
106
+ else {
107
+ if (!renderState.err) {
108
+ const { pageContextFromRoute } = renderState;
109
+ assert(pageContextFromRoute);
110
+ assert(pageContextFromRoute._pageId);
111
+ assert(hasProp(pageContextFromRoute, '_pageId', 'string')); // Help TS
112
+ objectAssign(pageContext, pageContextFromRoute);
113
+ try {
114
+ renderState.pageContextFromHooks = await getPageContextFromHooks_uponNavigation(pageContext);
115
+ }
116
+ catch (err) {
117
+ onError(err);
118
+ }
119
+ if (abortRender())
120
+ return;
121
+ }
122
+ }
123
+ if ('err' in renderState) {
124
+ const { err } = renderState;
125
+ if (!isAbortError(err)) {
126
+ // We don't swallow 404 errors:
127
+ // - On the server-side, Vike swallows / doesn't show any 404 error log because it's expected that a user may go to some random non-existent URL. (We don't want to flood the app's error tracking with 404 logs.)
128
+ // - On the client-side, if the user navigates to a 404 then it means that the UI has a broken link. (It isn't expected that users can go to some random URL using the client-side router, as it would require, for example, the user to manually change the URL of a link by manually manipulating the DOM which highly unlikely.)
129
+ console.error(err);
130
+ }
131
+ else {
132
+ // We swallow throw redirect()/render() called by client-side hooks onBeforeRender() and guard()
133
+ // We handle the abort error down below.
134
+ }
135
+ if (shouldSwallowAndInterrupt(err, pageContext, isFirstRender))
136
+ return;
137
+ if (isAbortError(err)) {
138
+ const errAbort = err;
139
+ logAbortErrorHandled(err, pageContext._isProduction, pageContext);
140
+ const pageContextAbort = errAbort._pageContextAbort;
141
+ // throw render('/some-url')
142
+ if (pageContextAbort._urlRewrite) {
143
+ await renderPageClientSide({
144
+ ...renderArgs,
145
+ scrollTarget: 'scroll-to-top-or-hash',
146
+ pageContextsFromRewrite: [...pageContextsFromRewrite, pageContextAbort]
147
+ });
148
+ return;
149
+ }
150
+ // throw redirect('/some-url')
151
+ if (pageContextAbort._urlRedirect) {
152
+ const urlRedirect = pageContextAbort._urlRedirect.url;
153
+ if (urlRedirect.startsWith('http')) {
154
+ // External redirection
155
+ window.location.href = urlRedirect;
156
+ return;
157
+ }
158
+ else {
159
+ await renderPageClientSide({
160
+ ...renderArgs,
161
+ scrollTarget: 'scroll-to-top-or-hash',
162
+ urlOriginal: urlRedirect,
163
+ overwriteLastHistoryEntry: false,
164
+ isBackwardNavigation: false,
165
+ redirectCount: redirectCount + 1
166
+ });
167
+ }
168
+ return;
169
+ }
170
+ // throw render(statusCode)
171
+ assert(pageContextAbort.abortStatusCode);
172
+ objectAssign(pageContext, pageContextAbort);
173
+ if (pageContextAbort.abortStatusCode === 404) {
174
+ objectAssign(pageContext, { is404: true });
175
+ }
176
+ }
177
+ else {
178
+ objectAssign(pageContext, { is404: false });
179
+ }
180
+ try {
181
+ renderState.pageContextFromHooks = await getPageContextFromHooks_errorPage(pageContext);
182
+ }
183
+ catch (err2) {
184
+ // - When user hasn't defined a `_error.page.js` file
185
+ // - Some unpexected vike internal error
186
+ if (shouldSwallowAndInterrupt(err2, pageContext, isFirstRender))
187
+ return;
188
+ if (!isFirstRender) {
189
+ setTimeout(() => {
190
+ // We let the server show the 404 page
191
+ window.location.pathname = urlOriginal;
192
+ }, 0);
193
+ }
194
+ if (!isEquivalentError(err, err2)) {
195
+ throw err2;
196
+ }
197
+ else {
198
+ // Abort
199
+ return;
200
+ }
201
+ }
202
+ if (abortRender())
203
+ return;
204
+ }
205
+ const { pageContextFromHooks } = renderState;
206
+ assert(pageContextFromHooks);
207
+ objectAssign(pageContext, pageContextFromHooks);
208
+ // Set global onPageTransitionStart()
209
+ assertHook(pageContext, 'onPageTransitionStart');
210
+ globalObject.onPageTransitionStart = pageContext.exports.onPageTransitionStart;
211
+ // Set global hydrationCanBeAborted
212
+ if (pageContext.exports.hydrationCanBeAborted) {
213
+ setHydrationCanBeAborted();
214
+ }
215
+ else {
216
+ assertWarning(!isReact(), 'You seem to be using React; we recommend setting hydrationCanBeAborted to true, see https://vike.dev/clientRouting', { onlyOnce: true });
217
+ }
218
+ // There wasn't any `await` but result may change because we just called setHydrationCanBeAborted()
219
+ if (abortRender())
220
+ return;
221
+ // We use globalObject.renderPromise in order to ensure that there is never two concurrent onRenderClient() calls
222
+ if (globalObject.renderPromise) {
223
+ // Make sure that the previous render has finished
224
+ await globalObject.renderPromise;
225
+ assert(globalObject.renderPromise === undefined);
226
+ if (abortRender())
227
+ return;
228
+ }
229
+ changeUrl(urlOriginal, overwriteLastHistoryEntry);
230
+ globalObject.previousPageContext = pageContext;
231
+ assert(globalObject.renderPromise === undefined);
232
+ globalObject.renderPromise = (async () => {
233
+ await executeOnRenderClientHook(pageContext, true);
234
+ addLinkPrefetchHandlers(pageContext);
235
+ globalObject.renderPromise = undefined;
236
+ })();
237
+ await globalObject.renderPromise;
238
+ assert(globalObject.renderPromise === undefined);
239
+ /* We don't abort in order to ensure that onHydrationEnd() is called: we abort only after onHydrationEnd() is called.
240
+ if (abortRender(true)) return
241
+ */
242
+ // onHydrationEnd()
243
+ if (isFirstRender) {
244
+ assertHook(pageContext, 'onHydrationEnd');
245
+ const { onHydrationEnd } = pageContext.exports;
246
+ if (onHydrationEnd) {
247
+ const hookFilePath = pageContext.exportsAll.onHydrationEnd[0].exportSource;
248
+ assert(hookFilePath);
249
+ await executeHook(() => onHydrationEnd(pageContext), 'onHydrationEnd', hookFilePath);
250
+ if (abortRender(true))
251
+ return;
252
+ }
253
+ }
254
+ // We abort only after onHydrationEnd() is called
255
+ if (abortRender(true))
256
+ return;
257
+ // onPageTransitionEnd()
258
+ if (callTransitionHooks) {
259
+ if (pageContext.exports.onPageTransitionEnd) {
260
+ assertHook(pageContext, 'onPageTransitionEnd');
261
+ await pageContext.exports.onPageTransitionEnd(pageContext);
262
+ if (abortRender(true))
263
+ return;
264
+ }
265
+ globalObject.isTransitioning = undefined;
266
+ }
267
+ // Page scrolling
268
+ setScrollPosition(scrollTarget);
269
+ browserNativeScrollRestoration_disable();
270
+ setInitialRenderIsDone();
271
+ }
272
+ function changeUrl(url, overwriteLastHistoryEntry) {
273
+ if (getCurrentUrl() === url)
274
+ return;
275
+ browserNativeScrollRestoration_disable();
276
+ pushHistory(url, overwriteLastHistoryEntry);
277
+ updateState();
278
+ }
279
+ function shouldSwallowAndInterrupt(err, pageContext, isFirstRender) {
280
+ if (isAlreadyServerSideRouted(err))
281
+ return true;
282
+ if (handleErrorFetchingStaticAssets(err, pageContext, isFirstRender))
283
+ return true;
284
+ return false;
285
+ }
286
+ function handleErrorFetchingStaticAssets(err, pageContext, isFirstRender) {
287
+ if (!isErrorFetchingStaticAssets(err)) {
288
+ return false;
289
+ }
290
+ if (isFirstRender) {
291
+ disableClientRouting(err, false);
292
+ // This may happen if the frontend was newly deployed during hydration.
293
+ // Ideally: re-try a couple of times by reloading the page (not entirely trivial to implement since `localStorage` is needed.)
294
+ throw err;
295
+ }
296
+ else {
297
+ disableClientRouting(err, true);
298
+ }
299
+ serverSideRouteTo(pageContext.urlOriginal);
300
+ return true;
301
+ }
302
+ function disableClientRouting(err, log) {
303
+ assert(isErrorFetchingStaticAssets(err));
304
+ globalObject.clientRoutingIsDisabled = true;
305
+ if (log) {
306
+ // We don't use console.error() to avoid flooding error trackers such as Sentry
307
+ console.log(err);
308
+ }
309
+ // @ts-ignore Since dist/cjs/client/ is never used, we can ignore this error.
310
+ const isProd = import.meta.env.PROD;
311
+ assertInfo(false, [
312
+ 'Failed to fetch static asset.',
313
+ isProd ? 'This usually happens when a new frontend is deployed.' : null,
314
+ 'Falling back to Server Routing.',
315
+ '(The next page navigation will use Server Routing instead of Client Routing.)'
316
+ ]
317
+ .filter(Boolean)
318
+ .join(' '), { onlyOnce: true });
319
+ }
320
+ function getAbortRender() {
321
+ const renderNumber = ++globalObject.renderCounter;
322
+ assert(renderNumber >= 1);
323
+ let hydrationCanBeAborted = false;
324
+ const setHydrationCanBeAborted = () => {
325
+ hydrationCanBeAborted = true;
326
+ };
327
+ /** Whether the rendering should be aborted because a new rendering has started. We should call this after each `await`. */
328
+ const abortRender = (isRenderCleanup) => {
329
+ // Never abort hydration if `hydrationCanBeAborted` isn't `true`
330
+ if (!isRenderCleanup) {
331
+ const isHydration = renderNumber === 1;
332
+ if (isHydration && !hydrationCanBeAborted) {
333
+ return false;
334
+ }
335
+ }
336
+ // If there is a newer rendering, we should abort all previous renderings
337
+ return renderNumber !== globalObject.renderCounter;
338
+ };
339
+ return {
340
+ abortRender,
341
+ setHydrationCanBeAborted,
342
+ isFirstRender: renderNumber === 1
343
+ };
344
+ }
345
+ function getRenderCount() {
346
+ return globalObject.renderCounter;
347
+ }
@@ -0,0 +1,6 @@
1
+ export { browserNativeScrollRestoration_disable };
2
+ export { setupNativeScrollRestoration };
3
+ export { setInitialRenderIsDone };
4
+ declare function setupNativeScrollRestoration(): void;
5
+ declare function setInitialRenderIsDone(): void;
6
+ declare function browserNativeScrollRestoration_disable(): void;
@@ -0,0 +1,25 @@
1
+ // Handle the browser's native scroll restoration mechanism
2
+ export { browserNativeScrollRestoration_disable };
3
+ export { setupNativeScrollRestoration };
4
+ export { setInitialRenderIsDone };
5
+ import { getGlobalObject, onPageHide, onPageShow } from './utils.js';
6
+ const globalObject = getGlobalObject('scrollRestoration.ts', {});
7
+ // We use the browser's native scroll restoration mechanism only for the first render
8
+ function setupNativeScrollRestoration() {
9
+ browserNativeScrollRestoration_enable();
10
+ onPageHide(browserNativeScrollRestoration_enable);
11
+ onPageShow(() => globalObject.initialRenderIsDone && browserNativeScrollRestoration_disable());
12
+ }
13
+ function setInitialRenderIsDone() {
14
+ globalObject.initialRenderIsDone = true;
15
+ }
16
+ function browserNativeScrollRestoration_disable() {
17
+ if ('scrollRestoration' in window.history) {
18
+ window.history.scrollRestoration = 'manual';
19
+ }
20
+ }
21
+ function browserNativeScrollRestoration_enable() {
22
+ if ('scrollRestoration' in window.history) {
23
+ window.history.scrollRestoration = 'auto';
24
+ }
25
+ }
@@ -0,0 +1,7 @@
1
+ export { setScrollPosition };
2
+ export { autoSaveScrollPosition };
3
+ export type { ScrollTarget };
4
+ import { type ScrollPosition } from './history.js';
5
+ type ScrollTarget = ScrollPosition | 'scroll-to-top-or-hash' | 'preserve-scroll';
6
+ declare function setScrollPosition(scrollTarget: ScrollTarget): void;
7
+ declare function autoSaveScrollPosition(): void;
@@ -0,0 +1,77 @@
1
+ export { setScrollPosition };
2
+ export { autoSaveScrollPosition };
3
+ import { assert, onPageHide, sleep, throttle } from './utils.js';
4
+ import { saveScrollPosition } from './history.js';
5
+ function setScrollPosition(scrollTarget) {
6
+ if (scrollTarget === 'preserve-scroll') {
7
+ return;
8
+ }
9
+ let scrollPosition;
10
+ if (scrollTarget === 'scroll-to-top-or-hash') {
11
+ const hash = getUrlHash();
12
+ // We replicate the browser's native behavior
13
+ if (hash && hash !== 'top') {
14
+ const hashTarget = document.getElementById(hash) || document.getElementsByName(hash)[0];
15
+ if (hashTarget) {
16
+ hashTarget.scrollIntoView();
17
+ return;
18
+ }
19
+ }
20
+ scrollPosition = { x: 0, y: 0 };
21
+ }
22
+ else {
23
+ assert('x' in scrollTarget && 'y' in scrollTarget);
24
+ scrollPosition = scrollTarget;
25
+ }
26
+ setScroll(scrollPosition);
27
+ }
28
+ /** Change the browser's scoll position, in a way that works during a repaint. */
29
+ function setScroll(scrollPosition) {
30
+ const scroll = () => window.scrollTo(scrollPosition.x, scrollPosition.y);
31
+ const done = () => window.scrollX === scrollPosition.x && window.scrollY === scrollPosition.y;
32
+ // In principle, this `done()` call should force the repaint to be finished. But that doesn't seem to be the case with `Firefox 97.0.1`.
33
+ if (done())
34
+ return;
35
+ scroll();
36
+ // Because `done()` doesn't seem to always force the repaint to be finished, we potentially need to retry again.
37
+ if (done())
38
+ return;
39
+ requestAnimationFrame(() => {
40
+ scroll();
41
+ if (done())
42
+ return;
43
+ setTimeout(async () => {
44
+ scroll();
45
+ if (done())
46
+ return;
47
+ // In principle, `requestAnimationFrame() -> setTimeout(, 0)` should be enough.
48
+ // - https://stackoverflow.com/questions/61281139/waiting-for-repaint-in-javascript
49
+ // - But it's not enough for `Firefox 97.0.1`.
50
+ // - The following strategy is very agressive. It doesn't need to be that aggressive for Firefox. But we do it to be safe.
51
+ const start = new Date().getTime();
52
+ while (true) {
53
+ await sleep(10);
54
+ scroll();
55
+ if (done())
56
+ return;
57
+ const millisecondsElapsed = new Date().getTime() - start;
58
+ if (millisecondsElapsed > 100)
59
+ return;
60
+ }
61
+ }, 0);
62
+ });
63
+ }
64
+ function getUrlHash() {
65
+ let { hash } = window.location;
66
+ if (hash === '')
67
+ return null;
68
+ assert(hash.startsWith('#'));
69
+ hash = hash.slice(1);
70
+ return hash;
71
+ }
72
+ // Save scroll position (needed for back-/forward navigation)
73
+ function autoSaveScrollPosition() {
74
+ // Safari cannot handle more than 100 `history.replaceState()` calls within 30 seconds (https://github.com/vikejs/vike/issues/46)
75
+ window.addEventListener('scroll', throttle(saveScrollPosition, Math.ceil(1000 / 3)), { passive: true });
76
+ onPageHide(saveScrollPosition);
77
+ }
@@ -1,3 +1,2 @@
1
1
  export { skipLink };
2
- export { isClientSideRoutable } from './skipLink/isClientSideRoutable.js';
3
2
  declare function skipLink(linkTag: HTMLElement): boolean;
@@ -1,9 +1,6 @@
1
1
  export { skipLink };
2
- export { isClientSideRoutable } from './skipLink/isClientSideRoutable.js';
3
2
  import { getBaseServer } from './getBaseServer.js';
4
- import { isExternalLink } from './isExternalLink.js';
5
- import { assert, parseUrl, isBaseServer, isParsable } from './utils.js';
6
- import { isDisableAutomaticLinkInterception } from './useClientRouter.js';
3
+ import { assert, parseUrl, isBaseServer, isParsable, isExternalLink } from './utils.js';
7
4
  function skipLink(linkTag) {
8
5
  const url = linkTag.getAttribute('href');
9
6
  if (url === null)
@@ -27,7 +24,7 @@ function skipLink(linkTag) {
27
24
  return true;
28
25
  return false;
29
26
  }
30
- // TODO/v1-release: remove this in favor of synchronously checking whether URL matches the route of a page (possible since Async Route Functions are now deprecated)
27
+ // TODO/v1-release: remove this in favor of synchronously checking whether URL matches the route of a page (possible since Async Route Functions will be deprecated)
31
28
  function isVikeLink(linkTag) {
32
29
  const disableAutomaticLinkInterception = isDisableAutomaticLinkInterception();
33
30
  if (!disableAutomaticLinkInterception) {
@@ -59,3 +56,10 @@ function hasBaseServer(url) {
59
56
  const { hasBaseServer } = parseUrl(url, baseServer);
60
57
  return hasBaseServer;
61
58
  }
59
+ function isDisableAutomaticLinkInterception() {
60
+ // @ts-ignore
61
+ return !!window._disableAutomaticLinkInterception;
62
+ /* globalObject should be used if we want to make disableAutomaticLinkInterception a page-by-page setting
63
+ return globalObject.disableAutomaticLinkInterception ?? false
64
+ */
65
+ }
@@ -20,3 +20,5 @@ export * from '../../utils/sleep.js';
20
20
  export * from '../../utils/slice.js';
21
21
  export * from '../../utils/throttle.js';
22
22
  export * from '../../utils/assertRoutingType.js';
23
+ export * from '../../utils/onPageVisibilityChange.js';
24
+ export * from '../../utils/isExternalLink.js';
@@ -26,3 +26,5 @@ export * from '../../utils/sleep.js';
26
26
  export * from '../../utils/slice.js';
27
27
  export * from '../../utils/throttle.js';
28
28
  export * from '../../utils/assertRoutingType.js';
29
+ export * from '../../utils/onPageVisibilityChange.js';
30
+ export * from '../../utils/isExternalLink.js';