astro 6.3.7 → 6.4.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 (57) hide show
  1. package/components/Code.astro +1 -1
  2. package/dist/assets/fonts/config.d.ts +4 -4
  3. package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
  4. package/dist/content/content-layer.js +16 -10
  5. package/dist/content/data-store.d.ts +1 -1
  6. package/dist/content/runtime-assets.d.ts +2 -2
  7. package/dist/content/runtime.d.ts +1 -1
  8. package/dist/content/runtime.js +3 -1
  9. package/dist/content/utils.d.ts +1 -1
  10. package/dist/content/utils.js +1 -1
  11. package/dist/core/app/entrypoints/node.d.ts +1 -1
  12. package/dist/core/app/entrypoints/node.js +2 -0
  13. package/dist/core/app/node.d.ts +16 -0
  14. package/dist/core/app/node.js +56 -13
  15. package/dist/core/base-pipeline.js +2 -1
  16. package/dist/core/build/internal.d.ts +7 -0
  17. package/dist/core/build/plugins/plugin-chunk-imports.d.ts +6 -0
  18. package/dist/core/build/plugins/plugin-chunk-imports.js +16 -17
  19. package/dist/core/build/plugins/plugin-css.js +33 -0
  20. package/dist/core/build/static-build.js +13 -8
  21. package/dist/core/config/merge.js +4 -0
  22. package/dist/core/config/schemas/base.d.ts +16 -10
  23. package/dist/core/config/schemas/base.js +23 -3
  24. package/dist/core/config/schemas/relative.d.ts +60 -42
  25. package/dist/core/config/validate.js +52 -0
  26. package/dist/core/constants.js +1 -1
  27. package/dist/core/dev/dev.js +1 -1
  28. package/dist/core/errors/errors-data.d.ts +8 -3
  29. package/dist/core/errors/errors-data.js +8 -2
  30. package/dist/core/fetch/fetch-state.js +49 -0
  31. package/dist/core/messages/runtime.js +1 -1
  32. package/dist/core/preview/index.js +6 -5
  33. package/dist/core/preview/static-preview-server.js +2 -1
  34. package/dist/core/render/params-and-props.js +1 -1
  35. package/dist/core/render/route-cache.d.ts +1 -1
  36. package/dist/core/render/route-cache.js +2 -2
  37. package/dist/core/routing/create-manifest.js +10 -0
  38. package/dist/core/routing/validation.js +1 -1
  39. package/dist/core/server-islands/vite-plugin-server-islands.d.ts +6 -1
  40. package/dist/core/server-islands/vite-plugin-server-islands.js +13 -3
  41. package/dist/core/session/config.d.ts +1 -1
  42. package/dist/markdown/index.d.ts +4 -0
  43. package/dist/markdown/index.js +14 -0
  44. package/dist/prerender/utils.js +5 -1
  45. package/dist/runtime/server/render/component.js +5 -3
  46. package/dist/types/public/config.d.ts +33 -2
  47. package/dist/types/public/content.d.ts +1 -1
  48. package/dist/types/public/index.d.ts +1 -1
  49. package/dist/types/public/integrations.d.ts +8 -0
  50. package/dist/vite-plugin-app/app.js +2 -9
  51. package/dist/vite-plugin-integrations-container/index.js +15 -6
  52. package/dist/vite-plugin-markdown/content-entry-type.js +7 -4
  53. package/dist/vite-plugin-markdown/images.js +9 -11
  54. package/dist/vite-plugin-markdown/index.js +12 -11
  55. package/package.json +5 -7
  56. package/dist/jsx/rehype.d.ts +0 -5
  57. package/dist/jsx/rehype.js +0 -241
@@ -1,5 +1,5 @@
1
1
  ---
2
- import { createShikiHighlighter, type ThemePresets } from '@astrojs/markdown-remark/shiki';
2
+ import { createShikiHighlighter, type ThemePresets } from '@astrojs/internal-helpers/shiki';
3
3
  import type { ShikiTransformer, ThemeRegistration, ThemeRegistrationRaw } from 'shiki';
4
4
  import { bundledLanguages } from 'shiki/langs';
5
5
  import type { CodeLanguage } from '../dist/types/public/common.js';
@@ -7,11 +7,11 @@ export declare const StyleSchema: z.ZodEnum<{
7
7
  oblique: "oblique";
8
8
  }>;
9
9
  export declare const DisplaySchema: z.ZodEnum<{
10
- optional: "optional";
11
10
  auto: "auto";
11
+ optional: "optional";
12
+ fallback: "fallback";
12
13
  block: "block";
13
14
  swap: "swap";
14
- fallback: "fallback";
15
15
  }>;
16
16
  export declare const FontProviderSchema: z.ZodCustom<FontProvider<never>, FontProvider<never>>;
