vike 0.4.144-commit-f7ab002 → 0.4.144-commit-de18325

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 (79) hide show
  1. package/dist/cjs/__internal/index.js +6 -2
  2. package/dist/cjs/node/plugin/plugins/commonConfig.js +0 -3
  3. package/dist/cjs/node/plugin/plugins/devConfig/index.js +1 -0
  4. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +58 -40
  5. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +1 -0
  6. package/dist/cjs/node/plugin/plugins/previewConfig.js +5 -0
  7. package/dist/cjs/node/prerender/runPrerender.js +10 -11
  8. package/dist/cjs/node/prerender/utils.js +1 -1
  9. package/dist/cjs/node/runtime/html/serializePageContextClientSide.js +1 -0
  10. package/dist/cjs/node/runtime/renderPage/debugPageFiles.js +5 -5
  11. package/dist/cjs/node/runtime/renderPage/renderPageAlreadyRouted.js +1 -1
  12. package/dist/cjs/node/runtime/renderPage.js +3 -3
  13. package/dist/cjs/node/runtime/utils.js +1 -1
  14. package/dist/cjs/shared/addUrlComputedProps.js +24 -12
  15. package/dist/cjs/shared/route/executeOnBeforeRouteHook.js +41 -15
  16. package/dist/cjs/shared/route/index.js +22 -32
  17. package/dist/cjs/shared/route/resolveRouteFunction.js +1 -1
  18. package/dist/cjs/shared/utils.js +1 -1
  19. package/dist/cjs/utils/{hasPropertyGetter.js → isPropertyGetter.js} +3 -3
  20. package/dist/cjs/utils/projectInfo.js +1 -1
  21. package/dist/esm/__internal/index.d.ts +6 -3
  22. package/dist/esm/__internal/index.js +8 -3
  23. package/dist/esm/client/client-routing-runtime/createPageContext.d.ts +2 -3
  24. package/dist/esm/client/client-routing-runtime/createPageContext.js +3 -3
  25. package/dist/esm/client/client-routing-runtime/entry.js +2 -2
  26. package/dist/esm/client/client-routing-runtime/getPageContext.d.ts +0 -1
  27. package/dist/esm/client/client-routing-runtime/getPageContext.js +6 -9
  28. package/dist/esm/client/client-routing-runtime/history.d.ts +3 -1
  29. package/dist/esm/client/client-routing-runtime/history.js +26 -8
  30. package/dist/esm/client/client-routing-runtime/installClientRouter.d.ts +21 -0
  31. package/dist/esm/client/client-routing-runtime/{useClientRouter.js → installClientRouter.js} +248 -242
  32. package/dist/esm/client/client-routing-runtime/isClientSideRoutable.d.ts +8 -0
  33. package/dist/esm/client/client-routing-runtime/isClientSideRoutable.js +15 -0
  34. package/dist/esm/client/client-routing-runtime/navigate.d.ts +0 -2
  35. package/dist/esm/client/client-routing-runtime/navigate.js +10 -8
  36. package/dist/esm/client/client-routing-runtime/prefetch.js +54 -28
  37. package/dist/esm/client/client-routing-runtime/skipLink.d.ts +0 -1
  38. package/dist/esm/client/client-routing-runtime/skipLink.js +1 -2
  39. package/dist/esm/client/server-routing-runtime/getPageContext.js +1 -1
  40. package/dist/esm/client/shared/executeOnRenderClientHook.js +6 -5
  41. package/dist/esm/client/shared/getPageContextProxyForUser.js +13 -7
  42. package/dist/esm/client/shared/loadPageFilesClientSide.d.ts +8 -3
  43. package/dist/esm/client/shared/loadPageFilesClientSide.js +5 -5
  44. package/dist/esm/node/plugin/plugins/commonConfig.js +0 -3
  45. package/dist/esm/node/plugin/plugins/devConfig/index.js +1 -0
  46. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +58 -40
  47. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVirtualFilePageConfigs.js +1 -0
  48. package/dist/esm/node/plugin/plugins/previewConfig.js +5 -0
  49. package/dist/esm/node/prerender/runPrerender.js +11 -12
  50. package/dist/esm/node/prerender/utils.d.ts +1 -1
  51. package/dist/esm/node/prerender/utils.js +1 -1
  52. package/dist/esm/node/runtime/html/serializePageContextClientSide.js +1 -0
  53. package/dist/esm/node/runtime/renderPage/debugPageFiles.d.ts +5 -5
  54. package/dist/esm/node/runtime/renderPage/debugPageFiles.js +5 -5
  55. package/dist/esm/node/runtime/renderPage/loadPageFilesServerSide.d.ts +2 -2
  56. package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.js +1 -1
  57. package/dist/esm/node/runtime/renderPage.js +3 -3
  58. package/dist/esm/node/runtime/utils.d.ts +1 -1
  59. package/dist/esm/node/runtime/utils.js +1 -1
  60. package/dist/esm/shared/addUrlComputedProps.d.ts +1 -0
  61. package/dist/esm/shared/addUrlComputedProps.js +25 -13
  62. package/dist/esm/shared/route/executeOnBeforeRouteHook.d.ts +5 -8
  63. package/dist/esm/shared/route/executeOnBeforeRouteHook.js +41 -15
  64. package/dist/esm/shared/route/index.d.ts +12 -10
  65. package/dist/esm/shared/route/index.js +23 -33
  66. package/dist/esm/shared/route/resolveRouteFunction.js +1 -1
  67. package/dist/esm/shared/utils.d.ts +1 -1
  68. package/dist/esm/shared/utils.js +1 -1
  69. package/dist/esm/utils/isPropertyGetter.d.ts +1 -0
  70. package/dist/esm/utils/{hasPropertyGetter.js → isPropertyGetter.js} +1 -1
  71. package/dist/esm/utils/projectInfo.d.ts +1 -1
  72. package/dist/esm/utils/projectInfo.js +1 -1
  73. package/package.json +1 -1
  74. package/dist/esm/client/client-routing-runtime/getPageId.d.ts +0 -10
  75. package/dist/esm/client/client-routing-runtime/getPageId.js +0 -17
  76. package/dist/esm/client/client-routing-runtime/skipLink/isClientSideRoutable.d.ts +0 -2
  77. package/dist/esm/client/client-routing-runtime/skipLink/isClientSideRoutable.js +0 -15
  78. package/dist/esm/client/client-routing-runtime/useClientRouter.d.ts +0 -6
  79. package/dist/esm/utils/hasPropertyGetter.d.ts +0 -1
