astro 7.2.9 → 7.3.0

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 (88) hide show
  1. package/dist/assets/build/generate.d.ts +1 -1
  2. package/dist/assets/build/generate.js +3 -1
  3. package/dist/assets/endpoint/dev.js +6 -5
  4. package/dist/assets/endpoint/generic.js +5 -4
  5. package/dist/assets/endpoint/node.js +3 -3
  6. package/dist/assets/endpoint/shared.d.ts +3 -1
  7. package/dist/assets/endpoint/shared.js +9 -3
  8. package/dist/assets/fonts/config.d.ts +2 -2
  9. package/dist/assets/internal.d.ts +2 -1
  10. package/dist/assets/internal.js +11 -10
  11. package/dist/assets/services/service.d.ts +8 -7
  12. package/dist/assets/services/sharp.js +5 -5
  13. package/dist/assets/vite-plugin-assets.js +22 -5
  14. package/dist/cli/add/index.js +3 -1
  15. package/dist/cli/index.js +1 -1
  16. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  17. package/dist/cli/preferences/index.js +32 -30
  18. package/dist/cli/preview/index.js +44 -1
  19. package/dist/cli/telemetry/index.d.ts +2 -1
  20. package/dist/cli/telemetry/index.js +2 -2
  21. package/dist/content/content-layer.js +3 -3
  22. package/dist/content/data-store-writer.d.ts +18 -5
  23. package/dist/content/data-store-writer.js +11 -3
  24. package/dist/content/index.d.ts +1 -1
  25. package/dist/content/index.js +5 -1
  26. package/dist/content/mutable-data-store.d.ts +13 -1
  27. package/dist/content/mutable-data-store.js +35 -5
  28. package/dist/content/runtime.d.ts +8 -3
  29. package/dist/content/runtime.js +43 -32
  30. package/dist/content/types-generator.js +2 -1
  31. package/dist/content/utils.d.ts +5 -35
  32. package/dist/content/utils.js +71 -65
  33. package/dist/content/vite-plugin-content-imports.js +2 -1
  34. package/dist/content/vite-plugin-content-virtual-mod.d.ts +11 -1
  35. package/dist/content/vite-plugin-content-virtual-mod.js +36 -3
  36. package/dist/core/app/entrypoints/index.d.ts +4 -2
  37. package/dist/core/app/entrypoints/index.js +13 -11
  38. package/dist/core/app/entrypoints/virtual/dev.js +2 -2
  39. package/dist/core/app/prerender.d.ts +32 -0
  40. package/dist/core/app/prerender.js +22 -0
  41. package/dist/core/build/default-prerenderer.js +5 -2
  42. package/dist/core/build/generate.d.ts +3 -1
  43. package/dist/core/build/generate.js +40 -62
  44. package/dist/core/build/plugins/plugin-manifest.js +1 -1
  45. package/dist/core/cache/handler.js +12 -1
  46. package/dist/core/cache/memory-provider.js +35 -8
  47. package/dist/core/cache/runtime/cache.js +3 -0
  48. package/dist/core/cache/types.d.ts +2 -0
  49. package/dist/core/config/schemas/base.d.ts +5 -5
  50. package/dist/core/config/schemas/relative.d.ts +7 -7
  51. package/dist/core/constants.js +1 -1
  52. package/dist/core/cookies/cookies.d.ts +2 -1
  53. package/dist/core/cookies/cookies.js +5 -4
  54. package/dist/core/dev/dev.js +5 -3
  55. package/dist/core/fetch/fetch-state.d.ts +1 -1
  56. package/dist/core/fetch/fetch-state.js +4 -23
  57. package/dist/core/logger/core.d.ts +2 -0
  58. package/dist/core/logger/core.js +14 -0
  59. package/dist/core/logger/vite-plugin.d.ts +13 -2
  60. package/dist/core/logger/vite-plugin.js +8 -7
  61. package/dist/core/messages/runtime.js +1 -1
  62. package/dist/core/middleware/index.js +14 -1
  63. package/dist/core/render-scope/collect.d.ts +24 -0
  64. package/dist/core/render-scope/collect.js +27 -0
  65. package/dist/core/render-scope/node-scope.d.ts +10 -0
  66. package/dist/core/render-scope/node-scope.js +8 -0
  67. package/dist/core/render-scope/record.d.ts +14 -0
  68. package/dist/core/render-scope/record.js +12 -0
  69. package/dist/core/render-scope/scope.d.ts +38 -0
  70. package/dist/core/render-scope/scope.js +28 -0
  71. package/dist/core/rewrites/handler.js +1 -1
  72. package/dist/core/routing/create-manifest.js +5 -1
  73. package/dist/core/server-islands/endpoint.js +4 -1
  74. package/dist/core/server-islands/vite-plugin-server-islands.js +2 -2
  75. package/dist/core/sync/index.js +1 -1
  76. package/dist/core/util.d.ts +0 -9
  77. package/dist/core/util.js +0 -34
  78. package/dist/core/viteUtils.d.ts +1 -13
  79. package/dist/core/viteUtils.js +3 -33
  80. package/dist/types/public/integrations.d.ts +20 -13
  81. package/dist/vite-plugin-app/createAstroServerApp.js +1 -1
  82. package/dist/vite-plugin-routes/index.js +22 -8
  83. package/package.json +6 -6
  84. package/templates/content/module.mjs +13 -5
  85. package/dist/core/build/incremental-content-collector.d.ts +0 -10
  86. package/dist/core/build/incremental-content-collector.js +0 -33
  87. package/dist/core/build/incremental-image-collector.d.ts +0 -11
  88. package/dist/core/build/incremental-image-collector.js +0 -32
