astro 4.10.3 → 4.11.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 (37) hide show
  1. package/components/Code.astro +9 -0
  2. package/dist/actions/runtime/virtual/server.d.ts +1 -1
  3. package/dist/assets/internal.d.ts +1 -1
  4. package/dist/assets/internal.js +6 -0
  5. package/dist/assets/types.d.ts +8 -2
  6. package/dist/assets/types.js +7 -0
  7. package/dist/container/pipeline.d.ts +0 -1
  8. package/dist/container/pipeline.js +13 -32
  9. package/dist/content/runtime-assets.d.ts +3 -3
  10. package/dist/content/types-generator.js +27 -6
  11. package/dist/core/app/index.d.ts +4 -0
  12. package/dist/core/app/index.js +17 -6
  13. package/dist/core/app/pipeline.d.ts +1 -2
  14. package/dist/core/app/pipeline.js +11 -39
  15. package/dist/core/base-pipeline.d.ts +0 -9
  16. package/dist/core/build/pipeline.d.ts +1 -2
  17. package/dist/core/build/pipeline.js +16 -42
  18. package/dist/core/constants.js +1 -1
  19. package/dist/core/cookies/cookies.d.ts +5 -0
  20. package/dist/core/cookies/cookies.js +12 -0
  21. package/dist/core/cookies/response.d.ts +1 -0
  22. package/dist/core/cookies/response.js +1 -0
  23. package/dist/core/create-vite.js +0 -4
  24. package/dist/core/dev/dev.js +1 -1
  25. package/dist/core/errors/errors-data.d.ts +20 -11
  26. package/dist/core/errors/errors-data.js +7 -6
  27. package/dist/core/messages.js +2 -2
  28. package/dist/core/render-context.d.ts +1 -1
  29. package/dist/core/render-context.js +19 -6
  30. package/dist/core/request.js +7 -1
  31. package/dist/core/routing/rewrite.d.ts +10 -0
  32. package/dist/core/routing/rewrite.js +41 -0
  33. package/dist/vite-plugin-astro-server/pipeline.d.ts +1 -2
  34. package/dist/vite-plugin-astro-server/pipeline.js +15 -42
  35. package/dist/vite-plugin-astro-server/request.js +1 -1
  36. package/dist/vite-plugin-astro-server/route.js +39 -97
  37. package/package.json +4 -3
