astro 7.0.0-alpha.0 → 7.0.0-alpha.2

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 (251) hide show
  1. package/components/Code.astro +1 -1
  2. package/dist/actions/handler.d.ts +32 -0
  3. package/dist/actions/handler.js +45 -0
  4. package/dist/actions/runtime/server.js +1 -1
  5. package/dist/assets/build/generate.js +1 -1
  6. package/dist/assets/build/remote.d.ts +3 -2
  7. package/dist/assets/build/remote.js +16 -9
  8. package/dist/assets/endpoint/dev.js +1 -1
  9. package/dist/assets/endpoint/generic.js +8 -20
  10. package/dist/assets/endpoint/loadImage.d.ts +11 -0
  11. package/dist/assets/endpoint/loadImage.js +19 -0
  12. package/dist/assets/endpoint/shared.js +7 -2
  13. package/dist/assets/fonts/config.d.ts +4 -4
  14. package/dist/assets/fonts/core/optimize-fallbacks.js +38 -13
  15. package/dist/assets/fonts/definitions.d.ts +2 -2
  16. package/dist/assets/fonts/infra/system-fallbacks-provider.d.ts +2 -2
  17. package/dist/assets/fonts/infra/system-fallbacks-provider.js +46 -9
  18. package/dist/assets/fonts/types.d.ts +1 -0
  19. package/dist/assets/index.d.ts +1 -0
  20. package/dist/assets/index.js +2 -0
  21. package/dist/assets/internal.js +22 -3
  22. package/dist/assets/services/service.d.ts +1 -1
  23. package/dist/assets/services/service.js +9 -9
  24. package/dist/assets/services/sharp.js +53 -18
  25. package/dist/assets/utils/generateImageStylesCSS.js +26 -6
  26. package/dist/assets/utils/index.d.ts +1 -0
  27. package/dist/assets/utils/index.js +2 -0
  28. package/dist/assets/utils/inferSourceFormat.d.ts +8 -3
  29. package/dist/assets/utils/inferSourceFormat.js +15 -4
  30. package/dist/assets/utils/metadata.js +1 -1
  31. package/dist/assets/utils/redirectValidation.d.ts +48 -0
  32. package/dist/assets/utils/redirectValidation.js +48 -0
  33. package/dist/assets/utils/remoteProbe.js +25 -2
  34. package/dist/assets/utils/vendor/image-size/types/svg.js +1 -1
  35. package/dist/cli/add/index.js +7 -58
  36. package/dist/cli/dev/background.d.ts +16 -0
  37. package/dist/cli/dev/background.js +116 -0
  38. package/dist/cli/dev/index.js +82 -3
  39. package/dist/cli/dev/logs.d.ts +6 -0
  40. package/dist/cli/dev/logs.js +72 -0
  41. package/dist/cli/dev/status.d.ts +15 -0
  42. package/dist/cli/dev/status.js +27 -0
  43. package/dist/cli/dev/stop.d.ts +12 -0
  44. package/dist/cli/dev/stop.js +43 -0
  45. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  46. package/dist/container/index.js +18 -14
  47. package/dist/content/content-layer.js +16 -11
  48. package/dist/content/data-store.d.ts +1 -1
  49. package/dist/content/loaders/types.d.ts +1 -1
  50. package/dist/content/runtime-assets.d.ts +2 -2
  51. package/dist/content/runtime.d.ts +1 -1
  52. package/dist/content/runtime.js +9 -4
  53. package/dist/content/server-listeners.js +0 -4
  54. package/dist/content/types-generator.js +5 -1
  55. package/dist/content/utils.d.ts +1 -1
  56. package/dist/content/utils.js +1 -1
  57. package/dist/content/vite-plugin-content-assets.js +1 -0
  58. package/dist/content/vite-plugin-content-virtual-mod.js +9 -1
  59. package/dist/core/app/base.d.ts +42 -15
  60. package/dist/core/app/base.js +151 -375
  61. package/dist/core/app/dev/app.d.ts +3 -2
  62. package/dist/core/app/dev/app.js +4 -60
  63. package/dist/core/app/entrypoints/node.d.ts +1 -1
  64. package/dist/core/app/entrypoints/node.js +2 -0
  65. package/dist/core/app/entrypoints/virtual/dev.js +2 -0
  66. package/dist/core/app/entrypoints/virtual/prod.js +4 -1
  67. package/dist/core/app/node.d.ts +16 -0
  68. package/dist/core/app/node.js +59 -13
  69. package/dist/core/app/prepare-response.d.ts +11 -0
  70. package/dist/core/app/prepare-response.js +18 -0
  71. package/dist/core/app/render-options.d.ts +11 -0
  72. package/dist/core/app/render-options.js +11 -0
  73. package/dist/core/base-pipeline.d.ts +48 -1
  74. package/dist/core/base-pipeline.js +63 -8
  75. package/dist/core/build/app.d.ts +3 -4
  76. package/dist/core/build/app.js +3 -17
  77. package/dist/core/build/generate.js +8 -1
  78. package/dist/core/build/index.d.ts +0 -11
  79. package/dist/core/build/index.js +0 -3
  80. package/dist/core/build/internal.d.ts +7 -0
  81. package/dist/core/build/plugins/plugin-chunk-imports.d.ts +6 -0
  82. package/dist/core/build/plugins/plugin-chunk-imports.js +29 -17
  83. package/dist/core/build/plugins/plugin-css.js +40 -1
  84. package/dist/core/build/plugins/plugin-internals.js +1 -1
  85. package/dist/core/build/plugins/plugin-manifest.js +11 -1
  86. package/dist/core/build/static-build.js +22 -141
  87. package/dist/core/build/types.d.ts +0 -1
  88. package/dist/core/build/util.js +8 -1
  89. package/dist/core/build/vite-build-config.d.ts +28 -0
  90. package/dist/core/build/vite-build-config.js +165 -0
  91. package/dist/core/cache/handler.d.ts +29 -0
  92. package/dist/core/cache/handler.js +81 -0
  93. package/dist/core/compile/style.js +18 -1
  94. package/dist/core/config/index.d.ts +1 -1
  95. package/dist/core/config/index.js +4 -1
  96. package/dist/core/config/merge.js +4 -0
  97. package/dist/core/config/schemas/base.d.ts +25 -13
  98. package/dist/core/config/schemas/base.js +39 -10
  99. package/dist/core/config/schemas/relative.d.ts +76 -49
  100. package/dist/core/config/schemas/relative.js +2 -3
  101. package/dist/core/config/settings.js +2 -4
  102. package/dist/core/config/tsconfig.d.ts +24 -9
  103. package/dist/core/config/tsconfig.js +54 -45
  104. package/dist/core/config/validate.js +59 -0
  105. package/dist/core/constants.d.ts +27 -1
  106. package/dist/core/constants.js +14 -1
  107. package/dist/core/cookies/cookies.d.ts +7 -2
  108. package/dist/core/cookies/cookies.js +11 -4
  109. package/dist/core/cookies/response.d.ts +1 -1
  110. package/dist/core/cookies/response.js +1 -2
  111. package/dist/core/create-vite.js +18 -1
  112. package/dist/core/csp/config.js +17 -5
  113. package/dist/core/csp/runtime.js +6 -4
  114. package/dist/core/dev/dev.d.ts +1 -0
  115. package/dist/core/dev/dev.js +4 -13
  116. package/dist/core/dev/lockfile.d.ts +54 -0
  117. package/dist/core/dev/lockfile.js +93 -0
  118. package/dist/core/errors/build-handler.d.ts +17 -0
  119. package/dist/core/errors/build-handler.js +22 -0
  120. package/dist/core/errors/default-handler.d.ts +14 -0
  121. package/dist/core/errors/default-handler.js +144 -0
  122. package/dist/core/errors/dev-handler.d.ts +21 -0
  123. package/dist/core/errors/dev-handler.js +82 -0
  124. package/dist/core/errors/errors-data.d.ts +43 -38
  125. package/dist/core/errors/errors-data.js +79 -73
  126. package/dist/core/errors/handler.d.ts +9 -0
  127. package/dist/core/errors/handler.js +0 -0
  128. package/dist/core/errors/zod-error-map.js +30 -1
  129. package/dist/core/fetch/default-handler.d.ts +17 -0
  130. package/dist/core/fetch/default-handler.js +45 -0
  131. package/dist/core/fetch/fetch-state.d.ts +230 -0
  132. package/dist/core/fetch/fetch-state.js +896 -0
  133. package/dist/core/fetch/index.d.ts +61 -0
  134. package/dist/core/fetch/index.js +121 -0
  135. package/dist/core/fetch/types.d.ts +25 -0
  136. package/dist/core/fetch/types.js +0 -0
  137. package/dist/core/fetch/vite-plugin.d.ts +5 -0
  138. package/dist/core/fetch/vite-plugin.js +76 -0
  139. package/dist/core/hono/index.d.ts +21 -0
  140. package/dist/core/hono/index.js +101 -0
  141. package/dist/core/i18n/domain.d.ts +12 -0
  142. package/dist/core/i18n/domain.js +66 -0
  143. package/dist/core/i18n/handler.d.ts +18 -0
  144. package/dist/core/i18n/handler.js +122 -0
  145. package/dist/core/logger/core.d.ts +9 -1
  146. package/dist/core/logger/core.js +17 -1
  147. package/dist/core/messages/runtime.d.ts +0 -3
  148. package/dist/core/messages/runtime.js +1 -9
  149. package/dist/core/middleware/astro-middleware.d.ts +27 -0
  150. package/dist/core/middleware/astro-middleware.js +51 -0
  151. package/dist/core/module-loader/vite.js +1 -2
  152. package/dist/core/pages/handler.d.ts +20 -0
  153. package/dist/core/pages/handler.js +75 -0
  154. package/dist/core/preview/index.js +6 -5
  155. package/dist/core/preview/static-preview-server.js +5 -2
  156. package/dist/core/redirects/render.d.ts +2 -2
  157. package/dist/core/redirects/render.js +7 -8
  158. package/dist/core/render/params-and-props.js +2 -2
  159. package/dist/core/render/route-cache.d.ts +1 -0
  160. package/dist/core/render/route-cache.js +4 -4
  161. package/dist/core/render/slots.js +9 -2
  162. package/dist/core/rewrites/handler.d.ts +37 -0
  163. package/dist/core/rewrites/handler.js +67 -0
  164. package/dist/core/routing/3xx.js +8 -4
  165. package/dist/core/routing/create-manifest.js +11 -1
  166. package/dist/core/routing/handler.d.ts +17 -0
  167. package/dist/core/routing/handler.js +171 -0
  168. package/dist/core/routing/match.d.ts +0 -7
  169. package/dist/core/routing/match.js +0 -5
  170. package/dist/core/routing/parse-route.js +1 -1
  171. package/dist/core/routing/pattern.js +1 -1
  172. package/dist/core/routing/rewrite.js +2 -5
  173. package/dist/core/routing/router.d.ts +8 -0
  174. package/dist/core/routing/router.js +28 -0
  175. package/dist/core/routing/trailing-slash-handler.d.ts +18 -0
  176. package/dist/core/routing/trailing-slash-handler.js +67 -0
  177. package/dist/core/routing/validation.js +1 -1
  178. package/dist/core/server-islands/vite-plugin-server-islands.d.ts +6 -1
  179. package/dist/core/server-islands/vite-plugin-server-islands.js +13 -3
  180. package/dist/core/session/config.d.ts +1 -1
  181. package/dist/core/session/drivers.d.ts +1 -1
  182. package/dist/core/session/handler.d.ts +11 -0
  183. package/dist/core/session/handler.js +33 -0
  184. package/dist/core/session/runtime.js +7 -2
  185. package/dist/core/util/normalized-url.d.ts +10 -0
  186. package/dist/core/util/normalized-url.js +24 -0
  187. package/dist/core/util/pathname.d.ts +10 -1
  188. package/dist/core/util/pathname.js +13 -4
  189. package/dist/environments.js +1 -1
  190. package/dist/events/session.d.ts +8 -0
  191. package/dist/events/session.js +11 -0
  192. package/dist/i18n/middleware.d.ts +10 -0
  193. package/dist/i18n/middleware.js +4 -88
  194. package/dist/i18n/utils.js +2 -2
  195. package/dist/jsx/rehype.d.ts +1 -1
  196. package/dist/manifest/virtual-module.js +3 -1
  197. package/dist/markdown/index.d.ts +4 -0
  198. package/dist/markdown/index.js +14 -0
  199. package/dist/prefetch/index.js +12 -7
  200. package/dist/prerender/utils.js +5 -1
  201. package/dist/runtime/client/dev-toolbar/apps/audit/rules/a11y.js +9 -0
  202. package/dist/runtime/server/astro-island.js +57 -20
  203. package/dist/runtime/server/astro-island.prebuilt-dev.d.ts +1 -1
  204. package/dist/runtime/server/astro-island.prebuilt-dev.js +1 -1
  205. package/dist/runtime/server/astro-island.prebuilt.d.ts +1 -1
  206. package/dist/runtime/server/astro-island.prebuilt.js +1 -1
  207. package/dist/runtime/server/jsx.js +1 -1
  208. package/dist/runtime/server/render/common.js +10 -4
  209. package/dist/runtime/server/render/component.js +8 -6
  210. package/dist/runtime/server/render/head.js +1 -5
  211. package/dist/runtime/server/render/server-islands.js +2 -1
  212. package/dist/runtime/server/render/util.js +2 -2
  213. package/dist/runtime/server/scripts.js +6 -0
  214. package/dist/runtime/server/transition.d.ts +1 -6
  215. package/dist/runtime/server/transition.js +0 -8
  216. package/dist/transitions/events.d.ts +0 -14
  217. package/dist/transitions/events.js +0 -14
  218. package/dist/transitions/index.d.ts +0 -1
  219. package/dist/transitions/index.js +0 -2
  220. package/dist/transitions/vite-plugin-transitions.js +2 -4
  221. package/dist/types/public/config.d.ts +96 -14
  222. package/dist/types/public/content.d.ts +5 -5
  223. package/dist/types/public/index.d.ts +2 -1
  224. package/dist/types/public/integrations.d.ts +11 -3
  225. package/dist/types/public/internal.d.ts +1 -1
  226. package/dist/types/public/manifest.d.ts +1 -1
  227. package/dist/virtual-modules/i18n.d.ts +2 -2
  228. package/dist/virtual-modules/i18n.js +1 -1
  229. package/dist/vite-plugin-app/app.d.ts +13 -6
  230. package/dist/vite-plugin-app/app.js +51 -89
  231. package/dist/vite-plugin-app/createAstroServerApp.d.ts +3 -1
  232. package/dist/vite-plugin-app/createAstroServerApp.js +4 -3
  233. package/dist/vite-plugin-astro-server/plugin.js +11 -5
  234. package/dist/vite-plugin-astro-server/route-guard.d.ts +33 -0
  235. package/dist/vite-plugin-astro-server/route-guard.js +42 -23
  236. package/dist/vite-plugin-dev-status/index.d.ts +2 -0
  237. package/dist/vite-plugin-dev-status/index.js +15 -0
  238. package/dist/vite-plugin-head/index.js +36 -19
  239. package/dist/vite-plugin-hmr-reload/index.d.ts +1 -1
  240. package/dist/vite-plugin-hmr-reload/index.js +23 -1
  241. package/dist/vite-plugin-integrations-container/index.js +15 -6
  242. package/dist/vite-plugin-markdown/content-entry-type.js +7 -4
  243. package/dist/vite-plugin-markdown/images.js +9 -11
  244. package/dist/vite-plugin-markdown/index.js +12 -11
  245. package/dist/vite-plugin-utils/index.d.ts +1 -0
  246. package/dist/vite-plugin-utils/index.js +10 -1
  247. package/package.json +23 -16
  248. package/templates/content/types.d.ts +1 -0
  249. package/types/transitions.d.ts +0 -7
  250. package/dist/core/render-context.d.ts +0 -77
  251. package/dist/core/render-context.js +0 -826
