vike 0.4.147 → 0.4.148-commit-7596dcd

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 (74) hide show
  1. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getConfigValuesSerialized.js +17 -12
  2. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +3 -0
  3. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +116 -0
  4. package/dist/cjs/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +33 -45
  5. package/dist/cjs/node/prerender/runPrerender.js +85 -84
  6. package/dist/cjs/node/runtime/html/injectAssets/injectAssets__public.js +1 -1
  7. package/dist/cjs/node/runtime/html/renderHtml.js +1 -1
  8. package/dist/cjs/node/runtime/renderPage/createHttpResponseObject/assertNoInfiniteHttpRedirect.js +12 -12
  9. package/dist/cjs/node/runtime/renderPage/createHttpResponseObject.js +3 -3
  10. package/dist/cjs/node/runtime/renderPage/executeOnBeforeRenderHook.js +1 -1
  11. package/dist/cjs/node/runtime/renderPage/executeOnRenderHtmlHook.js +3 -3
  12. package/dist/cjs/node/runtime/renderPage/getHttpResponseBody.js +1 -1
  13. package/dist/cjs/node/runtime/renderPage/renderPageAlreadyRouted.js +21 -18
  14. package/dist/cjs/node/runtime/renderPage.js +73 -49
  15. package/dist/cjs/shared/getPageFiles/parseGlobResults.js +3 -3
  16. package/dist/cjs/shared/hooks/executeHook.js +18 -29
  17. package/dist/cjs/shared/hooks/getHook.js +104 -3
  18. package/dist/cjs/shared/page-configs/helpers/getConfigDefinedAtString.js +1 -1
  19. package/dist/cjs/shared/route/executeGuardHook.js +3 -2
  20. package/dist/cjs/shared/route/executeOnBeforeRouteHook.js +4 -4
  21. package/dist/cjs/shared/route/loadPageRoutes.js +10 -15
  22. package/dist/cjs/shared/route/resolveRedirects.js +8 -5
  23. package/dist/cjs/utils/parseUrl-extras.js +6 -1
  24. package/dist/cjs/utils/parseUrl.js +24 -16
  25. package/dist/cjs/utils/projectInfo.js +1 -1
  26. package/dist/esm/client/client-routing-runtime/createPageContext.d.ts +1 -1
  27. package/dist/esm/client/client-routing-runtime/getPageContextFromHooks.js +20 -10
  28. package/dist/esm/client/client-routing-runtime/onBrowserHistoryNavigation.js +2 -2
  29. package/dist/esm/client/client-routing-runtime/renderPageClientSide.js +18 -12
  30. package/dist/esm/client/shared/executeOnRenderClientHook.js +1 -1
  31. package/dist/esm/client/shared/getPageContextSerializedInHtml.js +1 -1
  32. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getConfigValuesSerialized.js +17 -12
  33. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/configDefinitionsBuiltIn.js +3 -0
  34. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.d.ts +5 -0
  35. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig/crawlPlusFiles.js +110 -0
  36. package/dist/esm/node/plugin/plugins/importUserCode/v1-design/getVikeConfig.js +34 -46
  37. package/dist/esm/node/prerender/runPrerender.js +87 -86
  38. package/dist/esm/node/runtime/html/injectAssets/injectAssets__public.js +1 -1
  39. package/dist/esm/node/runtime/html/renderHtml.js +1 -1
  40. package/dist/esm/node/runtime/renderPage/createHttpResponseObject/assertNoInfiniteHttpRedirect.d.ts +1 -1
  41. package/dist/esm/node/runtime/renderPage/createHttpResponseObject/assertNoInfiniteHttpRedirect.js +13 -13
  42. package/dist/esm/node/runtime/renderPage/createHttpResponseObject.d.ts +1 -1
  43. package/dist/esm/node/runtime/renderPage/createHttpResponseObject.js +3 -3
  44. package/dist/esm/node/runtime/renderPage/executeOnBeforeRenderHook.js +1 -1
  45. package/dist/esm/node/runtime/renderPage/executeOnRenderHtmlHook.d.ts +2 -2
  46. package/dist/esm/node/runtime/renderPage/executeOnRenderHtmlHook.js +3 -3
  47. package/dist/esm/node/runtime/renderPage/getHttpResponseBody.js +1 -1
  48. package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.d.ts +7 -7
  49. package/dist/esm/node/runtime/renderPage/renderPageAlreadyRouted.js +22 -19
  50. package/dist/esm/node/runtime/renderPage.js +74 -50
  51. package/dist/esm/shared/getPageFiles/parseGlobResults.js +1 -1
  52. package/dist/esm/shared/hooks/executeHook.d.ts +2 -2
  53. package/dist/esm/shared/hooks/executeHook.js +18 -29
  54. package/dist/esm/shared/hooks/getHook.d.ts +17 -7
  55. package/dist/esm/shared/hooks/getHook.js +103 -3
  56. package/dist/esm/shared/page-configs/Config.d.ts +21 -13
  57. package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.d.ts +1 -1
  58. package/dist/esm/shared/page-configs/helpers/getConfigDefinedAtString.js +1 -1
  59. package/dist/esm/shared/route/executeGuardHook.js +4 -3
  60. package/dist/esm/shared/route/executeOnBeforeRouteHook.d.ts +1 -8
  61. package/dist/esm/shared/route/executeOnBeforeRouteHook.js +6 -6
  62. package/dist/esm/shared/route/index.d.ts +2 -2
  63. package/dist/esm/shared/route/loadPageRoutes.d.ts +2 -2
  64. package/dist/esm/shared/route/loadPageRoutes.js +11 -16
  65. package/dist/esm/shared/route/resolveRedirects.js +8 -5
  66. package/dist/esm/utils/parseUrl-extras.d.ts +2 -0
  67. package/dist/esm/utils/parseUrl-extras.js +5 -0
  68. package/dist/esm/utils/parseUrl.js +24 -16
  69. package/dist/esm/utils/projectInfo.d.ts +2 -2
  70. package/dist/esm/utils/projectInfo.js +1 -1
  71. package/package.json +3 -3
  72. /package/dist/cjs/shared/page-configs/serialize/{assertPageConfigs.js → assertPageConfigsSerialized.js} +0 -0
  73. /package/dist/esm/shared/page-configs/serialize/{assertPageConfigs.d.ts → assertPageConfigsSerialized.d.ts} +0 -0
  74. /package/dist/esm/shared/page-configs/serialize/{assertPageConfigs.js → assertPageConfigsSerialized.js} +0 -0