@@ -3,6 +3,7 @@ import type { ThemePresets } from '@astrojs/markdown-remark';
3
3
  import type {
4
4
  BuiltinLanguage,
5
5
  LanguageRegistration,
6
+ ShikiTransformer,
6
7
  SpecialLanguage,
7
8
  ThemeRegistration,
8
9
  ThemeRegistrationRaw,
@@ -50,6 +51,12 @@ interface Props extends Omit<HTMLAttributes<'pre'>, 'lang'> {
50
51
  * @default false
51
52
  */
52
53
  inline?: boolean;
54
+ /**
55
+ * Shiki transformers to customize the generated HTML by manipulating the hast tree.
56
+ * Supports all transformers listed here: https://shiki.style/packages/transformers#transformers
57
+ * Instructions for custom transformers: https://shiki.style/guide/transformers
58
+ */
59
+ transformers?: ShikiTransformer[];
53
60
  }
54
61
 
55
62
  const {
@@ -59,6 +66,7 @@ const {
59
66
  themes = {},
60
67
  wrap = false,
61
68
  inline = false,
69
+ transformers = [],
62
70
  ...rest
63
71
  } = Astro.props;
64
72
 
@@ -85,6 +93,7 @@ const highlighter = await getCachedHighlighter({
85
93
  theme,
86
94
  themes,
87
95
  wrap,
96
+ transformers,
88
97
  });
89
98
 
90
99
  const html = await highlighter.highlight(code, typeof lang === 'string' ? lang : lang.name, {
@@ -14,7 +14,7 @@ export type ActionClient<TOutput, TAccept extends Accept, TInputSchema extends I
14
14
  } : ((input?: any) => Promise<Awaited<TOutput>>) & {
15
15
  safe: (input?: any) => Promise<SafeResult<never, Awaited<TOutput>>>;
16
16
  };
17
- export declare function defineAction<TOutput, TAccept extends Accept = 'json', TInputSchema extends InputSchema<Accept> | undefined = TAccept extends 'form' ? z.ZodType<FormData> : undefined>({ accept, input: inputSchema, handler, }: {
17
+ export declare function defineAction<TOutput, TAccept extends Accept, TInputSchema extends InputSchema<Accept> | undefined = TAccept extends 'form' ? z.ZodType<FormData> : undefined>({ accept, input: inputSchema, handler, }: {
18
18
  input?: TInputSchema;
19
19
  accept?: TAccept;
20
20
  handler: Handler<TInputSchema, TOutput>;
@@ -1,5 +1,5 @@
1
1
  import type { AstroConfig } from '../@types/astro.js';
2
2
  import { type ImageService } from './services/service.js';
3
- import type { GetImageResult, UnresolvedImageTransform } from './types.js';
3
+ import { type GetImageResult, type UnresolvedImageTransform } from './types.js';
4
4
  export declare function getConfiguredImageService(): Promise<ImageService>;
5
5
  export declare function getImage(options: UnresolvedImageTransform, imageConfig: AstroConfig['image']): Promise<GetImageResult>;
@@ -1,6 +1,9 @@
1
1
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
2
2
  import { DEFAULT_HASH_PROPS } from "./consts.js";
3
3
  import { isLocalService } from "./services/service.js";
4
+ import {
5
+ isImageMetadata
6
+ } from "./types.js";
4
7
  import { isESMImportedImage, isRemoteImage, resolveSrc } from "./utils/imageKind.js";
5
8
  import { probe } from "./utils/remoteProbe.js";
6
9
  async function getConfiguredImageService() {
@@ -36,6 +39,9 @@ async function getImage(options, imageConfig) {
36
39
  )
37
40
  });
38
41
  }
42
+ if (isImageMetadata(options)) {
43
+ throw new AstroError(AstroErrorData.ExpectedNotESMImage);
44
+ }
39
45
  const service = await getConfiguredImageService();
40
46
  const resolvedOptions = {
41
47
  ...options,
@@ -20,16 +20,20 @@ declare global {
20
20
  referencedImages?: Set<string>;
21
21
  };
22
22
  }
23
+ declare const isESMImport: unique symbol;
24
+ export type OmitBrand<T> = Omit<T, typeof isESMImport>;
23
25
  /**
24
26
  * Type returned by ESM imports of images
25
27
  */
26
- export interface ImageMetadata {
28
+ export type ImageMetadata = {
27
29
  src: string;
28
30
  width: number;
29
31
  height: number;
30
32
  format: ImageInputFormat;
31
33
  orientation?: number;
32
- }
34
+ [isESMImport]?: true;
35
+ };
36
+ export declare function isImageMetadata(src: any): src is ImageMetadata;
33
37
  /**
34
38
  * A yet to be completed with an url `SrcSetValue`. Other hooks will only see a resolved value, where the URL of the image has been added.
35
39
  */
@@ -49,6 +53,8 @@ export type UnresolvedImageTransform = Omit<ImageTransform, 'src'> & {
49
53
  default: ImageMetadata;
50
54
  }>;
51
55
  inferSize?: boolean;
56
+ } & {
57
+ [isESMImport]?: never;
52
58
  };
53
59
  /**
54
60
  * Options accepted by the image transformation service.
@@ -0,0 +1,7 @@
1
+ const isESMImport = Symbol("#isESM");
2
+ function isImageMetadata(src) {
3
+ return src.fsPath && !("fsPath" in src);
4
+ }
5
+ export {
6
+ isImageMetadata
7
+ };
@@ -8,5 +8,4 @@ export declare class ContainerPipeline extends Pipeline {
8
8
  tryRewrite(payload: RewritePayload, request: Request): Promise<[RouteData, ComponentInstance, URL]>;
9
9
  insertRoute(route: RouteData, componentInstance: ComponentInstance): void;
10
10
  getComponentByRoute(_routeData: RouteData): Promise<ComponentInstance>;
11
- rewriteKnownRoute(pathname: string, _sourceRoute: RouteData): ComponentInstance;
12
11
  }
@@ -1,11 +1,12 @@
1
1
  import { Pipeline } from "../core/base-pipeline.js";
2
- import { InvalidRewrite404, RouteNotFound } from "../core/errors/errors-data.js";
2
+ import { RouteNotFound } from "../core/errors/errors-data.js";
3
3
  import { AstroError } from "../core/errors/index.js";
4
4
  import {
5
5
  createModuleScriptElement,
6
6
  createStylesheetElementSet
7
7
  } from "../core/render/ssr-element.js";
8
- import { DEFAULT_404_ROUTE, default404Page } from "../core/routing/astro-designed-error-pages.js";
8
+ import { DEFAULT_404_ROUTE } from "../core/routing/astro-designed-error-pages.js";
9
+ import { findRouteToRewrite } from "../core/routing/rewrite.js";
9
10
  class ContainerPipeline extends Pipeline {
10
11
  /**
11
12
  * Internal cache to store components instances by `RouteData`.
@@ -52,30 +53,16 @@ class ContainerPipeline extends Pipeline {
52
53
  return { links, styles, scripts };
53
54
  }
54
55
  async tryRewrite(payload, request) {
55
- let foundRoute;
56
- let finalUrl = void 0;
57
- for (const route of this.manifest.routes) {
58
- if (payload instanceof URL) {
59
- finalUrl = payload;
60
- } else if (payload instanceof Request) {
61
- finalUrl = new URL(payload.url);
62
- } else {
63
- finalUrl = new URL(payload, new URL(request.url).origin);
64
- }
65
- if (route.routeData.pattern.test(decodeURI(finalUrl.pathname))) {
66
- foundRoute = route.routeData;
67
- break;
68
- } else if (finalUrl.pathname === "/404") {
69
- foundRoute = DEFAULT_404_ROUTE;
70
- break;
71
- }
72
- }
73
- if (foundRoute && finalUrl) {
74
- const componentInstance = await this.getComponentByRoute(foundRoute);
75
- return [foundRoute, componentInstance, finalUrl];
76
- } else {
77
- throw new AstroError(RouteNotFound);
78
- }
56
+ const [foundRoute, finalUrl] = findRouteToRewrite({
57
+ payload,
58
+ request,
59
+ routes: this.manifest?.routes.map((r) => r.routeData),
60
+ trailingSlash: this.manifest.trailingSlash,
61
+ buildFormat: this.manifest.buildFormat,
62
+ base: this.manifest.base
63
+ });
64
+ const componentInstance = await this.getComponentByRoute(foundRoute);
65
+ return [foundRoute, componentInstance, finalUrl];
79
66
  }
80
67
  insertRoute(route, componentInstance) {
81
68
  this.#componentsInterner.set(route, {
@@ -90,12 +77,6 @@ class ContainerPipeline extends Pipeline {
90
77
  // @ts-expect-error It needs to be implemented.
91
78
  async getComponentByRoute(_routeData) {
92
79
  }
93
- rewriteKnownRoute(pathname, _sourceRoute) {
94
- if (pathname === "/404") {
95
- return { default: default404Page };
96
- }
97
- throw new AstroError(InvalidRewrite404);
98
- }
99
80
  }
100
81
  export {
101
82
  ContainerPipeline
@@ -2,10 +2,10 @@ import type { PluginContext } from 'rollup';
2
2
  import { z } from 'zod';
3
3
  export declare function createImage(pluginContext: PluginContext, shouldEmitFile: boolean, entryFilePath: string): () => z.ZodEffects<z.ZodString, z.ZodNever | {
4
4
  ASTRO_ASSET: string;
5
- src: string;
5
+ format: "jpeg" | "jpg" | "png" | "tiff" | "webp" | "gif" | "svg" | "avif";
6
6
  width: number;
7
7
  height: number;
8
- format: "jpeg" | "jpg" | "png" | "tiff" | "webp" | "gif" | "svg" | "avif";
9
- orientation?: number | undefined;
8
+ src: string;
10
9
  fsPath: string;
10
+ orientation?: number | undefined;
11
11
  }, string>;
@@ -318,13 +318,26 @@ async function writeContentFiles({
318
318
  // This ensures `getCollection('empty-collection')` doesn't raise a type error
319
319
  collectionConfig?.type ?? "data"
320
320
  ) : collection.type;
321
+ const collectionEntryKeys = Object.keys(collection.entries).sort();
322
+ const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : "any";
321
323
  switch (resolvedType) {
322
324
  case "content":
325
+ if (collectionEntryKeys.length === 0) {
326
+ contentTypesStr += `${collectionKey}: Record<string, {
327
+ id: string;
328
+ slug: string;
329
+ body: string;
330
+ collection: ${collectionKey};
331
+ data: ${dataType};
332
+ render(): Render[".md"];
333
+ }>;
334
+ `;
335
+ break;
336
+ }
323
337
  contentTypesStr += `${collectionKey}: {
324
338
  `;
325
- for (const entryKey of Object.keys(collection.entries).sort()) {
339
+ for (const entryKey of collectionEntryKeys) {
326
340
  const entryMetadata = collection.entries[entryKey];
327
- const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : "any";
328
341
  const renderType = `{ render(): Render[${JSON.stringify(
329
342
  path.extname(JSON.parse(entryKey))
330
343
  )}] }`;
@@ -342,16 +355,24 @@ async function writeContentFiles({
342
355
  `;
343
356
  break;
344
357
  case "data":
345
- dataTypesStr += `${collectionKey}: {
358
+ if (collectionEntryKeys.length === 0) {
359
+ dataTypesStr += `${collectionKey}: Record<string, {
360
+ id: string;
361
+ collection: ${collectionKey};
362
+ data: ${dataType};
363
+ }>;
346
364
  `;
347
- for (const entryKey of Object.keys(collection.entries).sort()) {
348
- const dataType = collectionConfig?.schema ? `InferEntrySchema<${collectionKey}>` : "any";
349
- dataTypesStr += `${entryKey}: {
365
+ } else {
366
+ dataTypesStr += `${collectionKey}: {
367
+ `;
368
+ for (const entryKey of collectionEntryKeys) {
369
+ dataTypesStr += `${entryKey}: {
350
370
  id: ${entryKey};
351
371
  collection: ${collectionKey};
352
372
  data: ${dataType}
353
373
  };
354
374
  `;
375
+ }
355
376
  dataTypesStr += `};
356
377
  `;
357
378
  }
@@ -39,6 +39,10 @@ export interface RenderErrorOptions {
39
39
  * Whether to skip middleware while rendering the error page. Defaults to false.
40
40
  */
41
41
  skipMiddleware?: boolean;
42
+ /**
43
+ * Allows passing an error to 500.astro. It will be available through `Astro.props.error`.
44
+ */
45
+ error?: unknown;
42
46
  }
43
47
  export declare class App {
44
48
  #private;
@@ -195,8 +195,9 @@ class App {
195
195
  }
196
196
  if (locals) {
197
197
  if (typeof locals !== "object") {
198
- this.#logger.error(null, new AstroError(AstroErrorData.LocalsNotAnObject).stack);
199
- return this.#renderError(request, { status: 500 });
198
+ const error = new AstroError(AstroErrorData.LocalsNotAnObject);
199
+ this.#logger.error(null, error.stack);
200
+ return this.#renderError(request, { status: 500, error });
200
201
  }
201
202
  Reflect.set(request, clientLocalsSymbol, locals);
202
203
  }
@@ -229,13 +230,16 @@ class App {
229
230
  response = await renderContext.render(await mod.page());
230
231
  } catch (err) {
231
232
  this.#logger.error(null, err.stack || err.message || String(err));
232
- return this.#renderError(request, { locals, status: 500 });
233
+ return this.#renderError(request, { locals, status: 500, error: err });
233
234
  }
234
235
  if (REROUTABLE_STATUS_CODES.includes(response.status) && response.headers.get(REROUTE_DIRECTIVE_HEADER) !== "no") {
235
236
  return this.#renderError(request, {
236
237
  locals,
237
238
  response,
238
- status: response.status
239
+ status: response.status,
240
+ // We don't have an error to report here. Passing null means we pass nothing intentionally
241
+ // while undefined means there's no error
242
+ error: response.status === 500 ? null : void 0
239
243
  });
240
244
  }
241
245
  if (response.headers.has(REROUTE_DIRECTIVE_HEADER)) {
@@ -276,7 +280,13 @@ class App {
276
280
  * If it is a known error code, try sending the according page (e.g. 404.astro / 500.astro).
277
281
  * This also handles pre-rendered /404 or /500 routes
278
282
  */
279
- async #renderError(request, { locals, status, response: originalResponse, skipMiddleware = false }) {
283
+ async #renderError(request, {
284
+ locals,
285
+ status,
286
+ response: originalResponse,
287
+ skipMiddleware = false,
288
+ error
289
+ }) {
280
290
  const errorRoutePath = `/${status}${this.#manifest.trailingSlash === "always" ? "/" : ""}`;
281
291
  const errorRouteData = matchRoute(errorRoutePath, this.#manifestData);
282
292
  const url = new URL(request.url);
@@ -300,7 +310,8 @@ class App {
300
310
  pathname: this.#getPathnameFromRequest(request),
301
311
  request,
302
312
  routeData: errorRouteData,
303
- status
313
+ status,
314
+ props: { error }
304
315
  });
305
316
  const response2 = await renderContext.render(await mod.page());
306
317
  return this.#mergeResponses(response2, originalResponse);
@@ -7,7 +7,6 @@ export declare class AppPipeline extends Pipeline {
7
7
  headElements(routeData: RouteData): Pick<SSRResult, 'scripts' | 'styles' | 'links'>;
8
8
  componentMetadata(): void;
9
9
  getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
10
- tryRewrite(payload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
10
+ tryRewrite(payload: RewritePayload, request: Request, _sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
11
11
  getModuleForRoute(route: RouteData): Promise<SinglePageBuiltModule>;
12
- rewriteKnownRoute(pathname: string, _sourceRoute: RouteData): ComponentInstance;
13
12
  }
@@ -5,6 +5,7 @@ import { AstroError } from "../errors/index.js";
5
5
  import { RedirectSinglePageBuiltModule } from "../redirects/component.js";
6
6
  import { createModuleScriptElement, createStylesheetElementSet } from "../render/ssr-element.js";
7
7
  import { DEFAULT_404_ROUTE } from "../routing/astro-designed-error-pages.js";
8
+ import { findRouteToRewrite } from "../routing/rewrite.js";
8
9
  class AppPipeline extends Pipeline {
9
10
  #manifestData;
10
11
  static create(manifestData, {
@@ -62,38 +63,17 @@ class AppPipeline extends Pipeline {
62
63
  const module = await this.getModuleForRoute(routeData);
63
64
  return module.page();
64
65
  }
65
- async tryRewrite(payload, request, sourceRoute) {
66
- let foundRoute;
67
- let finalUrl = void 0;
68
- for (const route of this.#manifestData.routes) {
69
- if (payload instanceof URL) {
70
- finalUrl = payload;
71
- } else if (payload instanceof Request) {
72
- finalUrl = new URL(payload.url);
73
- } else {
74
- finalUrl = new URL(payload, new URL(request.url).origin);
75
- }
76
- if (route.pattern.test(decodeURI(finalUrl.pathname))) {
77
- foundRoute = route;
78
- break;
79
- } else if (finalUrl.pathname === "/404") {
80
- foundRoute = DEFAULT_404_ROUTE;
81
- break;
82
- }
83
- }
84
- if (foundRoute && finalUrl) {
85
- if (foundRoute.pathname === "/404") {
86
- const componentInstance = this.rewriteKnownRoute(foundRoute.pathname, sourceRoute);
87
- return [foundRoute, componentInstance, finalUrl];
88
- } else {
89
- const componentInstance = await this.getComponentByRoute(foundRoute);
90
- return [foundRoute, componentInstance, finalUrl];
91
- }
92
- }
93
- throw new AstroError({
94
- ...RewriteEncounteredAnError,
95
- message: RewriteEncounteredAnError.message(payload.toString())
66
+ async tryRewrite(payload, request, _sourceRoute) {
67
+ const [foundRoute, finalUrl] = findRouteToRewrite({
68
+ payload,
69
+ request,
70
+ routes: this.manifest?.routes.map((r) => r.routeData),
71
+ trailingSlash: this.manifest.trailingSlash,
72
+ buildFormat: this.manifest.buildFormat,
73
+ base: this.manifest.base
96
74
  });
75
+ const componentInstance = await this.getComponentByRoute(foundRoute);
76
+ return [foundRoute, componentInstance, finalUrl];
97
77
  }
98
78
  async getModuleForRoute(route) {
99
79
  if (route.component === DEFAULT_404_COMPONENT) {
@@ -121,14 +101,6 @@ class AppPipeline extends Pipeline {
121
101
  );
122
102
  }
123
103
  }
124
- // We don't need to check the source route, we already are in SSR
125
- rewriteKnownRoute(pathname, _sourceRoute) {
126
- if (pathname === "/404") {
127
- return { default: () => new Response(null, { status: 404 }) };
128
- } else {
129
- return { default: () => new Response(null, { status: 500 }) };
130
- }
131
- }
132
104
  }
133
105
  export {
134
106
  AppPipeline
@@ -73,15 +73,6 @@ export declare abstract class Pipeline {
73
73
  * @param routeData
74
74
  */
75
75
  abstract getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
76
- /**
77
- * Attempts to execute a rewrite of a known Astro route:
78
- * - /404 -> src/pages/404.astro -> template
79
- * - /500 -> src/pages/500.astro
80
- *
81
- * @param pathname The pathname where the user wants to rewrite e.g. "/404"
82
- * @param sourceRoute The original route where the first request started. This is needed in case a pipeline wants to check if the current route is pre-rendered or not
83
- */
84
- abstract rewriteKnownRoute(pathname: string, sourceRoute: RouteData): ComponentInstance;
85
76
  }
86
77
  export interface HeadElements extends Pick<SSRResult, 'scripts' | 'styles' | 'links'> {
87
78
  }
@@ -38,7 +38,6 @@ export declare class BuildPipeline extends Pipeline {
38
38
  */
39
39
  retrieveRoutesToGenerate(): Map<PageBuildData, string>;
40
40
  getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
41
- tryRewrite(payload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
41
+ tryRewrite(payload: RewritePayload, request: Request, _sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
42
42
  retrieveSsrEntry(route: RouteData, filePath: string): Promise<SinglePageBuiltModule>;
43
- rewriteKnownRoute(_pathname: string, sourceRoute: RouteData): ComponentInstance;
44
43
  }
@@ -1,7 +1,6 @@
1
1
  import { getOutputDirectory } from "../../prerender/utils.js";
2
2
  import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
3
- import { InvalidRewrite404, RewriteEncounteredAnError } from "../errors/errors-data.js";
4
- import { AstroError } from "../errors/index.js";
3
+ import { DEFAULT_404_COMPONENT } from "../constants.js";
5
4
  import { routeIsFallback, routeIsRedirect } from "../redirects/helpers.js";
6
5
  import { RedirectSinglePageBuiltModule } from "../redirects/index.js";
7
6
  import { Pipeline } from "../render/index.js";
@@ -10,7 +9,8 @@ import {
10
9
  createModuleScriptsSet,
11
10
  createStylesheetElementSet
12
11
  } from "../render/ssr-element.js";
13
- import { DEFAULT_404_ROUTE } from "../routing/astro-designed-error-pages.js";
12
+ import { default404Page } from "../routing/astro-designed-error-pages.js";
13
+ import { findRouteToRewrite } from "../routing/rewrite.js";
14
14
  import { isServerLikeOutput } from "../util.js";
15
15
  import { getOutDirWithinCwd } from "./common.js";
16
16
  import { cssOrder, getPageData, mergeInlineCss } from "./internal.js";
@@ -201,45 +201,25 @@ class BuildPipeline extends Pipeline {
201
201
  if (this.#componentsInterner.has(routeData)) {
202
202
  const entry = this.#componentsInterner.get(routeData);
203
203
  return await entry.page();
204
+ } else if (routeData.component === DEFAULT_404_COMPONENT) {
205
+ return { default: default404Page };
204
206
  } else {
205
207
  const filePath = this.#routesByFilePath.get(routeData);
206
208
  const module = await this.retrieveSsrEntry(routeData, filePath);
207
209
  return module.page();
208
210
  }
209
211
  }
210
- async tryRewrite(payload, request, sourceRoute) {
211
- let foundRoute;
212
- let finalUrl = void 0;
213
- for (const route of this.options.manifest.routes) {
214
- if (payload instanceof URL) {
215
- finalUrl = payload;
216
- } else if (payload instanceof Request) {
217
- finalUrl = new URL(payload.url);
218
- } else {
219
- finalUrl = new URL(payload, new URL(request.url).origin);
220
- }
221
- if (route.pattern.test(decodeURI(finalUrl.pathname))) {
222
- foundRoute = route;
223
- break;
224
- } else if (finalUrl.pathname === "/404") {
225
- foundRoute = DEFAULT_404_ROUTE;
226
- break;
227
- }
228
- }
229
- if (foundRoute && finalUrl) {
230
- if (foundRoute.pathname === "/404") {
231
- const componentInstance = await this.rewriteKnownRoute(foundRoute.pathname, sourceRoute);
232
- return [foundRoute, componentInstance, finalUrl];
233
- } else {
234
- const componentInstance = await this.getComponentByRoute(foundRoute);
235
- return [foundRoute, componentInstance, finalUrl];
236
- }
237
- } else {
238
- throw new AstroError({
239
- ...RewriteEncounteredAnError,
240
- message: RewriteEncounteredAnError.message(payload.toString())
241
- });
242
- }
212
+ async tryRewrite(payload, request, _sourceRoute) {
213
+ const [foundRoute, finalUrl] = findRouteToRewrite({
214
+ payload,
215
+ request,
216
+ routes: this.options.manifest.routes,
217
+ trailingSlash: this.config.trailingSlash,
218
+ buildFormat: this.config.build.format,
219
+ base: this.config.base
220
+ });
221
+ const componentInstance = await this.getComponentByRoute(foundRoute);
222
+ return [foundRoute, componentInstance, finalUrl];
243
223
  }
244
224
  async retrieveSsrEntry(route, filePath) {
245
225
  if (this.#componentsInterner.has(route)) {
@@ -285,12 +265,6 @@ class BuildPipeline extends Pipeline {
285
265
  }
286
266
  return RedirectSinglePageBuiltModule;
287
267
  }
288
- rewriteKnownRoute(_pathname, sourceRoute) {
289
- if (!isServerLikeOutput(this.config) || sourceRoute.prerender) {
290
- throw new AstroError(InvalidRewrite404);
291
- }
292
- throw new Error(`Unreachable, in SSG this route shouldn't be generated`);
293
- }
294
268
  }
295
269
  function createEntryURL(filePath, outFolder) {
296
270
  return new URL("./" + filePath + `?time=${Date.now()}`, outFolder);
@@ -1,4 +1,4 @@
1
- const ASTRO_VERSION = "4.10.3";
1
+ const ASTRO_VERSION = "4.11.0";
2
2
  const REROUTE_DIRECTIVE_HEADER = "X-Astro-Reroute";
3
3
  const ROUTE_TYPE_HEADER = "X-Astro-Route-Type";
4
4
  const DEFAULT_404_COMPONENT = "astro-default-404.astro";
@@ -58,6 +58,11 @@ declare class AstroCookies implements AstroCookiesInterface {
58
58
  * @param options Options for the cookie, such as the path and security settings.
59
59
  */
60
60
  set(key: string, value: string | Record<string, any>, options?: AstroCookieSetOptions): void;
61
+ /**
62
+ * Merges a new AstroCookies instance into the current instance. Any new cookies
63
+ * will be added to the current instance, overwriting any existing cookies with the same name.
64
+ */
65
+ merge(cookies: AstroCookies): void;
61
66
  /**
62
67
  * Astro.cookies.header() returns an iterator for the cookies that have previously
63
68
  * been set by either Astro.cookies.set() or Astro.cookies.delete().
@@ -136,6 +136,18 @@ class AstroCookies {
136
136
  });
137
137
  }
138
138
  }
139
+ /**
140
+ * Merges a new AstroCookies instance into the current instance. Any new cookies
141
+ * will be added to the current instance, overwriting any existing cookies with the same name.
142
+ */
143
+ merge(cookies) {
144
+ const outgoing = cookies.#outgoing;
145
+ if (outgoing) {
146
+ for (const [key, value] of outgoing) {
147
+ this.#ensureOutgoingMap().set(key, value);
148
+ }
149
+ }
150
+ }
139
151
  /**
140
152
  * Astro.cookies.header() returns an iterator for the cookies that have previously
141
153
  * been set by either Astro.cookies.set() or Astro.cookies.delete().
@@ -1,4 +1,5 @@
1
1
  import { AstroCookies } from './cookies.js';
2
2
  export declare function attachCookiesToResponse(response: Response, cookies: AstroCookies): void;
3
3
  export declare function responseHasCookies(response: Response): boolean;
4
+ export declare function getFromResponse(response: Response): AstroCookies | undefined;
4
5
  export declare function getSetCookiesFromResponse(response: Response): Generator<string, string[]>;
@@ -26,6 +26,7 @@ function* getSetCookiesFromResponse(response) {
26
26
  }
27
27
  export {
28
28
  attachCookiesToResponse,
29
+ getFromResponse,
29
30
  getSetCookiesFromResponse,
30
31
  responseHasCookies
31
32
  };
@@ -146,10 +146,6 @@ async function createVite(commandConfig, { settings, logger, mode, command, fs =
146
146
  server: {
147
147
  hmr: process.env.NODE_ENV === "test" || process.env.NODE_ENV === "production" ? false : void 0,
148
148
  // disable HMR for test
149
- // handle Vite URLs
150
- proxy: {
151
- // add proxies here
152
- },
153
149
  watch: {
154
150
  // Prevent watching during the build to speed it up
155
151
  ignored: mode === "build" ? ["**"] : void 0
@@ -19,7 +19,7 @@ async function dev(inlineConfig) {
19
19
  await telemetry.record([]);
20
20
  const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
21
21
  const logger = restart.container.logger;
22
- const currentVersion = "4.10.3";
22
+ const currentVersion = "4.11.0";
23
23
  const isPrerelease = currentVersion.includes("-");
24
24
  if (!isPrerelease) {
25
25
  try {
@@ -588,6 +588,26 @@ export declare const ExpectedImageOptions: {
588
588
  title: string;
589
589
  message: (options: string) => string;
590
590
  };
591
+ /**
592
+ * @docs
593
+ * @see
594
+ * - [Images](https://docs.astro.build/en/guides/images/)
595
+ * @description
596
+ * An ESM-imported image cannot be passed directly to `getImage()`. Instead, pass an object with the image in the `src` property.
597
+ *
598
+ * ```diff
599
+ * import { getImage } from "astro:assets";
600
+ * import myImage from "../assets/my_image.png";
601
+ * - const optimizedImage = await getImage( myImage );
602
+ * + const optimizedImage = await getImage({ src: myImage });
603
+ * ```
604
+ */
605
+ export declare const ExpectedNotESMImage: {
606
+ name: string;
607
+ title: string;
608
+ message: string;
609
+ hint: string;
610
+ };
591
611
  /**
592
612
  * @docs
593
613
  * @see
@@ -1010,17 +1030,6 @@ export declare const RewriteEncounteredAnError: {
1010
1030
  title: string;
1011
1031
  message: (route: string, stack?: string) => string;
1012
1032
  };
1013
- /**
1014
- * @docs
1015
- * @description
1016
- *
1017
- * The user tried to rewrite a 404 page inside a static page.
1018
- */
1019
- export declare const InvalidRewrite404: {
1020
- name: string;
1021
- title: string;
1022
- message: string;
1023
- };
1024
1033
  /**
1025
1034
  * @docs
1026
1035
  * @description
@@ -209,6 +209,12 @@ const ExpectedImageOptions = {
209
209
  title: "Expected image options.",
210
210
  message: (options) => `Expected getImage() parameter to be an object. Received \`${options}\`.`
211
211
  };
212
+ const ExpectedNotESMImage = {
213
+ name: "ExpectedNotESMImage",
214
+ title: "Expected image options, not an ESM-imported image.",
215
+ message: "An ESM-imported image cannot be passed directly to `getImage()`. Instead, pass an object with the image in the `src` property.",
216
+ hint: "Try changing `getImage(myImage)` to `getImage({ src: myImage })`"
217
+ };
212
218
  const IncompatibleDescriptorOptions = {
213
219
  name: "IncompatibleDescriptorOptions",
214
220
  title: "Cannot set both `densities` and `widths`",
@@ -361,11 +367,6 @@ const RewriteEncounteredAnError = {
361
367
  title: "Astro couldn't find the route to rewrite, or if was found but it emitted an error during the rendering phase.",
362
368
  message: (route, stack) => `The route ${route} that you tried to render doesn't exist, or it emitted an error during the rendering phase. ${stack ? stack : ""}.`
363
369
  };
364
- const InvalidRewrite404 = {
365
- name: "InvalidRewrite404",
366
- title: "You attempted to rewrite a 404 inside a static page, and this isn't allowed.",
367
- message: "Rewriting a 404 is only allowed inside on-demand pages."
368
- };
369
370
  const CantRenderPage = {
370
371
  name: "CantRenderPage",
371
372
  title: "Astro can't render the route.",
@@ -581,6 +582,7 @@ export {
581
582
  EnvUnsupportedGetSecret,
582
583
  ExpectedImage,
583
584
  ExpectedImageOptions,
585
+ ExpectedNotESMImage,
584
586
  FailedToFetchRemoteImageDimensions,
585
587
  FailedToFindPageMapSSR,
586
588
  FailedToLoadModuleSSR,
@@ -604,7 +606,6 @@ export {
604
606
  InvalidGlob,
605
607
  InvalidImageService,
606
608
  InvalidPrerenderExport,
607
- InvalidRewrite404,
608
609
  LocalImageUsedWrongly,
609
610
  LocalsNotAnObject,
610
611
  MarkdownFrontmatterParseError,
@@ -37,7 +37,7 @@ function serverStart({
37
37
  host,
38
38
  base
39
39
  }) {
40
- const version = "4.10.3";
40
+ const version = "4.11.0";
41
41
  const localPrefix = `${dim("\u2503")} Local `;
42
42
  const networkPrefix = `${dim("\u2503")} Network `;
43
43
  const emptyPrefix = " ".repeat(11);
@@ -269,7 +269,7 @@ function printHelp({
269
269
  message.push(
270
270
  linebreak(),
271
271
  ` ${bgGreen(black(` ${commandName} `))} ${green(
272
- `v${"4.10.3"}`
272
+ `v${"4.11.0"}`
273
273
  )} ${headline}`
274
274
  );
275
275
  }
@@ -29,7 +29,7 @@ export declare class RenderContext {
29
29
  * A safety net in case of loops
30
30
  */
31
31
  counter: number;
32
- static create({ locals, middleware, pathname, pipeline, request, routeData, status, }: Pick<RenderContext, 'pathname' | 'pipeline' | 'request' | 'routeData'> & Partial<Pick<RenderContext, 'locals' | 'middleware' | 'status'>>): RenderContext;
32
+ static create({ locals, middleware, pathname, pipeline, request, routeData, status, props, }: Pick<RenderContext, 'pathname' | 'pipeline' | 'request' | 'routeData'> & Partial<Pick<RenderContext, 'locals' | 'middleware' | 'status' | 'props'>>): RenderContext;
33
33
  /**
34
34
  * The main function of the RenderContext.
35
35
  *
@@ -15,6 +15,7 @@ import {
15
15
  responseSentSymbol
16
16
  } from "./constants.js";
17
17
  import { AstroCookies, attachCookiesToResponse } from "./cookies/index.js";
18
+ import { getFromResponse } from "./cookies/response.js";
18
19
  import { AstroError, AstroErrorData } from "./errors/index.js";
19
20
  import { callMiddleware } from "./middleware/callMiddleware.js";
20
21
  import { sequence } from "./middleware/index.js";
@@ -52,7 +53,8 @@ class RenderContext {
52
53
  pipeline,
53
54
  request,
54
55
  routeData,
55
- status = 200
56
+ status = 200,
57
+ props
56
58
  }) {
57
59
  return new RenderContext(
58
60
  pipeline,
@@ -61,7 +63,11 @@ class RenderContext {
61
63
  pathname,
62
64
  request,
63
65
  routeData,
64
- status
66
+ status,
67
+ void 0,
68
+ void 0,
69
+ void 0,
70
+ props
65
71
  );
66
72
  }
67
73
  /**
@@ -113,14 +119,16 @@ class RenderContext {
113
119
  );
114
120
  }
115
121
  }
122
+ let response2;
116
123
  switch (this.routeData.type) {
117
- case "endpoint":
118
- return renderEndpoint(componentInstance, ctx, serverLike, logger);
124
+ case "endpoint": {
125
+ response2 = await renderEndpoint(componentInstance, ctx, serverLike, logger);
126
+ break;
127
+ }
119
128
  case "redirect":
120
129
  return renderRedirect(this);
121
130
  case "page": {
122
131
  const result = await this.createResult(componentInstance);
123
- let response2;
124
132
  try {
125
133
  response2 = await renderPage(
126
134
  result,
@@ -138,12 +146,17 @@ class RenderContext {
138
146
  if (this.routeData.route === "/404" || this.routeData.route === "/500" || this.isRewriting) {
139
147
  response2.headers.set(REROUTE_DIRECTIVE_HEADER, "no");
140
148
  }
141
- return response2;
149
+ break;
142
150
  }
143
151
  case "fallback": {
144
152
  return new Response(null, { status: 500, headers: { [ROUTE_TYPE_HEADER]: "fallback" } });
145
153
  }
146
154
  }
155
+ const responseCookies = getFromResponse(response2);
156
+ if (responseCookies) {
157
+ cookies.merge(responseCookies);
158
+ }
159
+ return response2;
147
160
  };
148
161
  const response = await callMiddleware(
149
162
  middleware,
@@ -12,7 +12,13 @@ function createRequest({
12
12
  locals,
13
13
  staticLike = false
14
14
  }) {
15
- const headersObj = staticLike ? void 0 : headers instanceof Headers ? headers : new Headers(Object.entries(headers));
15
+ const headersObj = staticLike ? void 0 : headers instanceof Headers ? headers : new Headers(
16
+ // Filter out HTTP/2 pseudo-headers. These are internally-generated headers added to all HTTP/2 requests with trusted metadata about the request.
17
+ // Examples include `:method`, `:scheme`, `:authority`, and `:path`.
18
+ // They are always prefixed with a colon to distinguish them from other headers, and it is an error to add the to a Headers object manually.
19
+ // See https://httpwg.org/specs/rfc7540.html#HttpRequest
20
+ Object.entries(headers).filter(([name]) => !name.startsWith(":"))
21
+ );
16
22
  if (typeof url === "string") url = new URL(url);
17
23
  const imageEndpoint = prependForwardSlash(appendForwardSlash(base)) + "_image";
18
24
  if (staticLike && url.pathname !== imageEndpoint) {
@@ -0,0 +1,10 @@
1
+ import type { AstroConfig, RewritePayload, RouteData } from '../../@types/astro.js';
2
+ export type FindRouteToRewrite = {
3
+ payload: RewritePayload;
4
+ routes: RouteData[];
5
+ request: Request;
6
+ trailingSlash: AstroConfig['trailingSlash'];
7
+ buildFormat: AstroConfig['build']['format'];
8
+ base: AstroConfig['base'];
9
+ };
10
+ export declare function findRouteToRewrite({ payload, routes, request, trailingSlash, buildFormat, base, }: FindRouteToRewrite): [RouteData, URL];
@@ -0,0 +1,41 @@
1
+ import { shouldAppendForwardSlash } from "../build/util.js";
2
+ import { appendForwardSlash, removeTrailingForwardSlash } from "../path.js";
3
+ import { DEFAULT_404_ROUTE } from "./astro-designed-error-pages.js";
4
+ function findRouteToRewrite({
5
+ payload,
6
+ routes,
7
+ request,
8
+ trailingSlash,
9
+ buildFormat,
10
+ base
11
+ }) {
12
+ let finalUrl = void 0;
13
+ if (payload instanceof URL) {
14
+ finalUrl = payload;
15
+ } else if (payload instanceof Request) {
16
+ finalUrl = new URL(payload.url);
17
+ } else {
18
+ finalUrl = new URL(payload, new URL(request.url).origin);
19
+ }
20
+ let foundRoute;
21
+ for (const route of routes) {
22
+ const pathname = shouldAppendForwardSlash(trailingSlash, buildFormat) ? appendForwardSlash(finalUrl.pathname) : base !== "/" ? removeTrailingForwardSlash(finalUrl.pathname) : finalUrl.pathname;
23
+ if (route.pattern.test(decodeURI(pathname))) {
24
+ foundRoute = route;
25
+ break;
26
+ }
27
+ }
28
+ if (foundRoute) {
29
+ return [foundRoute, finalUrl];
30
+ } else {
31
+ const custom404 = routes.find((route) => route.route === "/404");
32
+ if (custom404) {
33
+ return [custom404, finalUrl];
34
+ } else {
35
+ return [DEFAULT_404_ROUTE, finalUrl];
36
+ }
37
+ }
38
+ }
39
+ export {
40
+ findRouteToRewrite
41
+ };
@@ -19,7 +19,6 @@ export declare class DevPipeline extends Pipeline {
19
19
  preload(routeData: RouteData, filePath: URL): Promise<ComponentInstance>;
20
20
  clearRouteCache(): void;
21
21
  getComponentByRoute(routeData: RouteData): Promise<ComponentInstance>;
22
- tryRewrite(payload: RewritePayload, request: Request, sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
22
+ tryRewrite(payload: RewritePayload, request: Request, _sourceRoute: RouteData): Promise<[RouteData, ComponentInstance, URL]>;
23
23
  setManifestData(manifestData: ManifestData): void;
24
- rewriteKnownRoute(route: string, sourceRoute: RouteData): ComponentInstance;
25
24
  }
@@ -1,12 +1,15 @@
1
1
  import { fileURLToPath } from "node:url";
2
2
  import { getInfoOutput } from "../cli/info/index.js";
3
3
  import {} from "../core/base-pipeline.js";
4
+ import { shouldAppendForwardSlash } from "../core/build/util.js";
4
5
  import { ASTRO_VERSION, DEFAULT_404_COMPONENT } from "../core/constants.js";
5
6
  import { enhanceViteSSRError } from "../core/errors/dev/index.js";
6
- import { InvalidRewrite404, RewriteEncounteredAnError } from "../core/errors/errors-data.js";
7
+ import { RewriteEncounteredAnError } from "../core/errors/errors-data.js";
7
8
  import { AggregateError, AstroError, CSSError, MarkdownError } from "../core/errors/index.js";
9
+ import { prependForwardSlash, removeTrailingForwardSlash } from "../core/path.js";
8
10
  import { Pipeline, loadRenderer } from "../core/render/index.js";
9
11
  import { DEFAULT_404_ROUTE, default404Page } from "../core/routing/astro-designed-error-pages.js";
12
+ import { findRouteToRewrite } from "../core/routing/rewrite.js";
10
13
  import { isPage, isServerLikeOutput, resolveIdToUrl, viteID } from "../core/util.js";
11
14
  import { PAGE_SCRIPT_ID } from "../vite-plugin-scripts/index.js";
12
15
  import { getStylesForURL } from "./css.js";
@@ -133,54 +136,24 @@ class DevPipeline extends Pipeline {
133
136
  return await this.preload(routeData, filePath);
134
137
  }
135
138
  }
136
- async tryRewrite(payload, request, sourceRoute) {
137
- let foundRoute;
139
+ async tryRewrite(payload, request, _sourceRoute) {
138
140
  if (!this.manifestData) {
139
141
  throw new Error("Missing manifest data. This is an internal error, please file an issue.");
140
142
  }
141
- let finalUrl = void 0;
142
- for (const route of this.manifestData.routes) {
143
- if (payload instanceof URL) {
144
- finalUrl = payload;
145
- } else if (payload instanceof Request) {
146
- finalUrl = new URL(payload.url);
147
- } else {
148
- finalUrl = new URL(payload, new URL(request.url).origin);
149
- }
150
- if (route.pattern.test(decodeURI(finalUrl.pathname))) {
151
- foundRoute = route;
152
- break;
153
- } else if (finalUrl.pathname === "/404") {
154
- foundRoute = DEFAULT_404_ROUTE;
155
- break;
156
- }
157
- }
158
- if (foundRoute && finalUrl) {
159
- if (foundRoute.pathname === "/404") {
160
- const componentInstance = this.rewriteKnownRoute(foundRoute.pathname, sourceRoute);
161
- return [foundRoute, componentInstance, finalUrl];
162
- } else {
163
- const componentInstance = await this.getComponentByRoute(foundRoute);
164
- return [foundRoute, componentInstance, finalUrl];
165
- }
166
- } else {
167
- throw new AstroError({
168
- ...RewriteEncounteredAnError,
169
- message: RewriteEncounteredAnError.message(payload.toString())
170
- });
171
- }
143
+ const [foundRoute, finalUrl] = findRouteToRewrite({
144
+ payload,
145
+ request,
146
+ routes: this.manifestData?.routes,
147
+ trailingSlash: this.config.trailingSlash,
148
+ buildFormat: this.config.build.format,
149
+ base: this.config.base
150
+ });
151
+ const componentInstance = await this.getComponentByRoute(foundRoute);
152
+ return [foundRoute, componentInstance, finalUrl];
172
153
  }
173
154
  setManifestData(manifestData) {
174
155
  this.manifestData = manifestData;
175
156
  }
176
- rewriteKnownRoute(route, sourceRoute) {
177
- if (isServerLikeOutput(this.config) && sourceRoute.prerender) {
178
- if (route === "/404") {
179
- return { default: default404Page };
180
- }
181
- }
182
- throw new AstroError(InvalidRewrite404);
183
- }
184
157
  }
185
158
  export {
186
159
  DevPipeline
@@ -11,7 +11,7 @@ async function handleRequest({
11
11
  incomingResponse
12
12
  }) {
13
13
  const { config, loader } = pipeline;
14
- const origin = `${loader.isHttps() ? "https" : "http"}://${incomingRequest.headers.host}`;
14
+ const origin = `${loader.isHttps() ? "https" : "http"}://${incomingRequest.headers[":authority"] ?? incomingRequest.headers.host}`;
15
15
  const url = new URL(origin + incomingRequest.url);
16
16
  let pathname;
17
17
  if (config.trailingSlash === "never" && !incomingRequest.url) {
@@ -108,11 +108,8 @@ async function handleRoute({
108
108
  }) {
109
109
  const timeStart = performance.now();
110
110
  const { config, loader, logger } = pipeline;
111
- if (!matchedRoute && !config.i18n) {
112
- if (isLoggedRequest(pathname)) {
113
- logger.info(null, req({ url: pathname, method: incomingRequest.method, statusCode: 404 }));
114
- }
115
- return handle404Response(origin, incomingRequest, incomingResponse);
111
+ if (!matchedRoute) {
112
+ throw new Error("No route matched, and default 404 route was not found.");
116
113
  }
117
114
  let request;
118
115
  let renderContext;
@@ -121,96 +118,40 @@ async function handleRoute({
121
118
  let route;
122
119
  const middleware = (await loadMiddleware(loader)).onRequest;
123
120
  const locals = Reflect.get(incomingRequest, clientLocalsSymbol);
124
- if (!matchedRoute) {
125
- if (config.i18n) {
126
- const locales = config.i18n.locales;
127
- const pathNameHasLocale = pathname.split("/").filter(Boolean).some((segment) => {
128
- let found = false;
129
- for (const locale of locales) {
130
- if (typeof locale === "string") {
131
- if (normalizeTheLocale(locale) === normalizeTheLocale(segment)) {
132
- found = true;
133
- break;
134
- }
135
- } else {
136
- if (locale.path === segment) {
137
- found = true;
138
- break;
139
- }
140
- }
141
- }
142
- return found;
143
- });
144
- if (!pathNameHasLocale && pathname !== "/") {
145
- return handle404Response(origin, incomingRequest, incomingResponse);
146
- }
147
- }
148
- request = createRequest({
149
- base: config.base,
150
- url,
151
- headers: incomingRequest.headers,
152
- logger,
153
- // no route found, so we assume the default for rendering the 404 page
154
- staticLike: config.output === "static" || config.output === "hybrid"
155
- });
156
- route = {
157
- component: "",
158
- generate(_data) {
159
- return "";
160
- },
161
- params: [],
162
- // Disable eslint as we only want to generate an empty RegExp
163
- // eslint-disable-next-line prefer-regex-literals
164
- pattern: new RegExp(""),
165
- prerender: false,
166
- segments: [],
167
- type: "fallback",
168
- route: "",
169
- fallbackRoutes: [],
170
- isIndex: false
171
- };
172
- renderContext = RenderContext.create({
173
- pipeline,
174
- pathname,
175
- middleware,
176
- request,
177
- routeData: route
178
- });
179
- } else {
180
- const filePath = matchedRoute.filePath;
181
- const { preloadedComponent } = matchedRoute;
182
- route = matchedRoute.route;
183
- request = createRequest({
184
- base: config.base,
185
- url,
186
- headers: incomingRequest.headers,
187
- method: incomingRequest.method,
188
- body,
189
- logger,
190
- clientAddress: incomingRequest.socket.remoteAddress,
191
- staticLike: config.output === "static" || route.prerender
192
- });
193
- for (const [name, value] of Object.entries(config.server.headers ?? {})) {
194
- if (value) incomingResponse.setHeader(name, value);
195
- }
196
- options = {
197
- pipeline,
198
- filePath,
199
- preload: preloadedComponent,
200
- pathname,
201
- request,
202
- route
203
- };
204
- mod = preloadedComponent;
205
- renderContext = RenderContext.create({
206
- locals,
207
- pipeline,
208
- pathname,
209
- middleware,
210
- request,
211
- routeData: route
212
- });
121
+ const filePath = matchedRoute.filePath;
122
+ const { preloadedComponent } = matchedRoute;
123
+ route = matchedRoute.route;
124
+ request = createRequest({
125
+ base: config.base,
126
+ url,
127
+ headers: incomingRequest.headers,
128
+ method: incomingRequest.method,
129
+ body,
130
+ logger,
131
+ clientAddress: incomingRequest.socket.remoteAddress,
132
+ staticLike: config.output === "static" || route.prerender
133
+ });
134
+ for (const [name, value] of Object.entries(config.server.headers ?? {})) {
135
+ if (value) incomingResponse.setHeader(name, value);
213
136
  }
137
+ options = {
138
+ pipeline,
139
+ filePath,
140
+ preload: preloadedComponent,
141
+ pathname,
142
+ request,
143
+ route
144
+ };
145
+ mod = preloadedComponent;
146
+ const isPrerendered404 = matchedRoute.route.route === "/404" && matchedRoute.route.prerender;
147
+ renderContext = RenderContext.create({
148
+ locals,
149
+ pipeline,
150
+ pathname,
151
+ middleware: isPrerendered404 ? void 0 : middleware,
152
+ request,
153
+ routeData: route
154
+ });
214
155
  let response;
215
156
  try {
216
157
  response = await renderContext.render(mod);
@@ -220,9 +161,10 @@ async function handleRoute({
220
161
  throw err;
221
162
  }
222
163
  logger.error("router", err.stack || err.message);
223
- const filePath = new URL(`./${custom500.component}`, config.root);
224
- const preloadedComponent = await pipeline.preload(custom500, filePath);
225
- response = await renderContext.render(preloadedComponent);
164
+ const filePath500 = new URL(`./${custom500.component}`, config.root);
165
+ const preloaded500Component = await pipeline.preload(custom500, filePath500);
166
+ renderContext.props.error = err;
167
+ response = await renderContext.render(preloaded500Component);
226
168
  status = 500;
227
169
  }
228
170
  if (isLoggedRequest(pathname)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "4.10.3",
3
+ "version": "4.11.0",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -169,9 +169,9 @@
169
169
  "yargs-parser": "^21.1.1",
170
170
  "zod": "^3.23.8",
171
171
  "zod-to-json-schema": "^3.23.0",
172
+ "@astrojs/markdown-remark": "5.1.0",
172
173
  "@astrojs/internal-helpers": "0.4.0",
173
- "@astrojs/telemetry": "3.1.0",
174
- "@astrojs/markdown-remark": "5.1.0"
174
+ "@astrojs/telemetry": "3.1.0"
175
175
  },
176
176
  "optionalDependencies": {
177
177
  "sharp": "^0.33.3"
@@ -214,6 +214,7 @@
214
214
  "rollup": "^4.18.0",
215
215
  "sass": "^1.77.5",
216
216
  "srcset-parse": "^1.1.0",
217
+ "undici": "^6.19.2",
217
218
  "unified": "^11.0.4",
218
219
  "astro-scripts": "0.0.14"
219
220
  },