@@ -1,19 +1,14 @@
1
1
  import { removeTrailingForwardSlash } from "@astrojs/internal-helpers/path";
2
2
  import { BaseApp } from "../core/app/entrypoints/index.js";
3
- import { getFirstForwardedValue, validateForwardedHeaders } from "../core/app/validate-headers.js";
4
3
  import { shouldAppendForwardSlash } from "../core/build/util.js";
5
4
  import { clientLocalsSymbol } from "../core/constants.js";
6
- import {
7
- MiddlewareNoDataOrNextCalled,
8
- MiddlewareNotAResponse
9
- } from "../core/errors/errors-data.js";
10
- import { createSafeError, isAstroError } from "../core/errors/index.js";
5
+ import { createSafeError } from "../core/errors/index.js";
6
+ import { DevErrorHandler } from "../core/errors/dev-handler.js";
11
7
  import { createRequest } from "../core/request.js";
12
8
  import { recordServerError } from "../vite-plugin-astro-server/error.js";
13
9
  import { runWithErrorHandling } from "../vite-plugin-astro-server/index.js";
14
10
  import { handle500Response, writeSSRResult } from "../vite-plugin-astro-server/response.js";
15
11
  import { RunnablePipeline } from "./pipeline.js";
16
- import { getCustom404Route, getCustom500Route } from "../core/routing/helpers.js";
17
12
  import { ensure404Route } from "../core/routing/astro-designed-error-pages.js";