17
17
  export declare const FontFamilySchema: z.ZodObject<{
@@ -45,11 +45,11 @@ export declare const FontFamilySchema: z.ZodObject<{
45
45
  fallbacks: z.ZodOptional<z.ZodArray<z.ZodString>>;
46
46
  optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
47
47
  display: z.ZodOptional<z.ZodEnum<{
48
- optional: "optional";
49
48
  auto: "auto";
49
+ optional: "optional";
50
+ fallback: "fallback";
50
51
  block: "block";
51
52
  swap: "swap";
52
- fallback: "fallback";
53
53
  }>>;
54
54
  stretch: z.ZodOptional<z.ZodString>;
55
55
  featureSettings: z.ZodOptional<z.ZodString>;
@@ -1,6 +1,6 @@
1
1
  class BuildTimeAstroVersionProvider {
2
2
  // Injected during the build through esbuild define
3
- version = "6.3.7";
3
+ version = "6.4.0";
4
4
  }
5
5
  export {
6
6
  BuildTimeAstroVersionProvider
@@ -1,8 +1,5 @@
1
1
  import { existsSync, promises as fs } from "node:fs";
2
- import {
3
- createMarkdownProcessor,
4
- parseFrontmatter
5
- } from "@astrojs/markdown-remark";
2
+ import { parseFrontmatter } from "@astrojs/internal-helpers/frontmatter";
6
3
  import PQueue from "p-queue";
7
4
  import xxhash from "xxhash-wasm";
8
5
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
@@ -28,7 +25,7 @@ class ContentLayer {
28
25
  #watcher;
29
26
  #lastConfigDigest;
30
27
  #unsubscribe;
31
- #markdownProcessor;
28
+ #markdownRenderer;
32
29
  #generateDigest;
33
30
  #contentConfigObserver;
34
31
  #queue;
@@ -108,9 +105,18 @@ class ContentLayer {
108
105
  };
109
106
  }
110
107
  async #processMarkdown(content, options) {
111
- this.#markdownProcessor ??= await createMarkdownProcessor(this.#settings.config.markdown);
108
+ if (!this.#markdownRenderer) {
109
+ const { markdown, image } = this.#settings.config;
110
+ this.#markdownRenderer = await markdown.processor.createRenderer({
111
+ image,
112
+ syntaxHighlight: markdown.syntaxHighlight,
113
+ shikiConfig: markdown.shikiConfig,
114
+ gfm: markdown.gfm,
115
+ smartypants: markdown.smartypants
116
+ });
117
+ }
112
118
  const { frontmatter, content: body } = parseFrontmatter(content);
113
- const { code, metadata } = await this.#markdownProcessor.render(body, {
119
+ const { code, metadata } = await this.#markdownRenderer.render(body, {
114
120
  frontmatter,
115
121
  fileURL: options?.fileURL
116
122
  });
@@ -191,7 +197,7 @@ ${contentConfig.error.message}`
191
197
  logger.info("Content config changed");
192
198
  shouldClear = true;
193
199
  }
194
- if (previousAstroVersion && previousAstroVersion !== "6.3.7") {
200
+ if (previousAstroVersion && previousAstroVersion !== "6.4.0") {
195
201
  logger.info("Astro version changed");
196
202
  shouldClear = true;
197
203
  }
@@ -199,8 +205,8 @@ ${contentConfig.error.message}`
199
205
  logger.info("Clearing content store");
200
206
  this.#store.clearAll();
201
207
  }