@@ -2,10 +2,9 @@ export { getVikeConfig };
2
2
  export { reloadVikeConfig };
3
3
  export { vikeConfigDependencies };
4
4
  export { isVikeConfigFile };
5
- import { assertPosixPath, assert, isObject, assertUsage, toPosixPath, assertWarning, objectEntries, hasProp, arrayIncludes, assertIsNotProductionRuntime, getMostSimilar, isNpmPackageImport, joinEnglish, lowerFirst, scriptFileExtensions, mergeCumulativeValues, requireResolve, getOutDirs, deepEqual, assertKeys } from '../../../utils.js';
5
+ import { assertPosixPath, assert, isObject, assertUsage, assertWarning, objectEntries, hasProp, arrayIncludes, assertIsNotProductionRuntime, getMostSimilar, isNpmPackageImport, joinEnglish, lowerFirst, mergeCumulativeValues, requireResolve, getOutDirs, deepEqual, assertKeys } from '../../../utils.js';
6
6
  import path from 'path';
7
7
  import { configDefinitionsBuiltIn, configDefinitionsBuiltInGlobal } from './getVikeConfig/configDefinitionsBuiltIn.js';
8
- import glob from 'fast-glob';
9
8
  import { getLocationId, getFilesystemRouteString, getFilesystemRouteDefinedBy, isInherited, sortAfterInheritanceOrder, isGlobalLocation, applyFilesystemRoutingRootEffect } from './getVikeConfig/filesystemRouting.js';
10
9
  import { isTmpFile, transpileAndExecuteFile } from './transpileAndExecuteFile.js';
11
10
  import { parseImportData } from './replaceImportStatements.js';
@@ -18,6 +17,7 @@ import { getConfigDefinedAtString } from '../../../../../shared/page-configs/hel
18
17
  import { assertExportsOfConfigFile, assertExportsOfValueFile } from '../../../../../shared/page-configs/assertExports.js';
19
18
  import { getConfigVike } from '../../../../shared/getConfigVike.js';
20
19
  import { assertConfigValueIsSerializable } from './getConfigValuesSerialized.js';
20
+ import { crawlPlusFiles } from './getVikeConfig/crawlPlusFiles.js';
21
21
  assertIsNotProductionRuntime();
22
22
  let devServerIsCorrupt = false;
23
23
  let wasConfigInvalid = null;
@@ -70,7 +70,7 @@ async function getVikeConfig(config, isDev, tolerateInvalidConfig = false, exten
70
70
  return await vikeConfigPromise;
71
71
  }
72
72
  async function loadInterfaceFiles(userRootDir, outDirRoot, isDev, extensions) {
73
- const plusFiles = await findPlusFiles(userRootDir, [outDirRoot], isDev, extensions);
73
+ const plusFiles = await findPlusFiles(userRootDir, outDirRoot, isDev, extensions);
74
74
  const configFiles = [];
75
75
  const valueFiles = [];
76
76
  plusFiles.forEach((f) => {
@@ -122,6 +122,7 @@ async function loadInterfaceFiles(userRootDir, outDirRoot, isDev, extensions) {
122
122
  interfaceFilesByLocationId[locationId].push(interfaceFile);
123
123
  }
124
124
  }));
125
+ assertAllConfigsAreKnown(interfaceFilesByLocationId);
125
126
  return interfaceFilesByLocationId;
126
127
  }
127
128
  function getConfigDefinition(configDefinitionsRelevant, configName, filePathToShowToUser) {
@@ -175,6 +176,18 @@ function getInterfaceFileFromConfigFile(configFile, isConfigExtend) {
175
176
  });
176
177
  return interfaceFile;
177
178
  }
179
+ /** Show error message upon unknown config */
180
+ function assertAllConfigsAreKnown(interfaceFilesByLocationId) {
181
+ Object.entries(interfaceFilesByLocationId).forEach(([locationId, interfaceFiles]) => {
182
+ const interfaceFilesRelevant = getInterfaceFilesRelevant(interfaceFilesByLocationId, locationId);
183
+ const configDefinitionsRelevant = getConfigDefinitions(interfaceFilesRelevant);
184
+ interfaceFiles.forEach((interfaceFile) => {
185
+ Object.keys(interfaceFile.configMap).forEach((configName) => {
186
+ assertConfigExists(configName, Object.keys(configDefinitionsRelevant), interfaceFile.filePath.filePathToShowToUser);
187
+ });
188
+ });
189
+ });
190
+ }
178
191
  async function loadVikeConfig_withErrorHandling(userRootDir, outDirRoot, isDev, extensions, tolerateInvalidConfig) {
179
192
  let hasError = false;
180
193
  let ret;
@@ -265,18 +278,23 @@ async function loadVikeConfig(userRootDir, outDirRoot, isDev, extensions) {
265
278
  };
266
279
  return pageConfig;
267
280
  }));