18
13
  import { matchRoute } from "../core/routing/dev.js";
19
14
  import { req } from "../core/messages/runtime.js";
@@ -21,13 +16,27 @@ class AstroServerApp extends BaseApp {
21
16
  settings;
22
17
  loader;
23
18
  manifestData;
24
- currentRenderContext = void 0;
25
19
  constructor(manifest, streaming = true, logger, manifestData, loader, settings, getDebugInfo) {
26
20
  super(manifest, streaming, settings, logger, loader, manifestData, getDebugInfo);
27
21
  this.settings = settings;
28
22
  this.loader = loader;
29
23
  this.manifestData = manifestData;
30
24
  }
25
+ /**
26
+ * Loads the user's `src/app.ts` (via `virtual:astro:fetchable`) and
27
+ * sets it as the fetch handler. Called on every request so that HMR
28
+ * invalidation of the virtual module is picked up automatically.
29
+ * Vite caches the module internally so repeated calls are cheap.
30
+ */
31
+ async #loadFetchHandler() {
32
+ try {
33
+ const mod = await this.loader.import("virtual:astro:fetchable");
34
+ if (mod?.default) {
35
+ this.setFetchHandler(mod.default);
36
+ }
37
+ } catch {
38
+ }
39
+ }
31
40
  isDev() {
32
41
  return true;
33
42
  }