@@ -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, objectAssign } 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';
8
+ import { disableClientRouting } from './installClientRouter.js';
10
9
  import { isExternalLink } from './isExternalLink.js';
10
+ import { isClientSideRoutable } from './isClientSideRoutable.js';
11
+ import { createPageContext } from './createPageContext.js';
12
+ import { route } from '../../shared/route/index.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} doesn't match the route of any of your pages`, {
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,20 @@ 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
+ objectAssign(pageContext, pageContextFromRoute);
112
+ if (!pageContext._pageId)
113
+ return;
114
+ if (!(await isClientSideRoutable(pageContext)))
115
+ return;
116
+ await prefetchAssets(pageContext._pageId, pageContext);
91
117
  }
@@ -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,8 @@
1
1
  export { skipLink };
2
- export { isClientSideRoutable } from './skipLink/isClientSideRoutable.js';
3
2
  import { getBaseServer } from './getBaseServer.js';
4
3
  import { isExternalLink } from './isExternalLink.js';
5
4
  import { assert, parseUrl, isBaseServer, isParsable } from './utils.js';
6
- import { isDisableAutomaticLinkInterception } from './useClientRouter.js';
5
+ import { isDisableAutomaticLinkInterception } from './installClientRouter.js';
7
6
  function skipLink(linkTag) {
8
7
  const url = linkTag.getAttribute('href');
9
8
  if (url === null)
@@ -26,7 +26,7 @@ async function loadPageFiles(pageId) {
26
26
  _pageFilesAll: pageFilesAll,
27
27
  _pageConfigs: pageConfigs
28
28
  });
29
- objectAssign(pageContextAddendum, await loadPageFilesClientSide(pageFilesAll, pageConfigs, pageId));
29
+ objectAssign(pageContextAddendum, await loadPageFilesClientSide(pageId, pageContextAddendum));
30
30
  pageFilesAll
31
31
  .filter((p) => p.fileType !== '.page.server')
32
32
  .forEach((p) => {
@@ -19,10 +19,11 @@ async function executeOnRenderClientHook(pageContext, isClientRouting) {
19
19
  }
20
20
  }
21
21
  if (!hook) {
22
- const urlLogical = getUrlLogical(pageContext);
22
+ const urlToShowToUser = getUrlToShowToUser(pageContext);
23
+ assert(urlToShowToUser);
23
24
  if (pageContext._pageConfigs.length > 0) {
24
25
  // V1 design
25
- assertUsage(false, `No onRenderClient() hook defined for URL '${urlLogical}', but it's needed, see https://vike.dev/onRenderClient`);
26
+ assertUsage(false, `No onRenderClient() hook defined for URL '${urlToShowToUser}', but it's needed, see https://vike.dev/onRenderClient`);
26
27
  }
