astro 6.2.1 → 6.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 (137) hide show
  1. package/dist/actions/handler.d.ts +32 -0
  2. package/dist/actions/handler.js +45 -0
  3. package/dist/actions/runtime/server.js +1 -1
  4. package/dist/assets/build/generate.js +1 -1
  5. package/dist/assets/build/remote.d.ts +3 -2
  6. package/dist/assets/build/remote.js +16 -9
  7. package/dist/assets/endpoint/generic.js +4 -7
  8. package/dist/assets/endpoint/shared.js +7 -2
  9. package/dist/assets/index.d.ts +1 -0
  10. package/dist/assets/index.js +2 -0
  11. package/dist/assets/internal.js +16 -1
  12. package/dist/assets/services/sharp.js +16 -1
  13. package/dist/assets/utils/index.d.ts +1 -0
  14. package/dist/assets/utils/index.js +2 -0
  15. package/dist/assets/utils/redirectValidation.d.ts +48 -0
  16. package/dist/assets/utils/redirectValidation.js +48 -0
  17. package/dist/assets/utils/remoteProbe.js +25 -2
  18. package/dist/cli/add/index.js +7 -4
  19. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  20. package/dist/container/index.js +18 -14
  21. package/dist/content/content-layer.js +3 -4
  22. package/dist/content/loaders/types.d.ts +1 -1
  23. package/dist/content/server-listeners.js +0 -4
  24. package/dist/content/vite-plugin-content-virtual-mod.js +9 -1
  25. package/dist/core/app/base.d.ts +33 -15
  26. package/dist/core/app/base.js +120 -324
  27. package/dist/core/app/dev/app.d.ts +3 -2
  28. package/dist/core/app/dev/app.js +4 -60
  29. package/dist/core/app/entrypoints/virtual/dev.js +2 -0
  30. package/dist/core/app/entrypoints/virtual/prod.js +4 -1
  31. package/dist/core/app/prepare-response.d.ts +11 -0
  32. package/dist/core/app/prepare-response.js +18 -0
  33. package/dist/core/app/render-options.d.ts +11 -0
  34. package/dist/core/app/render-options.js +11 -0
  35. package/dist/core/base-pipeline.d.ts +38 -1
  36. package/dist/core/base-pipeline.js +50 -7
  37. package/dist/core/build/app.d.ts +3 -4
  38. package/dist/core/build/app.js +3 -17
  39. package/dist/core/build/plugins/plugin-css.js +7 -1
  40. package/dist/core/build/plugins/plugin-manifest.js +11 -1
  41. package/dist/core/build/static-build.js +16 -2
  42. package/dist/core/cache/handler.d.ts +29 -0
  43. package/dist/core/cache/handler.js +81 -0
  44. package/dist/core/compile/style.js +18 -1
  45. package/dist/core/config/index.d.ts +1 -1
  46. package/dist/core/config/index.js +4 -1
  47. package/dist/core/config/schemas/base.d.ts +4 -0
  48. package/dist/core/config/schemas/base.js +4 -0
  49. package/dist/core/config/schemas/relative.d.ts +6 -0
  50. package/dist/core/config/settings.js +2 -4
  51. package/dist/core/config/tsconfig.d.ts +24 -9
  52. package/dist/core/config/tsconfig.js +54 -45
  53. package/dist/core/constants.d.ts +27 -1
  54. package/dist/core/constants.js +14 -1
  55. package/dist/core/cookies/cookies.d.ts +7 -2
  56. package/dist/core/cookies/cookies.js +11 -4
  57. package/dist/core/cookies/response.d.ts +1 -1
  58. package/dist/core/cookies/response.js +1 -2
  59. package/dist/core/create-vite.js +15 -0
  60. package/dist/core/csp/runtime.js +6 -4
  61. package/dist/core/dev/dev.js +1 -1
  62. package/dist/core/errors/build-handler.d.ts +17 -0
  63. package/dist/core/errors/build-handler.js +22 -0
  64. package/dist/core/errors/default-handler.d.ts +14 -0
  65. package/dist/core/errors/default-handler.js +144 -0
  66. package/dist/core/errors/dev-handler.d.ts +21 -0
  67. package/dist/core/errors/dev-handler.js +82 -0
  68. package/dist/core/errors/handler.d.ts +9 -0
  69. package/dist/core/errors/handler.js +0 -0
  70. package/dist/core/fetch/default-handler.d.ts +17 -0
  71. package/dist/core/fetch/default-handler.js +45 -0
  72. package/dist/core/fetch/fetch-state.d.ts +244 -0
  73. package/dist/core/fetch/fetch-state.js +779 -0
  74. package/dist/core/fetch/index.d.ts +61 -0
  75. package/dist/core/fetch/index.js +121 -0
  76. package/dist/core/fetch/types.d.ts +6 -0
  77. package/dist/core/fetch/types.js +0 -0
  78. package/dist/core/fetch/vite-plugin.d.ts +5 -0
  79. package/dist/core/fetch/vite-plugin.js +69 -0
  80. package/dist/core/hono/index.d.ts +21 -0
  81. package/dist/core/hono/index.js +98 -0
  82. package/dist/core/i18n/handler.d.ts +18 -0
  83. package/dist/core/i18n/handler.js +119 -0
  84. package/dist/core/logger/core.d.ts +8 -0
  85. package/dist/core/logger/core.js +16 -0
  86. package/dist/core/messages/runtime.js +1 -1
  87. package/dist/core/middleware/astro-middleware.d.ts +27 -0
  88. package/dist/core/middleware/astro-middleware.js +53 -0
  89. package/dist/core/pages/handler.d.ts +20 -0
  90. package/dist/core/pages/handler.js +74 -0
  91. package/dist/core/redirects/render.d.ts +2 -2
  92. package/dist/core/redirects/render.js +7 -8
  93. package/dist/core/render/params-and-props.js +1 -1
  94. package/dist/core/render/slots.js +9 -2
  95. package/dist/core/rewrites/handler.d.ts +37 -0
  96. package/dist/core/rewrites/handler.js +67 -0
  97. package/dist/core/routing/3xx.js +8 -4
  98. package/dist/core/routing/handler.d.ts +17 -0
  99. package/dist/core/routing/handler.js +172 -0
  100. package/dist/core/routing/match.d.ts +0 -7
  101. package/dist/core/routing/match.js +0 -5
  102. package/dist/core/routing/pattern.js +1 -1
  103. package/dist/core/routing/rewrite.js +1 -4
  104. package/dist/core/routing/trailing-slash-handler.d.ts +18 -0
  105. package/dist/core/routing/trailing-slash-handler.js +67 -0
  106. package/dist/core/session/drivers.d.ts +1 -1
  107. package/dist/core/session/handler.d.ts +11 -0
  108. package/dist/core/session/handler.js +33 -0
  109. package/dist/core/session/runtime.js +7 -2
  110. package/dist/core/util/normalized-url.d.ts +10 -0
  111. package/dist/core/util/normalized-url.js +21 -0
  112. package/dist/i18n/middleware.d.ts +10 -0
  113. package/dist/i18n/middleware.js +4 -88
  114. package/dist/i18n/utils.js +2 -2
  115. package/dist/prefetch/index.js +12 -7
  116. package/dist/runtime/server/astro-island.js +57 -20
  117. package/dist/runtime/server/astro-island.prebuilt-dev.d.ts +1 -1
  118. package/dist/runtime/server/astro-island.prebuilt-dev.js +1 -1
  119. package/dist/runtime/server/astro-island.prebuilt.d.ts +1 -1
  120. package/dist/runtime/server/astro-island.prebuilt.js +1 -1
  121. package/dist/runtime/server/render/common.js +10 -4
  122. package/dist/runtime/server/render/server-islands.js +2 -1
  123. package/dist/runtime/server/scripts.js +6 -0
  124. package/dist/types/public/config.d.ts +46 -12
  125. package/dist/types/public/content.d.ts +4 -4
  126. package/dist/types/public/internal.d.ts +1 -1
  127. package/dist/vite-plugin-app/app.d.ts +4 -5
  128. package/dist/vite-plugin-app/app.js +20 -68
  129. package/dist/vite-plugin-astro/compile.js +2 -2
  130. package/dist/vite-plugin-astro/utils.d.ts +1 -0
  131. package/dist/vite-plugin-astro/utils.js +9 -1
  132. package/dist/vite-plugin-head/index.js +36 -19
  133. package/dist/vite-plugin-utils/index.d.ts +1 -0
  134. package/dist/vite-plugin-utils/index.js +3 -0
  135. package/package.json +13 -6
  136. package/dist/core/render-context.d.ts +0 -77
  137. package/dist/core/render-context.js +0 -826