@@ -82,24 +91,21 @@ class AstroServerApp extends BaseApp {
82
91
  });
83
92
  return pipeline;
84
93
  }
85
- async createRenderContext(payload) {
86
- this.currentRenderContext = await super.createRenderContext(payload);
87
- return this.currentRenderContext;
88
- }
94
+ /**
95
+ * Handle a request.
96
+ * @returns The return value indicates whether or not the request was handled
97
+ * by this handler. If the result is not `true`, then the request has not
98
+ * been handled yet and other handlers can be run.
99
+ */
89
100
  async handleRequest({
90
101
  controller,
91
102
  incomingRequest,
92
103
  incomingResponse,
93
- isHttps
104
+ isHttps,
105
+ prerenderOnly
94
106
  }) {
95
- const validated = validateForwardedHeaders(
96
- getFirstForwardedValue(incomingRequest.headers["x-forwarded-proto"]),
97
- getFirstForwardedValue(incomingRequest.headers["x-forwarded-host"]),
98
- getFirstForwardedValue(incomingRequest.headers["x-forwarded-port"]),
99
- this.manifest.allowedDomains
100
- );
101
- const protocol = validated.protocol ?? (isHttps ? "https" : "http");
102
- const host = validated.host ?? incomingRequest.headers[":authority"] ?? incomingRequest.headers.host;
107
+ const protocol = isHttps ? "https" : "http";
108
+ const host = incomingRequest.headers[":authority"] ?? incomingRequest.headers.host;
103
109
  const origin = `${protocol}://${host}`;
104
110
  const url = new URL(origin + incomingRequest.url);
105
111
  let pathname;
@@ -108,33 +114,40 @@ class AstroServerApp extends BaseApp {
108
114
  } else {
109
115
  pathname = decodeURI(url.pathname);
110
116
  }
111
- if (this.manifest.trailingSlash === "never" && pathname === "/" && this.manifest.base !== "/") {
112
- pathname = "";
113
- }
114
117
  url.pathname = removeTrailingForwardSlash(this.manifest.base) + url.pathname;
115
118
  if (url.pathname.endsWith("/") && !shouldAppendForwardSlash(this.manifest.trailingSlash, this.manifest.buildFormat)) {
116
119
  url.pathname = url.pathname.slice(0, -1);
117
120
  }
118
- let body = void 0;
119
- if (!(incomingRequest.method === "GET" || incomingRequest.method === "HEAD")) {
120
- let bytes = [];
121
- await new Promise((resolve) => {
122
- incomingRequest.on("data", (part) => {
123
- bytes.push(part);
124
- });
125
- incomingRequest.on("end", resolve);
126
- });
127
- body = Buffer.concat(bytes);
128
- }
129
121
  const self = this;
122
+ await self.#loadFetchHandler();
123
+ let handled = true;
130
124
  await runWithErrorHandling({
131
125
  controller,
132
126
  pathname,
133
127
  async run() {
134
128
  const matchedRoute = await self.devMatch(pathname);
135
129
  if (!matchedRoute) {
130
+ if (prerenderOnly) {
131
+ handled = false;
132
+ return;
133
+ }
136
134
  throw new Error("No route matched, and default 404 route was not found.");
137
135
  }
136
+ if (prerenderOnly && !matchedRoute.routeData.prerender) {
137
+ handled = false;
138
+ return;
139
+ }
140
+ let body = void 0;
141
+ if (!(incomingRequest.method === "GET" || incomingRequest.method === "HEAD")) {
142
+ let bytes = [];
143
+ await new Promise((resolve) => {
144
+ incomingRequest.on("data", (part) => {
145
+ bytes.push(part);
146
+ });
147
+ incomingRequest.on("end", resolve);
148
+ });
149
+ body = Buffer.concat(bytes);
150
+ }
138
151
  const request = createRequest({
139
152
  url,
140
153
  headers: incomingRequest.headers,
@@ -170,64 +183,13 @@ class AstroServerApp extends BaseApp {
170
183
  return error;
171
184
  }
172
185
  });
186
+ return handled;
173
187
  }
174
188
  match(request, _allowPrerenderedRoutes) {
175
189
  return super.match(request, true);
176
190
  }
177
- async renderError(request, {
178
- skipMiddleware = false,
179
- error,
180
- status,
181
- response: _response,
182
- ...resolvedRenderOptions
183
- }) {
184
- if (isAstroError(error) && [MiddlewareNoDataOrNextCalled.name, MiddlewareNotAResponse.name].includes(error.name)) {
185
- throw error;
186
- }
187
- const renderRoute = async (routeData) => {
188
- try {
189
- const preloadedComponent = await this.pipeline.getComponentByRoute(routeData);
190
- const renderContext = await this.createRenderContext({
191
- locals: resolvedRenderOptions.locals,
192
- pipeline: this.pipeline,
193
- pathname: this.getPathnameFromRequest(request),
194
- skipMiddleware,
195
- request,
196
- routeData,
197
- clientAddress: resolvedRenderOptions.clientAddress,
198
- status,
199
- shouldInjectCspMetaTags: !!this.manifest.csp
200
- });
201
- renderContext.props.error = error;
202
- const response = await renderContext.render(preloadedComponent);
203
- if (error) {
204
- this.logger.error("router", error.stack || error.message);
205
- }
206
- return response;
207
- } catch (_err) {
208
- if (skipMiddleware === false) {
209
- return this.renderError(request, {
210
- ...resolvedRenderOptions,
211
- status: 500,
212
- skipMiddleware: true,
213
- error: _err
214
- });
215
- }
216
- throw _err;
217
- }
218
- };
219
- if (status === 404) {
220
- const custom404 = getCustom404Route(this.manifestData);
221
- if (custom404) {
222
- return renderRoute(custom404);
223
- }
224
- }
225
- const custom500 = getCustom500Route(this.manifestData);
226
- if (!custom500) {
227
- throw error;
228
- } else {
229
- return renderRoute(custom500);
230
- }
191
+ createErrorHandler() {
192
+ return new DevErrorHandler(this, { shouldInjectCspMetaTags: true });
231
193
  }
232
194
  logRequest({ pathname, method, statusCode, isRewrite, reqTime }) {
233
195
  if (pathname === "/favicon.ico") {
@@ -4,5 +4,7 @@ import type { ModuleLoader } from '../core/module-loader/index.js';
4
4
  import type { AstroSettings } from '../types/astro.js';
5
5
  import type { DevServerController } from '../vite-plugin-astro-server/controller.js';
6
6
  export default function createAstroServerApp(controller: DevServerController, settings: AstroSettings, loader: ModuleLoader, logger?: AstroLogger): Promise<{
7
- handler(incomingRequest: http.IncomingMessage, incomingResponse: http.ServerResponse): void;
7
+ handler(incomingRequest: http.IncomingMessage, incomingResponse: http.ServerResponse, options?: {
8
+ prerenderOnly?: boolean;
9
+ }): Promise<boolean>;
8
10
  }>;
@@ -60,12 +60,13 @@ async function createAstroServerApp(controller, settings, loader, logger) {
60
60
  });
61
61
  }
62
62
  return {
63
- handler(incomingRequest, incomingResponse) {
64
- app.handleRequest({
63
+ handler(incomingRequest, incomingResponse, options) {
64
+ return app.handleRequest({
65
65
  controller,
66
66
  incomingRequest,
67
67
  incomingResponse,
68
- isHttps: loader?.isHttps() ?? false
68
+ isHttps: loader?.isHttps() ?? false,
69
+ prerenderOnly: options?.prerenderOnly
69
70
  });
70
71
  }
71
72
  };
@@ -107,15 +107,21 @@ function createVitePluginAstroServer({
107
107
  }
108
108
  try {
109
109
  const pathname = decodeURI(new URL(request.url, "http://localhost").pathname);
110
- const { routes } = await prerenderHandler.environment.runner.import("virtual:astro:routes");
111
- const routesList = { routes: routes.map((r) => r.routeData) };
110
+ const { routes } = await prerenderHandler.environment.runner.import(
111
+ "virtual:astro:routes"
112
+ );
113
+ const routesList = { routes: routes.map((route) => route.routeData) };
112
114
  const matches = matchAllRoutes(pathname, routesList);
113
115
  if (!matches.some((route) => route.prerender)) {
114
116
  return next();
115
117
  }
116
- localStorage.run(request, () => {
117
- prerenderHandler.handler(request, response);
118
- });
118
+ const handled = await localStorage.run(
119
+ request,
120
+ () => prerenderHandler.handler(request, response, { prerenderOnly: true })
121
+ );
122
+ if (!handled) {
123
+ return next();
124
+ }
119
125
  } catch (err) {
120
126
  next(err);
121
127
  }
@@ -1,5 +1,38 @@
1
1
  import type * as vite from 'vite';
2
2
  import type { AstroSettings } from '../types/astro.js';
3
+ /**
4
+ * Outcome of the route guard evaluation for a dev-server request.
5
+ *
6
+ * - **`next`** — Allow the request through to downstream middleware.
7
+ * - **`block`** — The file exists at the project root but outside srcDir/publicDir.
8
+ * Respond with a 404.
9
+ */
10
+ export type RouteGuardDecision = {
11
+ action: 'next';
12
+ } | {
13
+ action: 'block';
14
+ pathname: string;
15
+ };
16
+ /**
17
+ * Filesystem query results needed by the route guard decision function.
18
+ * Callers resolve these from the real filesystem; tests can provide them directly.
19
+ */
20
+ export interface RouteGuardFsInfo {
21
+ /** Whether the resolved pathname exists inside the project's `publicDir` (e.g. `public/robots.txt`). */
22
+ existsInPublic: boolean;
23
+ /** Whether the resolved pathname exists inside the project's `srcDir` (e.g. `src/pages/index.astro`). */
24
+ existsInSrc: boolean;
25
+ /** Whether the resolved pathname exists at the project root as a **file** (not a directory). Directories are allowed through because they may share names with valid page routes. */
26
+ existsAtRootAsFile: boolean;
27
+ }
28
+ /**
29
+ * Pure decision function for the route guard middleware.
30
+ *
31
+ * Determines whether a request should be blocked (file exists at project root
32
+ * but outside srcDir/publicDir) or allowed through. The filesystem lookups are
33
+ * injected via `fsInfo` so this function remains pure and unit-testable.
34
+ */
35
+ export declare function evaluateRouteGuard(url: string, acceptHeader: string, fsInfo: RouteGuardFsInfo): RouteGuardDecision;
3
36
  /**
4
37
  * Middleware that prevents Vite from serving files that exist outside
5
38
  * of srcDir and publicDir when accessed via direct URL navigation.
@@ -10,6 +10,30 @@ const VITE_INTERNAL_PREFIXES = [
10
10
  "/node_modules/",
11
11
  "/.astro/"
12
12
  ];
13
+ function evaluateRouteGuard(url, acceptHeader, fsInfo) {
14
+ if (!acceptHeader.includes("text/html")) {
15
+ return { action: "next" };
16
+ }
17
+ let pathname;
18
+ try {
19
+ pathname = decodeURI(new URL(url, "http://localhost").pathname);
20
+ } catch {
21
+ return { action: "next" };
22
+ }
23
+ if (VITE_INTERNAL_PREFIXES.some((prefix) => pathname.startsWith(prefix))) {
24
+ return { action: "next" };
25
+ }
26
+ if (url.includes("?")) {
27
+ return { action: "next" };
28
+ }
29
+ if (fsInfo.existsInPublic || fsInfo.existsInSrc) {
30
+ return { action: "next" };
31
+ }
32
+ if (fsInfo.existsAtRootAsFile) {
33
+ return { action: "block", pathname };
34
+ }
35
+ return { action: "next" };
36
+ }
13
37
  function routeGuardMiddleware(settings) {
14
38
  const { config } = settings;
15
39
  return function devRouteGuard(req, res, next) {
@@ -18,41 +42,36 @@ function routeGuardMiddleware(settings) {
18
42
  return next();
19
43
  }
20
44
  const accept = req.headers.accept || "";
21
- if (!accept.includes("text/html")) {
22
- return next();
23
- }
24
45
  let pathname;
25
46
  try {
26
47
  pathname = decodeURI(new URL(url, "http://localhost").pathname);
27
48
  } catch {
28
49
  return next();
29
50
  }
30
- if (VITE_INTERNAL_PREFIXES.some((prefix) => pathname.startsWith(prefix))) {
31
- return next();
32
- }
33
- if (url.includes("?")) {
34
- return next();
35
- }
36
- const publicFilePath = new URL("." + pathname, config.publicDir);
37
- if (fs.existsSync(publicFilePath)) {
38
- return next();
39
- }
40
- const srcFilePath = new URL("." + pathname, config.srcDir);
41
- if (fs.existsSync(srcFilePath)) {
42
- return next();
51
+ const fsInfo = {
52
+ existsInPublic: fs.existsSync(new URL("." + pathname, config.publicDir)),
53
+ existsInSrc: fs.existsSync(new URL("." + pathname, config.srcDir)),
54
+ existsAtRootAsFile: false
55
+ };
56
+ if (accept.includes("text/html") && !url.includes("?") && !VITE_INTERNAL_PREFIXES.some((prefix) => pathname.startsWith(prefix))) {
57
+ try {
58
+ const stat = fs.statSync(new URL("." + pathname, config.root));
59
+ fsInfo.existsAtRootAsFile = stat.isFile();
60
+ } catch {
61
+ }
43
62
  }
44
- const rootFilePath = new URL("." + pathname, config.root);
45
- try {
46
- const stat = fs.statSync(rootFilePath);
47
- if (stat.isFile()) {
48
- const html = notFoundTemplate(pathname);
63
+ const decision = evaluateRouteGuard(url, accept, fsInfo);
64
+ switch (decision.action) {
65
+ case "block": {
66
+ const html = notFoundTemplate(decision.pathname);
49
67
  return writeHtmlResponse(res, 404, html);
50
68
  }
51
- } catch {
69
+ case "next":
70
+ return next();
52
71
  }
53
- return next();
54
72
  };
55
73
  }
56
74
  export {
75
+ evaluateRouteGuard,
57
76
  routeGuardMiddleware
58
77
  };
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from 'vite';
2
+ export declare function vitePluginDevStatus(): Plugin;
@@ -0,0 +1,15 @@
1
+ function vitePluginDevStatus() {
2
+ return {
3
+ name: "astro:dev-status",
4
+ configureServer(viteServer) {
5
+ viteServer.middlewares.use(function devStatusHandler(req, res, next) {
6
+ if (req.url !== "/_astro/status") return next();
7
+ res.setHeader("Content-Type", "application/json");
8
+ res.end(JSON.stringify({ ok: true }));
9
+ });
10
+ }
11
+ };
12
+ }
13
+ export {
14
+ vitePluginDevStatus
15
+ };
@@ -9,11 +9,20 @@ import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../core/constants.js";
9
9
  const VIRTUAL_COMPONENT_METADATA = "virtual:astro:component-metadata";
10
10
  const RESOLVED_VIRTUAL_COMPONENT_METADATA = `\0${VIRTUAL_COMPONENT_METADATA}`;
11
11
  function configHeadVitePlugin() {
12
- let environment;
12
+ let environments = [];
13
+ function findModule(id) {
14
+ for (const env of environments) {
15
+ const mod = env.moduleGraph.getModuleById(id);
16
+ if (mod) return mod;
17
+ }
18
+ return void 0;
19
+ }
13
20
  function invalidateComponentMetadataModule() {
14
- const virtualMod = environment.moduleGraph.getModuleById(RESOLVED_VIRTUAL_COMPONENT_METADATA);
15
- if (virtualMod) {
16
- environment.moduleGraph.invalidateModule(virtualMod);
21
+ for (const env of environments) {
22
+ const virtualMod = env.moduleGraph.getModuleById(RESOLVED_VIRTUAL_COMPONENT_METADATA);
23
+ if (virtualMod) {
24
+ env.moduleGraph.invalidateModule(virtualMod);
25
+ }
17
26
  }
18
27
  }
19
28
  function buildImporterGraphFromEnvironment(seed) {
@@ -23,7 +32,7 @@ function configHeadVitePlugin() {
23
32
  const current = queue.pop();
24
33
  if (collected.has(current)) continue;
25
34
  collected.add(current);
26
- const mod = environment.moduleGraph.getModuleById(current);
35
+ const mod = findModule(current);
27
36
  for (const importer of mod?.importers ?? []) {
28
37
  if (importer.id) {
29
38
  queue.push(importer.id);
@@ -31,7 +40,7 @@ function configHeadVitePlugin() {
31
40
  }
32
41
  }
33
42
  return buildImporterGraphFromModuleInfo(collected, (id) => {
34
- const mod = environment.moduleGraph.getModuleById(id);
43
+ const mod = findModule(id);
35
44
  if (!mod) return null;
36
45
  return {
37
46
  importers: Array.from(mod.importers).map((importer) => importer.id).filter((moduleId) => !!moduleId),
@@ -61,7 +70,10 @@ function configHeadVitePlugin() {
61
70
  enforce: "pre",
62
71
  apply: "serve",
63
72
  configureServer(devServer) {
64
- environment = devServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
73
+ environments = [
74
+ devServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr],
75
+ devServer.environments[ASTRO_VITE_ENVIRONMENT_NAMES.prerender]
76
+ ].filter((e) => !!e);
65
77
  devServer.watcher.on("add", invalidateComponentMetadataModule);
66
78
  devServer.watcher.on("unlink", invalidateComponentMetadataModule);
67
79
  devServer.watcher.on("change", invalidateComponentMetadataModule);
@@ -70,19 +82,24 @@ function configHeadVitePlugin() {
70
82
  if (id !== RESOLVED_VIRTUAL_COMPONENT_METADATA) {
71
83
  return;
72
84
  }
85
+ const seen = /* @__PURE__ */ new Set();
73
86
  const componentMetadataEntries = [];
74
- for (const [moduleId, mod] of environment.moduleGraph.idToModuleMap) {
75
- const info = this.getModuleInfo(moduleId) ?? (mod.id ? this.getModuleInfo(mod.id) : null);
76
- if (!info) continue;
77
- const astro = getAstroMetadata(info);
78
- if (!astro) continue;
79
- componentMetadataEntries.push([
80
- moduleId,
81
- {
82
- containsHead: astro.containsHead,
83
- propagation: astro.propagation
84
- }
85
- ]);
87
+ for (const env of environments) {
88
+ for (const [moduleId, mod] of env.moduleGraph.idToModuleMap) {
89
+ if (seen.has(moduleId)) continue;
90
+ const info = this.getModuleInfo(moduleId) ?? (mod.id ? this.getModuleInfo(mod.id) : null);
91
+ if (!info) continue;
92
+ const astro = getAstroMetadata(info);
93
+ if (!astro) continue;
94
+ seen.add(moduleId);
95
+ componentMetadataEntries.push([
96
+ moduleId,
97
+ {
98
+ containsHead: astro.containsHead,
99
+ propagation: astro.propagation
100
+ }
101
+ ]);
102
+ }
86
103
  }
87
104
  return {
88
105
  code: `export const componentMetadataEntries = ${JSON.stringify(componentMetadataEntries)};`
@@ -1,4 +1,4 @@
1
- import type { Plugin } from 'vite';
1
+ import { type Plugin } from 'vite';
2
2
  /**
3
3
  * The very last Vite plugin to reload the browser if any SSR-only module are updated
4
4
  * which will require a full page reload. This mimics the behaviour of Vite 5 where
@@ -1,3 +1,4 @@
1
+ import { isRunnableDevEnvironment } from "vite";
1
2
  import { VIRTUAL_PAGE_RESOLVED_MODULE_ID } from "../vite-plugin-pages/const.js";
2
3
  import { getDevCssModuleNameFromPageVirtualModuleName } from "../vite-plugin-css/util.js";
3
4
  import { isAstroServerEnvironment } from "../environments.js";
@@ -16,7 +17,7 @@ function hmrReload() {
16
17
  enforce: "post",
17
18
  hotUpdate: {
18
19
  order: "post",
19
- handler({ modules, server, timestamp }) {
20
+ handler({ modules, server, timestamp, file }) {
20
21
  if (!isAstroServerEnvironment(this.environment)) return;
21
22
  let hasSsrOnlyModules = false;
22
23
  let hasSkippedStyleModules = false;
@@ -42,12 +43,33 @@ function hmrReload() {
42
43
  }
43
44
  }
44
45
  if (hasSsrOnlyModules) {
46
+ if (isRunnableDevEnvironment(this.environment)) {
47
+ for (const invalidated of invalidatedModules) {
48
+ if (invalidated.id == null) continue;
49
+ const runnerModule = this.environment.runner.evaluatedModules.getModuleById(
50
+ invalidated.id
51
+ );
52
+ if (runnerModule) {
53
+ this.environment.runner.evaluatedModules.invalidateModule(runnerModule);
54
+ }
55
+ }
56
+ }
45
57
  server.ws.send({ type: "full-reload" });
58
+ if (!isRunnableDevEnvironment(this.environment)) {
59
+ this.environment.hot.send({
60
+ type: "full-reload",
61
+ triggeredBy: file,
62
+ path: "*"
63
+ });
64
+ }
46
65
  return [];
47
66
  }
48
67
  if (hasSkippedStyleModules) {
49
68
  return [];
50
69
  }
70
+ if (modules.length > 0) {
71
+ return [];
72
+ }
51
73
  }
52
74
  }
53
75
  };
@@ -4,22 +4,31 @@ function astroIntegrationsContainerPlugin({
4
4
  settings,
5
5
  logger
6
6
  }) {
7
+ let server;
7
8
  return {
8
9
  name: "astro:integration-container",
9
- async configureServer(server) {
10
- if (server.config.isProduction) return;
11
- await runHookServerSetup({ config: settings.config, server, logger });
10
+ async configureServer(_server) {
11
+ server = _server;
12
+ if (_server.config.isProduction) return;
13
+ await runHookServerSetup({ config: settings.config, server: _server, logger });
12
14
  },
13
15
  async buildStart() {
14
16
  if (settings.injectedRoutes.length === settings.resolvedInjectedRoutes.length) return;
15
17
  settings.resolvedInjectedRoutes = await Promise.all(
16
- settings.injectedRoutes.map((route) => resolveEntryPoint.call(this, route))
18
+ settings.injectedRoutes.map((route) => resolveEntryPoint(route, server, this))
17
19
  );
18
20
  }
19
21
  };
20
22
  }
21
- async function resolveEntryPoint(route) {
22
- const resolvedId = await this.resolve(route.entrypoint.toString()).then((res) => res?.id).catch(() => void 0);
23
+ async function resolveEntryPoint(route, server, pluginContext) {
24
+ const entrypoint = route.entrypoint.toString();
25
+ let resolvedId;
26
+ if (server) {
27
+ const resolved = await server.environments.ssr.pluginContainer.resolveId(entrypoint);
28
+ resolvedId = resolved?.id;
29
+ } else {
30
+ resolvedId = await pluginContext.resolve(entrypoint).then((res) => res?.id).catch(() => void 0);
31
+ }
23
32
  if (!resolvedId) return route;
24
33
  const resolvedEntryPoint = new URL(`file://${normalizePath(resolvedId)}`);
25
34
  return { ...route, resolvedEntryPoint };
@@ -1,5 +1,4 @@
1
1
  import { fileURLToPath, pathToFileURL } from "node:url";
2
- import { createMarkdownProcessor } from "@astrojs/markdown-remark";
3
2
  import { safeParseFrontmatter } from "../content/utils.js";
4
3
  const markdownContentEntryType = {
5
4
  extensions: [".md"],
@@ -15,9 +14,13 @@ const markdownContentEntryType = {
15
14
  // We need to handle propagation for Markdown because they support layouts which will bring in styles.
16
15
  handlePropagation: true,
17
16
  async getRenderFunction(config) {
18
- const processor = await createMarkdownProcessor({
19
- image: config.image,
20
- ...config.markdown
17
+ const { markdown, image } = config;
18
+ const processor = await markdown.processor.createRenderer({
19
+ image,
20
+ syntaxHighlight: markdown.syntaxHighlight,
21
+ shikiConfig: markdown.shikiConfig,
22
+ gfm: markdown.gfm,
23
+ smartypants: markdown.smartypants
21
24
  });
22
25
  return async function renderToString(entry) {
23
26
  const result = await processor.render(entry.body ?? "", {
@@ -13,13 +13,12 @@ function getMarkdownCodeForImages(localImagePaths, remoteImagePaths, html) {
13
13
  "\\\\$&"
14
14
  )} + '[^"]*)"', 'g');
15
15
  let match;
16
- let occurrenceCounter = 0;
17
16
  while ((match = regex.exec(html)) !== null) {
18
- const matchKey = ${rawUrl} + '_' + occurrenceCounter;
19
- const imageProps = JSON.parse(match[1].replace(/&#x22;/g, '"').replace(/&#x27;/g, "'"));
17
+ const imageProps = JSON.parse(match[1].replace(/&(?:#x22|quot);/g, '"').replace(/&(?:#x27|apos);/g, "'"));
20
18
  const { src, ...props } = imageProps;
21
- imageSources[matchKey] = await getImage({src: Astro__${entry.safeName}, ...props});
22
- occurrenceCounter++;
19
+ // Key on the decoded src so it lines up with the lookup in updateImageReferences,
20
+ // which JSON-parses the attribute too (so its key uses the decoded path).
21
+ imageSources[src + '_' + imageProps.index] = await getImage({src: Astro__${entry.safeName}, ...props});
23
22
  }
24
23
  }`;
25
24
  }).join("\n")}
@@ -31,12 +30,9 @@ function getMarkdownCodeForImages(localImagePaths, remoteImagePaths, html) {
31
30
  "\\\\$&"
32
31
  )} + '[^"]*)"', 'g');
33
32
  let match;
34
- let occurrenceCounter = 0;
35
33
  while ((match = regex.exec(html)) !== null) {
36
- const matchKey = ${rawUrl} + '_' + occurrenceCounter;
37
- const props = JSON.parse(match[1].replace(/&#x22;/g, '"').replace(/&#x27;/g, "'"));
38
- imageSources[matchKey] = await getImage(props);
39
- occurrenceCounter++;
34
+ const props = JSON.parse(match[1].replace(/&(?:#x22|quot);/g, '"').replace(/&(?:#x27|apos);/g, "'"));
35
+ imageSources[props.src + '_' + props.index] = await getImage(props);
40
36
  }
41
37
  }`;
42
38
  }).join("\n")}
@@ -47,7 +43,9 @@ function getMarkdownCodeForImages(localImagePaths, remoteImagePaths, html) {
47
43
  const imageSources = await images(html);
48
44
 
49
45
  return html.replaceAll(/__ASTRO_IMAGE_="([^"]+)"/gm, (full, imagePath) => {
50
- const decodedImagePath = JSON.parse(imagePath.replace(/&#x22;/g, '"'));
46
+ // Markdown processors disagree on character-reference style \u2014 remark emits
47
+ // \`&#x22;\`/\`&#x27;\`, satteri emits \`&quot;\`/\`&apos;\`. Decode both before JSON.parse.
48
+ const decodedImagePath = JSON.parse(imagePath.replace(/&(?:#x22|quot);/g, '"').replace(/&(?:#x27|apos);/g, "'"));
51
49
 
52
50
  // Use the 'index' property for each image occurrence
53
51
  const srcKey = decodedImagePath.src + '_' + decodedImagePath.index;