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
@@ -13,7 +13,7 @@ interface CallGetStaticPathsOptions {
13
13
  }
14
14
  export declare function callGetStaticPaths({ mod, route, routeCache, ssr, base, trailingSlash, }: CallGetStaticPathsOptions): Promise<GetStaticPathsResultKeyed>;
15
15
  interface RouteCacheEntry {
16
- mod?: ComponentInstance;
16
+ mod: ComponentInstance;
17
17
  staticPaths: GetStaticPathsResultKeyed;
18
18
  }
19
19
  /**
@@ -17,9 +17,9 @@ async function callGetStaticPaths({
17
17
  return cached.staticPaths;
18
18
  }
19
19
  validateDynamicRouteModule(mod, { ssr, route });
20
- if (ssr && !route.prerender) {
20
+ if (ssr && !route.prerender || route.origin === "internal") {
21
21
  const entry = Object.assign([], { keyed: /* @__PURE__ */ new Map() });
22
- routeCache.set(route, { ...cached, staticPaths: entry });
22
+ routeCache.set(route, { ...cached, mod, staticPaths: entry });
23
23
  return entry;
24
24
  }
25
25
  let staticPaths = [];
@@ -388,6 +388,16 @@ function createRedirectRoutes({ settings }, routeMap) {
388
388
  message: InvalidRedirectDestination.message(from, destination)
389
389
  });
390
390
  }