268
- // Show error message upon unknown config
269
- Object.entries(interfaceFilesByLocationId).forEach(([locationId, interfaceFiles]) => {
270
- const interfaceFilesRelevant = getInterfaceFilesRelevant(interfaceFilesByLocationId, locationId);
271
- const configDefinitionsRelevant = getConfigDefinitions(interfaceFilesRelevant);
272
- interfaceFiles.forEach((interfaceFile) => {
273
- Object.keys(interfaceFile.configMap).forEach((configName) => {
274
- assertConfigExists(configName, Object.keys(configDefinitionsRelevant), interfaceFile.filePath.filePathToShowToUser);
275
- });
276
- });
277
- });
281
+ assertPageConfigs(pageConfigs);
278
282
  return { pageConfigs, pageConfigGlobal, globalVikeConfig };
279
283
  }
284
+ function assertPageConfigs(pageConfigs) {
285
+ pageConfigs.forEach((pageConfig) => {
286
+ assertOnBeforeRenderEnv(pageConfig);
287
+ });
288
+ }
289
+ function assertOnBeforeRenderEnv(pageConfig) {
290
+ const onBeforeRenderConfig = pageConfig.configValueSources.onBeforeRender?.[0];
291
+ if (!onBeforeRenderConfig)
292
+ return;
293
+ const onBeforeRenderEnv = onBeforeRenderConfig.configEnv;
294
+ const isClientRouting = !!pageConfig.configValues.clientRouting?.value;
295
+ // When using Server Routing, loading a onBeforeRender() hook on the client-side hasn't any effect (the Server Routing's client runtime never calls it); it unnecessarily bloats client bundle sizes
296
+ assertUsage(!(onBeforeRenderEnv.client && !isClientRouting), `Page ${pageConfig.pageId} has an onBeforeRender() hook with env ${pc.cyan(JSON.stringify(onBeforeRenderEnv))} which doesn't make sense because the page is using Server Routing: onBeforeRender() can be run in the client only when using Client Routing.`);
297
+ }
280
298
  function interfacefileIsAlreaydLoaded(interfaceFile) {
281
299
  const configMapValues = Object.values(interfaceFile.configMap);
282
300
  const isAlreadyLoaded = configMapValues.some((conf) => 'configValue' in conf);
@@ -776,39 +794,9 @@ function getComputed(configValueSources, configDefinitionsRelevant) {
776
794
  });
777
795
  return configValuesComputed;
778
796
  }
