vike 0.4.227-commit-e36b916 → 0.4.227-commit-25aa0f8

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.
@@ -568,6 +568,7 @@ function getConfigValueSource(configName, plusFile, configDef, userRootDir) {
568
568
  // Defined over pointer import
569
569
  assert(confVal.valueIsLoaded);
570
570
  const pointerImport = resolvePointerImport(confVal.value, plusFile.filePath, userRootDir, configName);
571
+ console.log('pointerImport', pointerImport);
571
572
  const configDefinedAt = getConfigDefinedAt('Config', configName, definedAtFilePath_);
572
573
  assertUsage(pointerImport, `${configDefinedAt} should be an import`);
573
574
  valueFilePath = pointerImport.fileExportPath.filePathAbsoluteVite;
@@ -3,7 +3,6 @@ import { assert, assertPosixPath, toPosixPath, pathJoin, assertIsNpmPackageImpor
3
3
  import { createRequire } from 'module';
4
4
  // @ts-ignore import.meta.url is shimmed at dist/cjs by dist-cjs-fixup.js.
5
5
  const importMetaUrl = import.meta.url;
6
- const require_ = createRequire(importMetaUrl);
7
6
  assertIsNotProductionRuntime();
8
7
  async function resolveClientEntriesDev(clientEntry, viteDevServer) {
9
8
  let root = viteDevServer.config.root;
@@ -26,6 +25,7 @@ async function resolveClientEntriesDev(clientEntry, viteDevServer) {
26
25
  }
27
26
  else {
28
27
  if (clientEntry.startsWith('@@vike/')) {
28
+ const require_ = createRequire(importMetaUrl);
29
29
  assert(clientEntry.endsWith('.js'));
30
30
  try {
31
31
  // For Vitest (which doesn't resolve vike to its dist but to its source files)
@@ -39,6 +39,7 @@ async function resolveClientEntriesDev(clientEntry, viteDevServer) {
39
39
  }
40
40
  }
41
41
  else {
42
+ const require_ = createRequire(root);
42
43
  assertIsNpmPackageImport(clientEntry);
43
44
  filePath = require_.resolve(clientEntry);
44
45
  }
@@ -14,6 +14,7 @@ type ProvidedByHook = null | {
14
14
  type PageContextPrerendered = {
15
15
  urlOriginal: string;
16
16
  _providedByHook?: ProvidedByHook;
17
+ pageId: string;
17
18
  };
18
19
  type Output<PageContext = PageContextPrerendered> = {
19
20
  filePath: string;
@@ -53,4 +54,5 @@ declare function runPrerender_forceExit(): void;
53
54
  type PrerenderContextPublic = {
54
55
  output: Output<PageContextServer>;
55
56
  pageContexts: PageContextServer[];
57
+ pageContexts404: PageContextServer;
56
58
  };
@@ -4,8 +4,8 @@ export { runPrerenderFromAutoRun };
4
4
  export { runPrerender_forceExit };
5
5
  import path from 'path';
6
6
  import { route } from '../../shared/route/index.js';
7
- import { assert, assertUsage, assertWarning, hasProp, objectAssign, isObjectWithKeys, isCallable, isPropertyGetter, assertPosixPath, urlToFile, isPlainObject, pLimit, isArray, changeEnumerable, onSetupPrerender, isObject, makePublicCopy, PROJECT_VERSION } from './utils.js';
8
- import { prerenderPage, prerender404Page, getPageContextInitEnhanced } from '../runtime/renderPage/renderPageAlreadyRouted.js';
7
+ import { assert, assertUsage, assertWarning, hasProp, objectAssign, isObjectWithKeys, isCallable, isPropertyGetter, assertPosixPath, urlToFile, isPlainObject, pLimit, isArray, onSetupPrerender, isObject, makePublicCopy, PROJECT_VERSION, preservePropertyGetters } from './utils.js';
8
+ import { prerenderPage, getPageContextInitEnhanced } from '../runtime/renderPage/renderPageAlreadyRouted.js';
9
9
  import pc from '@brillout/picocolors';
10
10
  import { cpus } from 'os';
11
11
  import { getGlobalContextInternal, initGlobalContext_runPrerender, setGlobalContext_isPrerendering } from '../runtime/globalContext.js';
@@ -15,8 +15,7 @@ import { getPageContextRequestUrl } from '../../shared/getPageContextRequestUrl.
15
15
  import { getUrlFromRouteString } from '../../shared/route/resolveRouteString.js';
16
16
  import { getConfigValueRuntime } from '../../shared/page-configs/getConfigValueRuntime.js';
17
17
  import { loadConfigValues } from '../../shared/page-configs/loadConfigValues.js';
18
- import { isErrorPage } from '../../shared/error-page.js';
19
- import { getPageContextUrlComputed } from '../../shared/getPageContextUrlComputed.js';
18
+ import { getErrorPageId, isErrorPage } from '../../shared/error-page.js';
20
19
  import { isAbortError } from '../../shared/route/abort.js';
21
20
  import { loadUserFilesServerSide } from '../runtime/renderPage/loadUserFilesServerSide.js';
22
21
  import { getHookFromPageConfig, getHookFromPageConfigGlobal, getHookTimeoutDefault, getHook_setIsPrerenderering } from '../../shared/hooks/getHook.js';
@@ -86,8 +85,7 @@ async function runPrerender(options = {}, standaloneTrigger) {
86
85
  const { partial, noExtraDir, parallel, defaultLocalValue, isPrerenderingEnabled } = prerenderConfigGlobal;
87
86
  if (!isPrerenderingEnabled) {
88
87
  assert(standaloneTrigger);
89
- // TODO/now: make it assertUsage() and remove dist/server/entry.mjs if pre-rendering is completely disabled
90
- assertWarning(false, `You're executing ${pc.cyan(standaloneTrigger)} but you didn't enable pre-rendering. Use the ${pc.cyan('prerender')} setting (${pc.underline('https://vike.dev/prerender')}) to enable pre-rendering for at least one page.`, { onlyOnce: true });
88
+ assertUsage(false, `You're executing ${pc.cyan(standaloneTrigger)} but you didn't enable pre-rendering. Use the ${pc.cyan('prerender')} setting (${pc.underline('https://vike.dev/prerender')}) to enable pre-rendering for at least one page.`);
91
89
  }
92
90
  const concurrencyLimit = pLimit(parallel === false || parallel === 0 ? 1 : parallel === true || parallel === undefined ? cpus().length : parallel);
93
91
  await initGlobalContext_runPrerender();
@@ -96,27 +94,37 @@ async function runPrerender(options = {}, standaloneTrigger) {
96
94
  const prerenderContext = {
97
95
  noExtraDir: noExtraDir ?? false,
98
96
  pageContexts: [],
97
+ pageContexts404: [],
99
98
  pageContextInit: options.pageContextInit ?? null,
100
99
  prerenderedPageContexts: {},
101
100
  output: []
102
101
  };
103
102
  const doNotPrerenderList = [];
104
103
  await collectDoNoPrerenderList(vikeConfig.pageConfigs, doNotPrerenderList, defaultLocalValue, concurrencyLimit, globalContext);
104
+ // Allow user to create `pageContext` for parameterized routes and/or bulk data fetching
105
+ // https://vike.dev/onBeforePrerenderStart
105
106
  await callOnBeforePrerenderStartHooks(prerenderContext, globalContext, concurrencyLimit, doNotPrerenderList);
106
- await handlePagesWithStaticRoutes(prerenderContext, globalContext, doNotPrerenderList, concurrencyLimit);
107
- await callOnPrerenderStartHook(prerenderContext, globalContext);
107
+ // Create `pageContext` for each page with a static route
108
+ const urlList = getUrlListFromPagesWithStaticRoute(globalContext, doNotPrerenderList);
109
+ await createPageContextsForOnPrerenderStartHook(urlList, prerenderContext, globalContext, concurrencyLimit, false);
110
+ // Create `pageContext` for 404 page
111
+ const urlList404 = getUrlList404(globalContext);
112
+ await createPageContextsForOnPrerenderStartHook(urlList404, prerenderContext, globalContext, concurrencyLimit, true);
113
+ // Allow user to duplicate the list of `pageContext` for i18n
114
+ // https://vike.dev/onPrerenderStart
115
+ await callOnPrerenderStartHook(prerenderContext, globalContext, concurrencyLimit);
108
116
  let prerenderedCount = 0;
109
117
  // Write files as soon as pages finish rendering (instead of writing all files at once only after all pages have rendered).
110
118
  const onComplete = async (htmlFile) => {
111
119
  prerenderedCount++;
112
- if (htmlFile.pageId) {
113
- prerenderContext.prerenderedPageContexts[htmlFile.pageId] = htmlFile.pageContext;
114
- }
120
+ const { pageId } = htmlFile.pageContext;
121
+ assert(pageId);
122
+ prerenderContext.prerenderedPageContexts[pageId] = htmlFile.pageContext;
115
123
  await writeFiles(htmlFile, root, outDirClient, options.onPagePrerender, prerenderContext.output, logLevel);
116
124
  };
117
- await routeAndPrerender(prerenderContext, globalContext, concurrencyLimit, onComplete);
125
+ await prerenderPages(prerenderContext, concurrencyLimit, onComplete);
118
126
  warnContradictoryNoPrerenderList(prerenderContext.prerenderedPageContexts, doNotPrerenderList);
119
- await prerender404(prerenderContext, globalContext, onComplete);
127
+ await prerenderPages404(prerenderContext, onComplete, concurrencyLimit);
120
128
  if (logLevel === 'info') {
121
129
  console.log(`${pc.green(`✓`)} ${prerenderedCount} HTML documents pre-rendered.`);
122
130
  }
@@ -228,12 +236,13 @@ async function callOnBeforePrerenderStartHooks(prerenderContext, globalContext,
228
236
  });
229
237
  })));
230
238
  await Promise.all(onBeforePrerenderStartHooks.map(({ hookFn, hookName, hookFilePath, pageId, hookTimeout }) => concurrencyLimit(async () => {
231
- if (doNotPrerenderList.find((p) => p.pageId === pageId)) {
239
+ if (doNotPrerenderList.find((p) => p.pageId === pageId))
232
240
  return;
233
- }
234
241
  const prerenderResult = await executeHook(() => hookFn(), { hookName, hookFilePath, hookTimeout }, null);
235
242
  const result = normalizeOnPrerenderHookResult(prerenderResult, hookFilePath, hookName);
243
+ // Handle result
236
244
  await Promise.all(result.map(async ({ url, pageContext }) => {
245
+ // Assert no duplication
237
246
  {
238
247
  const pageContextFound = prerenderContext.pageContexts.find((pageContext) => isSameUrl(pageContext.urlOriginal, url));
239
248
  if (pageContextFound) {
@@ -244,30 +253,23 @@ async function callOnBeforePrerenderStartHooks(prerenderContext, globalContext,
244
253
  assertUsage(false, `URL ${pc.cyan(url)} provided ${providedTwice}. Make sure to provide the URL only once instead.`);
245
254
  }
246
255
  }
247
- const pageContextNew = await createPageContext(url, prerenderContext, globalContext);
248
- objectAssign(pageContextNew, {
249
- _providedByHook: {
250
- hookFilePath,
251
- hookName
252
- }
253
- });
256
+ // Add result
257
+ const providedByHook = { hookFilePath, hookName };
258
+ const pageContextNew = await createPageContext(url, prerenderContext, globalContext, false, undefined, providedByHook);
254
259
  prerenderContext.pageContexts.push(pageContextNew);
255
260
  if (pageContext) {
256
- objectAssign(pageContextNew, {
257
- _pageContextAlreadyProvidedByOnPrerenderHook: true
258
- });
261
+ objectAssign(pageContextNew, { _pageContextAlreadyProvidedByOnPrerenderHook: true });
259
262
  objectAssign(pageContextNew, pageContext);
260
263
  }
261
264
  }));
262
265
  })));
263
266
  }
264
- async function handlePagesWithStaticRoutes(prerenderContext, globalContext, doNotPrerenderList, concurrencyLimit) {
265
- // Pre-render pages with a static route
266
- await Promise.all(globalContext.pageRoutes.map((pageRoute) => concurrencyLimit(async () => {
267
+ function getUrlListFromPagesWithStaticRoute(globalContext, doNotPrerenderList) {
268
+ const urlList = [];
269
+ globalContext.pageRoutes.map((pageRoute) => {
267
270
  const { pageId } = pageRoute;
268
- if (doNotPrerenderList.find((p) => p.pageId === pageId)) {
271
+ if (doNotPrerenderList.find((p) => p.pageId === pageId))
269
272
  return;
270
- }
271
273
  let urlOriginal;
272
274
  if (!('routeString' in pageRoute)) {
273
275
  // Abort since the page's route is a Route Function
@@ -283,43 +285,116 @@ async function handlePagesWithStaticRoutes(prerenderContext, globalContext, doNo
283
285
  urlOriginal = url;
284
286
  }
285
287
  assert(urlOriginal.startsWith('/'));
288
+ urlList.push({ urlOriginal, pageId });
289
+ });
290
+ return urlList;
291
+ }
292
+ function getUrlList404(globalContext) {
293
+ const urlList = [];
294
+ const errorPageId = getErrorPageId(globalContext.pageFilesAll, globalContext.pageConfigs);
295
+ if (errorPageId) {
296
+ urlList.push({
297
+ // A URL is required for `viteDevServer.transformIndexHtml(url,html)`
298
+ urlOriginal: '/404',
299
+ pageId: errorPageId
300
+ });
301
+ }
302
+ return urlList;
303
+ }
304
+ async function createPageContextsForOnPrerenderStartHook(urlList, prerenderContext, globalContext, concurrencyLimit, is404) {
305
+ await Promise.all(urlList.map(({ urlOriginal, pageId }) => concurrencyLimit(async () => {
286
306
  // Already included in a onBeforePrerenderStart() hook
287
- if (prerenderContext.pageContexts.find((pageContext) => isSameUrl(pageContext.urlOriginal, urlOriginal))) {
307
+ if ([...prerenderContext.pageContexts, ...prerenderContext.pageContexts404].find((pageContext) => isSameUrl(pageContext.urlOriginal, urlOriginal))) {
288
308
  return;
289
309
  }
290
- const routeParams = {};
291
- const pageContext = await createPageContext(urlOriginal, prerenderContext, globalContext);
292
- objectAssign(pageContext, {
293
- _providedByHook: null,
294
- routeParams,
295
- pageId: pageId,
296
- _debugRouteMatches: [
297
- {
298
- pageId,
299
- routeType: pageRoute.routeType,
300
- routeString: urlOriginal,
301
- routeParams
302
- }
303
- ]
304
- });
305
- objectAssign(pageContext, await loadUserFilesServerSide(pageContext));
306
- prerenderContext.pageContexts.push(pageContext);
310
+ const pageContext = await createPageContext(urlOriginal, prerenderContext, globalContext, is404, pageId, null);
311
+ if (is404) {
312
+ prerenderContext.pageContexts404.push(pageContext);
313
+ }
314
+ else {
315
+ prerenderContext.pageContexts.push(pageContext);
316
+ }
307
317
  })));
308
318
  }
309
- async function createPageContext(urlOriginal, prerenderContext, globalContext) {
310
- const pageContextInit = { urlOriginal };
311
- objectAssign(pageContextInit, prerenderContext.pageContextInit);
312
- const pageContext = await getPageContextInitEnhanced(pageContextInit, globalContext, true, {});
319
+ async function createPageContext(urlOriginal, prerenderContext, globalContext, is404, pageId, providedByHook) {
320
+ const pageContextInit = {
321
+ urlOriginal,
322
+ ...prerenderContext.pageContextInit
323
+ };
324
+ const pageContext = await getPageContextInitEnhanced(pageContextInit, globalContext, true);
313
325
  assert(pageContext.isPrerendering === true);
314
326
  objectAssign(pageContext, {
315
327
  _urlHandler: null,
328
+ _httpRequestId: null,
316
329
  _urlRewrite: null,
317
330
  _noExtraDir: prerenderContext.noExtraDir,
318
- _prerenderContext: prerenderContext
331
+ _prerenderContext: prerenderContext,
332
+ _providedByHook: providedByHook,
333
+ _urlOriginalModifiedByHook: null,
334
+ is404
319
335
  });
336
+ if (!is404) {
337
+ const pageContextFromRoute = await route(pageContext);
338
+ assert(hasProp(pageContextFromRoute, 'pageId', 'null') || hasProp(pageContextFromRoute, 'pageId', 'string')); // Help TS
339
+ assertRouteMatch(pageContextFromRoute, pageContext);
340
+ assert(pageContextFromRoute.pageId);
341
+ objectAssign(pageContext, pageContextFromRoute);
342
+ }
343
+ else {
344
+ assert(pageId);
345
+ objectAssign(pageContext, {
346
+ pageId,
347
+ _debugRouteMatches: [],
348
+ routeParams: {}
349
+ });
350
+ }
351
+ objectAssign(pageContext, await loadUserFilesServerSide(pageContext));
352
+ let usesClientRouter;
353
+ {
354
+ const { pageId } = pageContext;
355
+ assert(pageId);
356
+ assert(globalContext.isPrerendering);
357
+ if (globalContext.pageConfigs.length > 0) {
358
+ const pageConfig = globalContext.pageConfigs.find((p) => p.pageId === pageId);
359
+ assert(pageConfig);
360
+ usesClientRouter = getConfigValueRuntime(pageConfig, 'clientRouting', 'boolean')?.value ?? false;
361
+ }
362
+ else {
363
+ usesClientRouter = globalContext.usesClientRouter;
364
+ }
365
+ }
366
+ objectAssign(pageContext, { _usesClientRouter: usesClientRouter });
320
367
  return pageContext;
321
368
  }
322
- async function callOnPrerenderStartHook(prerenderContext, globalContext) {
369
+ function assertRouteMatch(pageContextFromRoute, pageContext) {
370
+ if (pageContextFromRoute.pageId !== null) {
371
+ assert(pageContextFromRoute.pageId);
372
+ return;
373
+ }
374
+ let hookName;
375
+ let hookFilePath;
376
+ if (pageContext._urlOriginalModifiedByHook) {
377
+ hookName = pageContext._urlOriginalModifiedByHook.hookName;
378
+ hookFilePath = pageContext._urlOriginalModifiedByHook.hookFilePath;
379
+ }
380
+ else if (pageContext._providedByHook) {
381
+ hookName = pageContext._providedByHook.hookName;
382
+ hookFilePath = pageContext._providedByHook.hookFilePath;
383
+ }
384
+ if (hookName) {
385
+ assert(hookFilePath);
386
+ const { urlOriginal } = pageContext;
387
+ assert(urlOriginal);
388
+ assertUsage(false, `The ${hookName}() hook defined by ${hookFilePath} returns a URL ${pc.cyan(urlOriginal)} that ${noRouteMatch}. Make sure that the URLs returned by ${hookName}() always match the route of a page.`);
389
+ }
390
+ else {
391
+ // `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.
392
+ assert(pageContextFromRoute._routingProvidedByOnBeforeRouteHook);
393
+ // Abort since the URL doesn't correspond to any page
394
+ return;
395
+ }
396
+ }
397
+ async function callOnPrerenderStartHook(prerenderContext, globalContext, concurrencyLimit) {
323
398
  let onPrerenderStartHook;
324
399
  // V1 design
325
400
  if (globalContext.pageConfigs.length > 0) {
@@ -389,8 +464,11 @@ async function callOnPrerenderStartHook(prerenderContext, globalContext) {
389
464
  pageContext._urlOriginalBeforeHook = pageContext.urlOriginal;
390
465
  });
391
466
  const docLink = 'https://vike.dev/i18n#pre-rendering';
392
- // Set `enumerable` to `false` to avoid computed URL properties from being iterated & copied in onPrerenderStart() hook, e.g. /examples/i18n/
393
- const { restoreEnumerable, addPageContextComputedUrl } = makePageContextComputedUrlNonEnumerable(prerenderContext.pageContexts);
467
+ prerenderContext.pageContexts.forEach((pageContext) => {
468
+ // Preserve URL computed properties when the user is copying pageContext is his onPrerenderStart() hook, e.g. /examples/i18n/
469
+ // https://vike.dev/i18n#pre-rendering
470
+ preservePropertyGetters(pageContext);
471
+ });
394
472
  let result = await executeHook(() => {
395
473
  const prerenderContextPublic = makePublic(prerenderContext);
396
474
  // TODO/v1-release: remove warning
@@ -405,7 +483,11 @@ async function callOnPrerenderStartHook(prerenderContext, globalContext) {
405
483
  });
406
484
  return hookFn(prerenderContextPublic);
407
485
  }, onPrerenderStartHook, null);
408
- restoreEnumerable();
486
+ // Before applying result
487
+ prerenderContext.pageContexts.forEach((pageContext) => {
488
+ ;
489
+ pageContext._restorePropertyGetters?.();
490
+ });
409
491
  if (result === null || result === undefined) {
410
492
  return;
411
493
  }
@@ -436,82 +518,41 @@ async function callOnPrerenderStartHook(prerenderContext, globalContext) {
436
518
  }
437
519
  delete pageContext.url;
438
520
  });
521
+ // After applying result
439
522
  prerenderContext.pageContexts.forEach((pageContext) => {
523
+ ;
524
+ pageContext._restorePropertyGetters?.();
525
+ });
526
+ // Assert URL modified by user
527
+ await Promise.all(prerenderContext.pageContexts.map((pageContext) => concurrencyLimit(async () => {
440
528
  if (pageContext.urlOriginal !== pageContext._urlOriginalBeforeHook) {
441
529
  pageContext._urlOriginalModifiedByHook = {
442
530
  hookFilePath,
443
531
  hookName
444
532
  };
533
+ const pageContextFromRoute = await route(pageContext,
534
+ // Avoid calling onBeforeRoute() twice, otherwise user's onBeforeRoute() will wrongfully believe URL doesn't have locale when onBeforeRoute() removes the local from the URL
535
+ true);
536
+ assertRouteMatch(pageContextFromRoute, pageContext);
445
537
  }
446
- });
447
- addPageContextComputedUrl(prerenderContext.pageContexts);
538
+ })));
448
539
  }
449
- async function routeAndPrerender(prerenderContext, globalContext, concurrencyLimit, onComplete) {
450
- assert(globalContext.isPrerendering);
451
- // Route all URLs
452
- await Promise.all(prerenderContext.pageContexts.map((pageContext) => concurrencyLimit(async () => {
453
- const { urlOriginal } = pageContext;
454
- assert(urlOriginal);
455
- const pageContextFromRoute = await route(pageContext);
456
- assert(hasProp(pageContextFromRoute, 'pageId', 'null') || hasProp(pageContextFromRoute, 'pageId', 'string'));
457
- if (pageContextFromRoute.pageId === null) {
458
- let hookName;
459
- let hookFilePath;
460
- if (pageContext._providedByHook) {
461
- hookName = pageContext._providedByHook.hookName;
462
- hookFilePath = pageContext._providedByHook.hookFilePath;
463
- }
464
- else if (pageContext._urlOriginalModifiedByHook) {
465
- hookName = pageContext._urlOriginalModifiedByHook.hookName;
466
- hookFilePath = pageContext._urlOriginalModifiedByHook.hookFilePath;
467
- }
468
- if (hookName) {
469
- assert(hookFilePath);
470
- assertUsage(false, `The ${hookName}() hook defined by ${hookFilePath} returns a URL ${pc.cyan(urlOriginal)} that ${noRouteMatch}. Make sure that the URLs returned by ${hookName}() always match the route of a page.`);
471
- }
472
- else {
473
- // `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.
474
- assert(pageContextFromRoute._routingProvidedByOnBeforeRouteHook);
475
- // Abort since the URL doesn't correspond to any page
476
- return;
477
- }
478
- }
479
- assert(pageContextFromRoute.pageId);
480
- objectAssign(pageContext, pageContextFromRoute);
481
- const { pageId: pageId } = pageContext;
482
- objectAssign(pageContext, await loadUserFilesServerSide(pageContext));
483
- let usesClientRouter;
484
- {
485
- if (pageContext._pageConfigs.length > 0) {
486
- const pageConfig = pageContext._pageConfigs.find((p) => p.pageId === pageId);
487
- assert(pageConfig);
488
- usesClientRouter = getConfigValueRuntime(pageConfig, 'clientRouting', 'boolean')?.value ?? false;
489
- }
490
- else {
491
- usesClientRouter = globalContext.usesClientRouter;
492
- }
493
- }
494
- objectAssign(pageContext, {
495
- is404: null,
496
- _httpRequestId: null,
497
- _usesClientRouter: usesClientRouter
498
- });
540
+ async function prerenderPages(prerenderContext, concurrencyLimit, onComplete) {
541
+ await Promise.all(prerenderContext.pageContexts.map((pageContextBeforeRender) => concurrencyLimit(async () => {
499
542
  let res;
500
543
  try {
501
- res = await prerenderPage(pageContext);
544
+ res = await prerenderPage(pageContextBeforeRender);
502
545
  }
503
546
  catch (err) {
504
- assertIsNotAbort(err, pc.cyan(pageContext.urlOriginal));
547
+ assertIsNotAbort(err, pc.cyan(pageContextBeforeRender.urlOriginal));
505
548
  throw err;
506
549
  }
507
- const { documentHtml, pageContextSerialized } = res;
550
+ const { documentHtml, pageContextSerialized, pageContext } = res;
508
551
  await onComplete({
509
- urlOriginal,
510
552
  pageContext,
511
553
  htmlString: documentHtml,
512
554
  pageContextSerialized,
513
- doNotCreateExtraDirectory: prerenderContext.noExtraDir,
514
- pageId
555
+ doNotCreateExtraDirectory: prerenderContext.noExtraDir
515
556
  });
516
557
  })));
517
558
  }
@@ -543,31 +584,27 @@ async function warnMissingPages(prerenderedPageContexts, globalContext, doNotPre
543
584
  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#partial) to suppress this warning.`, { onlyOnce: true });
544
585
  });
545
586
  }
546
- async function prerender404(prerenderContext, globalContext, onComplete) {
547
- if (!Object.values(prerenderContext.prerenderedPageContexts).find(({ urlOriginal }) => urlOriginal === '/404')) {
587
+ async function prerenderPages404(prerenderContext, onComplete, concurrencyLimit) {
588
+ await Promise.all(prerenderContext.pageContexts404.map((pageContextBeforeRender) => concurrencyLimit(async () => {
548
589
  let result;
549
590
  try {
550
- result = await prerender404Page(prerenderContext.pageContextInit, globalContext);
591
+ result = await prerenderPage(pageContextBeforeRender);
551
592
  }
552
593
  catch (err) {
553
594
  assertIsNotAbort(err, 'the 404 page');
554
595
  throw err;
555
596
  }
556
- if (result) {
557
- const urlOriginal = '/404';
558
- const { documentHtml, pageContext } = result;
559
- await onComplete({
560
- urlOriginal,
561
- pageContext,
562
- htmlString: documentHtml,
563
- pageContextSerialized: null,
564
- doNotCreateExtraDirectory: true,
565
- pageId: null
566
- });
567
- }
568
- }
597
+ const { documentHtml, pageContext } = result;
598
+ await onComplete({
599
+ pageContext,
600
+ htmlString: documentHtml,
601
+ pageContextSerialized: null,
602
+ doNotCreateExtraDirectory: true
603
+ });
604
+ })));
569
605
  }
570
- async function writeFiles({ urlOriginal, pageContext, htmlString, pageContextSerialized, doNotCreateExtraDirectory }, root, outDirClient, onPagePrerender, output, logLevel) {
606
+ async function writeFiles({ pageContext, htmlString, pageContextSerialized, doNotCreateExtraDirectory }, root, outDirClient, onPagePrerender, output, logLevel) {
607
+ const { urlOriginal } = pageContext;
571
608
  assert(urlOriginal.startsWith('/'));
572
609
  const writeJobs = [
573
610
  write(urlOriginal, pageContext, 'HTML', htmlString, root, outDirClient, doNotCreateExtraDirectory, onPagePrerender, output, logLevel)
@@ -712,26 +749,6 @@ function assertIsNotAbort(err, urlOr404) {
712
749
  assert(abortCall);
713
750
  assertUsage(false, `${pc.cyan(abortCall)} thrown${thrownBy} while pre-rendering ${urlOr404} but ${pc.cyan(abortCaller)} isn't supported for pre-rendered pages`);
714
751
  }
715
- function makePageContextComputedUrlNonEnumerable(pageContexts) {
716
- change(false);
717
- return { restoreEnumerable, addPageContextComputedUrl };
718
- function restoreEnumerable() {
719
- change(true);
720
- }
721
- function addPageContextComputedUrl(pageContexts) {
722
- // Add URL computed props to the user-generated pageContext copies
723
- pageContexts.forEach((pageContext) => {
724
- const pageContextUrlComputed = getPageContextUrlComputed(pageContext);
725
- objectAssign(pageContext, pageContextUrlComputed);
726
- });
727
- }
728
- function change(enumerable) {
729
- pageContexts.forEach((pageContext) => {
730
- changeEnumerable(pageContext, 'urlPathname', enumerable);
731
- changeEnumerable(pageContext, 'urlParsed', enumerable);
732
- });
733
- }
734
- }
735
752
  function validatePrerenderConfig(
736
753
  // Guaranteed by configDef.type to be either an object or boolean
737
754
  prerenderConfig) {
@@ -770,7 +787,8 @@ prerenderConfig) {
770
787
  function makePublic(prerenderContext) {
771
788
  const prerenderContextPublic = makePublicCopy(prerenderContext, 'prerenderContext', [
772
789
  'output', // vite-plugin-vercel
773
- 'pageContexts' // https://vike.dev/i18n#pre-rendering
790
+ 'pageContexts', // https://vike.dev/i18n#pre-rendering
791
+ 'pageContexts404' // https://vike.dev/i18n#pre-rendering
774
792
  ]);
775
793
  return prerenderContextPublic;
776
794
  }
@@ -13,6 +13,6 @@ export * from '../../utils/pLimit.js';
13
13
  export * from '../../utils/isFilePathAbsoluteFilesystem.js';
14
14
  export * from '../../utils/isArray.js';
15
15
  export * from '../../utils/isObject.js';
16
- export * from '../../utils/changeEnumerable.js';
17
16
  export * from '../../utils/makePublicCopy.js';
18
17
  export * from '../../utils/isNullish.js';
18
+ export * from '../../utils/preservePropertyGetters.js';
@@ -15,6 +15,6 @@ export * from '../../utils/pLimit.js';
15
15
  export * from '../../utils/isFilePathAbsoluteFilesystem.js';
16
16
  export * from '../../utils/isArray.js';
17
17
  export * from '../../utils/isObject.js';
18
- export * from '../../utils/changeEnumerable.js';
19
18
  export * from '../../utils/makePublicCopy.js';
20
19
  export * from '../../utils/isNullish.js';
20
+ export * from '../../utils/preservePropertyGetters.js';