@@ -36,66 +36,71 @@ const loaderReturnSchema = z.union([
36
36
  }).passthrough()
37
37
  )
38
38
  ]);
39
- const collectionConfigParser = z.union([
40
- z.object({
41
- type: z.literal("content").optional(),
42
- schema: z.any().optional(),
43
- loader: z.never().optional()
44
- }),
45
- z.object({
46
- type: z.literal("data").optional(),
47
- schema: z.any().optional(),
48
- loader: z.never().optional()
49
- }),
50
- z.object({
51
- type: z.literal(CONTENT_LAYER_TYPE),
52
- schema: z.any().optional(),
53
- loader: z.union([
54
- z.function(),
55
- z.object({
56
- name: z.string(),
57
- load: z.function({
58
- input: [z.custom()],
59
- output: z.custom()
39
+ function contentConfigParser(logger) {
40
+ return z.object({
41
+ collections: z.record(
42
+ z.string(),
43
+ z.union([
44
+ z.object({
45
+ type: z.literal("content").optional(),
46
+ schema: z.any().optional(),
47
+ loader: z.never().optional()
60
48
  }),
61
- schema: z.any().transform((v) => {
62
- if (typeof v === "function") {
63
- console.warn(
64
- `Your loader's schema is defined using a function. This is no longer supported and the schema will be ignored. Please update your loader to use the \`createSchema()\` utility instead, or report this to the loader author. In a future major version, this will cause the loader to break entirely.`
65
- );
66
- return void 0;
67
- }
68
- return v;
69
- }).superRefine((v, ctx) => {
70
- if (v !== void 0 && !("_zod" in v)) {
71
- ctx.addIssue({
72
- code: z.ZodIssueCode.custom,
73
- message: "Invalid Zod schema"
74
- });
75
- return z.NEVER;
76
- }
77
- }).optional(),
78
- createSchema: z.function({
79
- input: [],
80
- output: z.promise(
49
+ z.object({
50
+ type: z.literal("data").optional(),
51
+ schema: z.any().optional(),
52
+ loader: z.never().optional()
53
+ }),
54
+ z.object({
55
+ type: z.literal(CONTENT_LAYER_TYPE),
56
+ schema: z.any().optional(),
57
+ loader: z.union([
58
+ z.function(),
81
59
  z.object({
82
- schema: z.custom((v) => "_zod" in v),
83
- types: z.string()
60
+ name: z.string(),
61
+ load: z.function({
62
+ input: [z.custom()],
63
+ output: z.custom()
64
+ }),
65
+ schema: z.any().transform((v) => {
66
+ if (typeof v === "function") {
67
+ logger.warn(
68
+ "content",
69
+ `Your loader's schema is defined using a function. This is no longer supported and the schema will be ignored. Please update your loader to use the \`createSchema()\` utility instead, or report this to the loader author. In a future major version, this will cause the loader to break entirely.`
70
+ );
71
+ return void 0;
72
+ }
73
+ return v;
74
+ }).superRefine((v, ctx) => {
75
+ if (v !== void 0 && !("_zod" in v)) {
76
+ ctx.addIssue({
77
+ code: z.ZodIssueCode.custom,
78
+ message: "Invalid Zod schema"
79
+ });
80
+ return z.NEVER;
81
+ }
82
+ }).optional(),
83
+ createSchema: z.function({
84
+ input: [],
85
+ output: z.promise(
86
+ z.object({
87
+ schema: z.custom((v) => "_zod" in v),
88
+ types: z.string()
89
+ })
90
+ )
91
+ }).optional()
84
92
  })
85
- )
86
- }).optional()
87
- })
88
- ])
89
- }),
90
- z.object({
91
- type: z.literal(LIVE_CONTENT_TYPE).optional().default(LIVE_CONTENT_TYPE),
92
- schema: z.any().optional(),
93
- loader: z.function()
94
- })
95
- ]);
96
- const contentConfigParser = z.object({
97
- collections: z.record(z.string(), collectionConfigParser)
98
- });
93
+ ])
94
+ }),
95
+ z.object({
96
+ type: z.literal(LIVE_CONTENT_TYPE).optional().default(LIVE_CONTENT_TYPE),
97
+ schema: z.any().optional(),
98
+ loader: z.function()
99
+ })
100
+ ])
101
+ )
102
+ });
103
+ }
99
104
  function parseEntrySlug({
100
105
  id,
101
106
  collection,
@@ -358,7 +363,8 @@ function isDeferredModule(viteId) {
358
363
  async function loadContentConfig({
359
364
  fs,
360
365
  settings,
361
- environment
366
+ environment,
367
+ logger
362
368
  }) {
363
369
  const contentPaths = getContentPaths(
364
370
  settings.config,
@@ -370,19 +376,17 @@ async function loadContentConfig({
370
376
  }
371
377
  const configPathname = fileURLToPath(contentPaths.config.url);
372
378
  const unparsedConfig = await environment.runner.import(configPathname);
373
- const config = contentConfigParser.safeParse(unparsedConfig);
379
+ const config = contentConfigParser(logger).safeParse(unparsedConfig);
374
380
  if (config.success) {
375
381
  const hasher = await xxhash();
376
382
  const digest = hasher.h64ToString(await fs.promises.readFile(configPathname, "utf-8"));
377
383
  return { ...config.data, digest };
378
384
  } else {
379
385
  const message = config.error.issues.map((issue) => ` \u2192 ${colors.green(issue.path.join("."))}: ${colors.red(issue.message)}`).join("\n");
380
- console.error(
381
- `${colors.green("[content]")} There was a problem with your content config:
386
+ logger.error("content", `There was a problem with your content config:
382
387
 
383
388
  ${message}
384
- `
385
- );
389
+ `);
386
390
  const liveCollections = Object.entries(unparsedConfig.collections ?? {}).filter(
387
391
  ([, collection]) => collection?.type === LIVE_CONTENT_TYPE
388
392
  );
@@ -401,7 +405,8 @@ ${message}
401
405
  async function autogenerateCollections({
402
406
  config,
403
407
  settings,
404
- fs
408
+ fs,
409
+ logger
405
410
  }) {
406
411
  if (!config) {
407
412
  return config;
@@ -455,7 +460,8 @@ async function autogenerateCollections({
455
460
  }
456
461
  }
457
462
  if (orphanedCollections.length > 0) {
458
- console.warn(
463
+ logger.warn(
464
+ "SKIP_FORMAT",
459
465
  `
460
466
  Auto-generating collections for folders in "src/content/" that are not defined as collections.
461
467
  This is deprecated, so you should define these collections yourself in "src/content.config.ts".
@@ -138,7 +138,8 @@ export const _internal = {
138
138
  await reloadContentConfigObserver({
139
139
  fs,
140
140
  settings,
141
- environment: viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro]
141
+ environment: viteServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro],
142
+ logger
142
143
  });
143
144
  }
144
145
  for (const modUrl of environment.moduleGraph.urlToModuleMap.keys()) {
@@ -1,9 +1,19 @@
1
1
  import nodeFs from 'node:fs';
2
- import { type Plugin } from 'vite';
2
+ import { type Plugin, type ViteDevServer } from 'vite';
3
3
  import type { AstroSettings } from '../types/astro.js';
4
+ import type { MutableDataStore } from './mutable-data-store.js';
4
5
  interface AstroContentVirtualModPluginParams {
5
6
  settings: AstroSettings;
6
7
  fs: typeof nodeFs;
7
8
  }
9
+ /**
10
+ * Invalidates the content virtual modules directly whenever the given store
11
+ * writes to disk. The watcher listeners in `configureServer` cover writes from
12
+ * other processes, but the watcher can miss the atomic rename that commits a
13
+ * write on some platforms (notably Windows, see #17335), leaving dev serving
14
+ * stale content until a restart. Subscribing to the store's own write
15
+ * notifications makes invalidation of this process's writes deterministic.
16
+ */
17
+ export declare function attachDataStoreInvalidation(store: MutableDataStore, server: ViteDevServer, settings: AstroSettings): void;
8
18
  export declare function astroContentVirtualModPlugin({ settings, fs, }: AstroContentVirtualModPluginParams): Plugin;
9
19
  export {};
@@ -70,6 +70,35 @@ function invalidateDataStore(viteServer, { notifyClient = true } = {}) {
70
70
  });
71
71
  }
72
72
  }
73
+ const directInvalidations = /* @__PURE__ */ new Map();
74
+ const DIRECT_INVALIDATION_ECHO_MS = 1e3;
75
+ function markDirectInvalidation(path) {
76
+ directInvalidations.set(path, Date.now());
77
+ }
78
+ function isDirectInvalidationEcho(path) {
79
+ const time = directInvalidations.get(path);
80
+ return time !== void 0 && Date.now() - time < DIRECT_INVALIDATION_ECHO_MS;
81
+ }
82
+ function getDevDataStoreFile(settings) {
83
+ if (getDataStoreChunkSize(settings) !== void 0) {
84
+ return new URL(DATA_STORE_MANIFEST_FILE, getDataStoreDir(settings, true));
85
+ }
86
+ return getDataStoreFile(settings, true);
87
+ }
88
+ function attachDataStoreInvalidation(store, server, settings) {
89
+ const dataStorePath = fileURLToPath(getDevDataStoreFile(settings));
90
+ const assetImportsPath = fileURLToPath(new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir));
91
+ store.onFileWritten((path) => {
92
+ if (path === dataStorePath) {
93
+ markDirectInvalidation(dataStorePath);
94
+ invalidateDataStore(server);
95
+ invalidateAssetImports(server, assetImportsPath);
96
+ } else if (path === assetImportsPath) {
97
+ markDirectInvalidation(assetImportsPath);
98
+ invalidateAssetImports(server, assetImportsPath);
99
+ }
100
+ });
101
+ }
73
102
  function astroContentVirtualModPlugin({
74
103
  settings,
75
104
  fs
@@ -270,16 +299,19 @@ function astroContentVirtualModPlugin({
270
299
  const dataStorePath = fileURLToPath(dataStoreFile);
271
300
  const assetImportsPath = fileURLToPath(new URL(ASSET_IMPORTS_FILE, settings.dotAstroDir));
272
301
  server.watcher.on("add", (addedPath) => {
273
- if (addedPath === dataStorePath) {
302
+ if (addedPath === dataStorePath && !isDirectInvalidationEcho(dataStorePath)) {
274
303
  invalidateDataStore(server);
275
304
  invalidateAssetImports(server, assetImportsPath);
276
305
  }
277
306
  });
278
307
  server.watcher.on("change", (changedPath) => {
279
308
  if (changedPath === dataStorePath) {
309
+ if (isDirectInvalidationEcho(dataStorePath)) {
310
+ return;
311
+ }
280
312
  invalidateDataStore(server);
281
313
  invalidateAssetImports(server, assetImportsPath);
282
- } else if (changedPath === assetImportsPath) {
314
+ } else if (changedPath === assetImportsPath && !isDirectInvalidationEcho(assetImportsPath)) {
283
315
  invalidateAssetImports(server, assetImportsPath);
284
316
  }
285
317
  });
@@ -314,5 +346,6 @@ async function generateContentEntryFile({
314
346
  return virtualModContents;
315
347
  }
316
348
  export {
317
- astroContentVirtualModPlugin
349
+ astroContentVirtualModPlugin,
350
+ attachDataStoreInvalidation
318
351
  };
@@ -4,5 +4,7 @@ export { BaseApp, type RenderErrorOptions, type RenderOptions, type LogRequestPa
4
4
  export { fromRoutingStrategy, toRoutingStrategy } from '../common.js';
5
5
  export { createConsoleLogger } from '../../logger/impls/console.js';
6
6
  export { deserializeManifest, deserializeRouteData, deserializeRouteInfo, serializeRouteData, serializeRouteInfo, } from '../manifest.js';
7
- export { beginContentEntryCollection, endContentEntryCollection, } from '../../build/incremental-content-collector.js';
8
- export { beginImageCollection, endImageCollection, recordStaticImage, } from '../../build/incremental-image-collector.js';
7
+ export { getInstalledRenderScope, installRenderScope, type RenderCollectors, type RenderCollectorScope, } from '../../render-scope/scope.js';
8
+ export { recordStaticImage } from '../../render-scope/record.js';
9
+ export { collectPrerenderMetadata, type CollectedPrerenderMetadata, } from '../../render-scope/collect.js';
10
+ export { renderForPrerender, type PrerenderableApp, type PrerenderRenderOptions, } from '../prerender.js';
@@ -12,27 +12,29 @@ import {
12
12
  serializeRouteInfo
13
13
  } from "../manifest.js";
14
14
  import {
15
- beginContentEntryCollection,
16
- endContentEntryCollection
17
- } from "../../build/incremental-content-collector.js";
15
+ getInstalledRenderScope,
16
+ installRenderScope
17
+ } from "../../render-scope/scope.js";
18
+ import { recordStaticImage } from "../../render-scope/record.js";
18
19
  import {
19
- beginImageCollection,
20
- endImageCollection,
21
- recordStaticImage
22
- } from "../../build/incremental-image-collector.js";
20
+ collectPrerenderMetadata
21
+ } from "../../render-scope/collect.js";
22
+ import {
23
+ renderForPrerender
24
+ } from "../prerender.js";
23
25
  export {
24
26
  App,
25
27
  BaseApp,
26
- beginContentEntryCollection,
27
- beginImageCollection,
28
+ collectPrerenderMetadata,
28
29
  createConsoleLogger,
29
30
  deserializeManifest,
30
31
  deserializeRouteData,
31
32
  deserializeRouteInfo,
32
- endContentEntryCollection,
33
- endImageCollection,
34
33
  fromRoutingStrategy,
34
+ getInstalledRenderScope,
35
+ installRenderScope,
35
36
  recordStaticImage,
37
+ renderForPrerender,
36
38
  serializeRouteData,
37
39
  serializeRouteInfo,
38
40
  toRoutingStrategy
@@ -11,7 +11,7 @@ import { updateRouteTable } from "../../../routing/route-table.js";
11
11
  import { DevFacadeApp } from "../../dev-facade.js";
12
12
  let hmrWired = false;
13
13
  const createApp = ({ streaming } = {}) => {
14
- setLogger(manifest, createConsoleLogger(manifest.logLevel));
14
+ setLogger(manifest, createConsoleLogger({ level: manifest.logLevel }));
15
15
  setEnvironment(manifest, createNonRunnableEnvironment());
16
16
  const app = new DevFacadeApp(manifest, streaming);
17
17
  app.setFetchHandler(fetchable);
@@ -22,7 +22,7 @@ const createApp = ({ streaming } = {}) => {
22
22
  const { routes: newRoutes } = await import("virtual:astro:routes");
23
23
  updateRouteTable(
24
24
  manifest,
25
- newRoutes.map((r) => r.routeData)
25
+ newRoutes.map((route) => route.routeData)
26
26
  );
27
27
  } catch (e) {
28
28
  getLogger(manifest).error("router", `Failed to update routes via HMR:
@@ -0,0 +1,32 @@
1
+ import type { PrerenderResult } from '../../types/public/integrations.js';
2
+ import type { RouteData } from '../../types/public/internal.js';
3
+ import type { AstroLogger } from '../logger/core.js';
4
+ export interface PrerenderableApp {
5
+ logger: AstroLogger;
6
+ render(request: Request, opts: {
7
+ routeData?: RouteData;
8
+ }): Promise<Response>;
9
+ }
10
+ export interface PrerenderRenderOptions {
11
+ routeData?: RouteData;
12
+ /** Collect per-render incremental metadata and return it on the result. Default false. */
13
+ collectMetadata?: boolean;
14
+ }
15
+ /**
16
+ * Renders a single prerendered path through `app.render`, optionally collecting
17
+ * the per-render incremental metadata (content entries rendered, image
18
+ * transforms resolved) and returning it by value on the result.
19
+ *
20
+ * When not collecting, this short-circuits to a bare `app.render` — the
21
+ * response is the app's own, unbuffered. When collecting, the response body is
22
+ * fully buffered *inside* the render scope so lazily-driven rendering work
23
+ * records before the metadata snapshot; the returned response is a
24
+ * reconstruction carrying the buffered body with status/statusText/headers and
25
+ * body-nullness preserved.
26
+ *
27
+ * This function performs no scope installation; installing a render scope for
28
+ * the rendering runtime is the caller's responsibility. When collection is
29
+ * requested but no scope is installed, `metadata` is `undefined`
30
+ * ("not tracked").
31
+ */
32
+ export declare function renderForPrerender(app: PrerenderableApp, request: Request, options?: PrerenderRenderOptions): Promise<PrerenderResult>;
@@ -0,0 +1,22 @@
1
+ import { collectPrerenderMetadata } from "../render-scope/collect.js";
2
+ const NULL_BODY_STATUSES = [101, 204, 205, 304];
3
+ async function renderForPrerender(app, request, options) {
4
+ const routeData = options?.routeData;
5
+ if (!options?.collectMetadata) {
6
+ return { response: await app.render(request, { routeData }) };
7
+ }
8
+ const { value: response, metadata } = await collectPrerenderMetadata(async () => {
9
+ const rendered = await app.render(request, { routeData });
10
+ const bytes = rendered.body === null ? null : await rendered.arrayBuffer();
11
+ const nullBody = bytes === null || NULL_BODY_STATUSES.includes(rendered.status);
12
+ return new Response(nullBody ? null : bytes, {
13
+ status: rendered.status,
14
+ statusText: rendered.statusText,
15
+ headers: rendered.headers
16
+ });
17
+ }, app.logger);
18
+ return { response, metadata };
19
+ }
20
+ export {
21
+ renderForPrerender
22
+ };
@@ -1,3 +1,5 @@
1
+ import { renderForPrerender } from "../app/prerender.js";
2
+ import { ensureAsyncRenderScope } from "../render-scope/node-scope.js";
1
3
  import { StaticPaths } from "../../runtime/prerender/static-paths.js";
2
4
  function createDefaultPrerenderer({
3
5
  internals,
@@ -24,8 +26,9 @@ function createDefaultPrerenderer({
24
26
  const staticPaths = new StaticPaths(prerenderer.app);
25
27
  return staticPaths.getAll();
26
28
  },
27
- async render(request, { routeData }) {
28
- return prerenderer.app.render(request, { routeData });
29
+ async render(request, { routeData, collectMetadata }) {
30
+ if (collectMetadata) ensureAsyncRenderScope();
31
+ return renderForPrerender(prerenderer.app, request, { routeData, collectMetadata });
29
32
  },
30
33
  async teardown() {
31
34
  }
@@ -23,6 +23,8 @@ interface RenderToPathPayload {
23
23
  options: StaticBuildOptions;
24
24
  routeToHeaders?: RouteToHeaders;
25
25
  logger: AstroLogger;
26
+ /** Ask the prerenderer to collect and report per-render incremental metadata. */
27
+ collectMetadata?: boolean;
26
28
  }
27
29
  /**
28
30
  * Renders a single prerendered path to an in-memory result.
@@ -48,5 +50,5 @@ interface RenderToPathPayload {
48
50
  * not need to inspect the headers after the call can omit this.
49
51
  * @param params.logger - Logger instance.
50
52
  */
51
- export declare function renderPath({ prerenderer, pathname, route, options, routeToHeaders, logger, }: RenderToPathPayload): Promise<RenderPathResult | null>;
53
+ export declare function renderPath({ prerenderer, pathname, route, options, routeToHeaders, logger, collectMetadata, }: RenderToPathPayload): Promise<RenderPathResult | null>;
52
54
  export {};
@@ -26,15 +26,9 @@ import { getRedirectLocationOrThrow } from "../redirects/index.js";
26
26
  import { createRequest } from "../request.js";
27
27
  import { redirectTemplate } from "../routing/3xx.js";
28
28
  import { routeIsRedirect } from "../routing/helpers.js";
29
- import { matchRoute } from "../routing/match.js";
30
29
  import { getOutputFilename } from "../output-filename.js";
31
30
  import { getOutFile, getOutFolder } from "./common.js";
32
31
  import { createDefaultPrerenderer } from "./default-prerenderer.js";
33
- import {
34
- beginContentEntryCollection,
35
- endContentEntryCollection
36
- } from "./incremental-content-collector.js";
37
- import { beginImageCollection, endImageCollection } from "./incremental-image-collector.js";
38
32
  import { IncrementalBuildCache } from "./incremental.js";
39
33
  import { computeConfigHash } from "./config-hash/index.js";
40
34
  import { computeLockfileHash } from "./lockfile/index.js";
@@ -77,32 +71,25 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
77
71
  let staticImageList = getStaticImageList();
78
72
  let cache = null;
79
73
  if (options.settings.config.experimental.incrementalBuild) {
80
- if (options.settings.config.build.concurrency > 1) {
81
- logger.warn(
82
- "build",
83
- "The incremental build cache is disabled because `build.concurrency` is greater than 1."
84
- );
85
- } else {
86
- const [configHash, lockfileHash, keyDigest] = await Promise.all([
87
- computeConfigHash(options.settings.config),
88
- computeLockfileHash(fileURLToPath(options.settings.config.root)),
89
- options.key.then(hashCryptoKey)
90
- ]);
91
- cache = IncrementalBuildCache.load(
92
- options.settings,
93
- configHash,
94
- lockfileHash,
95
- keyDigest,
96
- internals.contentEntryRenderHashes ?? /* @__PURE__ */ new Map(),
97
- options.force
98
- );
99
- }
74
+ const [configHash, lockfileHash, keyDigest] = await Promise.all([
75
+ computeConfigHash(options.settings.config),
76
+ computeLockfileHash(fileURLToPath(options.settings.config.root)),
77
+ options.key.then(hashCryptoKey)
78
+ ]);
79
+ cache = IncrementalBuildCache.load(
80
+ options.settings,
81
+ configHash,
82
+ lockfileHash,
83
+ keyDigest,
84
+ internals.contentEntryRenderHashes ?? /* @__PURE__ */ new Map(),
85
+ options.force
86
+ );
100
87
  }
101
88
  try {
102
89
  const pathsWithRoutes = await prerenderer.getStaticPaths();
103
90
  const hasI18nDomains = ssr && options.settings.config.i18n?.domains && Object.keys(options.settings.config.i18n.domains).length > 0;
104
91
  const { config } = options.settings;
105
- const builtPaths = /* @__PURE__ */ new Set();
92
+ const builtPaths = /* @__PURE__ */ new Map();
106
93
  const filteredPaths = [];
107
94
  const fallbackPaths = [];
108
95
  for (const pathWithRoute of pathsWithRoutes) {
@@ -115,33 +102,28 @@ ${colors.bgGreen(colors.black(` ${verb} static routes `))}`);
115
102
  }
116
103
  const normalized = removeTrailingForwardSlash(pathname);
117
104
  if (!builtPaths.has(normalized)) {
118
- builtPaths.add(normalized);
105
+ builtPaths.set(normalized, route);
119
106
  } else {
120
- const matchedRoute = matchRoute(decodeURI(pathname), options.routesList);
121
- if (!matchedRoute) {
122
- continue;
123
- }
124
- if (matchedRoute !== route) {
125
- if (config.prerenderConflictBehavior === "error") {
126
- throw new AstroError({
127
- ...AstroErrorData.PrerenderRouteConflict,
128
- message: AstroErrorData.PrerenderRouteConflict.message(
129
- matchedRoute.route,
130
- route.route,
131
- normalized
132
- ),
133
- hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route)
134
- });
135
- } else if (config.prerenderConflictBehavior === "warn") {
136
- const msg = AstroErrorData.PrerenderRouteConflict.message(
137
- matchedRoute.route,
107
+ const winningRoute = builtPaths.get(normalized);
108
+ if (config.prerenderConflictBehavior === "error") {
109
+ throw new AstroError({
110
+ ...AstroErrorData.PrerenderRouteConflict,
111
+ message: AstroErrorData.PrerenderRouteConflict.message(
112
+ winningRoute.route,
138
113
  route.route,
139
114
  normalized
140
- );
141
- logger.warn("build", msg);
142
- }
143
- continue;
115
+ ),
116
+ hint: AstroErrorData.PrerenderRouteConflict.hint(winningRoute.route, route.route)
117
+ });
118
+ } else if (config.prerenderConflictBehavior === "warn") {
119
+ const msg = AstroErrorData.PrerenderRouteConflict.message(
120
+ winningRoute.route,
121
+ route.route,
122
+ normalized
123
+ );
124
+ logger.warn("build", msg);
144
125
  }
126
+ continue;
145
127
  }
146
128
  const paths = route.type === "fallback" ? fallbackPaths : filteredPaths;
147
129
  paths.push(pathWithRoute);
@@ -283,7 +265,8 @@ async function renderPath({
283
265
  route,
284
266
  options,
285
267
  routeToHeaders = /* @__PURE__ */ new Map(),
286
- logger
268
+ logger,
269
+ collectMetadata
287
270
  }) {
288
271
  const { config } = options.settings;
289
272
  if (route.type === "fallback" && route.pathname !== "/") {
@@ -324,7 +307,7 @@ async function renderPath({
324
307
  let metadata;
325
308
  try {
326
309
  const rendered = normalizePrerenderResult(
327
- await prerenderer.render(request, { routeData: route })
310
+ await prerenderer.render(request, { routeData: route, collectMetadata })
328
311
  );
329
312
  response = rendered.response;
330
313
  metadata = rendered.metadata;
@@ -431,22 +414,17 @@ async function generatePathWithPrerenderer(prerenderer, pathname, route, options
431
414
  if (route.type === "page") {
432
415
  addPageName(pathname, options);
433
416
  }
434
- if (cache) {
435
- beginContentEntryCollection();
436
- beginImageCollection();
437
- }
438
417
  const result = await renderPath({
439
418
  prerenderer,
440
419
  pathname,
441
420
  route,
442
421
  options,
443
422
  routeToHeaders,
444
- logger
423
+ logger,
424
+ collectMetadata: cache !== null
445
425
  });
446
- const collectedContentEntryKeys = cache ? endContentEntryCollection() : void 0;
447
- const collectedStaticImages = cache ? endImageCollection() : void 0;
448
- const contentEntryKeys = result?.metadata?.contentEntryKeys ?? collectedContentEntryKeys;
449
- const staticImages = result?.metadata?.staticImages ?? collectedStaticImages;
426
+ const contentEntryKeys = result?.metadata?.contentEntryKeys;
427
+ const staticImages = result?.metadata?.staticImages;
450
428
  const headers = cache ? [...routeToHeaders.get(pathname)?.headers ?? []] : void 0;
451
429
  if (!result) {
452
430
  logRenderTime(logger, timeStart, true);
@@ -454,7 +432,7 @@ async function generatePathWithPrerenderer(prerenderer, pathname, route, options
454
432
  }
455
433
  await nodeFs.promises.mkdir(result.outFolder, { recursive: true });
456
434
  await nodeFs.promises.writeFile(result.outFile, result.body);
457
- if (cache && cacheKey !== void 0) {
435
+ if (cache && cacheKey !== void 0 && result.metadata !== void 0) {
458
436
  await cache.writeOutputFile(options.settings, relativeOutFile, result.body);
459
437
  cache.record(
460
438
  route.component,
@@ -31,7 +31,7 @@ import { makePageDataKey } from "./util.js";
31
31
  import { cacheConfigToManifest } from "../../cache/utils.js";
32
32
  import { sessionConfigToManifest } from "../../session/utils.js";
33
33
  const MANIFEST_REPLACE = "@@ASTRO_MANIFEST_REPLACE@@";
34
- const replaceExp = new RegExp(`['"]${MANIFEST_REPLACE}['"]`, "g");
34
+ const replaceExp = new RegExp(`['"\`]${MANIFEST_REPLACE}['"\`]`, "g");
35
35
  async function manifestBuildPostHook(options, internals, {
36
36
  chunks,
37
37
  mutate
@@ -52,7 +52,18 @@ async function handleCache(state, next) {
52
52
  {
53
53
  request: state.request,
54
54
  url: new URL(state.request.url),
55
- waitUntil: state.renderOptions.waitUntil
55
+ waitUntil: state.renderOptions.waitUntil,
56
+ logger: {
57
+ info(msg) {
58
+ state.logger.info("cache", msg);
59
+ },
60
+ warn(msg) {
61
+ state.logger.warn("cache", msg);
62
+ },
63
+ error(msg) {
64
+ state.logger.error("cache", msg);
65
+ }
66
+ }
56
67
  },
57
68
  async () => {
58
69
  const res = await next();