@@ -1,6 +1,7 @@
1
1
  import type { RouteData } from '../../../types/public/index.js';
2
+ import type { ErrorHandler } from '../../errors/handler.js';
2
3
  import type { AstroLogger } from '../../logger/core.js';
3
- import { BaseApp, type DevMatch, type LogRequestPayload, type RenderErrorOptions } from '../base.js';
4
+ import { BaseApp, type DevMatch, type LogRequestPayload } from '../base.js';
4
5
  import type { SSRManifest } from '../types.js';
5
6
  import { NonRunnablePipeline } from './pipeline.js';
6
7
  import type { RoutesList } from '../../../types/astro.js';
@@ -20,6 +21,6 @@ export declare class DevApp extends BaseApp<NonRunnablePipeline> {
20
21
  updateRoutes(newRoutesList: RoutesList): void;
21
22
  match(request: Request): RouteData | undefined;
22
23
  devMatch(pathname: string): Promise<DevMatch | undefined>;
23
- renderError(request: Request, { skipMiddleware, error, status, response: _response, ...resolvedRenderOptions }: RenderErrorOptions): Promise<Response>;
24
+ protected createErrorHandler(): ErrorHandler;
24
25
  logRequest({ pathname, method, statusCode, isRewrite, reqTime }: LogRequestPayload): void;
25
26
  }
@@ -1,10 +1,6 @@
1
- import { MiddlewareNoDataOrNextCalled, MiddlewareNotAResponse } from "../../errors/errors-data.js";
2
- import { isAstroError } from "../../errors/index.js";
3
- import {
4
- BaseApp
5
- } from "../base.js";
1
+ import { DevErrorHandler } from "../../errors/dev-handler.js";
2
+ import { BaseApp } from "../base.js";
6
3
  import { NonRunnablePipeline } from "./pipeline.js";