27
28
  else {
28
29
  // TODO/v1-release: remove
@@ -30,11 +31,11 @@ async function executeOnRenderClientHook(pageContext, isClientRouting) {
30
31
  const pageClientsFilesLoaded = pageContext._pageFilesLoaded.filter((p) => p.fileType === '.page.client');
31
32
  let errMsg;
32
33
  if (pageClientsFilesLoaded.length === 0) {
33
- errMsg = 'No file `*.page.client.*` found for URL ' + urlLogical;
34
+ errMsg = 'No file `*.page.client.*` found for URL ' + urlToShowToUser;
34
35
  }
35
36
  else {
36
37
  errMsg =
37
- 'One of the following files should export a `render()` hook: ' +
38
+ 'One of the following files should export a render() hook: ' +
38
39
  pageClientsFilesLoaded.map((p) => p.filePath).join(' ');
39
40
  }
40
41
  assertUsage(false, errMsg);
@@ -47,7 +48,7 @@ async function executeOnRenderClientHook(pageContext, isClientRouting) {
47
48
  const hookResult = await executeHook(() => renderHook(pageContextForUserConsumption), hookName, hook.hookFilePath);
48
49
  assertUsage(hookResult === undefined, `The ${hookName}() hook defined by ${hook.hookFilePath} isn't allowed to return a value`);
49
50
  }
50
- function getUrlLogical(pageContext) {
51
+ function getUrlToShowToUser(pageContext) {
51
52
  let url;
52
53
  // try/catch to avoid passToClient assertUsage() (although: this may not be needed anymore, since we're now accessing pageContext and not pageContextForUserConsumption)
53
54
  try {
@@ -14,14 +14,14 @@ function getPageContextProxyForUser(pageContext) {
14
14
  const val = pageContext[prop];
15
15
  const propName = JSON.stringify(prop);
16
16
  assertUsage(val !== notSerializable, `pageContext[${propName}] couldn't be serialized and, therefore, is missing on the client-side. Check the server logs for more information.`);
17
- assertPassToClient(pageContext, prop, `pageContext[${propName}] isn't available on the client-side because ${propName} is missing in passToClient, see https://vike.dev/passToClient`);
17
+ assertIsDefined(pageContext, prop);
18
18
  return val;
19
19
  }
20
20
  });
21
21
  }
22
- function assertPassToClient(pageContext, prop, errMsg) {
23
- // We disable assertPassToClient() for the next attempt to read `prop`, because of how Vue's reactivity work. When changing a reactive object:
24
- // - Vue tries to read its old value first. This triggers a `assertPassToClient()` failure if e.g. `pageContextOldReactive.routeParams = pageContextNew.routeParams` and `pageContextOldReactive` has no `routeParams`.
22
+ function assertIsDefined(pageContext, prop) {
23
+ // We disable assertIsDefined() for the next attempt to read `prop`, because of how Vue's reactivity work. When changing a reactive object:
24
+ // - Vue tries to read its old value first. This triggers a `assertIsDefined()` failure if e.g. `pageContextOldReactive.routeParams = pageContextNew.routeParams` and `pageContextOldReactive` has no `routeParams`.
25
25
  // - Vue seems to read __v_raw before reading the property
26
26
  if (globalObject.prev === prop || globalObject.prev === '__v_raw')
27
27
  return;
@@ -30,17 +30,23 @@ function assertPassToClient(pageContext, prop, errMsg) {
30
30
  return;
31
31
  if (isExpected(prop))
32
32
  return;
33
+ const propName = JSON.stringify(prop);
34
+ /* This handling would be the clearest but, unfortunately, it's fundamentally problematic:
35
+ * - It would force the pageContext value consumer to be synchronized with the pageContext value provider. For example, if vike-react wants to conditionally do something dependening on wehther some optional pageContext value was provided by some optional vike-react-* integration package.
36
+ * - If a pageContext value is set by an optional hook, then it's expected that the value is undefined if the hook doesn't exist.
37
+ const errMsg = `pageContext[${propName}] is \`undefined\` on the client-side. If it's defined on the server-side then add ${propName} to passToClient (https://vike.dev/passToClient), otherwise make sure your client-side hooks always define it (e.g. set it to \`null\` instead of \`undefined\`).`
38
+ assertUsage(false, errMsg)
39
+ */
33
40
  if (pageContext._hasPageContextFromServer && !pageContext._hasPageContextFromClient) {
34
41
  // We can safely assume that the property is missing in passToClient, because the server-side defines all passToClient properties even if they have an undefined value:
35
42
  // ```
36
43
  // <script id="vike_pageContext" type="application/json">{"_pageId":"/pages/admin","user":"!undefined","pageProps":"!undefined","title":"!undefined","abortReason":"!undefined","_urlRewrite":null}</script>
37
44
  // ```
38
45
  // Note how properties have "!undefined" values => we can tell whether an undefined pageContext value exists in passToClient.
39
- assertUsage(false, errMsg);
46
+ assertUsage(false, `pageContext[${propName}] isn't available on the client-side because ${propName} is missing in passToClient, see https://vike.dev/passToClient`);
40
47
  }
41
48
  else {
42
- // Do nothing, not even a warning.
43
- // Because we don't know whether the user expects the pageContext value to be undefined. (E.g. a client-side onBeforeRender() hook conditionally setting a pageContext value.)
49
+ // Do nothing, not even a warning, because we don't know whether the user expects that the pageContext value can be undefined. (E.g. a pageContext value that is defined by an optional hook.)
44
50
  }
45
51
  }
46
52
  const IGNORE_LIST = [
@@ -1,8 +1,13 @@
1
- import { type PageFile, type PageContextExports } from '../../shared/getPageFiles.js';
2
- import type { PageConfigRuntime } from '../../shared/page-configs/PageConfig.js';
3
1
  export { loadPageFilesClientSide };
4
2
  export { isErrorFetchingStaticAssets };
5
- declare function loadPageFilesClientSide(pageFilesAll: PageFile[], pageConfigs: PageConfigRuntime[], pageId: string): Promise<PageContextExports & {
3
+ export type { PageContextPageFiles };
4
+ import { type PageFile, type PageContextExports } from '../../shared/getPageFiles.js';
5
+ import type { PageConfigRuntime } from '../../shared/page-configs/PageConfig.js';
6
+ type PageContextPageFiles = {
7
+ _pageFilesAll: PageFile[];
8
+ _pageConfigs: PageConfigRuntime[];
9
+ };
10
+ declare function loadPageFilesClientSide(pageId: string, pageContext: PageContextPageFiles): Promise<PageContextExports & {
6
11
  _pageFilesLoaded: PageFile[];
7
12
  }>;
8
13
  declare function isErrorFetchingStaticAssets(err: unknown): boolean;
@@ -1,12 +1,12 @@
1
+ export { loadPageFilesClientSide };
2
+ export { isErrorFetchingStaticAssets };
1
3
  import { getPageFilesClientSide, getExports } from '../../shared/getPageFiles.js';
2
4
  import { findPageConfig } from '../../shared/page-configs/findPageConfig.js';
3
5
  import { loadConfigValues } from '../../shared/page-configs/loadConfigValues.js';
4
- export { loadPageFilesClientSide };
5
- export { isErrorFetchingStaticAssets };
6
6
  const stamp = '__whileFetchingAssets';
7
- async function loadPageFilesClientSide(pageFilesAll, pageConfigs, pageId) {
8
- const pageFilesClientSide = getPageFilesClientSide(pageFilesAll, pageId);
9
- const pageConfig = findPageConfig(pageConfigs, pageId);
7
+ async function loadPageFilesClientSide(pageId, pageContext) {
8
+ const pageFilesClientSide = getPageFilesClientSide(pageContext._pageFilesAll, pageId);
9
+ const pageConfig = findPageConfig(pageContext._pageConfigs, pageId);
10
10
  let pageConfigLoaded;
11
11
  // @ts-ignore Since dist/cjs/client/ is never used, we can ignore this error.
12
12
  const isDev = import.meta.env.DEV;
@@ -13,9 +13,6 @@ function commonConfig() {
13
13
  return [
14
14
  {
15
15
  name: 'vike-commonConfig-1',
16
- config: () => ({
17
- appType: 'custom'
18
- }),
19
16
  configResolved(config) {
20
17
  installRequireShim_setUserRootDir(config.root);
21
18
  }
@@ -21,6 +21,7 @@ function devConfig() {
21
21
  apply,
22
22
  config() {
23
23
  return {
24
+ appType: 'custom',
24
25
  optimizeDeps: {
25
26
  exclude: [
26
27
  // We exclude the vike client to be able to use `import.meta.glob()`
@@ -105,11 +105,11 @@ async function loadInterfaceFiles(userRootDir, outDirRoot, isDev, extensions) {
105
105
  configName
106
106
  };
107
107
  {
108
- // We don't have access to custom config definitions yet
109
- // - We load +{configName}.js later
110
- // - But we do need to eagerly load +meta.js (to get all the custom config definitions)
108
+ // We don't have access to the custom config definitions defined by the user yet.
109
+ // - If `configDef` is `undefined` => we load the file +{configName}.js later.
110
+ // - We already need to load +meta.js here (to get the custom config definitions defined by the user)
111
111
  const configDef = getConfigDefinitionOptional(configDefinitionsBuiltIn, configName);
112
- if (configDef?.env === 'config-only') {
112
+ if (configDef && isConfigEnv(configDef, configName)) {
113
113
  await loadValueFile(interfaceFile, configName, userRootDir);
114
114
  }
115
115
  }
@@ -139,6 +139,22 @@ async function loadValueFile(interfaceValueFile, configName, userRootDir) {
139
139
  interfaceValueFile.configMap[configName_] = { configValue };
140
140
  });
141
141
  }
142
+ async function loadImportedFile(filePath, userRootDir, importedFilesLoaded) {
143
+ const f = filePath.filePathAbsoluteFilesystem;
144
+ if (!importedFilesLoaded[f]) {
145
+ importedFilesLoaded[f] = transpileAndExecuteFile(filePath, true, userRootDir).then((r) => r.fileExports);
146
+ }
147
+ const fileExports = await importedFilesLoaded[f];
148
+ return fileExports;
149
+ }
150
+ function isConfigEnv(configDef, configName) {
151
+ if (configDef.cumulative)
152
+ return true;
153
+ if (configDef.env === 'config-only')
154
+ return true;
155
+ // TODO: replace with proper `env: { config: boolean }` implementation
156
+ return configName === 'clientRouting';
157
+ }
142
158
  function getInterfaceFileFromConfigFile(configFile, isConfigExtend) {
143
159
  const { fileExports, filePath, extendsFilePaths } = configFile;
144
160
  const interfaceFile = {
@@ -199,7 +215,8 @@ async function loadVikeConfig_withErrorHandling(userRootDir, outDirRoot, isDev,
199
215
  }
200
216
  async function loadVikeConfig(userRootDir, outDirRoot, isDev, extensions) {
201
217
  const interfaceFilesByLocationId = await loadInterfaceFiles(userRootDir, outDirRoot, isDev, extensions);
202
- const { globalVikeConfig, pageConfigGlobal } = getGlobalConfigs(interfaceFilesByLocationId, userRootDir);
218
+ const importedFilesLoaded = {};
219
+ const { globalVikeConfig, pageConfigGlobal } = await getGlobalConfigs(interfaceFilesByLocationId, userRootDir, importedFilesLoaded);
203
220
  const pageConfigs = await Promise.all(Object.entries(interfaceFilesByLocationId)
204
221
  .filter(([_pageId, interfaceFiles]) => isDefiningPage(interfaceFiles))
205
222
  .map(async ([locationId]) => {
@@ -213,24 +230,24 @@ async function loadVikeConfig(userRootDir, outDirRoot, isDev, extensions) {
213
230
  if (isGlobalConfig(configName))
214
231
  return;
215
232
  const configDef = getConfigDefinition(configDefinitionsRelevant, configName, interfaceFile.filePath.filePathToShowToUser);
216
- if (configDef.env !== 'config-only')
233
+ if (!isConfigEnv(configDef, configName))
217
234
  return;
218
235
  const isAlreadyLoaded = interfacefileIsAlreaydLoaded(interfaceFile);
219
236
  if (isAlreadyLoaded)
220
237
  return;
221
- // Value files for built-in confg-only configs should have already been loaded at loadInterfaceFiles()
238
+ // Value files of built-in configs should have already been loaded at loadInterfaceFiles()
222
239
  assert(!(configName in configDefinitionsBuiltIn));
223
240
  await loadValueFile(interfaceFile, configName, userRootDir);
224
241
  }));
225
242
  const configValueSources = {};
226
- objectEntries(configDefinitionsRelevant)
243
+ await Promise.all(objectEntries(configDefinitionsRelevant)
227
244
  .filter(([configName]) => !isGlobalConfig(configName))
228
- .forEach(([configName, configDef]) => {
229
- const sources = resolveConfigValueSources(configName, configDef, interfaceFilesRelevant, userRootDir);
230
- if (!sources)
245
+ .map(async ([configName, configDef]) => {
246
+ const sources = await resolveConfigValueSources(configName, configDef, interfaceFilesRelevant, userRootDir, importedFilesLoaded);
247
+ if (sources.length === 0)
231
248
  return;
232
249
  configValueSources[configName] = sources;
233
- });
250
+ }));
234
251
  const { routeFilesystem, isErrorPage } = determineRouteFilesystem(locationId, configValueSources);
235
252
  applyEffectsAll(configValueSources, configDefinitionsRelevant);
236
253
  const configValuesComputed = getComputed(configValueSources, configDefinitionsRelevant);
@@ -280,7 +297,7 @@ function getInterfaceFileList(interfaceFilesByLocationId) {
280
297
  });
281
298
  return interfaceFiles;
282
299
  }
283
- function getGlobalConfigs(interfaceFilesByLocationId, userRootDir) {
300
+ async function getGlobalConfigs(interfaceFilesByLocationId, userRootDir, importedFilesLoaded) {
284
301
  const locationIds = Object.keys(interfaceFilesByLocationId);
285
302
  const interfaceFilesGlobal = Object.fromEntries(Object.entries(interfaceFilesByLocationId).filter(([locationId]) => {
286
303
  return isGlobalLocation(locationId, locationIds);
@@ -316,9 +333,9 @@ function getGlobalConfigs(interfaceFilesByLocationId, userRootDir) {
316
333
  const pageConfigGlobal = {
317
334
  configValueSources: {}
318
335
  };
319
- objectEntries(configDefinitionsBuiltInGlobal).forEach(([configName, configDef]) => {
320
- const sources = resolveConfigValueSources(configName, configDef, interfaceFilesGlobal, userRootDir);
321
- const configValueSource = sources?.[0];
336
+ await Promise.all(objectEntries(configDefinitionsBuiltInGlobal).map(async ([configName, configDef]) => {
337
+ const sources = await resolveConfigValueSources(configName, configDef, interfaceFilesGlobal, userRootDir, importedFilesLoaded);
338
+ const configValueSource = sources[0];
322
339
  if (!configValueSource)
323
340
  return;
324
341
  if (configName === 'onBeforeRoute' || configName === 'onPrerenderStart') {
@@ -333,23 +350,21 @@ function getGlobalConfigs(interfaceFilesByLocationId, userRootDir) {
333
350
  assertWarning(false, `Being able to define config ${pc.cyan(configName)} in ${filePathToShowToUser} is experimental and will likely be removed. Define the config ${pc.cyan(configName)} in Vike's Vite plugin options instead.`, { onlyOnce: true });
334
351
  globalVikeConfig[configName] = configValueSource.value;
335
352
  }
336
- });
353
+ }));
337
354
  return { pageConfigGlobal, globalVikeConfig };
338
355
  }
339
- function resolveConfigValueSources(configName, configDef, interfaceFilesRelevant, userRootDir) {
340
- let sources = null;
356
+ async function resolveConfigValueSources(configName, configDef, interfaceFilesRelevant, userRootDir, importedFilesLoaded) {
357
+ const sourcesInfo = [];
341
358
  // interfaceFilesRelevant is sorted by sortAfterInheritanceOrder()
342
359
  for (const interfaceFiles of Object.values(interfaceFilesRelevant)) {
343
360
  const interfaceFilesDefiningConfig = interfaceFiles.filter((interfaceFile) => interfaceFile.configMap[configName]);
344
361
  if (interfaceFilesDefiningConfig.length === 0)
345
362
  continue;
346
- sources = sources ?? [];
347
363
  const visited = new WeakSet();
348
364
  const add = (interfaceFile) => {
349
365
  assert(!visited.has(interfaceFile));
350
366
  visited.add(interfaceFile);
351
- const configValueSource = getConfigValueSource(configName, interfaceFile, configDef, userRootDir);
352
- sources.push(configValueSource);
367
+ sourcesInfo.push([configName, interfaceFile, configDef, userRootDir, importedFilesLoaded]);
353
368
  };
354
369
  // Main resolution logic
355
370
  {
@@ -373,7 +388,7 @@ function resolveConfigValueSources(configName, configDef, interfaceFilesRelevant
373
388
  if (interfaceFileWinner) {
374
389
  const interfaceFilesOverriden = [...interfaceValueFiles, ...interfaceConfigFiles].filter((f) => f !== interfaceFileWinner);
375
390
  // A user-land conflict of interfaceFiles with the same locationId means that the user has superfluously defined the config twice; the user should remove such redundancy making things unnecessarily ambiguous
376
- warnOverridenConfigValues(interfaceFileWinner, interfaceFilesOverriden, configName, configDef, userRootDir);
391
+ warnOverridenConfigValues(interfaceFileWinner, interfaceFilesOverriden, configName);
377
392
  [interfaceFileWinner, ...interfaceFilesOverriden].forEach((interfaceFile) => {
378
393
  add(interfaceFile);
379
394
  });
@@ -398,7 +413,7 @@ function resolveConfigValueSources(configName, configDef, interfaceFilesRelevant
398
413
  assert(visited.has(interfaceFile));
399
414
  });
400
415
  }
401
- assert(sources === null || sources.length > 0);
416
+ const sources = await Promise.all(sourcesInfo.map(async (args) => await getConfigValueSource(...args)));
402
417
  return sources;
403
418
  }
404
419
  function makeOrderDeterministic(interfaceFile1, interfaceFile2) {
@@ -409,22 +424,17 @@ function makeOrderDeterministic(interfaceFile1, interfaceFile2) {
409
424
  return filePathRelativeToUserRootDir.length;
410
425
  })(interfaceFile1, interfaceFile2);
411
426
  }
412
- function warnOverridenConfigValues(interfaceFileWinner, interfaceFilesOverriden, configName, configDef, userRootDir) {
427
+ function warnOverridenConfigValues(interfaceFileWinner, interfaceFilesOverriden, configName) {
413
428
  interfaceFilesOverriden.forEach((interfaceFileLoser) => {
414
- const configValueSourceLoser_ = getConfigValueSource(configName, interfaceFileLoser, configDef, userRootDir);
415
- const configValueSourceWinner = getConfigValueSource(configName, interfaceFileWinner, configDef, userRootDir);
416
- // prettier-ignore
417
- const configLoser_DefinedAt = getConfigDefinedAtString('Config', configName, configValueSourceLoser_);
418
- // prettier-ignore
419
- const configWinnerDefinedAt = getConfigDefinedAtString('config', configName, configValueSourceWinner);
420
- const errMsg = `${configLoser_DefinedAt} is overriden by another ${configWinnerDefinedAt}, remove one of the two`;
421
- assertWarning(false, errMsg, { onlyOnce: false });
429
+ const loserFilePath = interfaceFileLoser.filePath.filePathToShowToUser;
430
+ const winnerFilePath = interfaceFileWinner.filePath.filePathToShowToUser;
431
+ assertWarning(false, `Config ${configName} defined at ${loserFilePath} is always overwritten by ${configName} defined at ${winnerFilePath}, remove the superfluous ${configName} value defined at ${interfaceFileLoser}`, { onlyOnce: false });
422
432
  });
423
433
  }
424
434
  function isInterfaceFileUserLand(interfaceFile) {
425
435
  return (interfaceFile.isConfigFile && !interfaceFile.isConfigExtend) || interfaceFile.isValueFile;
426
436
  }
427
- function getConfigValueSource(configName, interfaceFile, configDef, userRootDir) {
437
+ async function getConfigValueSource(configName, interfaceFile, configDef, userRootDir, importedFilesLoaded) {
428
438
  const conf = interfaceFile.configMap[configName];
429
439
  assert(conf);
430
440
  const configEnv = configDef.env;
@@ -470,6 +480,18 @@ function getConfigValueSource(configName, interfaceFile, configDef, userRootDir)
470
480
  valueIsImportedAtRuntime: true,
471
481
  definedAt: import_
472
482
  };
483
+ // Load config value
484
+ if (isConfigEnv(configDef, configName)) {
485
+ if (import_.filePathAbsoluteFilesystem) {
486
+ assert(hasProp(import_, 'filePathAbsoluteFilesystem', 'string')); // Help TS
487
+ const fileExports = await loadImportedFile(import_, userRootDir, importedFilesLoaded);
488
+ configValueSource.value = fileExports[import_.fileExportName];
489
+ }
490
+ else {
491
+ const configDefinedAt = getConfigDefinedAtString('Config', configName, configValueSource);
492
+ assertUsage(!configDef.cumulative, `${configDefinedAt} cannot be defined over an aliased import`);
493
+ }
494
+ }
473
495
  return configValueSource;
474
496
  }
475
497
  else {
@@ -1109,12 +1131,8 @@ function mergeCumulative(configName, configValueSources) {
1109
1131
  let configValueSourcePrevious = null;
1110
1132
  configValueSources.forEach((configValueSource) => {
1111
1133
  const configDefinedAt = getConfigDefinedAtString('Config', configName, configValueSource);
1112
- const configNameColored = pc.cyan(configName);
1113
- // We could, in principle, also support cumulative values to be defined in +${configName}.js but it ins't completely trivial to implement
1114
- assertUsage('value' in configValueSource, `${configDefinedAt} is only allowed to be defined in a +config.h.js file. (Because the values of ${configNameColored} are cumulative.)`);
1115
- /* This is more confusing than adding value. For example, this explanation shouldn't be shown for the passToClient config.
1116
- const explanation = `(Because the values of ${configNameColored} are cumulative and therefore merged together.)` as const
1117
- */
1134
+ // We could, in principle, also support cumulative for values that aren't loaded at config-time but it isn't completely trivial to implement.
1135
+ assert('value' in configValueSource);
1118
1136
  // Make sure configValueSource.value is serializable
1119
1137
  getConfigValueSerialized(configValueSource.value, configName, getDefinedAt(configValueSource));
1120
1138
  const assertNoMixing = (isSet) => {
@@ -113,6 +113,7 @@ function getConfigValueSerialized(value, configName, definedAt) {
113
113
  serializationErrMsg = err.messageCore;
114
114
  }
115
115
  else {
116
+ // When a property getter throws an error
116
117
  console.error('Serialization error:');
117
118
  console.error(err);
118
119
  serializationErrMsg = 'see serialization error printed above';
@@ -13,6 +13,7 @@ function previewConfig() {
13
13
  apply: 'serve',
14
14
  config(config) {
15
15
  return {
16
+ appType: 'custom',
16
17
  build: {
17
18
  outDir: resolveOutDir(config)
18
19
  }
@@ -23,6 +24,10 @@ function previewConfig() {
23
24
  configVike = await getConfigVike(config);
24
25
  },
25
26
  configurePreviewServer(server) {
27
+ /* - Couldn't make `appType: 'mpa'` work as of npm:@brillout/vite@5.0.0-beta.14.0426910c
28
+ - This ugly hack to set appType for preview won't be need once https://github.com/vitejs/vite/pull/14855 is merged.
29
+ config.appType = 'mpa'
30
+ */
26
31
  markEnvAsPreview();
27
32
  return () => {
28
33
  assertDist();
@@ -5,7 +5,7 @@ export { prerenderForceExit };
5
5
  import '../runtime/page-files/setup.js';
6
6
  import path from 'path';
7
7
  import { route } from '../../shared/route/index.js';
8
- import { assert, assertUsage, assertWarning, hasProp, projectInfo, objectAssign, isObjectWithKeys, isCallable, getOutDirs, hasPropertyGetter, assertPosixPath, urlToFile, executeHook, isPlainObject, setNodeEnvToProduction } from './utils.js';
8
+ import { assert, assertUsage, assertWarning, hasProp, projectInfo, objectAssign, isObjectWithKeys, isCallable, getOutDirs, isPropertyGetter, assertPosixPath, urlToFile, executeHook, isPlainObject, setNodeEnvToProduction } from './utils.js';
9
9
  import { pLimit } from '../../utils/pLimit.js';
10
10
  import { prerenderPage, prerender404Page, getRenderContext, getPageContextInitEnhanced } from '../runtime/renderPage/renderPageAlreadyRouted.js';
11
11
  import pc from '@brillout/picocolors';
@@ -244,7 +244,7 @@ async function handlePagesWithStaticRoutes(prerenderContext, renderContext, doNo
244
244
  _providedByHook: null,
245
245
  routeParams,
246
246
  _pageId: pageId,
247
- _routeMatches: [
247
+ _debugRouteMatches: [
248
248
  {
249
249
  pageId,
250
250
  routeType: pageRoute.routeType,
@@ -343,7 +343,7 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
343
343
  enumerable: false,
344
344
  configurable: true
345
345
  });
346
- assert(hasPropertyGetter(pageContext, 'url'));
346
+ assert(isPropertyGetter(pageContext, 'url'));
347
347
  assert(pageContext.urlOriginal);
348
348
  pageContext._urlOriginalBeforeHook = pageContext.urlOriginal;
349
349
  });
@@ -382,7 +382,7 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
382
382
  prerenderContext.pageContexts = result.prerenderContext.pageContexts;
383
383
  prerenderContext.pageContexts.forEach((pageContext) => {
384
384
  // TODO/v1-release: remove
385
- if (!hasPropertyGetter(pageContext, 'url') && pageContext.url) {
385
+ if (pageContext.url && !isPropertyGetter(pageContext, 'url')) {
386
386
  assertWarning(false, msgPrefix +
387
387
  ' provided pageContext.url but it should provide pageContext.urlOriginal instead, see https://vike.dev/migration/0.4.23', { onlyOnce: true });
388
388
  pageContext.urlOriginal = pageContext.url;
@@ -407,10 +407,9 @@ async function routeAndPrerender(prerenderContext, htmlFiles, prerenderPageIds,
407
407
  await Promise.all(prerenderContext.pageContexts.map((pageContext) => concurrencyLimit(async () => {
408
408
  const { urlOriginal } = pageContext;
409
409
  assert(urlOriginal);
410
- const routeResult = await route(pageContext);
411
- assert(hasProp(routeResult.pageContextAddendum, '_pageId', 'null') ||
412
- hasProp(routeResult.pageContextAddendum, '_pageId', 'string'));
413
- if (routeResult.pageContextAddendum._pageId === null) {
410
+ const pageContextFromRoute = await route(pageContext);
411
+ assert(hasProp(pageContextFromRoute, '_pageId', 'null') || hasProp(pageContextFromRoute, '_pageId', 'string'));
412
+ if (pageContextFromRoute._pageId === null) {
414
413
  let hookName;
415
414
  let hookFilePath;
416
415
  if (pageContext._providedByHook) {
@@ -423,17 +422,17 @@ async function routeAndPrerender(prerenderContext, htmlFiles, prerenderPageIds,
423
422
  }
424
423
  if (hookName) {
425
424
  assert(hookFilePath);
426
- assertUsage(false, `The ${hookName}() hook defined by ${hookFilePath} returns a URL ${pc.cyan(urlOriginal)} that doesn't match any of your page routes. Make sure that the URLs returned by ${hookName}() always match the route of a page.`);
425
+ assertUsage(false, `The ${hookName}() hook defined by ${hookFilePath} returns a URL ${pc.cyan(urlOriginal)} that doesn't match the route of any of your pages. Make sure that the URLs returned by ${hookName}() always match the route of a page.`);
427
426
  }
428
427
  else {
429
428
  // `prerenderHookFile` is `null` when the URL was deduced by the Filesytem Routing of `.page.js` files. The `onBeforeRoute()` can override Filesystem Routing; it is therefore expected that the deduced URL may not match any page.
430
- assert(routeResult.pageContextAddendum._routingProvidedByOnBeforeRouteHook);
429
+ assert(pageContextFromRoute._routingProvidedByOnBeforeRouteHook);
431
430
  // Abort since the URL doesn't correspond to any page
432
431
  return;
433
432
  }
434
433
  }
435
- assert(routeResult.pageContextAddendum._pageId);
436
- objectAssign(pageContext, routeResult.pageContextAddendum);
434
+ assert(pageContextFromRoute._pageId);
435
+ objectAssign(pageContext, pageContextFromRoute);
437
436
  const { _pageId: pageId } = pageContext;
438
437
  objectAssign(pageContext, await loadPageFilesServerSide(pageContext));
439
438
  let usesClientRouter;
@@ -5,7 +5,7 @@ export * from '../../utils/objectAssign.js';
5
5
  export * from '../../utils/isObjectWithKeys.js';
6
6
  export * from '../../utils/isCallable.js';
7
7
  export * from '../../utils/getOutDirs.js';
8
- export * from '../../utils/hasPropertyGetter.js';
8
+ export * from '../../utils/isPropertyGetter.js';
9
9
  export * from '../../utils/filesystemPathHandling.js';
10
10
  export * from '../../utils/urlToFile.js';
11
11
  export * from '../../shared/hooks/executeHook.js';
@@ -7,7 +7,7 @@ export * from '../../utils/objectAssign.js';
7
7
  export * from '../../utils/isObjectWithKeys.js';
8
8
  export * from '../../utils/isCallable.js';
9
9
  export * from '../../utils/getOutDirs.js';
10
- export * from '../../utils/hasPropertyGetter.js';
10
+ export * from '../../utils/isPropertyGetter.js';
11
11
  export * from '../../utils/filesystemPathHandling.js';
12
12
  export * from '../../utils/urlToFile.js';
13
13
  export * from '../../shared/hooks/executeHook.js';