391
+ if (params.length > 0 && redirectRoute && !URL.canParse(destination) && getPrerenderDefault(config)) {
392
+ const destParams = redirectRoute.params ?? [];
393
+ const missingParams = params.filter((p) => !destParams.includes(p));
394
+ if (missingParams.length > 0) {
395
+ throw new AstroError({
396
+ ...InvalidRedirectDestination,
397
+ message: InvalidRedirectDestination.message(from, destination, missingParams)
398
+ });
399
+ }
400
+ }
391
401
  routes.push({
392
402
  type: "redirect",
393
403
  // For backwards compatibility, a redirect is never considered an index route.
@@ -3,7 +3,7 @@ function validateDynamicRouteModule(mod, {
3
3
  ssr,
4
4
  route
5
5
  }) {
6
- if ((!ssr || route.prerender) && !mod.getStaticPaths) {
6
+ if ((!ssr || route.prerender) && route.origin !== "internal" && !mod.getStaticPaths) {
7
7
  throw new AstroError({
8
8
  ...AstroErrorData.GetStaticPathsRequired,
9
9
  location: { file: route.component }
@@ -1,4 +1,4 @@
1
- import type { Plugin as VitePlugin } from 'vite';
1
+ import type { BuildEnvironment, Plugin as VitePlugin } from 'vite';
2
2
  import type { AstroPluginOptions } from '../../types/astro.js';
3
3
  import type { ServerIslandsState } from './shared-state.js';
4
4
  export declare const SERVER_ISLAND_MANIFEST = "virtual:astro:server-island-manifest";
@@ -6,3 +6,8 @@ export declare const SERVER_ISLAND_MAP_MARKER = "$$server-islands-map$$";
6
6
  export declare function vitePluginServerIslands({ settings, serverIslandsState, }: AstroPluginOptions & {
7
7
  serverIslandsState: ServerIslandsState;
8
8
  }): VitePlugin;
9
+ /**
10
+ * Checks if the prerender environment discovered any server islands during the build.
11
+ * This encapsulates the logic of finding the server islands plugin and querying its state.
12
+ */
13
+ export declare function hasServerIslands(environment: BuildEnvironment): boolean;
@@ -28,6 +28,11 @@ function vitePluginServerIslands({
28
28
  return {
29
29
  name: "astro:server-islands",
30
30
  enforce: "post",
31
+ api: {
32
+ hasServerIslands() {
33
+ return serverIslandsState.hasIslands();
34
+ }
35
+ },
31
36
  config(_config, { command: _command }) {
32
37
  command = _command;
33
38
  },
@@ -111,9 +116,8 @@ export const serverIslandNameMap = ${serverIslandPlaceholderNameMap};`
111
116
  }
112
117
  }
113
118
  if (id === RESOLVED_SERVER_ISLAND_MANIFEST) {
114
- if (command === "build" && settings.buildOutput) {
115
- const hasServerIslands = serverIslandsState.hasIslands();
116
- if (hasServerIslands && settings.buildOutput !== "server") {
119
+ if (command === "build") {
120
+ if (serverIslandsState.hasIslands() && !settings.adapter) {
117
121
  throw new AstroError(AstroErrorData.NoAdapterInstalledServerIslands);
118
122
  }
119
123
  }
@@ -163,8 +167,14 @@ export const serverIslandNameMap = ${serverIslandPlaceholderNameMap};`
163
167
  }
164
168
  };
165
169
  }
170
+ function hasServerIslands(environment) {
171
+ const plugins = environment.config.plugins ?? [];
172
+ const serverIslandsPlugin = plugins.find((p) => p.name === "astro:server-islands");
173
+ return typeof serverIslandsPlugin?.api?.hasServerIslands === "function" && serverIslandsPlugin.api.hasServerIslands();
174
+ }
166
175
  export {
167
176
  SERVER_ISLAND_MANIFEST,
168
177
  SERVER_ISLAND_MAP_MARKER,
178
+ hasServerIslands,
169
179
  vitePluginServerIslands
170
180
  };
@@ -15,9 +15,9 @@ export declare const SessionSchema: z.ZodObject<{
15
15
  path: z.ZodOptional<z.ZodString>;
16
16
  maxAge: z.ZodOptional<z.ZodNumber>;
17
17
  sameSite: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
18
+ none: "none";
18
19
  strict: "strict";
19
20
  lax: "lax";
20
- none: "none";
21
21
  }>, z.ZodBoolean]>>;
22
22
  secure: z.ZodOptional<z.ZodBoolean>;
23
23
  }, z.core.$strip>, z.ZodPipe<z.ZodString, z.ZodTransform<{
@@ -0,0 +1,4 @@
1
+ export { extractFrontmatter, isFrontmatterValid, parseFrontmatter, type ParseFrontmatterOptions, type ParseFrontmatterResult, } from '@astrojs/internal-helpers/frontmatter';
2
+ export { resolvePath } from '../core/viteUtils.js';
3
+ export { createDefaultAstroMetadata } from '../vite-plugin-astro/metadata.js';
4
+ export type { AstroMarkdownOptions, AstroMetadata, MarkdownProcessor, MarkdownRenderer, MarkdownRenderOptions, MarkdownRenderResult, MdxRenderer, MdxRendererOptions, MdxRenderResult, } from '@astrojs/internal-helpers/markdown';
@@ -0,0 +1,14 @@
1
+ import {
2
+ extractFrontmatter,
3
+ isFrontmatterValid,
4
+ parseFrontmatter
5
+ } from "@astrojs/internal-helpers/frontmatter";
6
+ import { resolvePath } from "../core/viteUtils.js";
7
+ import { createDefaultAstroMetadata } from "../vite-plugin-astro/metadata.js";
8
+ export {
9
+ createDefaultAstroMetadata,
10
+ extractFrontmatter,
11
+ isFrontmatterValid,
12
+ parseFrontmatter,
13
+ resolvePath
14
+ };
@@ -3,7 +3,11 @@ function getPrerenderDefault(config) {
3
3
  return config.output !== "server";
4
4
  }
5
5
  function getServerOutputDirectory(settings) {
6
- return settings.buildOutput === "server" ? settings.config.build.server : getOutDirWithinCwd(settings.config.outDir);
6
+ const preserveStructure = settings.adapter?.adapterFeatures?.preserveBuildServerDir;
7
+ if (settings.buildOutput === "server" || preserveStructure) {
8
+ return settings.config.build.server;
9
+ }
10
+ return getOutDirWithinCwd(settings.config.outDir);
7
11
  }
8
12
  function getPrerenderOutputDirectory(settings) {
9
13
  return new URL("./.prerender/", getServerOutputDirectory(settings));
@@ -18,7 +18,8 @@ import { componentIsHTMLElement, renderHTMLElement } from "./dom.js";
18
18
  import { maybeRenderHead } from "./head.js";
19
19
  import { createRenderInstruction } from "./instruction.js";
20
20
  import { containsServerDirective, ServerIslandComponent } from "./server-islands.js";
21
- import { renderSlot, renderSlots, renderSlotToString } from "./slot.js";
21
+ import { renderChild } from "./any.js";
22
+ import { renderSlots, renderSlotToString } from "./slot.js";
22
23
  import { formatList, internalSpreadAttributes, renderElement, voidElementNames } from "./util.js";
23
24
  const needsHeadRenderingSymbol = /* @__PURE__ */ Symbol.for("astro.needsHeadRendering");
24
25
  const rendererAliases = /* @__PURE__ */ new Map([["solid", "solid-js"]]);
@@ -333,10 +334,11 @@ function sanitizeElementName(tag) {
333
334
  }
334
335
  function renderFragmentComponent(result, slots = {}) {
335
336
  const slot = slots?.default;
337
+ const preRendered = slot?.(result);
336
338
  return {
337
339
  render(destination) {
338
- if (slot == null) return;
339
- return renderSlot(result, slot).render(destination);
340
+ if (preRendered == null) return;
341
+ return renderChild(destination, preRendered);
340
342
  }
341
343
  };
342
344
  }
@@ -1,6 +1,7 @@
1
1
  import type { OutgoingHttpHeaders } from 'node:http';
2
2
  import type { RemotePattern } from '@astrojs/internal-helpers/remote';
3
- import type { RehypePlugins, RemarkPlugins, RemarkRehype, ShikiConfig, Smartypants, SyntaxHighlightConfigType } from '@astrojs/markdown-remark';
3
+ import type { RehypePlugins, RemarkPlugins, RemarkRehype, ShikiConfig, Smartypants, SyntaxHighlightConfigType } from '@astrojs/internal-helpers/markdown';
4
+ import type { MarkdownProcessor } from '../../markdown/index.js';
4
5
  import type { UserConfig as OriginalViteUserConfig, SSROptions as ViteSSROptions } from 'vite';
5
6
  import type { FontFamily, FontProvider } from '../../assets/fonts/types.js';
6
7
  import type { ImageFit, ImageLayout } from '../../assets/types.js';
@@ -426,7 +427,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
426
427
  *
427
428
  * By default, Astro removes whitespace from your HTML, including line breaks, in a lossless manner from `.astro` components. Some whitespace may be preserved as needed to maintain the visual rendering of your HTML.
428
429
  *
429
- * Setting this option to `"jsx"` instead applies the JSX whitespace stripping rules used by frameworks like React. Leading and trailing whitespace is only preserved when explicitly included in the source code through constructs such as `{" "}`, and is otherwise removed entirely.
430
+ * Since 6.2.0, this option can also be set to `"jsx"`, Astro will apply the JSX whitespace stripping rules used by frameworks like React. Leading and trailing whitespace is only preserved when explicitly included in the source code through constructs such as `{" "}`, and is otherwise removed entirely.
430
431
  *
431
432
  * Setting this option to false disables HTML compression and preserves all whitespace.
432
433
  *
@@ -1980,6 +1981,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
1980
1981
  * @docs
1981
1982
  * @name markdown.remarkPlugins
1982
1983
  * @type {RemarkPlugins}
1984
+ * @deprecated Pass `remarkPlugins` to `unified({ remarkPlugins })` from `@astrojs/markdown-remark` and set it as `markdown.processor` instead. Will be removed in a future major.
1983
1985
  * @description
1984
1986
  * Pass [remark plugins](https://github.com/remarkjs/remark) to customize how your Markdown is built. You can import and apply the plugin function (recommended), or pass the plugin name as a string.
1985
1987
  *
@@ -1997,6 +1999,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
1997
1999
  * @docs
1998
2000
  * @name markdown.rehypePlugins
1999
2001
  * @type {RehypePlugins}
2002
+ * @deprecated Pass `rehypePlugins` to `unified({ rehypePlugins })` from `@astrojs/markdown-remark` and set it as `markdown.processor` instead. Will be removed in a future major.
2000
2003
  * @description
2001
2004
  * Pass [rehype plugins](https://github.com/remarkjs/remark-rehype) to customize how your Markdown's output HTML is processed. You can import and apply the plugin function (recommended), or pass the plugin name as a string.
2002
2005
  *
@@ -2016,6 +2019,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2016
2019
  * @type {boolean}
2017
2020
  * @default `true`
2018
2021
  * @version 2.0.0
2022
+ * @deprecated Pass `gfm` to your processor instead (e.g. `unified({ gfm: false })`). Will be removed in a future major.
2019
2023
  * @description
2020
2024
  * Astro uses [GitHub-flavored Markdown](https://github.com/remarkjs/remark-gfm) by default. To disable this, set the `gfm` flag to `false`:
2021
2025
  *
@@ -2034,6 +2038,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2034
2038
  * @type {boolean | Smartypants}
2035
2039
  * @default `true`
2036
2040
  * @version 2.0.0
2041
+ * @deprecated Pass `smartypants` to your processor instead (e.g. `unified({ smartypants: false })`). Will be removed in a future major.
2037
2042
  * @description
2038
2043
  * Whether to use the [SmartyPants formatter](https://daringfireball.net/projects/smartypants/) to transform straight quotes into smart quotes, dashes into en/em dashes, and triple dots into ellipses.
2039
2044
  *
@@ -2046,6 +2051,7 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2046
2051
  * @docs
2047
2052
  * @name markdown.remarkRehype
2048
2053
  * @type {RemarkRehype}
2054
+ * @deprecated Pass `remarkRehype` to `unified({ remarkRehype })` from `@astrojs/markdown-remark` and set it as `markdown.processor` instead. Will be removed in a future major.
2049
2055
  * @description
2050
2056
  * Pass options to [remark-rehype](https://github.com/remarkjs/remark-rehype#api).
2051
2057
  *
@@ -2059,6 +2065,31 @@ export interface AstroUserConfig<TLocales extends Locales = never, TDriver exten
2059
2065
  * ```
2060
2066
  */
2061
2067
  remarkRehype?: RemarkRehype;
2068
+ /**
2069
+ * @docs
2070
+ * @name markdown.processor
2071
+ * @type {MarkdownProcessor}
2072
+ * @version 6.4.0
2073
+ * @description
2074
+ * Configures the Markdown processor used to render `.md` files. Defaults to `unified()` from
2075
+ * `@astrojs/markdown-remark` (the remark/rehype pipeline).
2076
+ *
2077
+ * ```js
2078
+ * // astro.config.mjs
2079
+ * import { defineConfig } from 'astro/config';
2080
+ * import { unified } from '@astrojs/markdown-remark';
2081
+ * import remarkToc from 'remark-toc';
2082
+ *
2083
+ * export default defineConfig({
2084
+ * markdown: {
2085
+ * processor: unified({
2086
+ * remarkPlugins: [remarkToc],
2087
+ * }),
2088
+ * },
2089
+ * });
2090
+ * ```
2091
+ */
2092
+ processor?: MarkdownProcessor;
2062
2093
  };
2063
2094
  /**
2064
2095
  * @docs
@@ -1,4 +1,4 @@
1
- import type { MarkdownHeading } from '@astrojs/markdown-remark';
1
+ import type { MarkdownHeading } from '@astrojs/internal-helpers/markdown';
2
2
  import type * as rollup from 'rollup';
3
3
  import type { DataEntry, RenderedContent } from '../../content/data-store.js';
4
4
  import type { LiveCollectionError } from '../../content/loaders/errors.js';
@@ -1,5 +1,5 @@
1
1
  export type { RemotePattern } from '@astrojs/internal-helpers/remote';
2
- export type { MarkdownHeading, RehypePlugins, RemarkPlugins, ShikiConfig, } from '@astrojs/markdown-remark';
2
+ export type { MarkdownHeading, RehypePlugins, RemarkPlugins, ShikiConfig, } from '@astrojs/internal-helpers/markdown';
3
3
  export type { ExternalImageService, ImageService, LocalImageService, } from '../../assets/services/service.js';
4
4
  export type { AssetsGlobalStaticImagesList, GetImageResult, ImageInputFormat, ImageMetadata, ImageOutputFormat, ImageQuality, ImageQualityPreset, ImageTransform, UnresolvedImageTransform, } from '../../assets/types.js';
5
5
  export type { ContainerRenderer } from '../../container/index.js';
@@ -102,6 +102,14 @@ export interface AstroAdapterFeatures {
102
102
  * of the build output type.
103
103
  */
104
104
  preserveBuildClientDir?: boolean;
105
+ /**
106
+ * When true, static builds will preserve the server directory structure
107
+ * instead of outputting to outDir. This ensures static builds use
108
+ * build.server for server output, maintaining consistency with server builds.
109
+ * Useful for adapters that require a specific directory structure regardless
110
+ * of the build output type.
111
+ */
112
+ preserveBuildServerDir?: boolean;
105
113
  }
106
114
  interface AdapterExplicitProperties {
107
115
  /**
@@ -1,6 +1,5 @@
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
5
  import { createSafeError } from "../core/errors/index.js";
@@ -105,14 +104,8 @@ class AstroServerApp extends BaseApp {
105
104
  isHttps,
106
105
  prerenderOnly
107
106
  }) {
108
- const validated = validateForwardedHeaders(
109
- getFirstForwardedValue(incomingRequest.headers["x-forwarded-proto"]),
110
- getFirstForwardedValue(incomingRequest.headers["x-forwarded-host"]),
111
- getFirstForwardedValue(incomingRequest.headers["x-forwarded-port"]),
112
- this.manifest.allowedDomains
113
- );
114
- const protocol = validated.protocol ?? (isHttps ? "https" : "http");
115
- 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;
116
109
  const origin = `${protocol}://${host}`;
117
110
  const url = new URL(origin + incomingRequest.url);
118
111
  let pathname;
@@ -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;
@@ -1,9 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import { fileURLToPath, pathToFileURL } from "node:url";
3
- import {
4
- createMarkdownProcessor,
5
- isFrontmatterValid
6
- } from "@astrojs/markdown-remark";
3
+ import { isFrontmatterValid } from "@astrojs/internal-helpers/frontmatter";
7
4
  import { safeParseFrontmatter } from "../content/utils.js";
8
5
  import { AstroError, AstroErrorData } from "../core/errors/index.js";
9
6
  import { isMarkdownFile, isPage } from "../core/util.js";
@@ -20,12 +17,12 @@ const astroErrorModulePath = normalizePath(
20
17
  fileURLToPath(new URL("../core/errors/index.js", import.meta.url))
21
18
  );
22
19
  function markdown({ settings, logger }) {
23
- let processor;
20
+ let renderer;
24
21
  return {
25
22
  enforce: "pre",
26
23
  name: "astro:markdown",
27
24
  buildEnd() {
28
- processor = void 0;
25
+ renderer = void 0;
29
26
  },
30
27
  resolveId: {
31
28
  filter: {
@@ -62,13 +59,17 @@ function markdown({ settings, logger }) {
62
59
  const rawFile = await fs.promises.readFile(fileId, "utf-8");
63
60
  const raw = safeParseFrontmatter(rawFile, id);
64
61
  const fileURL = pathToFileURL(fileId);
65
- if (!processor) {
66
- processor = createMarkdownProcessor({
67
- image: settings.config.image,
68
- ...settings.config.markdown
62
+ if (!renderer) {
63
+ const { markdown: md, image } = settings.config;
64
+ renderer = md.processor.createRenderer({
65
+ image,
66
+ syntaxHighlight: md.syntaxHighlight,
67
+ shikiConfig: md.shikiConfig,
68
+ gfm: md.gfm,
69
+ smartypants: md.smartypants
69
70
  });
70
71
  }
71
- const renderResult = await (await processor).render(raw.content, {
72
+ const renderResult = await (await renderer).render(raw.content, {
72
73
  fileURL,
73
74
  frontmatter: raw.frontmatter
74
75
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro",
3
- "version": "6.3.7",
3
+ "version": "6.4.0",
4
4
  "description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
5
5
  "type": "module",
6
6
  "author": "withastro",
@@ -39,7 +39,6 @@
39
39
  "./astro-jsx": "./astro-jsx.d.ts",
40
40
  "./tsconfigs/*.json": "./tsconfigs/*",
41
41
  "./tsconfigs/*": "./tsconfigs/*.json",
42
- "./jsx/rehype.js": "./dist/jsx/rehype.js",
43
42
  "./jsx-runtime": {
44
43
  "types": "./jsx-runtime.d.ts",
45
44
  "default": "./dist/jsx-runtime/index.js"
@@ -49,6 +48,7 @@
49
48
  "default": "./dist/jsx-runtime/index.js"
50
49
  },
51
50
  "./compiler-runtime": "./dist/runtime/compiler/index.js",
51
+ "./markdown": "./dist/markdown/index.js",
52
52
  "./runtime/*": "./dist/runtime/*",
53
53
  "./config": "./dist/config/entrypoint.js",
54
54
  "./container": "./dist/container/index.js",
@@ -164,9 +164,9 @@
164
164
  "xxhash-wasm": "^1.1.0",
165
165
  "yargs-parser": "^22.0.0",
166
166
  "zod": "^4.3.6",
167
- "@astrojs/internal-helpers": "0.9.1",
168
- "@astrojs/telemetry": "3.3.2",
169
- "@astrojs/markdown-remark": "7.1.2"
167
+ "@astrojs/internal-helpers": "0.10.0",
168
+ "@astrojs/markdown-remark": "7.2.0",
169
+ "@astrojs/telemetry": "3.3.2"
170
170
  },
171
171
  "optionalDependencies": {
172
172
  "sharp": "^0.34.0"
@@ -188,8 +188,6 @@
188
188
  "expect-type": "^1.3.0",
189
189
  "fs-fixture": "^2.13.0",
190
190
  "hono": "^4.12.14",
191
- "mdast-util-mdx": "^3.0.0",
192
- "mdast-util-mdx-jsx": "^3.2.0",
193
191
  "node-mocks-http": "^1.17.2",
194
192
  "parse-srcset": "^1.0.2",
195
193
  "rehype-autolink-headings": "^7.1.0",
@@ -1,5 +0,0 @@
1
- import type { RehypePlugin } from '@astrojs/markdown-remark';
2
- import type { VFile } from 'vfile';
3
- import type { PluginMetadata } from '../vite-plugin-astro/types.js';
4
- export declare const rehypeAnalyzeAstroMetadata: RehypePlugin;
5
- export declare function getAstroMetadata(file: VFile): PluginMetadata["astro"] | undefined;