7
- import { getCustom404Route, getCustom500Route } from "../../routing/helpers.js";
8
4
  import { ensure404Route } from "../../routing/astro-designed-error-pages.js";
9
5
  import { matchRoute } from "../../routing/dev.js";
10
6
  import { req } from "../../messages/runtime.js";
@@ -53,60 +49,8 @@ class DevApp extends BaseApp {
53
49
  resolvedPathname: matchedRoute.resolvedPathname
54
50
  };
55
51
  }
56
- async renderError(request, {
57
- skipMiddleware = false,
58
- error,
59
- status,
60
- response: _response,
61
- ...resolvedRenderOptions
62
- }) {
63
- if (isAstroError(error) && [MiddlewareNoDataOrNextCalled.name, MiddlewareNotAResponse.name].includes(error.name)) {
64
- throw error;
65
- }
66
- const renderRoute = async (routeData) => {
67
- try {
68
- const preloadedComponent = await this.pipeline.getComponentByRoute(routeData);
69
- const renderContext = await this.createRenderContext({
70
- locals: resolvedRenderOptions.locals,
71
- pipeline: this.pipeline,
72
- pathname: this.getPathnameFromRequest(request),
73
- skipMiddleware,
74
- request,
75
- routeData,
76
- clientAddress: resolvedRenderOptions.clientAddress,
77
- status,
78
- shouldInjectCspMetaTags: false
79
- });
80
- renderContext.props.error = error;
81
- const response = await renderContext.render(preloadedComponent);
82
- if (error) {
83
- this.logger.error("router", error.stack || error.message);
84
- }
85
- return response;
86
- } catch (_err) {
87
- if (skipMiddleware === false) {
88
- return this.renderError(request, {
89
- ...resolvedRenderOptions,
90
- status: 500,
91
- skipMiddleware: true,
92
- error: _err
93
- });
94
- }
95
- throw _err;
96
- }
97
- };
98
- if (status === 404) {
99
- const custom404 = getCustom404Route(this.manifestData);
100
- if (custom404) {
101
- return renderRoute(custom404);
102
- }
103
- }
104
- const custom500 = getCustom500Route(this.manifestData);
105
- if (!custom500) {
106
- throw error;
107
- } else {
108
- return renderRoute(custom500);
109
- }
52
+ createErrorHandler() {
53
+ return new DevErrorHandler(this, { shouldInjectCspMetaTags: false });
110
54
  }
111
55
  logRequest({ pathname, method, statusCode, isRewrite, reqTime }) {
112
56
  if (pathname === "/favicon.ico") {
@@ -1,3 +1,4 @@
1
+ import fetchable from "virtual:astro:fetchable";
1
2
  import { manifest } from "virtual:astro:manifest";
2
3
  import { DevApp } from "../../dev/app.js";
3
4
  import { createConsoleLogger } from "../../../logger/impls/console.js";
@@ -5,6 +6,7 @@ let currentDevApp = null;
5
6
  const createApp = ({ streaming } = {}) => {
6
7
  const logger = createConsoleLogger(manifest.logLevel);
7
8
  currentDevApp = new DevApp(manifest, streaming, logger);
9
+ currentDevApp.setFetchHandler(fetchable);
8
10
  if (import.meta.hot) {
9
11
  import.meta.hot.on("astro:routes-updated", async () => {
10
12
  if (!currentDevApp) return;
@@ -1,7 +1,10 @@
1
+ import fetchable from "virtual:astro:fetchable";
1
2
  import { manifest } from "virtual:astro:manifest";
2
3
  import { App } from "../../app.js";
3
4
  const createApp = ({ streaming } = {}) => {
4
- return new App(manifest, streaming);
5
+ const app = new App(manifest, streaming);
6
+ app.setFetchHandler(fetchable);
7
+ return app;
5
8
  };
6
9
  export {
7
10
  createApp
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Strips internal-only headers from the response before sending it to the
3
+ * user agent, and optionally appends cookies written via `Astro.cookie.set()`
4
+ * to the `Set-Cookie` header.
5
+ *
6
+ * This is a pure function with no dependencies on the app; it is shared by
7
+ * `AstroHandler` and the various error handlers.
8
+ */
9
+ export declare function prepareResponse(response: Response, { addCookieHeader }: {
10
+ addCookieHeader: boolean;
11
+ }): void;
@@ -0,0 +1,18 @@
1
+ import { INTERNAL_RESPONSE_HEADERS, responseSentSymbol } from "../constants.js";
2
+ import { getSetCookiesFromResponse } from "../cookies/index.js";
3
+ function prepareResponse(response, { addCookieHeader }) {
4
+ for (const headerName of INTERNAL_RESPONSE_HEADERS) {
5
+ if (response.headers.has(headerName)) {
6
+ response.headers.delete(headerName);
7
+ }
8
+ }
9
+ if (addCookieHeader) {
10
+ for (const setCookieHeaderValue of getSetCookiesFromResponse(response)) {
11
+ response.headers.append("set-cookie", setCookieHeaderValue);
12
+ }
13
+ }
14
+ Reflect.set(response, responseSentSymbol, true);
15
+ }
16
+ export {
17
+ prepareResponse
18
+ };
@@ -0,0 +1,11 @@
1
+ import type { ResolvedRenderOptions } from './base.js';
2
+ /**
3
+ * Reads `ResolvedRenderOptions` that were attached to a request via
4
+ * `renderOptionsSymbol`. Returns `undefined` if no options are attached.
5
+ */
6
+ export declare function getRenderOptions(request: Request): ResolvedRenderOptions | undefined;
7
+ /**
8
+ * Attaches `ResolvedRenderOptions` to a request via `renderOptionsSymbol` so
9
+ * that downstream handlers can read them.
10
+ */
11
+ export declare function setRenderOptions(request: Request, options: ResolvedRenderOptions): void;
@@ -0,0 +1,11 @@
1
+ const renderOptionsSymbol = /* @__PURE__ */ Symbol.for("astro.renderOptions");
2
+ function getRenderOptions(request) {
3
+ return Reflect.get(request, renderOptionsSymbol);
4
+ }
5
+ function setRenderOptions(request, options) {
6
+ Reflect.set(request, renderOptionsSymbol, options);
7
+ }
8
+ export {
9
+ getRenderOptions,
10
+ setRenderOptions
11
+ };
@@ -14,13 +14,29 @@ import type { CompiledCacheRoute } from './cache/runtime/route-matching.js';
14
14
  import type { SessionDriverFactory } from './session/types.js';
15
15
  import { NodePool } from '../runtime/server/render/queue/pool.js';
16
16
  import { HTMLStringCache } from '../runtime/server/html-string-cache.js';
17
+ /**
18
+ * Bit flags for pipeline features that handler classes register as
19
+ * "used" when a custom `src/app.ts` fetch handler is in play. After the
20
+ * first request (dev) or at runtime (prod SSR), we compare against the
21
+ * manifest to warn about features the user configured but forgot to
22
+ * include in their custom pipeline.
23
+ */
24
+ export declare const PipelineFeatures: {
25
+ readonly redirects: number;
26
+ readonly sessions: number;
27
+ readonly actions: number;
28
+ readonly middleware: number;
29
+ readonly i18n: number;
30
+ readonly cache: number;
31
+ };
17
32
  /**
18
33
  * The `Pipeline` represents the static parts of rendering that do not change between requests.
19
34
  * These are mostly known when the server first starts up and do not change.
20
35
  *
21
- * Thus, a `Pipeline` is created once at process start and then used by every `RenderContext`.
36
+ * Thus, a `Pipeline` is created once at process start and then used by every `FetchState`.
22
37
  */
23
38
  export declare abstract class Pipeline {
39
+ #private;
24
40
  readonly internalMiddleware: MiddlewareHandler[];
25
41
  resolvedMiddleware: MiddlewareHandler | undefined;
26
42
  resolvedLogger: boolean;
@@ -30,6 +46,12 @@ export declare abstract class Pipeline {
30
46
  compiledCacheRoutes: CompiledCacheRoute[] | undefined;
31
47
  nodePool: NodePool | undefined;
32
48
  htmlStringCache: HTMLStringCache | undefined;
49
+ /**
50
+ * Bit mask of pipeline features activated by handler classes.
51
+ * Each handler sets its bit via `|=`. Only meaningful when a
52
+ * custom `src/app.ts` fetch handler is in use.
53
+ */
54
+ usedFeatures: number;
33
55
  logger: AstroLogger;
34
56
  readonly manifest: SSRManifest;
35
57
  /**
@@ -63,6 +85,10 @@ export declare abstract class Pipeline {
63
85
  readonly cacheProvider: SSRManifest['cacheProvider'];
64
86
  readonly cacheConfig: SSRManifest['cacheConfig'];
65
87
  readonly serverIslands: SSRManifest['serverIslandMappings'];
88
+ /** Route data derived from the manifest, used for route matching. */
89
+ manifestData: {
90
+ routes: RouteData[];
91
+ };
66
92
  constructor(logger: AstroLogger, manifest: SSRManifest,
67
93
  /**
68
94
  * "development" or "production" only
@@ -85,6 +111,17 @@ export declare abstract class Pipeline {
85
111
  }>) | undefined, cacheProvider?: (() => Promise<{
86
112
  default: CacheProviderFactory | null;
87
113
  }>) | undefined, cacheConfig?: import("./cache/types.js").SSRManifestCache | undefined, serverIslands?: (() => Promise<ServerIslandMappings> | ServerIslandMappings) | undefined);
114
+ /**
115
+ * Low-level route matching against the manifest routes. Returns the
116
+ * matched `RouteData` or `undefined`. Does not filter prerendered
117
+ * routes or check public assets — use `BaseApp.match()` for that.
118
+ */
119
+ matchRoute(pathname: string): RouteData | undefined;
120
+ /**
121
+ * Rebuilds the internal router after routes have been added or
122
+ * removed (e.g. by the dev server on HMR).
123
+ */
124
+ rebuildRouter(): void;
88
125
  abstract headElements(routeData: RouteData): Promise<HeadElements> | HeadElements;
89
126
  abstract componentMetadata(routeData: RouteData): Promise<SSRResult['componentMetadata']> | void;
90
127
  /**
@@ -1,5 +1,4 @@
1
1
  import { NOOP_ACTIONS_MOD } from "../actions/noop-actions.js";
2
- import { createI18nMiddleware } from "../i18n/middleware.js";
3
2
  import { createOriginCheckMiddleware } from "./app/middlewares.js";
4
3
  import { ActionNotFoundError } from "./errors/errors-data.js";
5
4
  import { AstroError } from "./errors/index.js";
@@ -8,10 +7,20 @@ import { sequence } from "./middleware/sequence.js";
8
7
  import { RedirectSinglePageBuiltModule } from "./redirects/index.js";
9
8
  import { RouteCache } from "./render/route-cache.js";
10
9
  import { createDefaultRoutes } from "./routing/default.js";
10
+ import { ensure404Route } from "./routing/astro-designed-error-pages.js";
11
+ import { Router } from "./routing/router.js";
11
12
  import { NodePool } from "../runtime/server/render/queue/pool.js";
12
13
  import { HTMLStringCache } from "../runtime/server/html-string-cache.js";
13
14
  import { FORBIDDEN_PATH_KEYS } from "@astrojs/internal-helpers/object";
14
15
  import { loadLogger } from "./logger/load.js";
16
+ const PipelineFeatures = {
17
+ redirects: 1 << 0,
18
+ sessions: 1 << 1,
19
+ actions: 1 << 2,
20
+ middleware: 1 << 3,
21
+ i18n: 1 << 4,
22
+ cache: 1 << 5
23
+ };
15
24
  class Pipeline {
16
25
  internalMiddleware;
17
26
  resolvedMiddleware = void 0;
@@ -22,6 +31,12 @@ class Pipeline {
22
31
  compiledCacheRoutes = void 0;
23
32
  nodePool;
24
33
  htmlStringCache;
34
+ /**
35
+ * Bit mask of pipeline features activated by handler classes.
36
+ * Each handler sets its bit via `|=`. Only meaningful when a
37
+ * custom `src/app.ts` fetch handler is in use.
38
+ */
39
+ usedFeatures = 0;
25
40
  logger;
26
41
  manifest;
27
42
  /**
@@ -55,6 +70,10 @@ class Pipeline {
55
70
  cacheProvider;
56
71
  cacheConfig;
57
72
  serverIslands;
73
+ /** Route data derived from the manifest, used for route matching. */
74
+ manifestData;
75
+ /** Pattern-matching router built from manifestData. */
76
+ #router;
58
77
  constructor(logger, manifest, runtimeMode, renderers, resolve, streaming, adapterName = manifest.adapterName, clientDirectives = manifest.clientDirectives, inlinedScripts = manifest.inlinedScripts, compressHTML = manifest.compressHTML, i18n = manifest.i18n, middleware = manifest.middleware, routeCache = new RouteCache(logger, runtimeMode), site = manifest.site ? new URL(manifest.site) : void 0, defaultRoutes = createDefaultRoutes(manifest), actions = manifest.actions, sessionDriver = manifest.sessionDriver, cacheProvider = manifest.cacheProvider, cacheConfig = manifest.cacheConfig, serverIslands = manifest.serverIslandMappings) {
59
78
  this.logger = logger;
60
79
  this.manifest = manifest;
@@ -76,12 +95,14 @@ class Pipeline {
76
95
  this.cacheProvider = cacheProvider;
77
96
  this.cacheConfig = cacheConfig;
78
97
  this.serverIslands = serverIslands;
98
+ this.manifestData = { routes: (manifest.routes ?? []).map((route) => route.routeData) };
99
+ ensure404Route(this.manifestData);
100
+ this.#router = new Router(this.manifestData.routes, {
101
+ base: manifest.base,
102
+ trailingSlash: manifest.trailingSlash,
103
+ buildFormat: manifest.buildFormat
104
+ });
79
105
  this.internalMiddleware = [];
80
- if (i18n?.strategy !== "manual") {
81
- this.internalMiddleware.push(
82
- createI18nMiddleware(i18n, manifest.base, manifest.trailingSlash, manifest.buildFormat)
83
- );
84
- }
85
106
  if (manifest.experimentalQueuedRendering.enabled) {
86
107
  this.nodePool = this.createNodePool(
87
108
  manifest.experimentalQueuedRendering.poolSize ?? 1e3,
@@ -92,6 +113,27 @@ class Pipeline {
92
113
  }
93
114
  }
94
115
  }
116
+ /**
117
+ * Low-level route matching against the manifest routes. Returns the
118
+ * matched `RouteData` or `undefined`. Does not filter prerendered
119
+ * routes or check public assets — use `BaseApp.match()` for that.
120
+ */
121
+ matchRoute(pathname) {
122
+ const match = this.#router.match(pathname, { allowWithoutBase: true });
123
+ if (match.type !== "match") return void 0;
124
+ return match.route;
125
+ }
126
+ /**
127
+ * Rebuilds the internal router after routes have been added or
128
+ * removed (e.g. by the dev server on HMR).
129
+ */
130
+ rebuildRouter() {
131
+ this.#router = new Router(this.manifestData.routes, {
132
+ base: this.manifest.base,
133
+ trailingSlash: this.manifest.trailingSlash,
134
+ buildFormat: this.manifest.buildFormat
135
+ });
136
+ }
95
137
  /**
96
138
  * Resolves the middleware from the manifest, and returns the `onRequest` function. If `onRequest` isn't there,
97
139
  * it returns a no-op function
@@ -243,5 +285,6 @@ class Pipeline {
243
285
  }
244
286
  }
245
287
  export {
246
- Pipeline
288
+ Pipeline,
289
+ PipelineFeatures
247
290
  };
@@ -1,20 +1,19 @@
1
- import { BaseApp, type RenderErrorOptions } from '../app/entrypoints/index.js';
1
+ import { BaseApp } from '../app/entrypoints/index.js';
2
2
  import type { SSRManifest } from '../app/types.js';
3
3
  import type { BuildInternals } from './internal.js';
4
4
  import { BuildPipeline } from './pipeline.js';
5
5
  import type { StaticBuildOptions } from './types.js';
6
- import type { CreateRenderContext, RenderContext } from '../render-context.js';
7
6
  import type { LogRequestPayload } from '../app/base.js';
7
+ import type { ErrorHandler } from '../errors/handler.js';
8
8
  import type { PoolStatsReport } from '../../runtime/server/render/queue/pool.js';
9
9
  export declare class BuildApp extends BaseApp<BuildPipeline> {
10
10
  createPipeline(_streaming: boolean, manifest: SSRManifest, ..._args: any[]): BuildPipeline;
11
- createRenderContext(payload: CreateRenderContext): Promise<RenderContext>;
12
11
  isDev(): boolean;
13
12
  setInternals(internals: BuildInternals): void;
14
13
  setOptions(options: StaticBuildOptions): void;
15
14
  getOptions(): StaticBuildOptions;
16
15
  getSettings(): import("../../types/astro.js").AstroSettings;
17
- renderError(request: Request, options: RenderErrorOptions): Promise<Response>;
16
+ protected createErrorHandler(): ErrorHandler;
18
17
  getQueueStats(): PoolStatsReport | undefined;
19
18
  logRequest(_options: LogRequestPayload): void;
20
19
  }
@@ -1,16 +1,12 @@
1
1
  import { BaseApp } from "../app/entrypoints/index.js";
2
2
  import { BuildPipeline } from "./pipeline.js";
3
+ import { BuildErrorHandler } from "../errors/build-handler.js";
3
4
  class BuildApp extends BaseApp {
4
5
  createPipeline(_streaming, manifest, ..._args) {
5
6
  return BuildPipeline.create({
6
7
  manifest
7
8
  });
8
9
  }
9
- async createRenderContext(payload) {
10
- return await super.createRenderContext({
11
- ...payload
12
- });
13
- }
14
10
  isDev() {
15
11
  return true;
16
12
  }
@@ -28,18 +24,8 @@ class BuildApp extends BaseApp {
28
24
  getSettings() {
29
25
  return this.pipeline.getSettings();
30
26
  }
31
- async renderError(request, options) {
32
- if (options.status === 500) {
33
- if (options.response) {
34
- return options.response;
35
- }
36
- throw options.error;
37
- } else {
38
- return super.renderError(request, {
39
- ...options,
40
- prerenderedErrorPageFetch: void 0
41
- });
42
- }
27
+ createErrorHandler() {
28
+ return new BuildErrorHandler(this);
43
29
  }
44
30
  getQueueStats() {
45
31
  if (this.pipeline.nodePool) {
@@ -1,6 +1,7 @@
1
1
  import { isCSSRequest } from "vite";
2
2
  import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../../constants.js";
3
3
  import { isPropagatedAssetBoundary } from "../../head-propagation/boundary.js";
4
+ import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from "../../../vite-plugin-pages/const.js";
4
5
  import {
5
6
  getParentExtendedModuleInfos,
6
7
  getParentModuleInfos,
@@ -18,7 +19,12 @@ function pluginCSS(options, internals) {
18
19
  function isBuildCssBoundary(id, ctx) {
19
20
  if (isPropagatedAssetBoundary(id)) return true;
20
21
  const info = ctx.getModuleInfo(id);
21
- return info ? moduleIsTopLevelPage(info) : false;
22
+ if (!info || !moduleIsTopLevelPage(info)) return false;
23
+ const allImporters = info.importers.concat(info.dynamicImporters);
24
+ const hasNonVirtualPageImporter = allImporters.some(
25
+ (importer) => !importer.includes(VIRTUAL_PAGE_RESOLVED_MODULE_ID)
26
+ );
27
+ return !hasNonVirtualPageImporter;
22
28
  }
23
29
  function rollupPluginAstroBuildCSS(options) {
24
30
  const { internals, buildOptions } = options;
@@ -47,7 +47,8 @@ async function manifestBuildPostHook(options, internals, {
47
47
  logger: options.logger,
48
48
  middlewareEntryPoint: shouldPassMiddlewareEntryPoint ? internals.middlewareEntryPoint : void 0
49
49
  });
50
- const code = injectManifest(manifest, ssrManifestChunk.code);
50
+ const ssrManifest = stripPrerenderedRouteStyles(manifest);
51
+ const code = injectManifest(ssrManifest, ssrManifestChunk.code);
51
52
  mutate(ssrManifestChunk.fileName, code, false);
52
53
  }
53
54
  const prerenderManifestChunk = chunks.find(
@@ -82,6 +83,15 @@ function injectManifest(manifest, code) {
82
83
  return JSON.stringify(manifest);
83
84
  });
84
85
  }
86
+ function stripPrerenderedRouteStyles(manifest) {
87
+ let stripped = false;
88
+ const routes = manifest.routes.map((route) => {
89
+ if (!route.routeData.prerender || route.styles.length === 0) return route;
90
+ stripped = true;
91
+ return { ...route, styles: [] };
92
+ });
93
+ return stripped ? { ...manifest, routes } : manifest;
94
+ }
85
95
  async function buildManifest(opts, internals, staticFiles, encodedKey) {
86
96
  const { settings } = opts;
87
97
  const routes = [];
@@ -186,7 +186,14 @@ async function buildEnvironments(opts, internals) {
186
186
  }
187
187
  return [prefix, cleanChunkName(name), suffix].join("");
188
188
  },
189
- assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`,
189
+ assetFileNames(assetInfo) {
190
+ const name = assetInfo.names?.[0] ?? "";
191
+ if (name.includes(ASTRO_PAGE_EXTENSION_POST_PATTERN)) {
192
+ const [sanitizedName] = name.split(ASTRO_PAGE_EXTENSION_POST_PATTERN);
193
+ return `${settings.config.build.assets}/${sanitizedName}.[hash][extname]`;
194
+ }
195
+ return `${settings.config.build.assets}/[name].[hash][extname]`;
196
+ },
190
197
  ...viteConfig.build?.rollupOptions?.output,
191
198
  entryFileNames(chunkInfo) {
192
199
  if (chunkInfo.facadeModuleId?.startsWith(VIRTUAL_PAGE_RESOLVED_MODULE_ID)) {
@@ -289,7 +296,14 @@ async function buildEnvironments(opts, internals) {
289
296
  chunkFileNames(chunkInfo) {
290
297
  return `${settings.config.build.assets}/${cleanChunkName(chunkInfo.name)}.[hash].js`;
291
298
  },
292
- assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`,
299
+ assetFileNames(assetInfo) {
300
+ const name = assetInfo.names?.[0] ?? "";
301
+ if (name.includes(ASTRO_PAGE_EXTENSION_POST_PATTERN)) {
302
+ const [sanitizedName] = name.split(ASTRO_PAGE_EXTENSION_POST_PATTERN);
303
+ return `${settings.config.build.assets}/${sanitizedName}.[hash][extname]`;
304
+ }
305
+ return `${settings.config.build.assets}/[name].[hash][extname]`;
306
+ },
293
307
  ...viteConfig.environments?.client?.build?.rollupOptions?.output
294
308
  }
295
309
  }
@@ -0,0 +1,29 @@
1
+ import type { BaseApp } from '../app/base.js';
2
+ import type { FetchState } from '../fetch/fetch-state.js';
3
+ import type { Pipeline } from '../base-pipeline.js';
4
+ /**
5
+ * Registers a cache provider on the given `FetchState`. When
6
+ * `state.resolve('cache')` is first called, the appropriate cache
7
+ * implementation is created (disabled, noop for dev, or real).
8
+ *
9
+ * Returns synchronously when cache is not configured or in dev mode.
10
+ */
11
+ export declare function provideCache(state: FetchState): Promise<void> | void;
12
+ /**
13
+ * Wraps a render callback with cache provider logic. Handles both
14
+ * runtime caching (onRequest) and CDN-based providers (headers only).
15
+ *
16
+ * - When a cache provider with `onRequest` is configured, the callback
17
+ * is wrapped so the provider can serve from cache or fall through.
18
+ * - When only CDN headers are needed, the callback runs directly and
19
+ * cache headers are applied to the response.
20
+ * - When no cache provider is configured, the callback runs as-is.
21
+ *
22
+ * Cache headers (`CDN-Cache-Control`, `Cache-Tag`) are stripped from
23
+ * the final response after the runtime provider has read them.
24
+ */
25
+ export declare class CacheHandler {
26
+ #private;
27
+ constructor(app: BaseApp<Pipeline>);
28
+ handle(state: FetchState, next: () => Promise<Response>): Promise<Response>;
29
+ }
@@ -0,0 +1,81 @@
1
+ import { PipelineFeatures } from "../base-pipeline.js";
2
+ import { AstroCache, applyCacheHeaders } from "./runtime/cache.js";
3
+ import { NoopAstroCache, DisabledAstroCache } from "./runtime/noop.js";
4
+ import { compileCacheRoutes, matchCacheRoute } from "./runtime/route-matching.js";
5
+ const CACHE_KEY = "cache";
6
+ function provideCache(state) {
7
+ const pipeline = state.pipeline;
8
+ if (!pipeline.cacheConfig) {
9
+ state.provide(CACHE_KEY, {
10
+ create: () => new DisabledAstroCache(pipeline.logger)
11
+ });
12
+ return;
13
+ }
14
+ if (pipeline.runtimeMode === "development") {
15
+ state.provide(CACHE_KEY, {
16
+ create: () => new NoopAstroCache()
17
+ });
18
+ return;
19
+ }
20
+ return provideCacheAsync(state, pipeline);
21
+ }
22
+ async function provideCacheAsync(state, pipeline) {
23
+ const cacheProvider = await pipeline.getCacheProvider();
24
+ state.provide(CACHE_KEY, {
25
+ create() {
26
+ const cache = new AstroCache(cacheProvider);
27
+ if (pipeline.cacheConfig?.routes) {
28
+ if (!pipeline.compiledCacheRoutes) {
29
+ pipeline.compiledCacheRoutes = compileCacheRoutes(
30
+ pipeline.cacheConfig.routes,
31
+ pipeline.manifest.base,
32
+ pipeline.manifest.trailingSlash
33
+ );
34
+ }
35
+ const matched = matchCacheRoute(state.pathname, pipeline.compiledCacheRoutes);
36
+ if (matched) {
37
+ cache.set(matched);
38
+ }
39
+ }
40
+ return cache;
41
+ }
42
+ });
43
+ }
44
+ class CacheHandler {
45
+ #app;
46
+ constructor(app) {
47
+ this.#app = app;
48
+ }
49
+ async handle(state, next) {
50
+ this.#app.pipeline.usedFeatures |= PipelineFeatures.cache;
51
+ if (!this.#app.pipeline.cacheProvider) {
52
+ return next();
53
+ }
54
+ const cache = state.resolve(CACHE_KEY);
55
+ const cacheProvider = await this.#app.pipeline.getCacheProvider();
56
+ if (cacheProvider?.onRequest) {
57
+ const response2 = await cacheProvider.onRequest(
58
+ {
59
+ request: state.request,
60
+ url: new URL(state.request.url),
61
+ waitUntil: state.renderOptions.waitUntil
62
+ },
63
+ async () => {
64
+ const res = await next();
65
+ applyCacheHeaders(cache, res);
66
+ return res;
67
+ }
68
+ );
69
+ response2.headers.delete("CDN-Cache-Control");
70
+ response2.headers.delete("Cache-Tag");
71
+ return response2;
72
+ }
73
+ const response = await next();
74
+ applyCacheHeaders(cache, response);
75
+ return response;
76
+ }
77
+ }
78
+ export {
79
+ CacheHandler,
80
+ provideCache
81
+ };
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import { createRequire } from "node:module";
2
3
  import { preprocessCSS } from "vite";
3
4
  import { AstroErrorData, CSSError, positionAt } from "../errors/index.js";
4
5
  import { normalizePath } from "../viteUtils.js";
@@ -31,6 +32,21 @@ function rewriteCssUrls(css, base) {
31
32
  return match;
32
33
  });
33
34
  }
35
+ function withNestingExcluded(viteConfig) {
36
+ let Features;
37
+ try {
38
+ const requireFromRoot = createRequire(viteConfig.root + "/");
39
+ Features = requireFromRoot("lightningcss").Features;
40
+ } catch {
41
+ return void 0;
42
+ }
43
+ const lcss = viteConfig.css?.lightningcss ?? {};
44
+ const prevExclude = lcss.exclude ?? 0;
45
+ return {
46
+ ...viteConfig,
47
+ css: { ...viteConfig.css, lightningcss: { ...lcss, exclude: prevExclude | Features.Nesting } }
48
+ };
49
+ }
34
50
  function createStylePreprocessor({
35
51
  filename,
36
52
  viteConfig,
@@ -44,7 +60,8 @@ function createStylePreprocessor({
44
60
  const lang = `.${attrs?.lang || "css"}`.toLowerCase();
45
61
  const id = `${filename}?astro&type=style&index=${index}&lang${lang}`;
46
62
  try {
47
- const result = await preprocessCSS(content, id, viteConfig);
63
+ const effectiveViteConfig = viteConfig.css?.transformer === "lightningcss" ? withNestingExcluded(viteConfig) ?? viteConfig : viteConfig;
64
+ const result = await preprocessCSS(content, id, effectiveViteConfig);
48
65
  const rewrittenCode = rewriteCssUrls(result.code, astroConfig.base);
49
66
  cssPartialCompileResults[index] = {
50
67
  // Use `in` operator to handle both Go compiler (boolean `true`) and
@@ -1,4 +1,4 @@
1
1
  export { resolveConfig, resolveConfigPath, resolveRoot, } from './config.js';
2
2
  export { mergeConfig } from './merge.js';
3
3
  export { createSettings } from './settings.js';
4
- export { loadTSConfig, updateTSConfigForFramework } from './tsconfig.js';
4
+ export { loadTSConfig, updateTSConfigForFramework, type TSConfigLoadedResult, type TSConfigResult, } from './tsconfig.js';
@@ -5,7 +5,10 @@ import {
5
5
  } from "./config.js";
6
6
  import { mergeConfig } from "./merge.js";
7
7
  import { createSettings } from "./settings.js";
8
- import { loadTSConfig, updateTSConfigForFramework } from "./tsconfig.js";
8
+ import {
9
+ loadTSConfig,
10
+ updateTSConfigForFramework
11
+ } from "./tsconfig.js";
9
12
  export {
10
13
  createSettings,
11
14
  loadTSConfig,