779
- async function findPlusFiles(userRootDir, ignoreDirs, isDev, extensions) {
780
- const timeBase = new Date().getTime();
781
- assertPosixPath(userRootDir);
782
- const ignorePatterns = [];
783
- for (const dir of ignoreDirs) {
784
- assertPosixPath(dir);
785
- ignorePatterns.push(`${path.posix.relative(userRootDir, dir)}/**`);
786
- }
787
- const result = await glob(`**/+*.${scriptFileExtensions}`, {
788
- ignore: [
789
- '**/node_modules/**',
790
- // Allow:
791
- // ```
792
- // +Page.js
793
- // +Page.telefunc.js
794
- // ```
795
- '**/*.telefunc.*',
796
- ...ignorePatterns
797
- ],
798
- cwd: userRootDir,
799
- dot: false
800
- });
801
- const time = new Date().getTime() - timeBase;
802
- if (isDev) {
803
- // We only warn in dev, because while building it's expected to take a long time as fast-glob is competing for resources with other tasks
804
- assertWarning(time < 2 * 1000, `Crawling your user files took an unexpected long time (${time}ms). Create a new issue on Vike's GitHub.`, {
805
- onlyOnce: 'slow-page-files-search'
806
- });
807
- }
808
- const plusFiles = result.map((p) => {
809
- p = toPosixPath(p);
810
- const filePathRelativeToUserRootDir = path.posix.join('/', p);
811
- const filePathAbsoluteFilesystem = path.posix.join(userRootDir, p);
797
+ async function findPlusFiles(userRootDir, outDirRoot, isDev, extensions) {
798
+ const files = await crawlPlusFiles(userRootDir, outDirRoot, isDev);
799
+ const plusFiles = files.map(({ filePathRelativeToUserRootDir, filePathAbsoluteFilesystem }) => {
812
800
  return {
813
801
  filePathRelativeToUserRootDir,
814
802
  filePathAbsoluteVite: filePathRelativeToUserRootDir,
@@ -16,14 +16,14 @@ import { getConfigVike } from '../shared/getConfigVike.js';
16
16
  import { getPageFilesServerSide } from '../../shared/getPageFiles.js';
17
17
  import { getPageContextRequestUrl } from '../../shared/getPageContextRequestUrl.js';
18
18
  import { getUrlFromRouteString } from '../../shared/route/resolveRouteString.js';
19
- import { getConfigValue, getConfigValueFilePathToShowToUser, getHookFilePathToShowToUser } from '../../shared/page-configs/helpers.js';
19
+ import { getConfigValue, getConfigValueFilePathToShowToUser } from '../../shared/page-configs/helpers.js';
20
20
  import { loadConfigValues } from '../../shared/page-configs/loadConfigValues.js';
21
21
  import { isErrorPage } from '../../shared/error-page.js';
22
22
  import { addUrlComputedProps } from '../../shared/addUrlComputedProps.js';
23
23
  import { assertPathIsFilesystemAbsolute } from '../../utils/assertPathIsFilesystemAbsolute.js';
24
24
  import { isAbortError } from '../../shared/route/abort.js';
25
25
  import { loadPageFilesServerSide } from '../runtime/renderPage/loadPageFilesServerSide.js';
26
- import { assertHookFn } from '../../shared/hooks/getHook.js';
26
+ import { getHookFromPageConfig, getHookFromPageConfigGlobal, getHookTimeoutDefault } from '../../shared/hooks/getHook.js';
27
27
  import { noRouteMatch } from '../../shared/route/noRouteMatch.js';
28
28
  import { getVikeConfig } from '../plugin/plugins/importUserCode/v1-design/getVikeConfig.js';
29
29
  async function prerenderFromAPI(options = {}) {
@@ -74,16 +74,22 @@ async function runPrerender(options, manuallyTriggered) {
74
74
  await callOnBeforePrerenderStartHooks(prerenderContext, renderContext, concurrencyLimit, doNotPrerenderList);
75
75
  await handlePagesWithStaticRoutes(prerenderContext, renderContext, doNotPrerenderList, concurrencyLimit);
76
76
  await callOnPrerenderStartHook(prerenderContext, renderContext);
77
- const prerenderedPages = {};
78
- const htmlFiles = [];
79
- await routeAndPrerender(prerenderContext, htmlFiles, prerenderedPages, concurrencyLimit);
80
- warnContradictoryNoPrerenderList(prerenderedPages, doNotPrerenderList);
81
- await prerender404(htmlFiles, renderContext, prerenderContext);
77
+ const prerenderedPageContexts = {};
78
+ let prerenderedCount = 0;
79
+ const onComplete = async (htmlFile) => {
80
+ prerenderedCount++;
81
+ if (htmlFile.pageId) {
82
+ prerenderedPageContexts[htmlFile.pageId] = htmlFile.pageContext;
83
+ }
84
+ await writeFiles(htmlFile, root, outDirClient, options.onPagePrerender, logLevel);
85
+ };
86
+ await routeAndPrerender(prerenderContext, concurrencyLimit, onComplete);
87
+ warnContradictoryNoPrerenderList(prerenderedPageContexts, doNotPrerenderList);
88
+ await prerender404(prerenderedPageContexts, renderContext, prerenderContext, onComplete);
82
89
  if (logLevel === 'info') {
83
- console.log(`${pc.green(`✓`)} ${htmlFiles.length} HTML documents pre-rendered.`);
90
+ console.log(`${pc.green(`✓`)} ${prerenderedCount} HTML documents pre-rendered.`);
84
91
  }
85
- await Promise.all(htmlFiles.map((htmlFile) => writeHtmlFile(htmlFile, root, outDirClient, concurrencyLimit, options.onPagePrerender, logLevel)));
86
- warnMissingPages(prerenderedPages, doNotPrerenderList, renderContext, partial);
92
+ warnMissingPages(prerenderedPageContexts, doNotPrerenderList, renderContext, partial);
87
93
  }
88
94
  async function collectDoNoPrerenderList(renderContext, pageConfigs, doNotPrerenderList, concurrencyLimit) {
89
95
  // V1 design
@@ -151,18 +157,16 @@ async function callOnBeforePrerenderStartHooks(prerenderContext, renderContext,
151
157
  await Promise.all(renderContext.pageConfigs.map((pageConfig) => concurrencyLimit(async () => {
152
158
  const hookName = 'onBeforePrerenderStart';
153
159
  const pageConfigLoaded = await loadConfigValues(pageConfig, false);
154
- const configValue = getConfigValue(pageConfigLoaded, hookName);
155
- if (!configValue)
160
+ const hook = getHookFromPageConfig(pageConfigLoaded, hookName);
161
+ if (!hook)
156
162
  return;
157
- const hookFn = configValue.value;
158
- const hookFilePath = getHookFilePathToShowToUser(configValue);
159
- assert(hookFilePath);
160
- assertHookFn(hookFn, { hookName, hookFilePath });
163
+ const { hookFn, hookFilePath, hookTimeout } = hook;
161
164
  onBeforePrerenderStartHooks.push({
162
165
  hookFn,
163
166
  hookName: 'onBeforePrerenderStart',
164
167
  hookFilePath,
165
- pageId: pageConfig.pageId
168
+ pageId: pageConfig.pageId,
169
+ hookTimeout
166
170
  });
167
171
  })));
168
172
  // 0.4 design
@@ -186,14 +190,15 @@ async function callOnBeforePrerenderStartHooks(prerenderContext, renderContext,
186
190
  hookFn,
187
191
  hookName: 'prerender',
188
192
  hookFilePath,
189
- pageId: p.pageId
193
+ pageId: p.pageId,
194
+ hookTimeout: getHookTimeoutDefault('onBeforePrerenderStart')
190
195
  });
191
196
  })));
192
- await Promise.all(onBeforePrerenderStartHooks.map(({ hookFn, hookName, hookFilePath, pageId }) => concurrencyLimit(async () => {
197
+ await Promise.all(onBeforePrerenderStartHooks.map(({ hookFn, hookName, hookFilePath, pageId, hookTimeout }) => concurrencyLimit(async () => {
193
198
  if (doNotPrerenderList.find((p) => p.pageId === pageId)) {
194
199
  return;
195
200
  }
196
- const prerenderResult = await hookFn();
201
+ const prerenderResult = await executeHook(() => hookFn(), { hookName, hookFilePath, hookTimeout });
197
202
  const result = normalizeOnPrerenderHookResult(prerenderResult, hookFilePath, hookName);
198
203
  result.forEach(({ url, pageContext }) => {
199
204
  {
@@ -292,23 +297,21 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
292
297
  let onPrerenderStartHook;
293
298
  // V1 design
294
299
  if (renderContext.pageConfigs.length > 0) {
295
- const { pageConfigGlobal } = renderContext;
296
- const configValue = pageConfigGlobal.configValues.onPrerenderStart;
297
- if (configValue?.value) {
298
- const { value: hookFn } = configValue;
299
- // config.onPrerenderStart isn't a computed nor a cumulative config => definedAt should always be defined
300
- const hookFilePath = getHookFilePathToShowToUser(configValue);
301
- assert(hookFilePath);
300
+ const hookName = 'onPrerenderStart';
301
+ const hook = getHookFromPageConfigGlobal(renderContext.pageConfigGlobal, hookName);
302
+ if (hook) {
303
+ assert(hook.hookName === 'onPrerenderStart');
302
304
  onPrerenderStartHook = {
303
- hookFn,
304
- hookName: 'onPrerenderStart',
305
- hookFilePath
305
+ ...hook,
306
+ // Make TypeScript happy
307
+ hookName
306
308
  };
307
309
  }
308
310
  }
309
311
  // Old design
310
312
  // TODO/v1-release: remove
311
313
  if (renderContext.pageConfigs.length === 0) {
314
+ const hookTimeout = getHookTimeoutDefault('onBeforePrerender');
312
315
  const pageFilesWithOnBeforePrerenderHook = renderContext.pageFilesAll.filter((p) => {
313
316
  assertExportNames(p);
314
317
  if (!p.exportNames?.includes('onBeforePrerender'))
@@ -334,7 +337,8 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
334
337
  onPrerenderStartHook = {
335
338
  hookFn: hook.onBeforePrerender,
336
339
  hookFilePath: hook.hookFilePath,
337
- hookName: 'onBeforePrerender'
340
+ hookName: 'onBeforePrerender',
341
+ hookTimeout
338
342
  };
339
343
  }
340
344
  if (!onPrerenderStartHook) {
@@ -369,7 +373,7 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
369
373
  });
370
374
  return prerenderContext.pageContexts;
371
375
  }
372
- }), hookName, hookFilePath);
376
+ }), onPrerenderStartHook);
373
377
  if (result === null || result === undefined) {
374
378
  return;
375
379
  }
@@ -411,7 +415,7 @@ async function callOnPrerenderStartHook(prerenderContext, renderContext) {
411
415
  addUrlComputedProps(pageContext);
412
416
  });
413
417
  }
414
- async function routeAndPrerender(prerenderContext, htmlFiles, prerenderedPages, concurrencyLimit) {
418
+ async function routeAndPrerender(prerenderContext, concurrencyLimit, onComplete) {
415
419
  const globalContext = getGlobalContext();
416
420
  assert(globalContext.isPrerendering);
417
421
  // Route all URLs
@@ -471,7 +475,7 @@ async function routeAndPrerender(prerenderContext, htmlFiles, prerenderedPages,
471
475
  throw err;
472
476
  }
473
477
  const { documentHtml, pageContextSerialized } = res;
474
- htmlFiles.push({
478
+ await onComplete({
475
479
  urlOriginal,
476
480
  pageContext,
477
481
  htmlString: documentHtml,
@@ -479,11 +483,10 @@ async function routeAndPrerender(prerenderContext, htmlFiles, prerenderedPages,
479
483
  doNotCreateExtraDirectory: prerenderContext._noExtraDir,
480
484
  pageId
481
485
  });
482
- prerenderedPages[pageId] = pageContext;
483
486
  })));
484
487
  }
485
- function warnContradictoryNoPrerenderList(prerenderedPages, doNotPrerenderList) {
486
- Object.entries(prerenderedPages).forEach(([pageId, pageContext]) => {
488
+ function warnContradictoryNoPrerenderList(prerenderedPageContexts, doNotPrerenderList) {
489
+ Object.entries(prerenderedPageContexts).forEach(([pageId, pageContext]) => {
487
490
  const doNotPrerenderListEntry = doNotPrerenderList.find((p) => p.pageId === pageId);
488
491
  const { urlOriginal, _providedByHook: providedByHook } = pageContext;
489
492
  {
@@ -495,7 +498,7 @@ function warnContradictoryNoPrerenderList(prerenderedPages, doNotPrerenderList)
495
498
  assertWarning(false, `The ${providedByHook.hookName}() hook defined by ${providedByHook.hookFilePath} returns the URL ${pc.cyan(urlOriginal)}, while ${setByConfigFile} sets the config ${pc.cyan(setByConfigName)} to ${pc.cyan(String(setByConfigValue))}. This is contradictory: either don't set the config ${pc.cyan(setByConfigName)} to ${pc.cyan(String(setByConfigValue))} or remove the URL ${pc.cyan(urlOriginal)} from the list of URLs to be pre-rendered.`, { onlyOnce: true });
496
499
  });
497
500
  }
498
- function warnMissingPages(prerenderedPages, doNotPrerenderList, renderContext, partial) {
501
+ function warnMissingPages(prerenderedPageContexts, doNotPrerenderList, renderContext, partial) {
499
502
  const isV1 = renderContext.pageConfigs.length > 0;
500
503
  const hookName = isV1 ? 'onBeforePrerenderStart' : 'prerender';
501
504
  /* TODO/after-v1-design-release: document setting `prerender: false` as an alternative to using prerender.partial (both in the warnings and the docs)
@@ -503,7 +506,7 @@ function warnMissingPages(prerenderedPages, doNotPrerenderList, renderContext, p
503
506
  const msgAddendum = `Explicitly opt-out by setting the config ${optOutName} to ${isV1 ? 'false' : 'true'} or use the option prerender.partial`
504
507
  */
505
508
  renderContext.allPageIds
506
- .filter((pageId) => !prerenderedPages[pageId])
509
+ .filter((pageId) => !prerenderedPageContexts[pageId])
507
510
  .filter((pageId) => !doNotPrerenderList.find((p) => p.pageId === pageId))
508
511
  .filter((pageId) => !isErrorPage(pageId, renderContext.pageConfigs))
509
512
  .forEach((pageId) => {
@@ -511,8 +514,8 @@ function warnMissingPages(prerenderedPages, doNotPrerenderList, renderContext, p
511
514
  assertWarning(partial, `Cannot pre-render page ${pageAt} because it has a non-static route, while no ${hookName}() hook returned any URL matching the page's route. You need to use a ${hookName}() hook (https://vike.dev/${hookName}) providing a list of URLs for ${pageAt} that should be pre-rendered. If you don't want to pre-render ${pageAt} then use the option prerender.partial (https://vike.dev/prerender-config#partial) to suppress this warning.`, { onlyOnce: true });
512
515
  });
513
516
  }
514
- async function prerender404(htmlFiles, renderContext, prerenderContext) {
515
- if (!htmlFiles.find(({ urlOriginal }) => urlOriginal === '/404')) {
517
+ async function prerender404(prerenderedPageContexts, renderContext, prerenderContext, onComplete) {
518
+ if (!Object.values(prerenderedPageContexts).find(({ urlOriginal }) => urlOriginal === '/404')) {
516
519
  let result;
517
520
  try {
518
521
  result = await prerender404Page(renderContext, prerenderContext.pageContextInit);
@@ -524,7 +527,7 @@ async function prerender404(htmlFiles, renderContext, prerenderContext) {
524
527
  if (result) {
525
528
  const urlOriginal = '/404';
526
529
  const { documentHtml, pageContext } = result;
527
- htmlFiles.push({
530
+ await onComplete({
528
531
  urlOriginal,
529
532
  pageContext,
530
533
  htmlString: documentHtml,
@@ -535,59 +538,57 @@ async function prerender404(htmlFiles, renderContext, prerenderContext) {
535
538
  }
536
539
  }
537
540
  }
538
- async function writeHtmlFile({ urlOriginal, pageContext, htmlString, pageContextSerialized, doNotCreateExtraDirectory }, root, outDirClient, concurrencyLimit, onPagePrerender, logLevel) {
541
+ async function writeFiles({ urlOriginal, pageContext, htmlString, pageContextSerialized, doNotCreateExtraDirectory }, root, outDirClient, onPagePrerender, logLevel) {
539
542
  assert(urlOriginal.startsWith('/'));
540
543
  const writeJobs = [
541
- write(urlOriginal, pageContext, '.html', htmlString, root, outDirClient, doNotCreateExtraDirectory, concurrencyLimit, onPagePrerender, logLevel)
544
+ write(urlOriginal, pageContext, '.html', htmlString, root, outDirClient, doNotCreateExtraDirectory, onPagePrerender, logLevel)
542
545
  ];
543
546
  if (pageContextSerialized !== null) {
544
- writeJobs.push(write(urlOriginal, pageContext, '.pageContext.json', pageContextSerialized, root, outDirClient, doNotCreateExtraDirectory, concurrencyLimit, onPagePrerender, logLevel));
547
+ writeJobs.push(write(urlOriginal, pageContext, '.pageContext.json', pageContextSerialized, root, outDirClient, doNotCreateExtraDirectory, onPagePrerender, logLevel));
545
548
  }
546
549
  await Promise.all(writeJobs);
547
550
  }
548
- function write(urlOriginal, pageContext, fileExtension, fileContent, root, outDirClient, doNotCreateExtraDirectory, concurrencyLimit, onPagePrerender, logLevel) {
549
- return concurrencyLimit(async () => {
550
- let fileUrl;
551
- if (fileExtension === '.html') {
552
- fileUrl = urlToFile(urlOriginal, '.html', doNotCreateExtraDirectory);
553
- }
554
- else {
555
- fileUrl = getPageContextRequestUrl(urlOriginal);
556
- }
557
- assertPosixPath(fileUrl);
558
- assert(fileUrl.startsWith('/'));
559
- const filePathRelative = fileUrl.slice(1);
560
- assert(!filePathRelative.startsWith('/'));
561
- assertPosixPath(outDirClient);
562
- assertPosixPath(filePathRelative);
563
- const filePath = path.posix.join(outDirClient, filePathRelative);
564
- if (onPagePrerender) {
565
- const prerenderPageContext = {};
566
- objectAssign(prerenderPageContext, pageContext);
567
- objectAssign(prerenderPageContext, {
568
- _prerenderResult: {
569
- filePath,
570
- fileContent
571
- }
572
- });
573
- await onPagePrerender(prerenderPageContext);
574
- }
575
- else {
576
- const { promises } = await import('fs');
577
- const { writeFile, mkdir } = promises;
578
- await mkdir(path.posix.dirname(filePath), { recursive: true });
579
- await writeFile(filePath, fileContent);
580
- if (logLevel === 'info') {
581
- assertPosixPath(root);
582
- assertPosixPath(outDirClient);
583
- let outDirClientRelative = path.posix.relative(root, outDirClient);
584
- if (!outDirClientRelative.endsWith('/')) {
585
- outDirClientRelative = outDirClientRelative + '/';
586
- }
587
- console.log(`${pc.dim(outDirClientRelative)}${pc.blue(filePathRelative)}`);
551
+ async function write(urlOriginal, pageContext, fileExtension, fileContent, root, outDirClient, doNotCreateExtraDirectory, onPagePrerender, logLevel) {
552
+ let fileUrl;
553
+ if (fileExtension === '.html') {
554
+ fileUrl = urlToFile(urlOriginal, '.html', doNotCreateExtraDirectory);
555
+ }
556
+ else {
557
+ fileUrl = getPageContextRequestUrl(urlOriginal);
558
+ }
559
+ assertPosixPath(fileUrl);
560
+ assert(fileUrl.startsWith('/'));
561
+ const filePathRelative = fileUrl.slice(1);
562
+ assert(!filePathRelative.startsWith('/'));
563
+ assertPosixPath(outDirClient);
564
+ assertPosixPath(filePathRelative);
565
+ const filePath = path.posix.join(outDirClient, filePathRelative);
566
+ if (onPagePrerender) {
567
+ const prerenderPageContext = {};
568
+ objectAssign(prerenderPageContext, pageContext);
569
+ objectAssign(prerenderPageContext, {
570
+ _prerenderResult: {
571
+ filePath,
572
+ fileContent
573
+ }
574
+ });
575
+ await onPagePrerender(prerenderPageContext);
576
+ }
577
+ else {
578
+ const { promises } = await import('fs');
579
+ const { writeFile, mkdir } = promises;
580
+ await mkdir(path.posix.dirname(filePath), { recursive: true });
581
+ await writeFile(filePath, fileContent);
582
+ if (logLevel === 'info') {
583
+ assertPosixPath(root);
584
+ assertPosixPath(outDirClient);
585
+ let outDirClientRelative = path.posix.relative(root, outDirClient);
586
+ if (!outDirClientRelative.endsWith('/')) {
587
+ outDirClientRelative = outDirClientRelative + '/';
588
588
  }
589
+ console.log(`${pc.dim(outDirClientRelative)}${pc.blue(filePathRelative)}`);
589
590
  }
590
- });
591
+ }
591
592
  }
592
593
  function normalizeOnPrerenderHookResult(prerenderResult, prerenderHookFile, hookName) {
593
594
  if (Array.isArray(prerenderResult)) {
@@ -1,7 +1,7 @@
1
1
  export { injectAssets__public };
2
2
  import { assertUsage, assertWarning, castProp, hasProp } from '../../utils.js';
3
3
  import { injectHtmlTagsToString } from '../injectAssets.js';
4
- // TODO: remove this on next semver major
4
+ // TODO/v1-release: remove
5
5
  async function injectAssets__public(htmlString, pageContext) {
6
6
  assertWarning(false, '`_injectAssets()` is deprecated and will be removed.', { onlyOnce: true, showStackTrace: true });
7
7
  assertUsage(typeof htmlString === 'string', '[injectAssets(htmlString, pageContext)]: Argument `htmlString` should be a string.', { showStackTrace: true });
@@ -174,7 +174,7 @@ async function renderTemplate(templateContent, pageContext) {
174
174
  };
175
175
  assertUsage(!isPromise(templateVar), getErrMsg('a promise', `Did you forget to ${pc.cyan('await')} the promise?`));
176
176
  if (templateVar === undefined || templateVar === null) {
177
- assertWarning(false, getErrMsg(`${pc.cyan(String(templateVar))} which will be converted to an empty string`, `Pass an empty string instead of ${pc.cyan(String(templateVar))} to remove this warning.`), { onlyOnce: false });
177
+ assertWarning(false, getErrMsg(`${pc.cyan(String(templateVar))} which will be converted to an empty string`, `Pass the empty string ${pc.cyan("''")} instead of ${pc.cyan(String(templateVar))} to remove this warning.`), { onlyOnce: false });
178
178
  templateVar = '';
179
179
  }
180
180
  {
@@ -1,2 +1,2 @@
1
1
  export { assertNoInfiniteHttpRedirect };
2
- declare function assertNoInfiniteHttpRedirect(urlRedirectOriginal: string, urlRedirectPathnameLogical: string): void;
2
+ declare function assertNoInfiniteHttpRedirect(urlRedirectTarget: string, urlLogical: string): void;
@@ -1,24 +1,24 @@
1
1
  export { assertNoInfiniteHttpRedirect };
2
- import { assert, assertUsage, getGlobalObject } from '../../utils.js';
2
+ import { assert, assertUsage, getGlobalObject, isUriWithProtocol } from '../../utils.js';
3
3
  import pc from '@brillout/picocolors';
4
4
  const globalObject = getGlobalObject('assertNoInfiniteHttpRedirect.ts', {
5
5
  redirectGraph: {}
6
6
  });
7
- function assertNoInfiniteHttpRedirect(urlRedirectOriginal, urlRedirectPathnameLogical) {
8
- if (urlRedirectOriginal.startsWith('http')) {
9
- // We assume that the redirect points to an external origin, and we can therefore assume that the app doesn't define an infinite loop (in itself).
10
- // - There isn't a reliable way to check whether the redirect points to an external origin or the same origin: the user usually passes the URL without origin.
7
+ function assertNoInfiniteHttpRedirect(urlRedirectTarget, urlLogical) {
8
+ if (isUriWithProtocol(urlRedirectTarget)) {
9
+ // We assume that urlRedirectTarget points to an origin that is external (not the same origin), and we can therefore assume that the app doesn't define an infinite loop (in itself).
10
+ // - There isn't a reliable way to check whether the redirect points to an external origin or the same origin. For same origins, we assume/hope the user to pass the URL without origin.
11
11
  // ```js
12
- // // URL origin is usually missing
12
+ // // For same-origin, the user usually/hopefully passes a URL without origin
13
13
  // renderPage({ urlOriginal: '/some/pathname' })
14
14
  // ```
15
15
  return;
16
16
  }
17
- assert(urlRedirectOriginal.startsWith('/'));
18
- assert(urlRedirectPathnameLogical.startsWith('/'));
17
+ assert(urlRedirectTarget.startsWith('/'));
18
+ assert(urlLogical.startsWith('/'));
19
19
  const graph = copy(globalObject.redirectGraph);
20
- graph[urlRedirectOriginal] ?? (graph[urlRedirectOriginal] = new Set());
21
- graph[urlRedirectOriginal].add(urlRedirectPathnameLogical);
20
+ graph[urlRedirectTarget] ?? (graph[urlRedirectTarget] = new Set());
21
+ graph[urlRedirectTarget].add(urlLogical);
22
22
  validate(graph);
23
23
  globalObject.redirectGraph = graph;
24
24
  }
@@ -26,6 +26,9 @@ function copy(G) {
26
26
  return Object.fromEntries(Object.entries(G).map(([key, val]) => [key, new Set(val)]));
27
27
  }
28
28
  // Adapted from: https://stackoverflow.com/questions/60904464/detect-cycle-in-directed-graph/60907076#60907076
29
+ function validate(G) {
30
+ Object.keys(G).forEach((n) => check(G, n, []));
31
+ }
29
32
  function check(G, n, path) {
30
33
  if (path.includes(n)) {
31
34
  const cycle = path.slice(path.indexOf(n)).concat(n);
@@ -33,6 +36,3 @@ function check(G, n, path) {
33
36
  }
34
37
  G[n]?.forEach((node) => check(G, node, [...path, n]));
35
38
  }
36
- function validate(G) {
37
- Object.keys(G).forEach((n) => check(G, n, []));
38
- }
@@ -25,4 +25,4 @@ declare function createHttpResponseObject(htmlRender: null | HtmlRender, renderH
25
25
  abortStatusCode?: AbortStatusCode;
26
26
  }): Promise<HttpResponse | null>;
27
27
  declare function createHttpResponsePageContextJson(pageContextSerialized: string): Promise<HttpResponse>;
28
- declare function createHttpResponseObjectRedirect({ url, statusCode }: UrlRedirect, urlPathnameLogical: string): HttpResponse;
28
+ declare function createHttpResponseObjectRedirect({ url, statusCode }: UrlRedirect, urlLogical: string): HttpResponse;
@@ -33,9 +33,9 @@ async function createHttpResponsePageContextJson(pageContextSerialized) {
33
33
  return httpResponse;
34
34
  }
35
35
  function createHttpResponseObjectRedirect({ url, statusCode },
36
- // The URL pathname we assume the redirect to be logically based on
37
- urlPathnameLogical) {
38
- assertNoInfiniteHttpRedirect(url, urlPathnameLogical);
36
+ // The URL we assume the redirect to be logically based on
37
+ urlLogical) {
38
+ assertNoInfiniteHttpRedirect(url, urlLogical);
39
39
  assert(url);
40
40
  assert(statusCode);
41
41
  assert(300 <= statusCode && statusCode <= 399);
@@ -13,7 +13,7 @@ async function executeOnBeforeRenderHooks(pageContext) {
13
13
  }
14
14
  const onBeforeRender = hook.hookFn;
15
15
  preparePageContextForUserConsumptionServerSide(pageContext);
16
- const hookResult = await executeHook(() => onBeforeRender(pageContext), 'onBeforeRender', hook.hookFilePath);
16
+ const hookResult = await executeHook(() => onBeforeRender(pageContext), hook);
17
17
  assertOnBeforeRenderHookReturn(hookResult, hook.hookFilePath);
18
18
  const pageContextFromHook = hookResult?.pageContext;
19
19
  Object.assign(pageContext, pageContextFromHook);
@@ -1,13 +1,13 @@
1
1
  export { executeOnRenderHtmlHook };
2
2
  export type { RenderHook };
3
3
  import { type HtmlRender } from '../html/renderHtml.js';
4
+ import { type Hook } from '../../../shared/hooks/getHook.js';
4
5
  import type { PageAsset } from './getPageAssets.js';
5
6
  import { type PageContextForUserConsumptionServerSide } from './preparePageContextForUserConsumptionServerSide.js';
6
7
  import type { PageConfigRuntime } from '../../../shared/page-configs/PageConfig.js';
7
8
  import type { PageContextSerialization } from '../html/serializePageContextClientSide.js';
8
9
  type GetPageAssets = () => Promise<PageAsset[]>;
9
- type RenderHook = {
10
- hookFilePath: string;
10
+ type RenderHook = Hook & {
11
11
  hookName: HookName;
12
12
  };
13
13
  type HookName = 'onRenderHtml' | 'render';
@@ -12,7 +12,7 @@ async function executeOnRenderHtmlHook(pageContext) {
12
12
  const { renderHook, hookFn } = getRenderHook(pageContext);
13
13
  objectAssign(pageContext, { _renderHook: renderHook });
14
14
  preparePageContextForUserConsumptionServerSide(pageContext);
15
- const hookReturnValue = await executeHook(() => hookFn(pageContext), renderHook.hookName, renderHook.hookFilePath);
15
+ const hookReturnValue = await executeHook(() => hookFn(pageContext), renderHook);
16
16
  const { documentHtml, pageContextProvidedByRenderHook, pageContextPromise, injectFilter } = processHookReturnValue(hookReturnValue, renderHook);
17
17
  Object.assign(pageContext, pageContextProvidedByRenderHook);
18
18
  objectAssign(pageContext, { _pageContextPromise: pageContextPromise });
@@ -50,10 +50,10 @@ function getRenderHook(pageContext) {
50
50
  }
51
51
  if (hook) {
52
52
  assert(hookName);
53
- const { hookFilePath, hookFn } = hook;
53
+ const { hookFilePath, hookFn, hookTimeout } = hook;
54
54
  hookFound = {
55
55
  hookFn,
56
- renderHook: { hookFilePath, hookName }
56
+ renderHook: { hookFn, hookFilePath, hookName, hookTimeout }
57
57
  };
58
58
  }
59
59
  }
@@ -7,7 +7,7 @@ import pc from '@brillout/picocolors';
7
7
  const streamDocs = 'See https://vike.dev/stream for more information.';
8
8
  function getHttpResponseBody(htmlRender, renderHook) {
9
9
  if (typeof htmlRender !== 'string') {
10
- assertUsage(false, getErrMsg(htmlRender, renderHook, 'body', `Use ${pc.cyan('pageContext.httpResponse.pipe()')} or ${pc.cyan('pageContext.httpResponse.getBody()')} instead`));
10
+ assertUsage(false, getErrMsg(htmlRender, renderHook, 'body', `Use ${pc.cyan('pageContext.httpResponse.pipe()')} instead`));
11
11
  }
12
12
  const body = htmlRender;
13
13
  return body;