202
- if ("6.3.7") {
203
- this.#store.metaStore().set("astro-version", "6.3.7");
208
+ if ("6.4.0") {
209
+ this.#store.metaStore().set("astro-version", "6.4.0");
204
210
  }
205
211
  if (currentConfigDigest) {
206
212
  this.#store.metaStore().set("content-config-digest", currentConfigDigest);
@@ -1,4 +1,4 @@
1
- import type { MarkdownHeading } from '@astrojs/markdown-remark';
1
+ import type { MarkdownHeading } from '@astrojs/internal-helpers/markdown';
2
2
  export interface RenderedContent {
3
3
  /** Rendered HTML string. If present then `render(entry)` will return a component that renders this HTML. */
4
4
  html: string;
@@ -2,10 +2,10 @@ import type { PluginContext } from 'rollup';
2
2
  import * as z from 'zod/v4';
3
3
  export declare function createImage(pluginContext: PluginContext, shouldEmitFile: boolean, entryFilePath: string): () => z.ZodPipe<z.ZodString, z.ZodTransform<z.ZodNever | {
4
4
  ASTRO_ASSET: string;
5
- format: import("../assets/types.js").ImageInputFormat;
6
- src: string;
7
5
  width: number;
8
6
  height: number;
7
+ format: import("../assets/types.js").ImageInputFormat;
8
+ src: string;
9
9
  fsPath: string;
10
10
  orientation?: number | undefined;
11
11
  }, string>>;
@@ -1,4 +1,4 @@
1
- import type { MarkdownHeading } from '@astrojs/markdown-remark';
1
+ import type { MarkdownHeading } from '@astrojs/internal-helpers/markdown';
2
2
  import * as z from 'zod/v4';
3
3
  import type * as zCore from 'zod/v4/core';
4
4
  import type { ImageMetadata } from '../assets/types.js';
@@ -322,7 +322,9 @@ async function updateImageReferencesInBody(html, fileName) {
322
322
  const { getImage } = await import("virtual:astro:get-image");
323
323
  for (const [_full, imagePath] of html.matchAll(CONTENT_LAYER_IMAGE_REGEX)) {
324
324
  try {
325
- const decodedImagePath = JSON.parse(imagePath.replaceAll("&#x22;", '"'));
325
+ const decodedImagePath = JSON.parse(
326
+ imagePath.replace(/&(?:#x22|quot);/g, '"').replace(/&(?:#x27|apos);/g, "'")
327
+ );
326
328
  let image;
327
329
  if (URL.canParse(decodedImagePath.src)) {
328
330
  image = await getImage(decodedImagePath);
@@ -125,7 +125,7 @@ export declare function getContentEntryIdAndSlug({ entry, contentDir, collection
125
125
  slug: string;
126
126
  };
127
127
  export declare function getEntryType(entryPath: string, paths: Pick<ContentPaths, 'config' | 'contentDir' | 'root'>, contentFileExts: string[], dataFileExts: string[]): 'content' | 'data' | 'config' | 'ignored';
128
- export declare function safeParseFrontmatter(source: string, id?: string): import("@astrojs/markdown-remark").ParseFrontmatterResult;
128
+ export declare function safeParseFrontmatter(source: string, id?: string): import("@astrojs/internal-helpers/frontmatter").ParseFrontmatterResult;
129
129
  /**
130
130
  * The content config is loaded separately from other `src/` files.
131
131
  * This global observable lets dependent plugins (like the content flag plugin)
@@ -1,7 +1,7 @@
1
1
  import fsMod from "node:fs";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath, pathToFileURL } from "node:url";
4
- import { parseFrontmatter } from "@astrojs/markdown-remark";
4
+ import { parseFrontmatter } from "@astrojs/internal-helpers/frontmatter";
5
5
  import { slug as githubSlug } from "github-slugger";
6
6
  import colors from "piccolore";
7
7
  import xxhash from "xxhash-wasm";
@@ -1 +1 @@
1
- export { NodeApp, loadApp, loadManifest, createRequest, writeResponse, getAbortControllerCleanup, } from '../node.js';
1
+ export { NodeApp, loadApp, loadManifest, createRequest, createRequestFromNodeRequest, writeResponse, getAbortControllerCleanup, } from '../node.js';
@@ -3,12 +3,14 @@ import {
3
3
  loadApp,
4
4
  loadManifest,
5
5
  createRequest,
6
+ createRequestFromNodeRequest,
6
7
  writeResponse,
7
8
  getAbortControllerCleanup
8
9
  } from "../node.js";
9
10
  export {
10
11
  NodeApp,
11
12
  createRequest,
13
+ createRequestFromNodeRequest,
12
14
  getAbortControllerCleanup,
13
15
  loadApp,
14
16
  loadManifest,
@@ -25,6 +25,22 @@ interface NodeRequest extends IncomingMessage {
25
25
  * })
26
26
  * ```
27
27
  */
28
+ /**
29
+ * Internal version of `createRequest` that skips X-Forwarded-* header
30
+ * validation. Forwarded headers are resolved later inside `FetchState`,
31
+ * so doing it here too would duplicate work on every request.
32
+ *
33
+ * Use this for all internal call sites that go through `app.render()`
34
+ * (which creates a `FetchState`). The public `createRequest` keeps the
35
+ * forwarded header logic for external adapters that may not use
36
+ * `FetchState`.
37
+ */
38
+ export declare function createRequestFromNodeRequest(req: NodeRequest, { skipBody, allowedDomains, bodySizeLimit, port: serverPort, }?: {
39
+ skipBody?: boolean;
40
+ allowedDomains?: Partial<RemotePattern>[];
41
+ bodySizeLimit?: number;
42
+ port?: number;
43
+ }): Request;
28
44
  export declare function createRequest(req: NodeRequest, { skipBody, allowedDomains, bodySizeLimit, port: serverPort, }?: {
29
45
  skipBody?: boolean;
30
46
  allowedDomains?: Partial<RemotePattern>[];
@@ -9,6 +9,48 @@ import {
9
9
  validateForwardedHeaders,
10
10
  validateHost
11
11
  } from "./validate-headers.js";
12
+ function createRequestFromNodeRequest(req, {
13
+ skipBody = false,
14
+ allowedDomains = [],
15
+ bodySizeLimit,
16
+ port: serverPort
17
+ } = {}) {
18
+ const controller = new AbortController();
19
+ const isEncrypted = "encrypted" in req.socket && req.socket.encrypted;
20
+ const protocol = isEncrypted ? "https" : "http";
21
+ const hostname = typeof req.headers.host === "string" ? req.headers.host : typeof req.headers[":authority"] === "string" ? req.headers[":authority"] : serverPort ? `localhost:${serverPort}` : "localhost";
22
+ let url;
23
+ try {
24
+ url = new URL(`${protocol}://${hostname}${req.url}`);
25
+ } catch {
26
+ url = new URL(`${protocol}://${hostname}`);
27
+ }
28
+ const options = {
29
+ method: req.method || "GET",
30
+ headers: makeRequestHeaders(req),
31
+ signal: controller.signal
32
+ };
33
+ const bodyAllowed = options.method !== "HEAD" && options.method !== "GET" && skipBody === false;
34
+ if (bodyAllowed) {
35
+ Object.assign(options, makeRequestBody(req, bodySizeLimit));
36
+ }
37
+ const request = new Request(url, options);
38
+ wireAbortController(req, controller);
39
+ const untrustedHostname = req.headers.host ?? req.headers[":authority"];
40
+ const validatedHostname = validateHost(
41
+ typeof untrustedHostname === "string" ? untrustedHostname : void 0,
42
+ protocol,
43
+ allowedDomains
44
+ );
45
+ const forwardedHost = getFirstForwardedValue(req.headers["x-forwarded-host"]);
46
+ const hostValidated = validatedHostname !== void 0 || forwardedHost !== void 0 && allowedDomains.length > 0;
47
+ const forwardedClientIp = hostValidated ? getFirstForwardedValue(req.headers["x-forwarded-for"]) : void 0;
48
+ const clientIp = forwardedClientIp || req.socket?.remoteAddress;
49
+ if (clientIp) {
50
+ Reflect.set(request, clientAddressSymbol, clientIp);
51
+ }
52
+ return request;
53
+ }
12
54
  function createRequest(req, {
13
55
  skipBody = false,
14
56
  allowedDomains = [],
@@ -51,6 +93,16 @@ function createRequest(req, {
51
93
  Object.assign(options, makeRequestBody(req, bodySizeLimit));
52
94
  }
53
95
  const request = new Request(url, options);
96
+ wireAbortController(req, controller);
97
+ const hostValidated = validated.host !== void 0 || validatedHostname !== void 0;
98
+ const forwardedClientIp = hostValidated ? getFirstForwardedValue(req.headers["x-forwarded-for"]) : void 0;
99
+ const clientIp = forwardedClientIp || req.socket?.remoteAddress;
100
+ if (clientIp) {
101
+ Reflect.set(request, clientAddressSymbol, clientIp);
102
+ }
103
+ return request;
104
+ }
105
+ function wireAbortController(req, controller) {
54
106
  const socket = getRequestSocket(req);
55
107
  if (socket && typeof socket.on === "function") {
56
108
  const existingCleanup = getAbortControllerCleanup(req);
@@ -85,13 +137,6 @@ function createRequest(req, {
85
137
  onSocketClose();
86
138
  }
87
139
  }
88
- const hostValidated = validated.host !== void 0 || validatedHostname !== void 0;
89
- const forwardedClientIp = hostValidated ? getFirstForwardedValue(req.headers["x-forwarded-for"]) : void 0;
90
- const clientIp = forwardedClientIp || req.socket?.remoteAddress;
91
- if (clientIp) {
92
- Reflect.set(request, clientAddressSymbol, clientIp);
93
- }
94
- return request;
95
140
  }
96
141
  async function writeResponse(source, destination) {
97
142
  const { status, headers, body, statusText } = source;
@@ -147,18 +192,15 @@ class NodeApp extends App {
147
192
  }
148
193
  match(req, allowPrerenderedRoutes = false) {
149
194
  if (!(req instanceof Request)) {
150
- req = createRequest(req, {
151
- skipBody: true,
152
- allowedDomains: this.manifest.allowedDomains
195
+ req = createRequestFromNodeRequest(req, {
196
+ skipBody: true
153
197
  });
154
198
  }
155
199
  return super.match(req, allowPrerenderedRoutes);
156
200
  }
157
201
  render(request, options) {
158
202
  if (!(request instanceof Request)) {
159
- request = createRequest(request, {
160
- allowedDomains: this.manifest.allowedDomains
161
- });
203
+ request = createRequestFromNodeRequest(request);
162
204
  }
163
205
  return super.render(request, options);
164
206
  }
@@ -283,6 +325,7 @@ async function loadApp(rootFolder) {
283
325
  export {
284
326
  NodeApp,
285
327
  createRequest,
328
+ createRequestFromNodeRequest,
286
329
  getAbortControllerCleanup,
287
330
  loadApp,
288
331
  loadManifest,
@@ -182,7 +182,8 @@ class Pipeline {
182
182
  if (this.resolvedActions) {
183
183
  return this.resolvedActions;
184
184
  } else if (this.actions) {
185
- return this.actions();
185
+ this.resolvedActions = await this.actions();
186
+ return this.resolvedActions;
186
187
  }
187
188
  return NOOP_ACTIONS_MOD;
188
189
  }
@@ -92,6 +92,13 @@ export interface BuildInternals {
92
92
  moduleIds: string[];
93
93
  prerender: boolean;
94
94
  }>;
95
+ /**
96
+ * Component exports that were rendered during the SSR build.
97
+ * Used by the client build's cssScopeTo recovery to distinguish between
98
+ * CSS that was tree-shaken because the component wasn't rendered in SSR
99
+ * vs CSS that was included in SSR.
100
+ */
101
+ ssrRenderedExports?: Map<string, Set<string>>;
95
102
  }
96
103
  /**
97
104
  * Creates internal maps used to coordinate the CSS and HTML plugins.
@@ -6,5 +6,11 @@ import type { StaticBuildOptions } from '../types.js';
6
6
  * bypass the HTML rendering pipeline and miss skew protection query params.
7
7
  *
8
8
  * Uses es-module-lexer to reliably parse both static and dynamic imports.
9
+ *
10
+ * This runs in `generateBundle` (not `renderChunk`) so that Vite's CSS plugin
11
+ * can first remove pure-CSS wrapper chunks and replace their imports with
12
+ * `/* empty css * /` comments. If we rewrote imports earlier (in `renderChunk`),
13
+ * the appended query params would break Vite's regex-based CSS chunk cleanup,
14
+ * leaving dangling imports to deleted chunks that 404 at runtime.
9
15
  */
10
16
  export declare function pluginChunkImports(options: StaticBuildOptions): VitePlugin | undefined;
@@ -12,25 +12,24 @@ function pluginChunkImports(options) {
12
12
  applyToEnvironment(environment) {
13
13
  return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.client;
14
14
  },
15
- async renderChunk(code, _chunk) {
16
- if (!code.includes("./")) {
17
- return null;
18
- }
15
+ async generateBundle(_options, bundle) {
19
16
  await init;
20
- const [imports] = parse(code);
21
- const relativeImports = imports.filter(
22
- (imp) => imp.n && /^\.\.?\//.test(imp.n) && /\.(?:js|mjs)$/.test(imp.n)
23
- );
24
- if (relativeImports.length === 0) {
25
- return null;
26
- }
27
- let rewritten = code;
28
- for (let i = relativeImports.length - 1; i >= 0; i--) {
29
- const imp = relativeImports[i];
30
- const insertAt = imp.d > -1 ? imp.e - 1 : imp.e;
31
- rewritten = rewritten.slice(0, insertAt) + "?" + queryString + rewritten.slice(insertAt);
17
+ for (const [, chunk] of Object.entries(bundle)) {
18
+ if (chunk.type !== "chunk") continue;
19
+ if (!chunk.code.includes("./")) continue;
20
+ const [imports] = parse(chunk.code);
21
+ const relativeImports = imports.filter(
22
+ (imp) => imp.n && /^\.\.?\//.test(imp.n) && /\.(?:js|mjs)$/.test(imp.n)
23
+ );
24
+ if (relativeImports.length === 0) continue;
25
+ let rewritten = chunk.code;
26
+ for (let i = relativeImports.length - 1; i >= 0; i--) {
27
+ const imp = relativeImports[i];
28
+ const insertAt = imp.d > -1 ? imp.e - 1 : imp.e;
29
+ rewritten = rewritten.slice(0, insertAt) + "?" + queryString + rewritten.slice(insertAt);
30
+ }
31
+ chunk.code = rewritten;
32
32
  }
33
- return { code: rewritten, map: null };
34
33
  }
35
34
  };
36
35
  }
@@ -46,10 +46,25 @@ function rollupPluginAstroBuildCSS(options) {
46
46
  internals.cssModuleToChunkIdMap.set(moduleId, chunk.fileName);
47
47
  }
48
48
  }
49
+ for (const [moduleId, moduleInfo] of Object.entries(chunk.modules || {})) {
50
+ if (moduleInfo.renderedExports.length > 0) {
51
+ const existing = internals.ssrRenderedExports?.get(moduleId);
52
+ if (existing) {
53
+ for (const exp of moduleInfo.renderedExports) {
54
+ existing.add(exp);
55
+ }
56
+ } else {
57
+ internals.ssrRenderedExports ??= /* @__PURE__ */ new Map();
58
+ internals.ssrRenderedExports.set(moduleId, new Set(moduleInfo.renderedExports));
59
+ }
60
+ }
61
+ }
49
62
  }
50
63
  }
51
64
  const renderedComponentExports = /* @__PURE__ */ new Map();
52
65
  const componentToPages = /* @__PURE__ */ new Map();
66
+ const deletedCssAssets = /* @__PURE__ */ new Map();
67
+ const cssScopeToAddedCss = /* @__PURE__ */ new Set();
53
68
  if (this.environment?.name === ASTRO_VITE_ENVIRONMENT_NAMES.client) {
54
69
  for (const [, item] of Object.entries(bundle)) {
55
70
  if (item.type !== "chunk") continue;
@@ -76,6 +91,9 @@ function rollupPluginAstroBuildCSS(options) {
76
91
  );
77
92
  if (allCssInSSR && shouldDeleteCSSChunk(allModules, internals)) {
78
93
  for (const cssId of meta.importedCss) {
94
+ if (bundle[cssId]) {
95
+ deletedCssAssets.set(cssId, bundle[cssId]);
96
+ }
79
97
  delete bundle[cssId];
80
98
  }
81
99
  }
@@ -157,6 +175,14 @@ function rollupPluginAstroBuildCSS(options) {
157
175
  }
158
176
  }
159
177
  }
178
+ const ssrExports = internals.ssrRenderedExports?.get(scopedToModule);
179
+ if (!ssrExports || !ssrExports.has(scopedToExport)) {
180
+ for (const cssId of meta.importedCss) {
181
+ if (deletedCssAssets.has(cssId)) {
182
+ cssScopeToAddedCss.add(cssId);
183
+ }
184
+ }
185
+ }
160
186
  }
161
187
  }
162
188
  }
@@ -191,6 +217,13 @@ function rollupPluginAstroBuildCSS(options) {
191
217
  }
192
218
  }
193
219
  }
220
+ if (cssScopeToAddedCss.size > 0) {
221
+ for (const cssId of cssScopeToAddedCss) {
222
+ if (deletedCssAssets.has(cssId) && !bundle[cssId]) {
223
+ bundle[cssId] = deletedCssAssets.get(cssId);
224
+ }
225
+ }
226
+ }
194
227
  }
195
228
  };
196
229
  const singleCssPlugin = {
@@ -10,10 +10,9 @@ import { emptyDir, removeEmptyDirs } from "../../core/fs/index.js";
10
10
  import { appendForwardSlash, prependForwardSlash } from "../../core/path.js";
11
11
  import { runHookBuildSetup } from "../../integrations/hooks.js";
12
12
  import { SERIALIZED_MANIFEST_RESOLVED_ID } from "../../manifest/serialized.js";
13
- import { getPrerenderOutputDirectory } from "../../prerender/utils.js";
13
+ import { getPrerenderOutputDirectory, getServerOutputDirectory } from "../../prerender/utils.js";
14
14
  import { PAGE_SCRIPT_ID } from "../../vite-plugin-scripts/index.js";
15
15
  import { routeIsRedirect } from "../routing/helpers.js";
16
- import { getOutDirWithinCwd } from "./common.js";
17
16
  import { generatePages } from "./generate.js";
18
17
  import { trackPageData } from "./internal.js";
19
18
  import { getAllBuildPlugins } from "./plugins/index.js";
@@ -23,7 +22,10 @@ import { getTimeStat, viteBuildReturnToRollupOutputs } from "./util.js";
23
22
  import { NOOP_MODULE_ID } from "./plugins/plugin-noop.js";
24
23
  import { ASTRO_VITE_ENVIRONMENT_NAMES } from "../constants.js";
25
24
  import { getSSRAssets } from "./internal.js";
26
- import { SERVER_ISLAND_MAP_MARKER } from "../server-islands/vite-plugin-server-islands.js";
25
+ import {
26
+ SERVER_ISLAND_MAP_MARKER,
27
+ hasServerIslands
28
+ } from "../server-islands/vite-plugin-server-islands.js";
27
29
  import { createViteBuildConfig } from "./vite-build-config.js";
28
30
  const PRERENDER_ENTRY_FILENAME_PREFIX = "prerender-entry";
29
31
  function extractRelevantChunks(outputs, prerender) {
@@ -155,7 +157,7 @@ async function buildEnvironments(opts, internals) {
155
157
  const prerenderChunks = extractRelevantChunks(prerenderOutputs, true);
156
158
  prerenderOutput = void 0;
157
159
  let ssrChunks = [];
158
- if (settings.buildOutput !== "static") {
160
+ if (needsServerBuild(settings, builder2)) {
159
161
  settings.timer.start("SSR build");
160
162
  let ssrOutput = await builder2.build(
161
163
  builder2.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr]
@@ -231,15 +233,12 @@ async function runManifestInjection(opts, internals, chunks, buildPostHooks) {
231
233
  }
232
234
  async function writeMutatedChunks(opts, mutations) {
233
235
  const { settings } = opts;
234
- const config = settings.config;
235
236
  for (const [fileName, mutation] of mutations) {
236
237
  let root;
237
238
  if (mutation.prerender) {
238
239
  root = getPrerenderOutputDirectory(settings);
239
- } else if (settings.buildOutput === "server") {
240
- root = config.build.server;
241
240
  } else {
242
- root = getOutDirWithinCwd(config.outDir);
241
+ root = getServerOutputDirectory(settings);
243
242
  }
244
243
  const fullPath = path.join(fileURLToPath(root), fileName);
245
244
  const fileURL = pathToFileURL(fullPath);
@@ -297,6 +296,12 @@ function getClientInput(internals, settings) {
297
296
  }
298
297
  return clientInput;
299
298
  }
299
+ function needsServerBuild(settings, builder) {
300
+ if (settings.buildOutput === "server") {
301
+ return true;
302
+ }
303
+ return hasServerIslands(builder.environments.prerender);
304
+ }
300
305
  function makeAstroPageEntryPointFileName(prefix, facadeModuleId, routes) {
301
306
  const pageModuleId = facadeModuleId.replace(prefix, "").replace(ASTRO_PAGE_EXTENSION_POST_PATTERN, ".");
302
307
  const route = routes.find((routeData) => routeData.component === pageModuleId);
@@ -38,6 +38,10 @@ function mergeConfigRecursively(defaults, overrides, rootPath) {
38
38
  merged[key] = value;
39
39
  continue;
40
40
  }
41
+ if (key === "processor" && rootPath === "markdown" && isObject(value) && typeof value.createRenderer === "function") {
42
+ merged[key] = value;
43
+ continue;
44
+ }
41
45
  if (isObject(existing) && isObject(value)) {
42
46
  merged[key] = mergeConfigRecursively(existing, value, rootPath ? `${rootPath}.${key}` : key);
43
47
  continue;
@@ -1,4 +1,4 @@
1
- import type { RehypePlugin as _RehypePlugin, RemarkPlugin as _RemarkPlugin, RemarkRehype as _RemarkRehype, Smartypants as _Smartypants, ShikiConfig } from '@astrojs/markdown-remark';
1
+ import type { RehypePlugin as _RehypePlugin, RemarkPlugin as _RemarkPlugin, RemarkRehype as _RemarkRehype, Smartypants as _Smartypants, ShikiConfig } from '@astrojs/internal-helpers/markdown';
2
2
  import type { OutgoingHttpHeaders } from 'node:http';
3
3
  import * as z from 'zod/v4';
4
4
  import type { ViteUserConfig } from '../../../types/public/config.js';
@@ -57,7 +57,7 @@ export declare const ASTRO_CONFIG_DEFAULTS: {
57
57
  allowedHosts: never[];
58
58
  };
59
59
  integrations: never[];
60
- markdown: Required<import("@astrojs/markdown-remark").AstroMarkdownOptions>;
60
+ markdown: Required<Omit<import("@astrojs/markdown-remark").AstroMarkdownOptions, "image">>;
61
61
  vite: {};
62
62
  legacy: {
63
63
  collectionsBackwardsCompat: false;
@@ -121,8 +121,8 @@ export declare const AstroConfigSchema: z.ZodObject<{
121
121
  redirects: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
122
122
  inlineStylesheets: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
123
123
  never: "never";
124
- auto: "auto";
125
124
  always: "always";
125
+ auto: "auto";
126
126
  }>>>;
127
127
  concurrency: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
128
128
  }, z.core.$strip>>;
@@ -140,10 +140,10 @@ export declare const AstroConfigSchema: z.ZodObject<{
140
140
  prefetch: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
141
141
  prefetchAll: z.ZodOptional<z.ZodBoolean>;
142
142
  defaultStrategy: z.ZodOptional<z.ZodEnum<{
143
- load: "load";
144
143
  tap: "tap";
145
144
  hover: "hover";
146
145
  viewport: "viewport";
146
+ load: "load";
147
147
  }>>;
148
148
  }, z.core.$strip>]>>;
149
149
  image: z.ZodPrefault<z.ZodObject<{
@@ -165,9 +165,9 @@ export declare const AstroConfigSchema: z.ZodObject<{
165
165
  }, z.core.$strip>>>;
166
166
  layout: z.ZodOptional<z.ZodEnum<{
167
167
  fixed: "fixed";
168
- none: "none";
169
168
  constrained: "constrained";
170
169
  "full-width": "full-width";
170
+ none: "none";
171
171
  }>>;
172
172
  objectFit: z.ZodOptional<z.ZodString>;
173
173
  objectPosition: z.ZodOptional<z.ZodString>;
@@ -331,8 +331,14 @@ export declare const AstroConfigSchema: z.ZodObject<{
331
331
  remarkPlugins: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodTuple<[z.ZodString, z.ZodAny], null>, z.ZodCustom<RemarkPlugin, RemarkPlugin>, z.ZodTuple<[z.ZodCustom<RemarkPlugin, RemarkPlugin>, z.ZodAny], null>]>>>;
332
332
  rehypePlugins: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodTuple<[z.ZodString, z.ZodAny], null>, z.ZodCustom<RehypePlugin, RehypePlugin>, z.ZodTuple<[z.ZodCustom<RehypePlugin, RehypePlugin>, z.ZodAny], null>]>>>;
333
333
  remarkRehype: z.ZodDefault<z.ZodCustom<RemarkRehype, RemarkRehype>>;
334
- gfm: z.ZodDefault<z.ZodBoolean>;
335
- smartypants: z.ZodPrefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodBoolean, z.ZodType<Smartypants, unknown, z.core.$ZodTypeInternals<Smartypants, unknown>>]>, z.ZodTransform<false | Smartypants, boolean | Smartypants>>>;
334
+ gfm: z.ZodOptional<z.ZodBoolean>;
335
+ smartypants: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodBoolean, z.ZodType<Smartypants, unknown, z.core.$ZodTypeInternals<Smartypants, unknown>>]>, z.ZodTransform<false | Smartypants, boolean | Smartypants>>>;
336
+ processor: z.ZodDefault<z.ZodObject<{
337
+ name: z.ZodString;
338
+ options: z.ZodDefault<z.ZodCustom<object, object>>;
339
+ createRenderer: z.ZodCustom<(shared: import("@astrojs/markdown-remark").AstroMarkdownOptions) => Promise<import("@astrojs/markdown-remark").MarkdownRenderer>, (shared: import("@astrojs/markdown-remark").AstroMarkdownOptions) => Promise<import("@astrojs/markdown-remark").MarkdownRenderer>>;
340
+ createMdxRenderer: z.ZodOptional<z.ZodCustom<((shared: import("@astrojs/markdown-remark").AstroMarkdownOptions, mdx: import("@astrojs/internal-helpers/markdown").MdxRendererOptions) => Promise<import("@astrojs/internal-helpers/markdown").MdxRenderer>) | undefined, ((shared: import("@astrojs/markdown-remark").AstroMarkdownOptions, mdx: import("@astrojs/internal-helpers/markdown").MdxRendererOptions) => Promise<import("@astrojs/internal-helpers/markdown").MdxRenderer>) | undefined>>;
341
+ }, z.core.$strip>>;
336
342
  }, z.core.$strip>>;
337
343
  vite: z.ZodDefault<z.ZodCustom<ViteUserConfig, ViteUserConfig>>;
338
344
  i18n: z.ZodOptional<z.ZodOptional<z.ZodObject<{
@@ -442,9 +448,9 @@ export declare const AstroConfigSchema: z.ZodObject<{
442
448
  path: z.ZodOptional<z.ZodString>;
443
449
  maxAge: z.ZodOptional<z.ZodNumber>;
444
450
  sameSite: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
451
+ none: "none";
445
452
  strict: "strict";
446
453
  lax: "lax";
447
- none: "none";
448
454
  }>, z.ZodBoolean]>>;
449
455
  secure: z.ZodOptional<z.ZodBoolean>;
450
456
  }, z.core.$strip>, z.ZodPipe<z.ZodString, z.ZodTransform<{
@@ -488,11 +494,11 @@ export declare const AstroConfigSchema: z.ZodObject<{
488
494
  fallbacks: z.ZodOptional<z.ZodArray<z.ZodString>>;
489
495
  optimizedFallbacks: z.ZodOptional<z.ZodBoolean>;
490
496
  display: z.ZodOptional<z.ZodEnum<{
491
- optional: "optional";
492
497
  auto: "auto";
498
+ optional: "optional";
499
+ fallback: "fallback";
493
500
  block: "block";
494
501
  swap: "swap";
495
- fallback: "fallback";
496
502
  }>>;
497
503
  stretch: z.ZodOptional<z.ZodString>;
498
504
  featureSettings: z.ZodOptional<z.ZodString>;