Skip to content
TIANKAI XIE

GitHub Pull Requests

Optional build-time activity feed using astro-loader-github-prs

withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.41.2

Patch Changes

  • #4015 bdbfffc Thanks @delucis! - Fixes an issue where aside icons were rendered incorrectly in projects where Astro’s MDX integration had optimization disabled
withastro/astro

Changes

Fixes an error that could appear after the dev server restarts when using an adapter that reuses its dev runtime across restarts (e.g. @astrojs/cloudflare). A request would fail with a 500:

The file does not exist at "node_modules/.vite/deps_ssr/astro_compiler-runtime.js?v=6419660d" which is in the optimize deps directory. The dependency might be incompatible with the dep optimizer. Try adding it to `optimizeDeps.exclude`.
  • Root cause: on restart, vite-plugin-astro re-ran configEnvironment and pushed astro onto the carried-over resolve.conditions again, producing a duplicate. The duplicate changed the environment's optimizeDeps config hash, forcing a spurious dependency re-optimization on every restart. That re-optimization could leave the reused runtime importing pre-bundled files that had just been rewritten, producing the error above.
  • Fix: only add the astro condition when it isn't already present, keeping the config hash stable across restarts so no needless re-optimization is triggered.

Testing

  • Adds a vite-plugin-astro unit test asserting configEnvironment adds the astro condition exactly once when called repeatedly (simulating restart).

Docs

  • No docs update needed; this is an internal dev-server bug fix with no API change.
withastro/starlight

Description

  • Fixes #3967 (comment)
  • We were relying on a type: "html" AST node for icons in our Markdown aside plugin (which provides support for our :::note type syntax). This happens to work when the MDX integration is configured with optimize: true (perhaps accidentally based on chatting to @Princesseuh). Starlight enables optimization by default, but if a user manually adds mdx(), optimization can be disabled, breaking this behaviour.
  • The fix is to use a mdxJsxTextElement AST node when processing MDX files that represents <Fragment set:html={icon}>.
  • Sätteri doesn’t currently provide a way to know if a plugin is running against an MDX file or a regular Markdown file, so for now this PR uses a basic check of the file’s extension. Not sure if we’re happy to publish with this to get the bug fixed or wait on an official flag.
withastro/starlight

Description

This PR updates the Playwright version used in the monorepo to workaround a browser installation issue on Node.js 24 and Node.js 26 (reported on Discord)

Basically, looks like Node.js 24.16.0 bumped libuv which surfaced this early-exit issue which was fixed in microsoft/playwright#40747.

I confirmed the issue by running locally npx playwright uninstall --all and then pnpm test:e2e on Node.js 24.17.0 which hang indefinitely. After this change and running both commands again, the installation and tests run successfully.

withastro/astro

Fixes #16767

In React 19 dev mode, the check() renderer probe calls the component inside a Tester wrapper during SSR. When the component uses hooks, React emits an "Invalid hook call" warning because the hooks run in Tester's render context instead of the component's own.

The warning is a false positive — the component works correctly. This wraps the probe render with a targeted console.error suppressor that filters only the "Invalid hook call" message, leaving all other errors intact.

withastro/astro

Changes

Docs is ahead of us in Prettier version and it breaks our smoke tests

Testing

N/A, though smoke tests should now pass

Docs

N/A

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.5

Patch Changes

  • #17202 c6d254d Thanks @matthewp! - Refactors path alias resolution to use Vite's native tsconfigPaths option

    This is an internal change with no expected impact on user projects. Astro now defers tsconfig and jsconfig paths alias resolution to Vite, keeping a small fallback for a few CSS cases Vite does not yet handle.

  • #17123 72e29bd Thanks @martrapp! - Fixes an issue where the ClientRouter wipes head elements after page transitions if the <head> contains a server:defer component.

  • #17232 257505e Thanks @matthewp! - Fixes a bug where <style> tags from components such as a content collection's Content could be silently dropped from the output when an await appeared before the component in an .astro file's markup.

  • #17193 a7352fd Thanks @jan-kubica! - Fixes the background dev server failing to start when astro is hoisted outside the project's node_modules (for example bun workspaces). The background process is now spawned from Astro's own resolved location instead of a path assumed under the project root.

@astrojs/cloudflare@14.0.3

Patch Changes

  • #17236 c411200 Thanks @matthewp! - Prevents warnings in the Cloudflare adapter about optimizing the @astrojs/cloudflare/entrypoints/server module in dev.

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3
withastro/astro

Changes

  • Adds @astrojs/cloudflare/entrypoints/server to optimizeDeps.include for the server (ssr) environment so the worker entrypoint is pre-bundled during dev startup.
  • Previously it was referenced only via wrangler.jsonc's main, so Vite's dep scan never reached it and discovered it lazily on the first request — logging ✨ new dependencies optimized: @astrojs/cloudflare/entrypoints/server and triggering a re-optimization (and reload).

Testing

  • No automated test added; verified manually by running astro dev on the astro-dev-platform fixture with a cleared .vite cache and confirming the entrypoint now appears in deps_ssr/_metadata.json (pre-bundled up front) instead of being optimized lazily.

Docs

  • No docs update needed; this is an internal dev-server optimization with no user-facing API change.
withastro/astro

Changes

This PR restores two workflows:

  • Automatic closure for missing reproduction. I adapted the script I created in the biome repository to the Astro repository. The script works in the other repo, so it's tested (with the secrets we have)
  • Restore the "Wontfix" workflow using the gh CLI

Testing

Green CI

Docs

N/A

withastro/starlight

Description

This PR adds zizmor to make Starlight's GitHub actions more robust and follow best practice security recommendations.

I adapted all actions to comply with zizmor's basic rules and also created a .github/zizmor.yml configuration file to ignore specific warnings which we accept (like usages of pull_request_target triggers in two workflow files).

I also added a new .github/workflows/zizmor.yml action, which validates all actions to continue to follow best practices.

Warning

Additionally, I adapted a small logic in .github/workflows/preview-release.yml by removing an external action that can be replaced with a simple gh CLI command (suggested by zizmor as well).

To be honest, it would be nothing short of a miracle if all workflows worked on the first try.

withastro/starlight

Removed duplicate entry for '**/examples/' in the ESLint config.

withastro/starlight

Description

  • Closes #
  • What does this PR change? Give us a brief description.
  • Did you change something visual? A before/after screenshot can be helpful.

Adds Starlight MD3 to the community themes list.

This PR adds:

  • a new theme entry for Starlight MD3 in docs/src/content/docs/resources/themes.mdx
  • light and dark preview images for the theme
  • a link to the live demo site

Theme demo:
https://axiaobo7788.github.io/starlight-material-design-theme/

Theme source:
https://github.com/aXiaobo7788/starlight-material-design-theme

npm package:
https://www.npmjs.com/package/starlight-theme-md3

This is a docs/resources update only and does not change Starlight runtime behavior.

Testing

Not run in this fork. The change only adds a community theme listing and static preview assets.

The linked theme package was separately built, packaged, and published as starlight-theme-md3@0.1.3.

withastro/astro

Changes

  • Fixes <style> tags from propagating components (e.g. a content collection's Content) being silently dropped when an await appears before the component in slot markup.
  • Head propagation discovers these components by eagerly pre-rendering slots. An await in the slot suspended that pre-render before the component was reached, so it registered too late and its styles were never collected. Astro now awaits pending async slot pre-renders while collecting head content on routes that use propagation. Routes without propagation are unaffected and keep streaming.

Testing

  • Adds a content-collections integration test: a Content rendered after an await in slot markup, asserting its propagated stylesheet survives.
  • Adds unit tests for the head-collection loop covering an async slot that registers a propagator after an await, including a nested case.

Docs

  • No docs update needed — restores expected behavior that component styles are collected regardless of where await appears.

Closes #17218

withastro/astro

Reverts #17228. I have a better fix for #17218 coming in a follow-up.

withastro/astro

Summary

Fix high severity security issue in packages/astro/src/core/session/handler.ts.

Vulnerability

Field Value
ID V-003
Severity HIGH
Scanner multi_agent_ai
Rule V-003
File packages/astro/src/core/session/handler.ts:45
Assessment Likely exploitable
Chain Complexity 2-step

Description: The session handler accepts attacker-provided session identifiers, allowing session fixation attacks where an attacker sets a user's session ID before login and hijacks the session after authentication.

Evidence

Exploitation scenario: Send malicious link with pre-set session ID parameter to user, then wait for user to authenticate and hijack their session.

Scanner confirmation: multi_agent_ai rule V-003 flagged this pattern.

Production code: This file is in the production codebase, not test-only code.

Threat Model Context

This is a Node.js library - vulnerabilities affect downstream consumers who use this package.

Changes

  • packages/astro/src/core/session/runtime.ts

Verification

  • Build passes
  • Scanner re-scan confirms fix
  • LLM code review passed

Security Invariant

Property: Protected endpoints reject unauthenticated requests

Regression test
import { createSessionHandler } from 'packages/astro/src/core/session/handler';

describe('Protected endpoints reject unauthenticated requests', () => {
  const payloads = [
    { description: 'missing token', token: null },
    { description: 'expired token', token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzAwMDAwMDB9.invalid' },
    { description: 'malformed token', token: 'malformed.token.here' },
    { description: 'empty token', token: '' },
    { description: 'valid token', token: 'valid.token.here' }
  ];

  test.each(payloads)('rejects adversarial input: $description', async ({ token }) => {
    const handler = createSessionHandler();
    const mockRequest = {
      headers: {
        authorization: token ? `Bearer ${token}` : undefined
      }
    };
    const mockResponse = {
      status: jest.fn().mockReturnThis(),
      json: jest.fn()
    };

    await handler(mockRequest as any, mockResponse as any);

    if (token !== 'valid.token.here') {
      expect(mockResponse.status).toHaveBeenCalledWith(401);
    } else {
      expect(mockResponse.status).not.toHaveBeenCalledWith(401);
    }
  });
});

This test guards against regressions — it's useful independent of the code change above.


Automated security fix by OrbisAI Security

withastro/astro

Changes

  • Fixes a race condition where <style> tags from propagating components (e.g. content collection Content with propagation: 'self') were silently dropped from the build output when await appeared in the template/markup section of an .astro file. Moving the await to the frontmatter was the only workaround.
  • The root cause: AstroComponentInstance pre-renders slots eagerly to trigger propagator registration, but when a slot function is async it returns a Promise. bufferPropagatedHead ran before the Promise resolved, collecting zero propagators and dropping their styles.
  • Fix: async slot pre-render Promises are tracked on a new pendingSlotEvaluations field of SSRMetadata. bufferPropagatedHead awaits them all before collecting head parts.

Closes #17218

Testing

  • Added 'awaits pending async slot evaluations before collecting head parts' to packages/astro/test/units/render/head-propagation/runtime.test.ts. It pushes a delayed Promise that registers a propagator, then verifies bufferPropagatedHead waits for it and collects the resulting style tag.
  • Updated runtime-adapters.test.ts and server-islands-render.test.ts fixtures to include the new pendingSlotEvaluations field on SSRMetadata.

Docs

No docs update needed — this is a bug fix restoring already-documented behavior (styles from components should always be collected regardless of where await appears).

withastro/astro

Summary

The core SSR request path parses request.url and rebuilds a few small things more often than it has to. This cuts that repeated work on the hot path. Nothing about the rendered output changes, and no public API moves; it's all internal.

This started as a bigger PR and was trimmed after review. The cross-adapter URL sharing (a new RenderOptions.url option) and the createOutgoingHttpHeaders rewrite are out, and the adapter side will come back on its own as a minor. What's left here is internal to astro core and stays a patch.

What changed

  • Domain-based i18n only looks at the Host header when a domains-* strategy is configured. That condition is now worked out once in the constructor, so every other app skips the probe, and the URL parse behind it, in both match() and render().
  • TrailingSlashHandler uses the raw pathname and search that FetchState captures before it normalizes the URL, instead of parsing request.url a second time to get them back.
  • getParams checks the route's own pattern and then its fallback routes, stopping at the first match, rather than building throwaway arrays with .map().map().find().
  • The in-memory cache provider reuses the URL already sitting on context.url (and checks the request method first) instead of parsing context.request.url again.
  • Domain-based i18n parses each domainLookupTable key once and keeps it per table, instead of re-parsing every key on every request.

Testing

No behavior change, so nothing new to test. The paths above are covered by the existing unit suites (routing, i18n, app, cache, fetch), which pass, and the typecheck is clean.

Benchmarks

Micro-benchmarks, median of 7 runs on the same machine, for the changes that stayed in this PR:

Before After
Domain i18n lookup (4 domains) 1331 ns 285 ns
Cache request (memory provider) 474 ns 243 ns
getParams matching 54 ns 40 ns

On a render-heavy page this is lost in the noise, since HTML rendering dominates. It shows up more on light responses like endpoints, redirects and 304s, where per-request setup is a bigger slice of the work.

Investigated and measured with help from Claude Opus 4.8.

withastro/astro

Changes

  • Inline .astro <style> blocks now respect vite.build.target (and cssTarget) even when vite.build.minify / cssMinify is false. Previously, the @astrojs/compiler-rs compiler used lightningcss internally for CSS scoping and inadvertently modernized CSS syntax (e.g. min-width: 1000pxwidth >= 1000px). With minification enabled, Vite's minifyCSS step would reverse this via target-based lowering. With minification disabled, that lowering was skipped entirely, leaving modernized syntax in the output regardless of the configured target.
  • Adds a lowerCssToTargets() step in compile.ts that runs lightningcss with minify: false and the user's configured targets after the compiler returns CSS, but only when command === 'build', cssMinify is falsy, and a cssTarget is set. The target string conversion (esbuild/Vite format → lightningcss format) mirrors Vite's internal convertTargets logic.

Closes #17225

Testing

  • Adds packages/astro/test/config-vite-css-target-no-minify.test.ts with a new fixture (config-vite-css-target-no-minify/) targeting safari14 with minify: false. Covers: MQ range syntax is not used (min-width preserved), and inset shorthand is not used (longhand top/right/bottom/left preserved).

Docs

  • No docs update needed — this is a bug fix restoring already-documented behavior for an existing config option.

Note: A changeset has not been added to this PR yet. One should be added before merge (patch bump for astro).

withastro/astro

Changes

  • Dynamic file endpoints like src/pages/api/[name].json.ts with trailingSlash: "always" now correctly return 200 for /api/bar.json and 404 for /api/bar.json/ in dev mode, matching production behavior and the documented Astro v6 rule that file endpoints are never served with a trailing slash.
  • Root cause: trailingSlashForPath checked pathname && hasFileExtension(pathname), but pathname is null for dynamic routes (any segment with a param). The fix adds an optional route fallback parameter — the string from joinSegments — which preserves file extensions even for dynamic segments (e.g., /api/[name].json), allowing the file-extension check to work correctly.

Testing

  • Added 'dynamic file endpoints force trailingSlash never. issues#17001' in packages/astro/test/units/routing/manifest.test.ts: creates a [name].json.ts page under trailingSlash: "always" and asserts the generated route pattern matches without a trailing slash and rejects with one.

Docs

  • No docs update needed — this restores already-documented Astro v6 behavior.

Closes #17001

withastro/astro

Changes

  • getCollection() in dev mode silently returned an empty array for content collections with ~500k+ entries. The virtual module for the data store used dataToEsm() to serialize the store as a JS object literal, which for large collections produces ~7.5MB of JS source. Vite's ssrTransformScript passed this to rolldown/oxc-parser's parseAstAsync(), whose resulting AST was too large to cross the NAPI bridge — throwing "Failed to convert rust String into napi string", silently caught in ImmutableDataStore.fromModule(), returning an empty store.
  • Replaced dataToEsm(parsed, { compact: true }) with export default JSON.parse(${JSON.stringify(jsonData)}). This embeds the data as a single string literal, keeping the AST tiny regardless of collection size. As a bonus, JSON.parse() of a string literal is faster to evaluate than a large object literal. Removed the now-unused dataToEsm import.

Testing

  • No new tests added — generating 500k entries in CI is too slow. All existing content collection tests (59/59 content-collections, 64/64 content-layer unit tests) continue to pass.

Docs

  • No docs update needed; this is a dev-mode bug fix with no API changes.

Closes #17220

withastro/starlight

Removed the LinkCard for 'starlight-utils' from the documentation as I no longer have time to maintain this (and it's very out of date). In the documentation site I offer some alternatives for the functionality that this package included.

withastro/astro

Changes

  • Warms up the dev server before the experimental client prerender prefetch tests run.
  • Avoids racing immediate page assertions against Vite dev reloads on Windows.

Testing

  • Updates the existing prefetch E2E setup for the client prerender block.

Docs

  • No docs needed; test-only change.
withastro/astro

Changes

  • resolveTargetVersion() now compares the dist-tag version against the currently installed version before applying it. If the dist-tag resolves to an older version than what's installed, the function falls back to latest instead of downgrading.
  • Fixes the case where pnpm dlx @astrojs/upgrade beta would silently downgrade companion packages (e.g. @astrojs/sitemap from 3.7.33.6.1-beta.3) when their beta dist-tag pointed to a pre-release that predated the current stable.
  • resolveTargetVersion is now exported so it can be unit-tested directly.

Testing

  • Added describe('resolveTargetVersion') in packages/upgrade/test/verify.test.ts with three cases: no-downgrade when the beta dist-tag is older than the installed version; correct upgrade when the beta dist-tag is newer; and fallback to latest when the requested dist-tag doesn't exist on the registry.

Docs

  • No docs update needed; this is a bug fix for internal @astrojs/upgrade behavior with no API surface change.

Closes #17024

withastro/astro

Changes

  • Clarifies that RouteCache provides a shared static-path table for matching, props resolution, and prerender generation.
  • Documents why dev request handling does not clear the route cache per request.

Testing

  • Not run; comment-only change.

Docs

  • No user-facing docs update needed; this only documents an internal invariant.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.4

Patch Changes

  • #17212 7ba0bb1 Thanks @matthewp! - Ensures transition directive values are HTML-escaped when rendered on hydrated islands

  • #17224 dc5e52f Thanks @astrobot-houston! - Fixes trailing slash handling for dynamic file endpoints in dev mode. Dynamic file endpoints (e.g., src/pages/api/[name].json.ts) with trailingSlash: "always" incorrectly required a trailing slash in dev mode, returning 404 for /api/bar.json and 200 for /api/bar.json/.

  • #17067 23f9446 Thanks @fkatsuhiro! - Fixed a bug where the development toolbar did not output a warning even though the implicit ARIA role and the manually specified role were duplicated.

  • #17234 d5fbee8 Thanks @ocavue! - Adds support for sharp v0.35. pnpm users no longer need to approve sharp's build script (see allowBuilds) when on v0.35.

  • #17223 5970ef4 Thanks @astrobot-houston! - Fixes getCollection() returning empty in dev mode for large content collections (500k+ entries)

  • #17184 799e5cd Thanks @Princesseuh! - Upgrades the Rust compiler to the latest, which fixes some bugs. Refer to its changelog for more information.

  • #17208 da8b573 Thanks @matthewp! - Hardens forwarded header handling so the internal request helper validates X-Forwarded-Host against security.allowedDomains before trusting X-Forwarded-For for clientAddress. Previously it only checked that the header was present, which was inconsistent with the public createRequest helper. This aligns both code paths; behavior is unchanged for correctly configured proxies.

@astrojs/rss@4.0.19

Patch Changes

  • #17209 fbcfa03 Thanks @matthewp! - Hardens RSS feed generation by escaping the source and enclosure item fields. These fields are now serialized as structured XML values, ensuring that special characters in values like source.title and enclosure.type are always treated as text rather than markup, consistent with how other feed fields are handled.

create-astro@5.2.1

Patch Changes

@astrojs/cloudflare@14.0.2

Patch Changes

  • #17049 ffceaa2 Thanks @astrobot-houston! - Fixes prerender errors being silently swallowed when pages throw during rendering in workerd, causing astro build to exit 0 and emit truncated HTML. The response body is now fully buffered inside workerd before being sent back to the build process, so streaming errors are caught and surfaced as build failures with clear error messages.

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3
withastro/astro

Changes

  • HTML-escapes transition directive values before copying them onto hydrated island attributes.
  • Keeps transition attributes on the island wrapper while preserving the existing render path.

Testing

  • Adds hydration unit coverage that renders generated island HTML and asserts transition directive attribute values are encoded.

Docs

  • No docs update needed; this aligns internal island attribute rendering with existing escaping behavior.
withastro/astro

Changes

Needed for smoke tests

Testing

Smoke tests should pass!

Docs

N/A

withastro/astro

Changes

  • Builds the source and enclosure item elements as structured XML objects instead of interpolating values into a string and re-parsing them. This ensures special characters in fields like source.title and enclosure.type are always serialized as text, consistent with how every other feed field is already handled.

Testing

  • Adds two cases covering source and enclosure fields containing XML special characters, asserting the values round-trip verbatim and produce exactly one element each.

Docs

  • No docs update needed; this is an internal serialization change with no API surface change.
withastro/astro

Changes

  • Aligns the internal createRequestFromNodeRequest helper with the public createRequest: X-Forwarded-For is only trusted for clientAddress when X-Forwarded-Host actually matches security.allowedDomains, instead of just checking that the header is present.
  • No behavior change for correctly configured proxies; this only tightens consistency between the two request helpers.

Testing

  • Adds a createRequestFromNodeRequest test block covering: trusted Host, trusted X-Forwarded-Host, a non-matching X-Forwarded-Host (now falls back to the socket address), and the no-allowedDomains case.

Docs

  • No docs update needed; allowedDomains behavior is unchanged from a user's perspective.
withastro/starlight

Description

  • What does this PR change? Give us a brief description.
    Hey everyone! 👋 I recently spent a couple of weeks building a local-first browser editor for Starlight called Axiom Studio. I originally built it to make my own MDX and frontmatter editing workflow easier and safer without needing any external CMS or database.

Thought it might be useful for other indie makers and developers in the Astro ecosystem, so I added it to the community tools list in plugins.mdx. (I also included a short demo video on the link if you're curious to see how the local editing actually works in action). Let me know if the format looks good!

  • Did you change something visual? A before/after screenshot can be helpful.
    Nope, no visual changes to the docs themselves. Just added the new <LinkCard> to the bottom of the grid.
withastro/astro

Closes #17204
Closes #17091

Changes

  • Adds an ensurePnpmBuildsAllowed() helper that writes allowBuilds entries for esbuild and sharp into pnpm-workspace.yaml before running pnpm install. This mirrors the existing ensureYarnLock() pattern for Yarn Berry.
  • pnpm v11.0.0 set strictDepBuilds to true by default, causing pnpm install to exit non-zero when any dependency has an unapproved build script. Both esbuild and sharp (Astro dependencies) have postinstall scripts, so pnpm create astro would fail with ERR_PNPM_IGNORED_BUILDS and leave node_modules uninstalled.
  • If allowBuilds is already present in pnpm-workspace.yaml, the helper skips writing to avoid overriding user configuration.

Testing

  • No new tests added — the helper runs during the live pnpm install call and is difficult to unit test in isolation. All 120 existing tests continue to pass.
  • Confirmed end-to-end by issue reporter (@jettwayio): npx create-astro --template starlight now creates node_modules successfully with pnpm v11.

Docs

  • No docs update needed — this is an internal scaffolding fix with no user-facing API change.

Note: A changeset for create-astro is needed before merging.

withastro/astro

Changes

  • Enables Vite's native resolve.tsconfigPaths by default.
  • Keeps Astro's custom alias plugin as a documented deprecated fallback for the cases it already supports.
  • Adds comments clarifying the fallback CSS/module support and future removal intent.

Testing

  • No new tests; existing alias coverage verifies the fallback behavior remains intact.

Docs

  • No docs update needed because this changes internal resolution defaults.

Related #17163

withastro/astro

Changes

This removes a lot of deps from Astro because rehype and co are quite heavy for just doing this. We already use ultrahtml elsewhere, so why not here.

Testing

Tests should pass

Docs

N/A

withastro/astro

It is better to merge #17179 first (while it does change only the remaining twitter:* meta tags).

Changes

  • Corrected the twitter:card meta tag to use name instead of property. Resource
  • Applied the fix in the blog component and matching e2e fixtures.

Testing

  • Verified generated HTML no longer includes the Twitter meta tags as property but name.
  • Confirmed remaining Twitter metadata continues to render correctly.

Docs

  • No docs changes needed.

Resources

Is there a resource or something that states that those tags are redundant? You should provide those resources together with the PR

Normally the link I'd have shown would've been: https://developer.x.com/en/docs/twitter-for-websites/cards/overview/markup
But it doesn't work because they've removed it and one cannot find it anywhere.

But Wayback Machine comes to help:

As you can see for twitter:* meta tags, name is used.

<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@">
<meta name="twitter:creator" content="@">
<meta property="og:url" content="">
<meta property="og:title" content="">
<meta property="og:description" content="">
<meta property="og:image" content="">
withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.41.1

Patch Changes

withastro/starlight

Description

  • Closes #3987
  • Updates Expressive Code to the new release that supports Astro 7
withastro/astro

Changes

  • The background dev server (auto-enabled for agentic environments) fails to start when astro is hoisted outside the project's node_modules (for example bun workspaces). The foreground server is unaffected.
  • cli/dev/background.ts spawns resolve(rootPath, 'node_modules', 'astro', 'bin', 'astro.mjs'), which doesn't exist in a hoisted layout, so the detached child exits with ERR_MODULE_NOT_FOUND and the parent reports Dev server process exited before becoming ready.
  • Fix: resolve the bin from Astro's own location (astro/package.json../bin/astro.mjs) so it spawns itself regardless of install layout.

Same family as #17135 / #17190.

Testing

Manual: an Astro app in a bun workspace with hoisted deps, background mode. Before: Dev server process exited before becoming ready (.astro/dev.log: Module not found …/node_modules/astro/bin/astro.mjs). After: starts normally.

Docs

None — restores existing behavior in monorepos.

withastro/astro

Changes

Hardens a flaky e2e test: Dev Toolbar - Audits › can warn about perf issue for below the fold image in absolute container.

The audit gates highlight creation on the image's load event (apps/audit/index.ts awaits load so the above/below-the-fold position checks have accurate layout). The test clicked the audit button and immediately asserted toHaveCount(1) with the default 6s expect timeout. When the optimized image loads slowly (e.g. a contended CI runner), the highlight is not created in time and the assertion intermittently sees 0 highlights, failing every retry.

The test now waits for the image to finish loading before triggering the audit, so the assertion no longer races the image load. No production code changes.

This flaked (all 3 retries) on unrelated CI, e.g. https://github.com/withastro/astro/actions/runs/28129089505/job/83301140796

Testing

  • pnpm --filter astro exec playwright test dev-toolbar-audits.test.ts runs 11 passing, executed twice for stability.
  • The targeted test passes locally. The change only adds a wait before the audit runs, so it cannot make the assertion less reliable.

Docs

No docs changes; this is test-only. No changeset, since there are no published package changes.

withastro/astro

Changes

Custom Markdoc transform functions were being dropped for tags and nodes whose names need bracket access, like side-note.

Here is why. When you set a custom render component, resolveComponentImports removes the transform unless that transform "respects" render (issue #9708, added in #15335). It worked that out by string-matching the transform's source for config.tags?.<key>?.render or config.nodes?.<key>?.render, which only covers dot notation.

A key like side-note can't be read with dot notation at all (config.tags?.side-note?.render parses as a subtraction), so those transforms have to use bracket notation: config.tags?.['side-note']?.render. The old check never matched that, so the transform was always treated as not respecting render and deleted, which broke the tag.

The detection now handles bracket notation alongside dot notation, plus optional chaining and whitespace. Includes a changeset (@astrojs/markdoc patch).

Fixes #17118.

Testing

Added packages/integrations/markdoc/test/resolve-component-imports.test.ts, a unit test over the exported resolveComponentImports:

  • a render-respecting transform on a dashed tag (side-note, via bracket access) is kept;
  • a transform that does not respect render is still removed so render wins.

I checked it both ways: the first case fails on main (transform deleted) and passes with this change. The full package suite is green too. pnpm --filter @astrojs/markdoc test runs 50 tests, including the existing render-with-transform test for #9708. tsc -b, biome check, prettier --check, and eslint are all clean on the changed files.

Docs

No docs changes. This restores the documented behavior (a custom render together with a custom transform) for tag and node names that contain dashes, and there is no public API change.

withastro/astro

Changes

  • astro check (and any command that auto-installs dependencies through getPackage) failed to find typescript and @astrojs/check when Astro itself is installed outside the project directory (e.g. a pnpm global/virtual store, or a monorepo where the store isn't under the project).
  • Root cause: getPackage resolved the dependency with require.resolve(pkg, { paths: [cwd] }) but then loaded it with a bare import(pkg). A bare specifier resolves relative to Astro's own location, not the project, so the import threw ERR_MODULE_NOT_FOUND even though the package existed in the project.
  • Fix: import the resolved absolute path via pathToFileURL(...), so dependencies are loaded from the project directory. The same fix is applied to the post-install import path. Resolving first with require.resolve is preserved (it also avoids caching a failed ESM import before installing).
  • Added a changeset (astro patch).

Fixes #17135.

Note: the root cause was already diagnosed by @astrobot-houston in the issue, and @matthewp verified the same fix via the pkg.pr.new preview (astro@9a53f77). Since the previous fix branch was lost, this PR provides a ready implementation with a regression test.

Testing

  • Added packages/astro/test/units/cli/install-package.test.ts: installs a dependency only inside a temporary project's node_modules (outside Astro's resolution scope) and asserts getPackage loads it from the provided cwd. This fails on the old bare-import behavior and passes with the fix.
  • pnpm --filter astro exec astro-scripts test "test/units/cli/install-package.test.ts" --strip-types1 passing.
  • biome check, prettier --check, and eslint pass on the changed files.

Docs

No docs changes needed — this restores the documented behavior (resolving dependencies from the user's project) without changing any public API.

withastro/astro

Changes

  • In dev mode, when multiple routes match a URL pattern, an error thrown inside one route's getStaticPaths() no longer prevents other candidate routes from being tried. Previously, any non-routing error was immediately re-thrown, causing valid sibling routes to return a 500 instead of their correct response.
  • The fix stores the first non-routing error and defers re-throwing it until all candidates are exhausted without a match, so the error still surfaces when there's genuinely no valid route.

Closes #9819

Testing

  • Added packages/astro/test/units/routing/dev-match-fallthrough.test.ts with two cases: one verifying that route B is matched when route A's getStaticPaths throws, and one verifying the error is still re-thrown when no valid alternative exists.

Docs

  • No docs update needed; this is a dev-mode bug fix with no API or user-facing behavior change beyond the bug itself.
withastro/astro

Changes

  • When pnpm's minimumReleaseAge policy blocks an install, @astrojs/upgrade now shows a clear message explaining that pnpm's policy is the cause, instead of the generic "Dependencies failed to install" message.
  • Fixes shell.ts falling back to stdout when stderr is empty on failure. pnpm writes its MINIMUM_RELEASE_AGE_VIOLATION error to stdout, so the previous code discarded it entirely and threw a meaningless "Process exited with code 1" error.

Testing

  • Adds 'pnpm minimumReleaseAge error shows specific message' in packages/upgrade/test/install.test.ts: verifies that a MINIMUM_RELEASE_AGE_VIOLATION shell error surfaces the minimumReleaseAge message and suppresses the generic fallback.

Docs

  • No docs update needed — this is a CLI error message improvement with no API or configuration surface change.

Closes #17182

withastro/astro

Changes

  • Adds astro/virtual-modules/transitions.js to the Cloudflare adapter's server optimizeDeps.include list. This prevents Vite from discovering the module mid-request during cold SSR, which was triggering an optimizer reload that invalidated React's hook dispatcher and caused "Invalid hook call" / useContext null errors when using <ClientRouter />.

Testing

  • No new tests — the bug only manifests with npm-installed packages (not workspace-linked), so it cannot be reproduced in the monorepo's test suite. The reporter confirmed the fix works in their minimal repro.

Docs

  • No docs update needed — this is an internal dev-server reliability fix.

Closes #17166

withastro/astro

Changes

  • This reverts a patch to changesets added in #15813
  • It is no longer needed and causes a turborepo warning (and perhaps we could look at whether it should be upstreamed)

Testing

n/a — dependency change only, existing tests should pass, as should our next release

Docs

n/a

withastro/astro

Changes

  • Adds a --no-ai flag to create astro, so users can opt out of creating AGENTS.md and CLAUDE.md files if they don’t need them.
  • Avoids people having to manually clean up after running create astro.

Testing

Added a small test to make sure the CLI flag is interpreted correctly.

Docs

AFAIK we only document the full list of flags in the package README, so I’ve updated that. That’s what’s linked in the docs here: https://docs.astro.build/en/install-and-setup/#cli-installation-flags
/cc @withastro/maintainers-docs for feedback!

withastro/astro

Summary

  • update Issue Triage to withastro/triagebot-action v0.3.8
  • picks up restored preview release publishing and expanded triage logging

Tests

  • Not run (workflow pin update only)
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

create-astro@5.2.0

Minor Changes

  • #17185 d64b09b Thanks @delucis! - Adds a --no-ai flag to allow users to opt out of creating AGENTS.md and CLAUDE.md files when running create astro

astro@7.0.3

Patch Changes

  • #17189 24d2c9e Thanks @astrobot-houston! - Fixes a bug where an error thrown inside one route's getStaticPaths() would prevent other valid routes from being matched in dev mode

  • #16932 8f4a3db Thanks @fkatsuhiro! - Fixes HMR for action files during development. Editing files in src/actions/ now takes effect on the next request without requiring a dev server restart.

  • #17087 fb0ab02 Thanks @jp-knj! - Fixes localized custom error pages in i18n projects so routes like /pt/404 are used for missing localized pages and return the correct status code

@astrojs/cloudflare@14.0.1

Patch Changes

  • #17175 7a7d879 Thanks @astrobot-houston! - Fixes astro dev OOM crashes for @astrojs/cloudflare users on Vite 8 by migrating the frontmatter scan plugin to Rolldown-compatible options.

  • #17187 0db4b57 Thanks @matthewp! - Fixes React invalid hook warning during cold SSR optimizer reload when using ClientRouter

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/markdoc@2.0.1

Patch Changes

@astrojs/upgrade@0.7.3

Patch Changes

  • #17188 675d11d Thanks @astrobot-houston! - Fixes @astrojs/upgrade showing a generic error when pnpm's minimumReleaseAge policy blocks installation. The error message now explains that pnpm's policy blocked the update and suggests running the install command manually.
withastro/astro

Summary

  • update issue triage workflow to triagebot-action v0.3.7
  • v0.3.7 adds Flue event logging so triage runs show thinking/tool/log progress in Actions

Testing

  • git diff --check
withastro/astro

Changes

  • Fixes astro dev OOM crashes for @astrojs/cloudflare users on Vite 8. The adapter's .astro frontmatter scan plugin was registered via optimizeDeps.esbuildOptions.plugins, which Vite 8 deprecates and ignores. Without the plugin, Vite's HTML scanner misparses .astro frontmatter comments containing backticks (e.g. // Use a `<script>` tag), causing a Rolldown PARSE_ERROR that skips pre-bundling entirely and can OOM the workerd isolate at startup.
  • Adds a Rolldown-compatible frontmatter scan plugin (rolldown-plugin-astro-frontmatter.ts) and switches the configEnvironment hook to register it via optimizeDeps.rolldownOptions. Also removes the banner: { js: '' } workaround, which was esbuild-specific and no longer needed.

Closes #17168

Testing

  • No new tests added — existing suites (ssr-deps, top-level-return, ts-astro-import, user-optimize-deps) already cover the frontmatter scan plugin behavior and all pass with the new Rolldown plugin.

Docs

  • No docs update needed; this is a bug fix with no user-facing API changes.
withastro/astro

Summary

  • update issue triage workflow to triagebot-action v0.3.6
  • v0.3.6 fixes verified-fix PR creation for legacy flue/fix-* branches and delays the issue verified label until PR creation succeeds

Testing

  • git diff --check
withastro/astro

Summary

  • update issue triage workflow to triagebot-action v0.3.5
  • v0.3.5 adds mocked triage-flow integration coverage in triagebot-action

Testing

  • git diff --check
withastro/astro

Summary

  • update issue triage workflow to triagebot-action v0.3.3
  • v0.3.3 fixes the bundled action syntax error from duplicate createRequire declarations
  • v0.3.3 also adds integration tests that run the built action entrypoint and standalone Flue session initialization

Testing

  • git diff --check
withastro/astro

Summary

  • update issue triage workflow to use triagebot-action v0.3.2
  • v0.3.2 fixes Flue session initialization now that the triage code runs as a standalone GitHub Action instead of under flue run

Testing

  • git diff --check
withastro/astro

Summary

  • update issue triage workflow to use triagebot-action v0.3.1
  • v0.3.1 fixes hyphenated action input parsing for inputs like read-token

Testing

  • git diff --check
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.2

Patch Changes

  • Updated dependencies [3b5e994]:
    • @astrojs/markdown-satteri@0.3.2

@astrojs/markdown-satteri@0.3.2

Patch Changes

  • #17165 3b5e994 Thanks @Princesseuh! - Fixes headings being listed twice in a page's headings metadata when an integration (such as Starlight) assigns heading IDs with its own heading pass before adding anchor links
withastro/astro

Changes

That plugin can run twice in some situations, so it shouldn't blindly append to data

Testing

Added a test

Docs

N/A

withastro/astro

For withastro/roadmap#1381

Changes

  • Remove obsolete <meta http-equiv="X-UA-Compatible" content="IE=edge"> tags from Astro examples, fixtures, and templates.
  • Keep the rest of the head markup unchanged.
  • Simplify HTML output and remove legacy IE-only boilerplate.

Testing

  • Updated the affected fixtures and example files.
  • Verified the change is limited to removing the legacy meta tag.

Docs

  • No docs update needed.
  • This change removes a legacy browser compatibility tag that is no longer useful for modern browser output.
withastro/astro

Changes

  • Adds a new createIslandSignal function exported from @astrojs/solid-js/signals that lets users create signals in .astro frontmatter and pass them to multiple client:* Solid components. All islands sharing the same signal get a single reactive instance on the client, so updating it in one island updates all others.
  • Signals are detected on the server via Symbol-based branding (Solid signal getters are plain functions with no intrinsic identity, unlike Preact signals which are duck-typeable objects). The server serializes a data-solid-signals JSON attribute mapping prop names to shared signal IDs ("sg0", "sg1", etc.). On the client, a module-level sharedSignalMap creates or reuses createSignal tuples by ID.
  • Supports getter props, setter props (encoded with a ! suffix on the signal ID), and signals nested inside arrays or objects.
  • Signal values remain callable during SSR (unlike Preact, where .peek() scalars replace signals before render). Props are replaced with scalar values only after rendering, before Astro's serializeProps runs.

Testing

  • packages/integrations/solid/test/signals.test.ts — Integration tests verifying data-solid-signals serialization: shared signals between islands get the same ID, array signals produce [id, index] tuples, object signals produce [id, key] tuples, and SSR output contains correct values.
  • packages/astro/e2e/solid-signals.test.ts — E2E tests verifying signals hydrate with the correct initial value and that clicking a button in one island reactively updates a shared signal displayed in another island.

Docs

  • Docs update needed to document the new @astrojs/solid-js/signals export and createIslandSignal API. Not included in this PR.
withastro/astro

Changes

Fixes an issue where using --background --host didn't take the new URLs into consideration.

Testing

Added new tests

Docs

No changes. The feature should be supported out of the box.

withastro/astro

Closes #17156

Changes

  • Tsconfig path aliases in CSS url() calls (e.g. background-image: url('@assets/ok.png')) now resolve correctly during build, restoring behavior from Astro 6.
  • The astro:tsconfig-alias-css pre-transform plugin previously only rewrote @import specifiers. Added a cssUrlRE regex to also rewrite url() references before Vite processes them. In Vite 8, CSS url() resolution does not go through user resolveId hooks, making the pre-transform rewrite the only viable approach.

Testing

  • New fixture and test (alias-css-url.test.ts) covering the regression: a <style> tag using url('@assets/ok.png') with a tsconfig @assets/* alias, verified to produce a resolved asset path in the build output.

Docs

  • No docs update needed; this restores existing documented behavior for tsconfig aliases in CSS.
withastro/astro

Changes

The job wasn't working properly anymore, and I believe the cause was the concurrency part.

Testing

Green CI

Docs

withastro/astro

Changes

With the upgrade to Astro 7 I ran into:

[sass] Error: Can't find stylesheet to import.
    ╷
  2 │     @import "@styles/_variables.scss";

in .astro files that have <style lang="scss">.

This extends the workaround fix for aliases.

Testing

Tested it by changing this locally.
Extended the css test case to also test scss imports.

Docs

No doc updates needed, just restores previous behavior.

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.1

Patch Changes

  • #17151 ccceda3 Thanks @matthewp! - Fixes astro dev incorrectly starting in background mode for Warp terminal users. Hybrid environments like Warp are no longer treated as AI agents for auto-background detection.

  • #17158 164df87 Thanks @ematipico! - Fixes astro dev --background --host not listing the network addresses. The background server start output and astro dev status now show every exposed network URL, matching the foreground dev server.

  • #17141 d785b9d Thanks @astrobot-houston! - Fixes responsive image CSS overriding user styles defined inside CSS @layer blocks. The generated image styles are now wrapped in @layer astro.images, ensuring they have lower cascade priority than user-defined layers.

  • #17150 1a61386 Thanks @matthewp! - Fixes astro dev --background failing on Windows with "Failed to spawn background dev server process"

withastro/astro

Changes

  • Switches agent detection in astro dev from isAgent() to detectAgenticEnvironment().type === "agent", excluding "hybrid" environments from auto-background mode. isAgent() returns true for both "agent" and "hybrid" types, which causes false positives in terminals like Warp that set TERM_PROGRAM=WarpTerminal.
  • Bumps am-i-vibing from ^0.3.0 to ^0.4.0 which classifies Warp as "hybrid" and includes the latest provider list.

Reported in ascorbic/am-i-vibing#89 and Discord.

Testing

  • No test changes. The fix is a one-line predicate change in isRunByAgent() and there are no existing tests for agent detection.

Docs

  • No docs update needed. This is a bug fix to existing behavior.
withastro/astro

Changes

  • Spawns process.execPath (node) directly with node_modules/astro/bin/astro.mjs instead of going through the node_modules/.bin/astro shim. On Windows, npm/pnpm/yarn create .cmd batch file shims in .bin/ that cannot be executed by child_process.spawn() without shell: true, causing spawn() to fail and child.pid to be undefined.

Closes #17146

Testing

  • No new tests. The spawn codepath in background.ts has no integration test coverage on any platform — existing tests only cover pure formatting/parsing helpers.

Docs

  • No docs changes needed. This is an internal bug fix; the --background flag API is unchanged.
withastro/astro

Changes

  • Fixes a TypeError: node.children.map is not a function crash when using @astrojs/markdoc with the shiki extension and placing a fenced code block inside a list item.
  • The root cause is an async/sync mismatch: the shiki fence transform is async, so Markdoc's internal transformChildren() returns a Promise when a code fence is nested in a list. Markdoc's built-in list node has a custom transform() that passes this Promise directly to new Tag() without awaiting it, leaving Tag.children as an unresolved Promise. createTreeNode() in TreeNode.ts then crashes calling .map() on it.
  • Fixed by adding await Promise.resolve(node.children) in createTreeNode() before calling .map(), which defensively resolves any Promise-valued children. Synchronous case is unaffected (resolving a non-Promise is a no-op).

Closes #17126

Testing

  • Added a test case in packages/integrations/markdoc/test/syntax-highlighting.test.ts that renders a .mdoc file with a shiki-highlighted code fence nested inside a list item, confirming the page builds without error and the highlighted code is present in the output.

Docs

  • No docs update needed — this is a bug fix for an internal rendering crash with no API changes.
withastro/astro

Closes #17139

Changes

  • Wraps all CSS generated by generateImageStylesCSS() in @layer astro.images { … }. Per the CSS cascade spec, unlayered styles always beat layered styles regardless of specificity — so the previous unlayered :where([data-astro-image=constrained]){max-width:100%} was silently overriding any user max-width defined inside a @layer, even though :where() has zero specificity. Putting these defaults into a named layer restores the original intent of :where(): Astro's image styles should always be easy for users to override.
  • The layer name astro.images uses a dotted namespace to avoid collisions with user-defined layer names while remaining valid for CSS processors (e.g. lightningcss).
  • Note for users: to guarantee astro.images sits below your own layers, declare the order explicitly: @layer astro.images, layout, page, components;. Without an explicit order declaration, the first-seen layer wins — which may or may not be astro.images depending on stylesheet load order.

Testing

  • Updated assertions in packages/astro/test/units/assets/utils.test.ts to expect the @layer astro.images { … } wrapper around the generated CSS output.

Docs

  • No docs update needed. image.responsiveStyles is already documented as producing overridable defaults; this fix makes that promise actually true when users write CSS layers.
withastro/astro

Changes

Root cause

When nested css file content updated, delete metadata, this cause full-reload.

if (isUpdatedFileCssDep) {
	astroFileToCompileMetadata.delete(astroFile);
}

Solution

Prevent full-reload by using compile metadata, and fall back to deleting metadata if undetected.

try {
	const code = await readFile(astroFile, 'utf-8');
	await compile(code, astroFile);
} catch {
	astroFileToCompileMetadata.delete(astroFile);
}

Testing

  • Prepared test data in the fixture
  • Mocked the HMR context
  • Mocked and executed the handleHotUpdate function
  • Verified that the metadata was not deleted

Before implementation

スクリーンショット 2026-06-22 4 34 30

After implementation

スクリーンショット 2026-06-22 4 34 06

Docs

close #16310

withastro/astro

What changed

  • adds a Cloudflare fixture with an Astro Action
  • verifies two consecutive builds succeed while reusing the warm Vite dependency cache
  • removes the broad three-attempt fixture build retry that masked stale prebundle failures

Why

Astro issue #16933 reported Cloudflare builds failing when src/actions/index.ts was present because dependency optimization could observe a stale prebundle. The runtime startup change in #16961 addressed the production failure; this PR adds deterministic regression coverage so that behavior remains protected and optimizer races fail visibly instead of being hidden by retries.

Validation

  • Cloudflare integration build passes
  • optimizer-sensitive test subset: 11 passed
  • full Cloudflare adapter suite: 263 passed, 1 skipped, 0 failed
  • Biome and Prettier checks pass
  • lockfile frozen install check passes
withastro/astro

Changes

  • ctx.request.signal in the dev server now fires abort and sets signal.aborted = true when the client disconnects, matching production behavior with the Node adapter.
  • In vite-plugin-app/app.ts, an AbortController is created per request, its abort() is wired to the socket's close event, and the signal is passed to createRequest() via the existing init option — no new API surface required.

Testing

  • Adds a dev-signal-test API endpoint fixture that awaits either signal.abort or a 5-second timeout, returning { aborted }.
  • Adds a new describe block in request-signal.test.ts that starts a dev server, aborts a fetch mid-flight, and asserts the endpoint observed aborted = true; a follow-up non-aborted request asserts aborted = false.

Docs

No docs update needed — this restores parity between dev and production; no user-facing API changed.

Closes #17120

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.0

Major Changes

  • #15819 cafec4e Thanks @delucis! - Upgrade to Vite v8

  • #16965 57ead0d Thanks @Princesseuh! - Makes 'jsx' the default value for compressHTML

    Astro now strips whitespace from your HTML using JSX rules by default, the same way frameworks like React do. Whitespace and line breaks around elements are removed, but meaningful whitespace within a single line — like a space between two inline elements — is preserved. To keep a space that would otherwise be removed, write it explicitly in your source, for example with {" "}.

    This can change rendered output where whitespace between inline elements was previously meaningful. To keep Astro's earlier behavior, set compressHTML: true for HTML-aware compression, or compressHTML: false to preserve all whitespace.

  • #16610 c63e7e4 Thanks @matthewp! - Adds background dev server management for AI coding agents.

    When an AI coding agent is detected, astro dev now automatically starts the dev server as a detached background process. This prevents the dev server from blocking the agent's terminal and allows it to continue working while the server runs.

    A lock file (.astro/dev.json) is written when the dev server starts, recording the server's URL, port, and PID. This prevents duplicate servers from being started for the same project.

    New flag and subcommands

    • astro dev --background — Start the dev server as a background process (this is what runs automatically when an agent is detected).
    • astro dev stop — Stop a running background dev server.
    • astro dev status — Check if a dev server is running and display its URL, PID, and uptime.
    • astro dev logs — View logs from a background dev server. Use --follow (-f) to stream new output as it's written.

    These allow you to start and manage dev servers programmatically and were designed with AI coding agents in mind.

    What should I do?

    No action is required. If you are not using an AI coding agent, astro dev behaves exactly as before. If you are using an agent, background mode is enabled automatically — the agent will receive the server URL and PID, and can use astro dev stop to shut it down.

    To opt out of automatic background mode when an agent is detected, set the environment variable ASTRO_DEV_BACKGROUND=0 before running astro dev.

  • #17010 0606073 Thanks @ocavue! - Removes the @astrojs/db package as it is no longer maintained.

    The @astrojs/db package were deprecated in v6.4.5 and is now removed. This means the astro db, astro login, astro logout, astro link, and astro init CLI commands have also been removed.

    If you were using Astro DB in your project, remove @astrojs/db from your project's dependencies and replace it with one of the following alternatives:

    • Node.js built-in SQLite: Node.js now includes a built-in node:sqlite module (available since Node.js v22.5.0). This is a good option if you are using the Node.js adapter and were using @astrojs/db for local SQLite storage.
    • Drizzle ORM: If you were using @astrojs/db for its Drizzle-based schema and query API, you can use Drizzle directly with any supported database.
    • Other database libraries: Use any database library that suits your deployment platform (e.g. Turso, PlanetScale, Neon).
  • #16462 c30a778 Thanks @Princesseuh! - Replaces the Go compiler with a Rust-based version.

    The Rust-based Astro compiler (@astrojs/compiler-rs) is now the default compiler. This new compiler is faster and more reliable, leading to faster build times and iteration cycles during development.

    This new compiler is more strict regarding invalid syntax. For example, unclosed HTML tags will now throw an error instead of being ignored. It also does not attempt to correct semantically invalid HTML anymore, instead leaving it to the browser to handle, similar to other tools or document.write() in JavaScript.

    The previous Go-based compiler has been removed, along with the experimental.rustCompiler flag used to opt into the Rust compiler. If you were setting experimental.rustCompiler in your astro.config.mjs, you can now remove it. No other action is required.

  • #16966 6650ec2 Thanks @Princesseuh! - Makes Sätteri the default Markdown processor

    Astro now renders .md files with satteri() from @astrojs/markdown-satteri, its native Markdown pipeline, instead of the remark/rehype pipeline. @astrojs/markdown-remark is no longer installed by default.

    To keep using the remark/rehype pipeline, install @astrojs/markdown-remark and set it as your processor:

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { unified } from '@astrojs/markdown-remark';
    
    export default defineConfig({
      markdown: {
        processor: unified(),
      },
    });

    The deprecated markdown.remarkPlugins, markdown.rehypePlugins, and markdown.remarkRehype options still work, but now require @astrojs/markdown-remark to be used.

  • #16877 3b7d76e Thanks @matthewp! - Enables advanced routing by default.

    The advanced routing feature introduced behind a flag in v6.3.0 is no longer experimental and is now enabled by default.

    This gives full control over how requests flow through your application, with first-class support for frameworks like Hono.

    Advanced routing now uses src/fetch.ts as default entrypoint instead of src/app.ts.

    If you were previously using this feature without a custom entrypoint, please configure fetchFile or rename your entrypoint to src/fetch.ts, and then remove the experimental flag from your Astro config:

    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      experimental {
    -    advancedRouting: true,
      },
    +  fetchFile: 'app.ts' // optional, you only need this if you cannot rename your entrypoint.
    });

    fetchFile is now a top-level config option instead of being nested under experimental.advancedRouting. If you were using a custom entrypoint, please update your Astro config to move its configuration:

    // astro.config.mjs
    export default defineConfig({
    -  experimental: {
    -    advancedRouting: {
    -      fetchFile: 'my-custom-entrypoint.ts',
    -    },
    -  },
    +  fetchFile: 'my-custom-entrypoint.ts',
    })

    You can also set fetchFile: null to disable the entrypoint if you are using src/fetch.ts for another purpose, or don’t need advanced routing features.

    If you have been waiting for stabilization before using advanced routing, you can now do so.

    Please see the advanced routing guide in docs for more about this feature.

  • #16725 10229f7 Thanks @ArmandPhilippot! - Removes deprecated APIs exported from astro:transitions.

    In Astro 6.x, some helpers available in astro:transitions and astro:transitions/client were deprecated.

    In Astro 7.0, the following APIs can no longer be used in your project:

    • TRANSITION_BEFORE_PREPARATION
    • TRANSITION_AFTER_PREPARATION
    • TRANSITION_BEFORE_SWAP
    • TRANSITION_AFTER_SWAP
    • TRANSITION_PAGE_LOAD
    • isTransitionBeforePreparationEvent()
    • isTransitionBeforeSwapEvent()
    • createAnimationScope()

    What should I do?

    Remove any occurrence of createAnimationScope():

    -import { createAnimationScope } from 'astro:transitions';

    Replace any occurrence of the other APIs using the lifecycle event names directly:

    -import {
    -	TRANSITION_AFTER_SWAP,
    -	isTransitionBeforePreparationEvent,
    -} from 'astro:transitions/client';
    
    -console.log(isTransitionBeforePreparationEvent(event));
    +console.log(event.type === 'astro:before-preparation');
    
    -console.log(TRANSITION_AFTER_SWAP);
    +console.log('astro:after-swap');

    Learn more about all utilities available in the View Transitions Router API Reference.

Minor Changes

  • #16998 57dcc31 Thanks @matthewp! - Exposes getFetchState() from astro/hono as a public API

    The getFetchState() function retrieves or lazily creates a FetchState from a Hono context object. This allows third-party packages to build Hono middleware that interacts with Astro's per-request state, giving the astro/hono API the same extensibility as astro/fetch.

    import { Hono } from 'hono';
    import { getFetchState, pages } from 'astro/hono';
    
    const app = new Hono();
    
    app.use(async (context, next) => {
      const state = getFetchState(context);
      state.locals.message = 'Hello from custom middleware';
      await next();
    });
    
    app.use(pages());
    
    export default app;
  • #16996 300641e Thanks @florian-lefebvre! - Adds a subset field to the FontData type exposed via fontData from astro:assets. When using multiple font subsets (e.g., subsets: ["latin", "korean"]), each font data entry now includes the subset name, making it possible to distinguish between font entries for different subsets that share the same weight and style.

  • #16745 f864a80 Thanks @ematipico! - The custom logger feature introduced behind a flag in v6.2.0 is no longer experimental and is available for general use.

    This feature provides better control over Astro's logging infrastructure by allowing you to replace the default console output with custom logging implementations (e.g., structured JSON). This is particularly useful for on-demand rendering when connecting to log aggregation services such as Kibana, Logstash, CloudWatch, Grafana, or Loki.

    Astro provides three built-in log handlers (json, node, and console), and you can also create your own.

    JSON logging

    import { defineConfig, logHandlers } from 'astro/config';
    
    export default defineConfig({
      logger: logHandlers.json({
        pretty: true,
        level: 'warn',
      }),
    });

    Custom logger

    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      logger: {
        entrypoint: '@org/custom-logger',
      },
    });

    Additionally, context.logger is now always available in API routes and middleware, even without a custom logger configured.

    If you were previously using this feature, please remove the experimental flag from your Astro config:

    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
    -  experimental: {
    -    logger: {
    -      entrypoint: '@org/custom-logger',
    -    },
    -  },
    +  logger: {
    +    entrypoint: '@org/custom-logger',
    +  },
    });

    If you have been waiting for stabilization before using custom loggers, you can now do so.

    Please see the Logger docs for more about this feature.

  • #16981 0d6d644 Thanks @ematipico! - Removes the setting experimental.queuedRendering. The new rendering engine is now stable and replaces the old one.

    As part of the stabilization, the queued rendering has been improved, and some features have been removed:

    • The construction of the queue has been removed, instead now Astro uses a streaming approach where components are rendered and flushed as they are encountered.
    • The node polling feature has been removed because it doesn't yield concrete savings.
    • The content cache has been descoped, and how only tag names are cached.
      If you were previously using this experimental feature, you must remove this experimental flag from your configuration as it no longer exists:
    // astro.config.mjs
    import { defineConfig } from "astro/config";
    
    export default defineConfig({
      experimental: {
    -    queuedRendering: {}
      }
    });
  • #17116 f95e58e Thanks @ascorbic! - Stabilizes route caching, removing the experimental.cache and experimental.routeRules flags and replacing them with the top-level cache and routeRules configuration options.

    Route caching, introduced experimentally in v6.0.0, is now stable. It gives you a platform-agnostic way to cache responses from on-demand rendered pages and endpoints, based on standard HTTP caching semantics.

    Update your config to move cache and routeRules out of the experimental block:

    // astro.config.mjs
    import { defineConfig, memoryCache } from 'astro/config';
    
    export default defineConfig({
    -  experimental: {
    -    cache: {
    -      provider: memoryCache(),
    -    },
    -    routeRules: {
    -      '/blog/[...path]': { maxAge: 300, swr: 60 },
    -    },
    -  },
    +  cache: {
    +    provider: memoryCache(),
    +  },
    +  routeRules: {
    +    '/blog/[...path]': { maxAge: 300, swr: 60 },
    +  },
    });

    Set caching directives in your routes with Astro.cache (in .astro pages) or context.cache (in API routes and middleware), and Astro translates them into the appropriate headers or runtime behavior depending on your configured cache provider. You can also define cache rules for routes declaratively in your config using routeRules, without modifying route code.

    See the route caching guide for more information.

Patch Changes

  • #16980 1f07343 Thanks @matthewp! - Removes state.provide(), state.resolve(), state.finalizeAll(), and App.Providers from the public advanced routing API. These context provider extension points are now internal-only. If you were using them in an integration, use locals to share per-request state instead.

  • #17111 c0f33ed Thanks @ematipico! - Harden the limits on the number of decoding on the URL.

  • #16982 1e000e2 Thanks @matthewp! - Improves the warning when accessing Astro.session without session storage configured. The session property is now always defined on the context object, and accessing it without configuration logs a helpful message instead of silently returning undefined.

  • #16335 9a53f77 Thanks @ascorbic! - Adds shared helper utilities for CDN cache provider authors for route caching

    Exports astro/cache/provider-utils with helpers for building platform-specific cache-control headers, generating path-based invalidation tags, and normalizing invalidation options. These are used internally by the first-party Netlify, Vercel, and Cloudflare cache providers.

  • #17095 e84ebc0 Thanks @matthewp! - Improves build performance by removing an unfiltered transform hook from the astro:head-metadata-build plugin. Head propagation modules are now identified by their module ID (?astroPropagatedAssets) instead of scanning every module's source code.

  • #17041 4c4a91c Thanks @iseraph-dev! - Fixes a bug where the advanced routing astro/hono / astro/fetch pages() handler returned the host framework's default Internal Server Error response instead of rendering the custom 500.astro page when a page threw during render. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way.

  • #17097 5e340d7 Thanks @iseraph-dev! - Fixes a bug where the advanced routing astro/hono / astro/fetch middleware() handler returned the host framework's default Internal Server Error response instead of rendering the custom 500.astro page when middleware threw. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way. Errors surfaced through next (the host framework's downstream chain) still propagate to the host's own error handler.

  • #15819 cafec4e Thanks @delucis! - Fixes --port flag being ignored after a Vite-triggered server restart (e.g. when a .env file changes)

  • #17104 b074a37 Thanks @iseraph-dev! - Fixes the custom 500.astro page receiving an empty error prop when the error originated in middleware.

  • #17078 04547ec Thanks @astrobot-houston! - Fixes a spurious Astro.request.headers warning on prerendered pages when security.allowedDomains is configured. The internal allowedDomains header validation now skips prerendered routes, since they use synthetic requests with no real headers.

  • #16603 deaaf3f Thanks @alexanderniebuhr! - Removes the warning that Astro does not support vite v8, since Astro v7 does support vite v8

  • #16335 9a53f77 Thanks @ascorbic! - Passes the Request object to CacheProvider.setHeaders() for route caching

    Cache providers now receive the incoming Request as a second argument to setHeaders(options, request). This allows CDN providers to read the request URL, headers, and other properties when generating cache response headers, for example to auto-tag responses with their pathname for path-based invalidation.

  • #17098 637a1b6 Thanks @matthewp! - Fixes internal Astro headers leaking from direct pages() handler responses

  • #17090 3cf76c0 Thanks @matthewp! - Fixes Vite and Rolldown build warnings

  • #16434 ee079d4 Thanks @ematipico! - Fixes an issue where i18n domains would return 404 when trailingSlash is set to never.

  • Updated dependencies [7e7ab87, ff7b718, 241250b]:

    • @astrojs/markdown-satteri@0.3.1

@astrojs/alpinejs@1.0.0

Major Changes

Patch Changes

@astrojs/cloudflare@14.0.0

Major Changes

Minor Changes

  • #16335 9a53f77 Thanks @ascorbic! - Adds an opt-in CDN cache provider for Astro route caching on Cloudflare Workers

    [!WARNING]
    This provider requires the Cloudflare Workers Cache feature, which is currently in private beta. It is opt-in: nothing changes unless you import cacheCloudflare() and set it as your provider. But without beta access it does not work and should not be used. Cloudflare Workers run in front of the cache, so cached responses are never served, and calling cache.invalidate() throws an error.

    Setup

    Import cacheCloudflare() from @astrojs/cloudflare/cache and set it as your cache provider:

    import { defineConfig } from 'astro/config';
    import cloudflare from '@astrojs/cloudflare';
    import { cacheCloudflare } from '@astrojs/cloudflare/cache';
    
    export default defineConfig({
      adapter: cloudflare(),
      cache: {
        provider: cacheCloudflare(),
      },
    });

    The adapter automatically enables the Worker caching layer when a Cloudflare cache provider is configured. No manual wrangler.jsonc changes are needed.

    Caching responses

    Use Astro.cache.set() in your pages and API routes to cache responses. The provider sets Cloudflare-CDN-Cache-Control and Cache-Tag headers, which are read by Cloudflare's built-in caching layer. Cache hits bypass Worker execution entirely, meaning your Worker is not invoked for cached responses.

    ---
    Astro.cache.set({ maxAge: 300, tags: ['products'] });
    const data = await fetchProducts();
    ---
    
    <ProductList items={data} />

    You can also set cache rules for groups of routes in your config:

    cache: { provider: cacheCloudflare() },
    routeRules: {
      '/products/[...slug]': { maxAge: 3600, tags: ['products'] },
      '/api/[...path]': { maxAge: 60, swr: 600 },
    },

    Invalidation

    Purge cached responses by tag or path from any API route or server endpoint:

    // src/pages/api/purge.ts
    export async function POST({ request, cache }) {
      await cache.invalidate({ tags: ['products'] });
      return new Response('Purged');
    }
    
    // Path-based invalidation (implemented via an auto-generated path tag)
    await cache.invalidate({ path: '/products/123' });

    Both tag-based and path-based invalidation are supported.

Patch Changes

  • #16961 96398e8 Thanks @adamchal! - Speeds up astro sync by no longer starting the Cloudflare runtime during type generation

  • #16671 fd926fd Thanks @alexanderniebuhr! - Removes deprecations warnings added in Astro v6 for Cloudflare specific Astro.locals properties.

  • #17027 241250b Thanks @ocavue! - Triggers beta prereleases for packages that are still on alpha

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/markdoc@2.0.0

Major Changes

Patch Changes

@astrojs/mdx@7.0.0

Major Changes

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

  • #17129 ff7b718 Thanks @Princesseuh! - Adds support for modifying frontmatter programmatically with the default Markdown processor.

    A Sätteri plugin can now read and mutate ctx.data.astro.frontmatter, and Astro uses the result as the page's frontmatter, in both Markdown and MDX.

Patch Changes

@astrojs/netlify@8.0.0

Major Changes

Minor Changes

  • #16335 9a53f77 Thanks @ascorbic! - Adds a CDN cache provider for Astro route caching on Netlify

    Setup

    Import cacheNetlify() from @astrojs/netlify/cache and set it as your cache provider:

    import { defineConfig } from 'astro/config';
    import netlify from '@astrojs/netlify';
    import { cacheNetlify } from '@astrojs/netlify/cache';
    
    export default defineConfig({
      adapter: netlify(),
      cache: {
        provider: cacheNetlify(),
      },
    });

    Caching responses

    Use Astro.cache.set() in your pages and API routes to cache responses on Netlify's edge network. The provider uses Netlify's durable cache so cached responses are shared across all edge nodes, reducing function invocations.

    ---
    Astro.cache.set({ maxAge: 300, tags: ['products'] });
    const data = await fetchProducts();
    ---
    
    <ProductList items={data} />

    You can also set cache rules for groups of routes in your config:

    cache: { provider: cacheNetlify() },
    routeRules: {
      '/products/[...slug]': { maxAge: 3600, tags: ['products'] },
      '/api/[...path]': { maxAge: 60, swr: 600 },
    },

    Invalidation

    Purge cached responses by tag or path from any API route or server endpoint:

    // src/pages/api/purge.ts
    export async function POST({ request, cache }) {
      await cache.invalidate({ tags: ['products'] });
      return new Response('Purged');
    }
    
    // Path-based invalidation
    await cache.invalidate({ path: '/products/123' });

    Both tag-based and path-based invalidation are supported.

Patch Changes

  • #17027 241250b Thanks @ocavue! - Triggers beta prereleases for packages that are still on alpha

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/preact@6.0.0

Major Changes

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

Patch Changes

@astrojs/react@6.0.0

Major Changes

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

Patch Changes

@astrojs/solid-js@7.0.0

Major Changes

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

Patch Changes

@astrojs/svelte@9.0.0

Major Changes

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

  • #16549 9d9d516 Thanks @ocavue! - Updates @sveltejs/vite-plugin-svelte to v7. No user action is necessary.

Patch Changes

@astrojs/vercel@11.0.0

Major Changes

Minor Changes

  • #16335 9a53f77 Thanks @ascorbic! - Adds a CDN cache provider for Astro route caching on Vercel

    Setup

    Import cacheVercel() from @astrojs/vercel/cache and set it as your cache provider:

    import { defineConfig } from 'astro/config';
    import vercel from '@astrojs/vercel';
    import { cacheVercel } from '@astrojs/vercel/cache';
    
    export default defineConfig({
      adapter: vercel(),
      cache: {
        provider: cacheVercel(),
      },
    });

    Caching responses

    Use Astro.cache.set() in your pages and API routes to cache responses on Vercel's edge network. The provider sets Vercel-CDN-Cache-Control and Vercel-Cache-Tag headers on responses.

    ---
    Astro.cache.set({ maxAge: 300, tags: ['products'] });
    const data = await fetchProducts();
    ---
    
    <ProductList items={data} />

    You can also set cache rules for groups of routes in your config:

    cache: { provider: cacheVercel() },
    routeRules: {
      '/products/[...slug]': { maxAge: 3600, tags: ['products'] },
      '/api/[...path]': { maxAge: 60, swr: 600 },
    },

    Invalidation

    Purge cached responses by tag or path from any API route or server endpoint:

    // src/pages/api/purge.ts
    export async function POST({ request, cache }) {
      await cache.invalidate({ tags: ['products'] });
      return new Response('Purged');
    }
    
    // Path-based invalidation
    await cache.invalidate({ path: '/products/123' });

    Both tag-based and path-based invalidation are supported. Tag invalidation is a soft invalidation, marking cached responses as stale so they can be revalidated in the background via stale-while-revalidate.

Patch Changes

@astrojs/vue@7.0.0

Major Changes

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

Patch Changes

create-astro@5.1.0

Minor Changes

  • #17122 cbd6123 Thanks @matthewp! - Adds a default AGENTS.md file to new projects with dev server instructions and documentation links. Also creates a CLAUDE.md symlink (with hard link fallback) pointing to AGENTS.md.

@astrojs/node@11.0.0

Patch Changes

  • #17054 d426b67 Thanks @astrobot-houston! - Fixes an issue where Astro files with non-ASCII characters in their name weren't correctly served after the build.

  • #17027 241250b Thanks @ocavue! - Triggers beta prereleases for packages that are still on alpha

@astrojs/markdown-satteri@0.3.1

Patch Changes

withastro/astro

Changes

Reworks the way metadata flows through Sätteri so that plugins can modify it, like you can on remark. This makes the code very similar to remark's and unlocks the ability for users to modify the frontmatter.

Testing

Added tests

Docs

Will need some updates, but nothing is technically untrue if this is merged, so could be done separately.

withastro/astro

Changes

  • scripts/turbo-run-affected.js appended the changed-file range [origin/<base>...HEAD] to every --filter, including exclusions like !./packages/language-tools/**/*. That scoped the exclusion to packages that themselves changed (and skipped the {…} directory wrapping it applies to normal directory filters), so the exclusion stopped applying.
  • As a result, an excluded package was still selected when affected by an upstream change: @astrojs/ts-plugin (affected whenever astro changes) got pulled into the test:integrations suite, which has no xvfb, so its VS Code test segfaults. It normally hides behind a turbo cache hit and only surfaces on a cold cache (e.g. a PR that touches pnpm-lock.yaml).
  • Fix: pass exclusion filters (!…) through unchanged so they stay absolute. The ts-plugin/VS Code tests then run only in the dedicated test-language-tools job (which sets up xvfb and already marks them non-failing).
  • No changeset: this only touches CI scripting, with no changes to any published package.

Testing

Verified with turbo run test … --dry=json (planning only, no tests executed), using the stabilise-cache range — a real case where astro changed but no language-tools package did, which is what triggers the bug:

  • Inclusion only--filter="@astrojs/*[origin/main...origin/stabilise-cache]" selects @astrojs/ts-plugin and @astrojs/language-server (affected via their dependency on astro).
  • Old behavior — adding the range-scoped exclusion --filter="!./packages/language-tools/**/*[origin/main...origin/stabilise-cache]" (what the script produced before): ts-plugin and language-server are still selected. ❌
  • Fixed behavior — adding the absolute exclusion --filter="!./packages/language-tools/**/*" (what the script produces now): ts-plugin and language-server are excluded. ✅

So test:integrations no longer pulls in language-tools packages, and the ts-plugin/VS Code tests run only in the dedicated test-language-tools job.

Docs

No docs needed: this is internal CI tooling with no user-facing behavior change.

withastro/astro

Changes

Just a bump to latest, fixes a few issues and improves performance with plugins.

Testing

Should pass

Docs

N/A

withastro/astro

The client router didn't copy server islands marker comments during head swap.
Now it does.

Closes #17105

Server islands within the <head> might very well not match their initial intention.
Nevertheless, I decided for this fix as it was quicker and less disruptive than documenting and checking that server:defer isn't used inside the <head>. Also, I saw no technical reason to prevent this and maybe the abiliability to comes in handy some day ;-)

Testing

New e2e test

Docs

n.a. / bug fix.

withastro/astro

Changes

  • create-astro now generates an AGENTS.md in new projects with dev server background mode instructions and links to commonly needed Astro docs.
  • Creates a CLAUDE.md symlink pointing to AGENTS.md, with a hard link fallback for Windows environments without Developer Mode.

Testing

  • Added generateAgentsMd tests verifying the background dev command and documentation links are present.

Docs

  • No docs update needed. This is a scaffolding change that generates a file for AI coding agents, not a user-facing API.
withastro/astro

Changes

  • Adds new config serverIslandHostname
  • Updates manifests (serialized and plugin) to use the new config value
  • Refactors existing logic for prepending config.base such that there won't be duplicated slashes

Testing

Tested using a local project that will make use of the feature.

Smoke tests:

  • Run astro dev
    -- with new config set -- observe that the config value is included at the beginning of the server island URL
    -- with the new config unset -- observe that the config value is unchanged
  • Run astro build
    -- with new config set -- observe that generated static code includes new config value at the beginning of the server island URL
    -- with the new config unset -- observe that the config value is unchanged

Ran unit tests for packages/astro

Docs

@withastro/maintainers-docs

This could affect the user's behavior if a user sets this value and isn't aware that they need to deploy the server side application to wherever this new hostname is proxying.

withastro/docs#14130

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

main is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on main.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

astro@7.0.0-beta.6

Minor Changes

  • #17116 f95e58e Thanks @ascorbic! - Stabilizes route caching, removing the experimental.cache and experimental.routeRules flags and replacing them with the top-level cache and routeRules configuration options.

    Route caching, introduced experimentally in v6.0.0, is now stable. It gives you a platform-agnostic way to cache responses from on-demand rendered pages and endpoints, based on standard HTTP caching semantics.

    Update your config to move cache and routeRules out of the experimental block:

    // astro.config.mjs
    import { defineConfig, memoryCache } from 'astro/config';
    
    export default defineConfig({
    -  experimental: {
    -    cache: {
    -      provider: memoryCache(),
    -    },
    -    routeRules: {
    -      '/blog/[...path]': { maxAge: 300, swr: 60 },
    -    },
    -  },
    +  cache: {
    +    provider: memoryCache(),
    +  },
    +  routeRules: {
    +    '/blog/[...path]': { maxAge: 300, swr: 60 },
    +  },
    });

    Set caching directives in your routes with Astro.cache (in .astro pages) or context.cache (in API routes and middleware), and Astro translates them into the appropriate headers or runtime behavior depending on your configured cache provider. You can also define cache rules for routes declaratively in your config using routeRules, without modifying route code.

    See the route caching guide for more information.

Patch Changes

  • #17090 3cf76c0 Thanks @matthewp! - Fixes Vite and Rolldown build warnings

  • Updated dependencies [7e7ab87]:

    • @astrojs/markdown-satteri@0.3.1-beta.2

create-astro@5.1.0-beta.0

Minor Changes

  • #17122 cbd6123 Thanks @matthewp! - Adds a default AGENTS.md file to new projects with dev server instructions and documentation links. Also creates a CLAUDE.md symlink (with hard link fallback) pointing to AGENTS.md.

@astrojs/cloudflare@14.0.0-beta.3

Patch Changes

  • #16961 96398e8 Thanks @adamchal! - Speeds up astro sync by no longer starting the Cloudflare runtime during type generation

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/mdx@7.0.0-beta.4

Patch Changes

@astrojs/markdown-satteri@0.3.1-beta.2

Patch Changes

withastro/starlight

Follow-up to #3923, this PR cleans up various parts regarding aside and heading link icons:

  • The Unified/Sätteri aside plugins now uses the same Starlight icons as the existing ones already rendered by the <Aside> component
  • The Unified/Sätteri heading link plugins and <AnchorHeading> component now uses a new common Starlight icon (link-alt to match the Unicons naming)
  • As it felt weird to only add new link-alt icon, I also added the matching link icon

The diff also shows how much more minified the aside component icons are compared to our previous Markdown plugin ones. I wonder if we forgot to minify them or just used a different preset 🤷

Not quite sure if a changeset for the deduping part of the PR should be added. Visually, nothing changed, and it's mostly SVG path changes. And if yes, not quite sure how to phrase it as my attempts so far kinda hinted wrong icons were rendered which is not the case 😅 Finally managed to write something I'm happy with.

withastro/astro

Changes

Stabilises the route caching feature. Moves the cache and routeRules configuration from experimental to top-level config.

It is not enabled by default, and needs a cache provider to be configured. This will likely be enabled for adapters once #16335 lands.

Testing

Updated tests

Docs

Draft docs: withastro/docs#13977

withastro/astro

Changes

I moved the code for issue triage over to https://github.com/withastro/triagebot-action so that:

  • Easier to test in isolation, don't have to "merge and pray" like we do now when making changes.
  • Potentially use triage in other projects, although no plans to add it yet.

Additionally I took the opportunity to make it into a full state machine, there are no more unknown states and the triage bot can recover from, for example, the reporter saying that the issue isn't fixed by the pending fix.

Also standardized the label names for consistency with triage: {name} format.

This PR removes the Flue code which now lives there, and uses the action.

Testing

  • The action has its own unit tests (27 passing) covering the FSM router and label management.

Docs

  • No docs update needed. This is an internal CI automation change.
withastro/astro

Changes

This PR cherry-pick the changes we just shipped in Astro v6.4.8

Testing

Green CI

Docs

N/A

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to 6-legacy, this PR will be updated.

Releases

astro@6.4.8

Patch Changes

withastro/astro

Changes

Improves the hardening of decoded URLs

Testing

Added new tests

Docs

N/A

withastro/astro

Changes

  • When a non-root base is set, the Cloudflare adapter now relocates public/_redirects to the assets root (dist/client/_redirects) alongside .assetsignore and _headers. Previously, the file was left at dist/client/<base>/_redirects where Cloudflare Workers Assets can't find it, silently dropping all user-defined redirects in production.
  • Because relocation happens before the auto-generated-redirects step, user-defined rules land at the root first and any route-derived redirects are correctly appended afterward.

Testing

  • Added two test cases to with-base.test.ts: one verifying _redirects exists at the client root (not nested under the base path), and one verifying user-defined redirect content from public/_redirects is preserved after build.
  • Added public/_redirects to the with-base fixture.

Docs

  • No docs update needed; this is a bug fix restoring behavior consistent with the default base: '/' case.

Closes #17101

withastro/astro

Changes

  • Updates the fix-verification flue workflow model from claude-sonnet-4-20250801 (which does not exist) to claude-sonnet-4-6 (Claude Sonnet 4.6, the current production Sonnet). The previous model claude-sonnet-4-20250514 was deprecated, and #17100 attempted to update it but used a non-existent model ID.

Testing

  • No test changes. The fix-verification workflow runs in CI via the Fix Verify GitHub Action — a successful run confirms the model resolves correctly.

Docs

  • No docs needed; internal CI workflow change only.
withastro/astro

Changes

Follow-up to #17041 and #17097. The #17097 description flagged one item as out of scope: on the composable astro/hono path the custom 500.astro rendered with an empty error prop, so Astro.props.error was undefined and the page could not show what threw. It turns out this is not specific to astro/hono, and not a middleware-handler issue. It lives in the production error handler and affects the standard adapter as well.

When middleware throws, the path's catch calls app.renderError(..., { error }). DefaultErrorHandler.renderError then renders the matching 500.astro by running the middleware chain again (so middleware can still set headers, locals, etc. on the error page). The error state reuses the original request, so the same user middleware runs again and throws again. The handler catches that and retries with skipMiddleware: true ("middleware may be the cause of the error"), but the retry object dropped the original error. It fell out of the spread and reached the error page as undefined.

The result: any path that rendered a custom 500 for a middleware-thrown error lost Astro.props.error (the standard @astrojs/node adapter, the all-in-one astro() handler, and the composable middleware()). Page-render errors via pages() were unaffected, because the middleware does not re-throw on the error render, so the first attempt succeeds with the error intact. The dev error handler was also unaffected; it already forwards an error on its retry.

The fix carries the original error through the skip-middleware retry (one line in packages/astro/src/core/errors/default-handler.ts). BuildErrorHandler delegates to DefaultErrorHandler, so prerendered error pages pick up the same fix.

Testing

  • Added a unit test in test/units/fetch/index.test.ts (the middleware() block): user middleware throws and the custom /500 page echoes Astro.props.error, asserting the thrown message reaches the page. It fails before the change (the error page renders blank) and passes after. The full unit suite stays green (2942 passing, 3 skipped).
  • Verified end to end against the #17092 reproduction (Hono, app.use(middleware()) + app.use(pages()), node standalone, a 500.astro that prints Astro.props.error):
    • GET /boom (middleware throws) now renders 500.astro with the message boom from middleware, previously blank.
    • GET /page-throws still renders 500.astro with its message (no regression).
    • GET /does-not-exist still renders the custom 404, and GET / still returns 200.

Notes

  • The retry passes the original error (the cause of the 500), which is what every non-retry path already passes. The dev handler instead forwards the re-thrown error on its retry; aligning the two is cosmetic and left out of this change.
  • The deliberate "no error to report" reroute paths (the status-code 404/500 reroutes that pass error: null/undefined) are unchanged.
withastro/astro

Changes

  • The LLM returns literal \n (backslash + n) instead of actual newlines when producing a string via tool-call results. Unescapes them before posting to GitHub so comments render with real line breaks.

Testing

  • No test changes. The .flue/ workflow code has no test harness; verified by inspecting the hex dump of the affected comment (bytes 5c 6e instead of 0a).

Docs

  • No docs needed — internal bot infrastructure change.
withastro/astro

Changes

  • Updates the fix-verification flue workflow model from claude-sonnet-4-20250514 (EOL) to claude-sonnet-4-20250801. The old model is no longer available, causing verification runs to fail.

Testing

  • No test changes — config-only update.

Docs

  • No docs needed — internal CI workflow change.
withastro/astro

Changes

  • Keeps a user-configured image.service when the Cloudflare adapter is using imageService: 'compile', so custom getURL() and getHTMLAttributes() hooks are used while prerendering markup in workerd.
  • Loads the configured custom service during the Node-side image generation pass, so custom transform() hooks are used for generated image assets.
  • Extends imageService: 'custom' to also generate optimized image assets at build time, mirroring compile. It runs the configured image.service (or Astro's default Sharp service when none is set) during the Node-side generation pass, while continuing to use the configured service for runtime image handling.
imageService user image.service Before After
'compile' none (default Sharp) Workerd-safe service for prerendered markup; Sharp on the Node side for generation; Sharp-free worker bundle. Unchanged.
'compile' custom Replaced the custom service with the workerd-safe service, so custom markup and transform() hooks were skipped. Preserves the custom service for prerendered markup and runs its transform() hook during image generation.
'custom' none (default Sharp) No build-time asset generation; the default Sharp service is bundled into the worker (where it cannot run). Generates assets with Sharp on the Node side. Runtime bundle unchanged — the default Sharp service is still bundled (and remains Workers-incompatible), per the documented custom tradeoff.
'custom' custom No build-time asset generation; the custom service was used only at runtime. Generates assets with the custom service at build time and continues to use it for runtime image handling.
'cloudflare' | 'cloudflare-binding' | 'passthrough' Existing adapter behavior for those modes. Unchanged.

The compile rows also cover the compound form imageService: { build: 'compile', runtime: ... }.

Testing

  • Reworks the Cloudflare compile image service test coverage into a build-time generation matrix that exercises both imageService: 'compile' and imageService: 'custom' across three service configurations: no user service (Astro's default Sharp service), a Sharp-free custom service, and a Sharp-backed custom service. Each cell asserts:
    • hashed _astro/*.webp assets are generated at build time (real WEBP bytes, or the custom transform() marker for the Sharp-free user service);
    • custom getHTMLAttributes() markup is preserved;
    • the worker bundle deps match each mode's contract: compile stays Sharp-free (Sharp runs only on the Node side); custom reflects its configured runtime service — clean for a Sharp-free user service, but dragging Sharp in for both a Sharp-backed user service and the no-image.service default.

Docs

  • The Cloudflare adapter guide could be updated to mention that the 'compile' mode “uses image.service, if defined otherwise a combination of internal dependencies to transform images locally at build time for prerendered routes.” And it could also mention that 'custom' will do the same thing during build time. But, it may not be necessary—I feel like this is what I expected to be the case. Happy to draft a withastro/docs PR.
  • Draft docs PR withastro/docs#14095

Related PRs

  • This is related to #16194, which adds an opt-in compound { build: 'cloudflare-binding' } mode so the Cloudflare Images binding transforms prerendered images during the build (writing optimized bytes directly, falling back to Sharp); this PR keeps local image-service generation paths working with custom image services and brings build-time generation to custom mode. The behaviors should compose: compile and custom respect custom local image services, while the opt-in cloudflare-binding build mode uses the Images binding.

Closes #16201

withastro/astro

Changes

  • Moves internal route/reroute metadata from response headers onto FetchState, preventing direct pages() responses from leaking x-astro-route-type or x-astro-reroute.
  • Updates routing, i18n, endpoint, and no-op middleware handling to use state fields instead of internal response headers.

Testing

  • Updated fetch unit tests to assert direct pages() responses do not expose Astro internal headers.
  • Verified fetch, i18n, routing, and endpoint tests.

Docs

  • No docs needed — internal metadata handling with no user-facing API changes.
withastro/astro

Changes

On the composable astro/hono path, middleware() delegated straight to AstroMiddleware.handle(), with none of the app-level error handling AstroHandler provides on the standard path. A throw in the user's own src/middleware.ts therefore reached the host framework's default error handler (Hono: plain-text Internal Server Error) instead of rendering the custom 500.astro. The all-in-one astro() handler and the standard adapter path both render 500.astro for the same throw, so the gap was specific to composing middleware() on its own. This is the middleware counterpart of #16952 / #17041, which fixed the pages() side and flagged this as out of scope (#17092).

  • Added AstroMiddleware.handleWithErrorFallback(app, state, renderRouteCallback) (core/middleware/astro-middleware.ts): wraps the middleware chain in a try/catch that logs the error and renders it via app.renderError(..., { status: 500, error }), the same behaviour AstroHandler.render() has on the standard path and PagesHandler.handleWithErrorFallback() got in #17041.
  • #17041 noted the catch couldn't simply live here, because middleware()'s next is the host's next(): a plain try/catch would also swallow errors thrown by host middleware running below it in the chain and hide them from the user's own app.onError. A sentinel handles that. Errors surfaced through the route-dispatch callback (the host's next, i.e. middleware mounted below middleware()) are tracked and re-thrown, so only errors from Astro's own middleware become 500.astro while host errors still reach app.onError.
  • Added the same unmatched-route guard pages() got in #17041. When no route matched (the custom 404 page is prerendered or absent, so routeData is unset), it returns a 404 marked with X-Astro-Error for the app's post-check instead of running middleware against a missing route. Without the guard, AstroMiddleware.handle() calls getProps() on the missing route, throws a TypeError, and the 500 catch above turns the unmatched request into a 500. This matches AstroHandler, which 404s before running middleware on the standard path.
  • Switched middleware() in core/fetch/index.ts to delegate to the new method (delegation only, no logic added - that file keeps its exports as thin wrappers).
  • Added a changeset (astro patch).

The triage on #17092 explored the same sentinel approach.

Testing

  • Added 'renders the custom 500 page when user middleware throws' - verifies a throw in user middleware returns HTTP 500 with the custom 500 page content.
  • Added 're-throws errors from the next callback instead of rendering the 500 page' - verifies an error surfaced through next propagates to the caller (so the host's error handling still runs) instead of being rendered as 500.astro.
  • Added 'returns a marked 404 without running middleware when the custom 404 route is prerendered' - verifies an unmatched request returns HTTP 404 with the X-Astro-Error marker and that user middleware and next are not run.
  • Verified end-to-end against the reproduction from #17092 (updated to cover both gaps; Hono, app.use(middleware()) + app.use(pages()), node standalone), and confirmed the composable path now matches the all-in-one astro() on every case:
    • GET /boom (throws in src/middleware.ts) -> 500 with the custom 500 page, previously Hono's plain-text Internal Server Error.
    • GET /page-throws -> 500 with the custom 500 page (the #17041 path, still working).
    • GET / -> 200.
    • With a host (Hono) middleware mounted below middleware() that throws, the request still reaches app.onError rather than 500.astro.
    • GET /does-not-exist (unmatched) with a prerendered custom 404 -> 404 rendering the custom 404 page, previously 500; with no custom 404 -> the default 404 page.

Out of scope (noticed while testing)

On the middleware path the rendered 500.astro gets an empty error prop, so the page can't show what threw. This looks pre-existing rather than introduced here: the all-in-one astro() handler behaves the same, while page-render errors via pages() do populate error. Better addressed separately.

Docs

  • No docs update needed - this restores the documented custom-error-page behaviour on the composable path (the pages() counterpart was #17041).

Closes #17092

withastro/astro

Changes

  • Removes the unfiltered transform hook from astro:head-metadata-build that ran on every module in the build. This plugin was consuming 36% of total plugin time in PLUGIN_TIMINGS reports. Head propagation modules are now identified by checking for the ?astroPropagatedAssets query param on their module ID in generateBundle, instead of scanning every module's source code for the "use astro:head-inject" directive.
  • Applies the same ID-based check to the dev plugin (astro:head-metadata) for consistency.
  • Removes the now-unused hasHeadPropagationCall function and the "use astro:head-inject" directive from templates/content/module.mjs (redundant — the virtual content module already sets propagation: "in-tree" via module metadata).

Testing

  • All 24 remaining head propagation unit tests pass (7 hint detection tests removed with the deleted module).
  • Head propagation prerender, astro-head, and CSS order integration tests pass.
  • Manual verification with examples/blog: added a component with global CSS to an MDX content entry, confirmed styles appear on the post page and do not bleed onto the blog index. Also confirmed that disabling the new check causes no regression because MDX uses the propagation: "self" metadata path independently.

Docs

  • No docs needed — internal build performance improvement with no user-facing API changes.
withastro/astro

Changes

Integration entrypoints are unsafe to import inside the app because they're:

  • Not written with bundling in mind
  • Can import some build-time only stuff

This PR fixes this by exporting the container renderer definition from a separate entrypoint, deprecating the old one for all integrations.

Fixes #16954
Fixes #17068

Testing

Updated tests to import from the new entrypoint

Docs

withastro/docs#14077

withastro/astro

Changes

  • Removes the deprecated resolve.alias customResolver option from the astro:tsconfig-alias plugin. CSS @import alias resolution is now handled by a separate enforce: "pre" transform hook that rewrites aliased specifiers to absolute paths before Vite's CSS plugin processes them. The existing resolveId hook for JS/TS imports is unchanged.
  • Disables Rolldown's checks.pluginTimings in the build config to suppress the [PLUGIN_TIMINGS] warning that fires by default.

Testing

  • All 4 existing alias test suites pass (11 tests), including alias-tsconfig.test.ts "works in css @import" which validates multi-path tsconfig alias resolution in CSS @import statements.

Docs

  • No docs needed — internal warning suppression with no user-facing API changes.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

main is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on main.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

astro@7.0.0-beta.5

Major Changes

  • #16965 57ead0d Thanks @Princesseuh! - Makes 'jsx' the default value for compressHTML

    Astro now strips whitespace from your HTML using JSX rules by default, the same way frameworks like React do. Whitespace and line breaks around elements are removed, but meaningful whitespace within a single line — like a space between two inline elements — is preserved. To keep a space that would otherwise be removed, write it explicitly in your source, for example with {" "}.

    This can change rendered output where whitespace between inline elements was previously meaningful. To keep Astro's earlier behavior, set compressHTML: true for HTML-aware compression, or compressHTML: false to preserve all whitespace.

Patch Changes

  • #17111 c0f33ed Thanks @ematipico! - Harden the limits on the number of decoding on the URL.

  • #17095 e84ebc0 Thanks @matthewp! - Improves build performance by removing an unfiltered transform hook from the astro:head-metadata-build plugin. Head propagation modules are now identified by their module ID (?astroPropagatedAssets) instead of scanning every module's source code.

  • #17041 4c4a91c Thanks @iseraph-dev! - Fixes a bug where the advanced routing astro/hono / astro/fetch pages() handler returned the host framework's default Internal Server Error response instead of rendering the custom 500.astro page when a page threw during render. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way.

  • #17097 5e340d7 Thanks @iseraph-dev! - Fixes a bug where the advanced routing astro/hono / astro/fetch middleware() handler returned the host framework's default Internal Server Error response instead of rendering the custom 500.astro page when middleware threw. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way. Errors surfaced through next (the host framework's downstream chain) still propagate to the host's own error handler.

  • #17104 b074a37 Thanks @iseraph-dev! - Fixes the custom 500.astro page receiving an empty error prop when the error originated in middleware.

  • #17098 637a1b6 Thanks @matthewp! - Fixes internal Astro headers leaking from direct pages() handler responses

@astrojs/mdx@7.0.0-beta.3

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

@astrojs/preact@6.0.0-beta.2

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

@astrojs/react@6.0.0-beta.2

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

@astrojs/solid-js@7.0.0-beta.2

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

@astrojs/svelte@9.0.0-beta.4

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

@astrojs/vue@7.0.0-beta.2

Minor Changes

  • #17093 4585fe5 Thanks @Princesseuh! - Replaces the import entrypoint of getContainerRenderer()

    A new container-renderer entrypoint exporting getContainerRenderer() has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.

    If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the getContainerRenderer() import for React:

    - import { getContainerRenderer } from '@astrojs/react';
    + import { getContainerRenderer } from '@astrojs/react/container-renderer';

    Importing getContainerRenderer() from the package root still works, but is now deprecated and logs a warning.

withastro/astro

Close: #12175
Closes AST-188

Changes

When it defines a locale-specific page like /pt/404.astro,

  • Astro resolves it for missing /pt/* routes, returns the correct 404 status for /pt/404,
  • keeps normal routes like /docs/404 as normal pages.

Testing

Added focused unit tests for the new localized error route helpers and app routing behavior, then reviewed the generated test cases to make sure they match the existing test style and cover the important cases.

The tests cover:

  • selecting localized /404 and /500 routes when available
  • falling back to the global error route when no localized route exists
  • returning 404 for /pt/404
  • keeping normal localized pages like /pt/about as 200
  • keeping normal routes like /docs/404 as normal pages
  • fetching prerendered localized 404 pages from the correct output path

Docs

This may need docs because this PR fixes support for localized custom error pages

withastro/starlight

Description

EDIT: I removed the "fixes (issue number)", as that is a redirect issue and will be addressed separately (#3957).

I was doing a manual setup with an existing project to add Starlight. I first noticed the broken link for integrations, which this PR addresses. I also found I needed to setup Astro first, but there wasn't a link to it in the document.

This PR:

  • fixes the broken link for the integrations page on the manual setup page
  • adds a link to the manual setup for Astro in the neighboring content
  • makes these changes across all translations with a manual-setup.mdx page

I made these changes by hand, but relied on AI to identify the correct text to update for adding the manual setup for Astro link. I checked with translation tools and they look correct, but I cannot say I have 100% confidence the added link encapsulates the correct text perfectly across all languages. I have tested this locally.

This is my first contribution to Astro - let me know if you have any suggestions. I can always remove the added link for Manual Setup of Astro if desired. I realize a discussion may have been warranted for that type of improvement.

withastro/astro

Changes

  • Updates .changeset/config.json baseBranch from origin/next to origin/main. After the v7 release merged next into main, main is the active development branch. The stale origin/next ref causes pnpm changeset status to fail in CI workflows like preview releases.

Testing

  • No test changes. This is a config-only fix.

Docs

  • No docs needed.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

main is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on main.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

astro@7.0.0-beta.4

Major Changes

  • #16966 6650ec2 Thanks @Princesseuh! - Makes Sätteri the default Markdown processor

    Astro now renders .md files with satteri() from @astrojs/markdown-satteri, its native Markdown pipeline, instead of the remark/rehype pipeline. @astrojs/markdown-remark is no longer installed by default.

    To keep using the remark/rehype pipeline, install @astrojs/markdown-remark and set it as your processor:

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { unified } from '@astrojs/markdown-remark';
    
    export default defineConfig({
      markdown: {
        processor: unified(),
      },
    });

    The deprecated markdown.remarkPlugins, markdown.rehypePlugins, and markdown.remarkRehype options still work, but now require @astrojs/markdown-remark to be used.

Patch Changes

  • #17078 04547ec Thanks @astrobot-houston! - Fixes a spurious Astro.request.headers warning on prerendered pages when security.allowedDomains is configured. The internal allowedDomains header validation now skips prerendered routes, since they use synthetic requests with no real headers.

@astrojs/node@11.0.0-beta.2

Patch Changes

  • #17054 d426b67 Thanks @astrobot-houston! - Fixes an issue where Astro files with non-ASCII characters in their name weren't correctly served after the build.
withastro/astro

Changes

This PR updates our workflows to release form 6-legacy branch

Closes AST-180

Testing

Green CI

Docs

withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight-markdoc@0.7.0

Minor Changes

  • #3951 1202dd4 Thanks @HiDeoo! - ⚠️ BREAKING CHANGE: The minimum supported version of Starlight is now 0.41.0

    Please use the @astrojs/upgrade command to upgrade your project:

    npx @astrojs/upgrade

@astrojs/starlight@0.41.0

Minor Changes

  • #3951 1202dd4 Thanks @HiDeoo! - Adds support for Astro v7, drops support for Astro v6.

    Upgrade Astro and dependencies

    ⚠️ BREAKING CHANGE: Astro v6 is no longer supported. Make sure you update Astro and any other official integrations at the same time as updating Starlight:

    npx @astrojs/upgrade

    Community Starlight plugins and Astro integrations may also need to be manually updated to work with Astro v7. If you encounter any issues, please reach out to the plugin or integration author to see if it is a known issue or if an updated version is being worked on.

    ⚠️ BREAKING CHANGE: This release drops official support for Chromium-based browsers prior to version 111 (released 07 March 2023) and Safari-based browsers prior to version 16.4 (released 27 March 2023). You can find a list of currently supported browsers and their versions using this browserslist query.

Patch Changes

  • #3953 a935d33 Thanks @HiDeoo! - Fixes Starlight Markdown processing being potentially applied to files that should not be processed.
withastro/astro

Changes

  • Fixes spurious Astro.request.headers warning when using security.allowedDomains with prerendered pages. The warning was incorrectly triggered by Astro's internal forwarded header resolution, not by user code.
  • Skips the #applyForwardedHeaders() call for prerendered routes since forwarded headers are meaningless for synthetic requests with no real headers.

Testing

  • Added packages/astro/test/units/app/prerender-allowed-domains.test.ts with tests verifying no header access warnings for prerendered routes and that header access still works for non-prerendered routes.

Docs

  • No docs update needed, this fixes a bug without changing user-facing behavior.

Closes #17077

withastro/starlight

Description

This PR adds Persian as a new language for the Starlight documentation and includes the initial translated pages.

Changes

  • Added fa to the locales configuration in docs/astro.config.mjs, docs/lunaria.config.json

  • Added the Persian language subtag to .github/labeler.yml

  • Added the Persian translation for:

    • docs/src/content/docs/fa/index.mdx
    • docs/src/content/docs/fa/404.mdx

Notes

This is the initial step for Persian localization. Additional documentation pages will be translated and submitted in future pull requests.

withastro/astro

Changes

Adds logic for astro add cli command to create .gitignore entries for cloudflare and netlify.

See also roadmap discussion

output

Testing

Tested locally using pnpm pack on an empty astro repo. Also created new test file for the cli add command.

Screenshot 2026-06-28 at 3 59 26 PM

Docs

The logger provides enough of a guide, so additional docs don't seem necessary.

withastro/astro

https://github.com/gajus/zod-compiler compiles Zod schemas at build time to 2-75x time faster express

withastro/astro

Changes

Fixes component URL resolution when hydrated component entry chunks are merged by Rollup manualChunks.

When a user configures manualChunks() to merge framework component modules, Rollup can emit a small proxy chunk for the component entry that re-exports from the final manual chunk. Astro previously mapped the hydrated component entry to that proxy chunk, causing astro-island component-url to reference the intermediary chunk instead of the final merged chunk.

This change resolves those proxy entry chunks to the directly imported output chunk that contains the original component module, while preserving the existing chunk when it already contains the entry module.

Testing

Added regression coverage for a Vue hydrated component fixture with manualChunks(id) { if (id.includes('.vue')) return 'components'; }.

The test verifies that:

  • astro-island component-url points at the emitted components.*.js manual chunk
  • the manual chunk is emitted
  • generated HTML does not reference the intermediary Foo.*.js proxy chunk

Validation

  • rtk pnpm -C packages/astro build
  • rtk pnpm -C packages/integrations/vue exec astro-scripts test "test/app-entrypoint.test.ts" --match "manual chunk"
  • rtk pnpm -C packages/integrations/vue exec astro-scripts test "test/app-entrypoint.test.ts"
  • rtk git diff --check

Fixes #17016

withastro/astro

Changes

  • Refactored selector to target only elements with an explicit role attribute: selector:[role]:is(${[...a11y_implicit_semantics.keys()].join(',')},input)
  • Fixed logic (if (role === implicitRole) return false;) that was causing validation failures to be silently ignored.
  • Bypass redundant role warnings for implicit ARIA role exceptions.
if(
  (localName === 'ul' && role === 'list') || 
  (localName === 'ol' && role === 'list') || 
  (localName === 'li' && role === 'listitem')) 
{ 
  return false; 
}

Testing

  • Add e2e tests for a11y-no-redundant-roles
    • Added Playwright tests to verify the fixed dev toolbar audit rule under both violating and valid scenarios.
  • Verify 8 redundant role violation cases
    • Ensured that warnings properly trigger when explicit roles duplicate their implicit ARIA.
  • Verify 7 valid exception and override cases
    • Confirmed that no false are triggered for legitimate browser-bug workarounds and intentional role overrides.

Before implementation

スクリーンショット 2026-06-14 0 36 16

After implementation

スクリーンショット 2026-06-14 0 45 30

Docs

Close #17062
Close #15995

withastro/astro

Changes

  • Removes the unconditional redirectRoute inclusion from getRoutesForEnvironment in packages/astro/src/vite-plugin-pages/pages.ts. In hybrid mode (output: 'static' + adapter), this was pulling prerendered pages into the SSR bundle when they were targets of config-level redirects, inflating the bundle by orders of magnitude (reporter saw 68MB vs ~1.4MB).
  • This is a simpler alternative to #17061. That PR added a route.redirectRoute.prerender === isPrerender guard, which is correct but redundant — redirect targets already appear in the routes list with their own prerender flag and are included by the existing route.prerender === isPrerender check during their own iteration. Removing the block entirely is the right fix because:
    • SSR redirect responses are generated from route metadata alone (renderRedirect uses segments/pathname for the Location header). No component is loaded — AstroHandler.render() short-circuits before getModuleForRoute is ever called.
    • The only codepath that needs the target's component is prerendering (for getStaticPaths on dynamic targets), but prerendered redirects and their targets share the same prerender flag, so both are already in the prerender environment's route set.

Closes #17060
Supersedes #17061

Testing

  • Added packages/astro/test/units/redirects/environment-filtering.test.ts with 4 test cases covering all combinations of redirect/target prerender flags: SSR redirect to prerendered target (must exclude target from SSR set), prerendered redirect to prerendered target (both included), SSR redirect to SSR target (both included), prerendered redirect to SSR target (must exclude target from prerender set).

Docs

  • No docs update needed — this is an internal build optimization fix with no user-facing API change.
withastro/astro

Changes

Bump volar-service-* deps 0.0.70 → 0.0.71 to resolve yaml CVE-2026-33532

volar-service-yaml@0.0.70 pulls yaml-language-server@~1.20.0yaml@2.7.1,
which is vulnerable to CVE-2026-33532 (GHSA-48c2-rrv3-qjmp, stack-overflow DoS,
fixed in yaml 2.8.3). volar-service-yaml@0.0.71 moves to
yaml-language-server@~1.23.0yaml@2.8.3.

Bumping the full volar-service-* set in lockstep to keep them aligned against
@volar/* ~2.4.28. This propagates the fix down to @astrojs/check via its
@astrojs/language-server ^2.16.7 range, no change needed there.

Testing

No tests added; patch version bump of volar dependencies.

Docs

This should have zero user-facing effects beyond removing CVE-2026-33532 from
their security scanners.

withastro/starlight

Description

While implementing support for markdown.processedDirs in a Starlight plugin, I realized on our implementation was a bit fragile. I would guess nobody ever ran into this issue in practice as this behavior existed since we initially introduced heading links.

For example, a configured path like ./src/content/custom/ could process ./src/content/custom-test/index.md.

withastro/astro

Follow-up to #17049, fixing the E2E failures discussed in #17049 (comment). Root-cause analysis in #17049 (comment) — this PR targets the flue/fix-17047 branch so it can be merged into #17049 directly.

Changes

  • src/prerenderer.ts: drop the status-based failure check. It treated any non-2xx prerender response as a build failure, but pages may intentionally return non-2xx while prerendering — the E2E Cloudflare fixture's missing.astro returns new Response(null, { status: 404 }) in production builds, which is what broke the E2E jobs. The x-astro-prerender-error header is now the only failure signal.
  • src/utils/prerender.ts: add installPrerenderErrorPropagation(). The header path was previously dead code: app.render() never rejects on render errors because AstroHandler catches the throw and the production DefaultErrorHandler renders a 500 error page, so the buffering catch in handlePrerenderRequest never fired (the new test was passing only via the status check). This override replicates core's BuildErrorHandler semantics on the worker app: render errors (status 500 with an error and no response) are rethrown, while intentional error responses and 404s flow through the default handler unchanged.
  • src/utils/handler.ts: install the override when isPrerender is true.

Error flow for a genuine render crash: throw → AstroHandler catch → patched renderError rethrows → app.render rejects → handlePrerenderRequest catch → 500 + x-astro-prerender-error header → Node-side render() throws → build fails.

Testing

  • test/prerenderer-errors.test.ts passes (both tests) — a page throwing during prerendering still fails the build.
  • packages/astro/e2e/fixtures/cloudflare builds successfully again with the intentional 404 in missing.astro — the exact case that failed the E2E jobs.
  • Full adapter test suite results match a baseline run of the unmodified #17049 code.

🤖 Generated with Claude Code

withastro/astro

What's the problem?

Closes #17001.

With trailingSlash: "always" set, dynamic file endpoints like src/pages/api/[name].json.ts require a trailing slash in dev:

Path Expected Dev Build
/api/bar.json 200 404 200
/api/bar.json/ 404 200 404

Static file endpoints (/feed.xml) worked correctly. Only dynamic ones were broken.

Root cause

trailingSlashForPath in create-manifest.ts was:

type === 'endpoint' && pathname && hasFileExtension(pathname) ? 'never' : config.trailingSlash

For any route with a dynamic segment, pathname is null (it's only set for fully-static paths). The null short-circuits the && chain before hasFileExtension ever runs, so the function falls back to config.trailingSlash regardless of whether the route has a file extension.

The build path works differently — it resolves concrete paths from getStaticPaths params — which is why the mismatch only surfaces in dev.

Fix

Pass the segment-joined route string as a fallback when pathname is null. joinSegments always preserves file extensions even for dynamic segments:

// /api/[name].json.ts -> /api/[name].json
joinSegments(segments) // '/api/[name].json'
const trailingSlashForPath = (
	pathname: string | null,
	config: AstroConfig,
	type: 'page' | 'endpoint',
	route?: string,
): AstroConfig['trailingSlash'] =>
	type === 'endpoint' && hasFileExtension(pathname ?? route ?? '') ? 'never' : config.trailingSlash;

At all three call sites (createFileBasedRoutes, createRoutesFromEntriesByDir, createInjectedRoutes), route is now computed before the trailing slash call and passed through.

Testing

Added 'dynamic file endpoints force trailingSlash never regardless of config. issues#17001' to packages/astro/test/units/routing/manifest.test.ts. It covers:

  • dynamic file endpoint matches without trailing slash
  • dynamic file endpoint does not match with trailing slash
  • existing static file endpoint behavior is unchanged
withastro/astro

Changes

  • Fixes prerendered routes with non-ASCII characters (Thai, Korean, Cyrillic, etc.) incorrectly returning 301 redirects to trailing slash URLs despite trailingSlash: "never" setting
  • Adds guarded decodeURI() call before filesystem lookup in serve-static.ts so percent-encoded URLs match actual directory names on disk
  • Resolves SEO issues where canonical redirect loops cause search engines to drop non-ASCII pages

Testing

  • Added Thai test fixture สวัสดี.astro with prerendering enabled
  • Added test verifying non-ASCII prerendered route serves 200 without trailing slash
  • Added test verifying non-ASCII prerendered route with trailing slash redirects to no-slash URL

Docs

  • No docs update needed — this fixes existing documented trailingSlash: "never" behavior

Closes #16989

withastro/astro

Changes

  • Fixes image compilation regression for Cloudflare adapter users with static output. The image optimizer was looking in wrong directory (dist/_astro/ instead of dist/client/_astro/), causing ENOENT build failures.
  • Replaces hardcoded outDir usage with getClientOutputDirectory(settings) helper in prepareAssetsGenerationEnv() to respect preserveBuildClientDir setting.

Testing

  • Confirmed by issue reporter (@kitschpatrol) that the fix resolves the build failure
  • Existing core image test suite (207 tests) and preserve-build-client-dir tests (7 tests) continue to pass

Docs

  • No docs update needed, this fixes broken functionality to work as originally documented

Closes #16919

withastro/astro

Closes #17047

Changes

  • Pages that throw during prerendering now correctly fail the build with a non-zero exit code instead of silently emitting truncated HTML
  • The Cloudflare adapter now buffers the full response body inside workerd before returning it over HTTP, so streaming errors are caught and surfaced as 500 responses
  • Prerender error messages are included in a custom x-astro-prerender-error header and checked by the Node-side build process

When a component throws mid-render (e.g. an unregistered <Font> CSS variable), the previous implementation would commit HTTP status 200 before the stream completed. The stream would then abort, but the Node-side build process received the truncated body as a successful 200 response and wrote it to disk, causing astro build to exit 0 with broken output.

Testing

  • Added packages/integrations/cloudflare/test/prerenderer-errors.test.ts with test case 'fails the build when a page throws during prerendering'
  • Added prerenderer-render-error test fixture with a page that throws during rendering to verify the fix

Docs

  • No docs update needed — this fixes existing documented behavior (builds should fail when pages throw during prerendering)
withastro/astro

Changes

Follow up of withastro/compiler-rs#45

Updates astro to use the latest version of the compiler

Testing

Green CI

Docs

withastro/starlight

Description

This PR adds support for Astro 7.

Known issues

Symbol Status
🟢 Fixed
🟡 Fix pending review or release
🔴 No fix yet, under investigation
  • 🟢 Many warnings (1 in dev, 6 in build) due to the use of resolve.alias[].customResolver now deprecated and probably related to the Astro astro:tsconfig-alias vite plugin still using it.

    [WARN] [vite] `resolve.alias` contains an alias with `customResolver` option. This is deprecated and will be removed in Vite 9. Please use a custom plugin with a resolveId hook and `enforce: 'pre'` instead.
  • 🟢 Warning at build time due to some plugins taking significant time during the build process which is due to the pluginTimings built-in Rolldown check enabled by default. Feels weird in a release about speed to get warnings about some things being slow. Maybe we should disable this check by default?

    [WARN] [vite] [PLUGIN_TIMINGS] Your build spent significant time in plugins. Here is a breakdown:
    - @mdx-js/rolldown (41%)
    - astro:head-metadata-build (36%)
    - vite-plugin-astro-expressive-code (4%)
    - astro:server-islands (3%)
    - vite:css-post (2%)
  • 🟢 Type-checking issue in packages/starlight/integrations/remark-asides.ts

  • 🟢 Some duplicated heading IDs in the Sätteri databag

Remaining tasks

  • Investigate size-limit CSS size change (probably Vite 8/Rolldown related but need to see what and confirm the hypothesis)
  • Bump Astro dependencies to latest stable versions once released.
  • Bump some peer dependencies versions once ready to release.
  • Search and address any TODO(HiDeoo) comments.
  • Add changesets.
    • Make sure changesets use the correct new Starlight version number (e.g. 0.41.0).
    • Make sure to have updated links to v7 migration guide.
  • Update the banner link in docs/src/content/docs/index.mdx to point to the correct version tag.
  • Re-enable link validation
    • Will probably happen post-release once the plugin is updated
  • Merge with the following Lunaria directive:
    @lunaria-ignore:docs/src/content/docs/pt-pt/index.mdx;docs/src/content/docs/uk/index.mdx;docs/src/content/docs/zh-cn/index.mdx
    
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

@astrojs/alpinejs@1.0.0-beta.1

Patch Changes

@astrojs/cloudflare@14.0.0-beta.2

Patch Changes

@astrojs/markdoc@2.0.0-beta.1

Patch Changes

@astrojs/mdx@7.0.0-beta.2

Patch Changes

@astrojs/netlify@8.0.0-beta.1

Patch Changes

@astrojs/node@11.0.0-beta.1

Patch Changes

@astrojs/preact@6.0.0-beta.1

Patch Changes

@astrojs/react@6.0.0-beta.1

Patch Changes

@astrojs/solid-js@7.0.0-beta.1

Patch Changes

@astrojs/svelte@9.0.0-beta.3

Patch Changes

@astrojs/vercel@11.0.0-beta.1

Patch Changes

@astrojs/vue@7.0.0-beta.1

Patch Changes

@astrojs/markdown-satteri@0.3.1-beta.1

Patch Changes

withastro/astro

Changes

  • Fixes CSS incorrectly loading on pages that don't import it when using client:only islands in production builds
  • Adds missing isCSSRequest filter to the client:only CSS association loop in plugin-css.ts, matching the existing filter in the non-client:only code path
  • Prevents CSS from unrelated modules being associated with pages when Rollup bundles pure utility modules into the same chunk as CSS-importing modules
  • Reporter confirmed dramatic CSS reductions: frontpage went from 14 stylesheets/718.2 KB to 1 stylesheet/481.2 KB

Testing

  • Adds reproduction test client-only-css-chunk-leak.test.ts that verifies CSS is only loaded on pages that actually import it

Docs

  • No docs update needed — this fixes incorrect behavior to match documented CSS scoping for islands

Closes #17043

withastro/astro

Changes

Under experimental.advancedRouting, the composable astro/hono pages() handler delegated straight to PagesHandler.handle(), with none of the app-level error handling that AstroHandler provides on the standard path. A page throwing during render therefore propagated to the host framework, which answered with its own default error response (Hono: plain-text Internal Server Error) instead of rendering the custom 500.astro page (#16952).

  • Added PagesHandler.handleWithErrorFallback(app, state) (core/pages/handler.ts): wraps route dispatch in a try/catch that logs the error and renders it via app.renderError(..., { status: 500, error }), the same behaviour AstroHandler.render() has on the standard path. Forwarding error keeps the documented error prop on 500.astro working. The logic lives in the handler module because core/fetch/index.ts deliberately keeps its exports as thin, logic-free wrappers.
  • When no route matched, handleWithErrorFallback() returns a 404 response marked with X-Astro-Error (the same pattern handle() uses for the un-dispatched redirect case), so the app's existing post-check renders the 404 error page a level up. This case is reachable because #16911 intentionally leaves routeData unset when the custom 404 route is prerendered (or absent), where PagesHandler.handle() would otherwise throw a TypeError and the catch would turn those unmatched requests into 500s. The 500 path keeps calling renderError() directly: the marker cannot carry the error object, so 500.astro would lose its error prop and the stack would not be logged.
  • Switched pages() in core/fetch/index.ts to delegate to the new method (delegation only, no logic added).
  • Added a changeset (astro patch).

Testing

  • Added 'renders the custom 500 page when a page throws during render' - verifies the full pages() pipeline returns HTTP 500 with the custom 500 page content when a page throws during render
  • Added 'returns a marked 404 for the app post-check when the custom 404 route is prerendered' - verifies unmatched requests return HTTP 404 carrying the X-Astro-Error marker instead of throwing
  • Verified end-to-end against the reproduction from #16952 (Hono + app.use(pages()), node standalone): GET /boom -> 500 with the custom 500 page (previously Hono's plain-text Internal Server Error), GET /does-not-exist -> 404 serving the prerendered 404 page with the marker stripped from the final response, GET / -> 200; the thrown error is logged with its stack trace

Out of scope (noticed while testing)

Errors thrown by Astro middleware itself (src/middleware.ts) on the composable path (app.use(middleware())) still reach the host framework's default error handler instead of 500.astro. On the standard path, AstroHandler.render()'s try/catch wraps the whole middleware chain, so middleware errors get the same 500.astro treatment. The equivalent catch can't simply live inside the composable middleware(): its next is the host's next(), so it would also intercept errors thrown by host middleware running below it in the chain and hide them from the user's own app.onError. The all-in-one astro() handler already provides full parity. A possible follow-up would be an error-page helper exported from astro/hono (e.g. app.onError(onError())) so composable users can opt in - happy to open a separate issue for this.

Docs

  • No docs update needed, this fixes broken functionality in an experimental feature so it matches the documented custom-error-page behavior (the 404 counterpart was #16911)

Closes #16952

withastro/astro

Closes #17039

Changes

  • Middleware file watcher now triggers HMR for files inside the src/middleware/ directory, not just src/middleware.{ts,js} files directly
  • Updates the Vite plugin path matcher to include both middleware. (existing) and middleware/ (new) prefixes, matching the fix pattern used for actions in #16932

Testing

  • Added packages/astro/test/units/middleware/middleware-hmr.test.ts with 8 test cases covering path matching for both file and directory patterns

Docs

  • No docs update needed — this fixes existing documented behavior that was broken for directory-based middleware organization
withastro/astro

What this fixes

joinPaths() in @astrojs/internal-helpers filters out non-string arguments (so callers can pass undefined for optional segments), but the branch that detects the last segment compared the map index against the original, unfiltered argument count:

export function joinPaths(...paths: (string | undefined)[]) {
	return paths
		.filter(isString)
		.map((path, i) => {
			if (i === 0) {
				return removeTrailingForwardSlash(path);
			} else if (i === paths.length - 1) { // ← unfiltered length
				return removeLeadingForwardSlash(path);
			} else {
				return trimSlashes(path);
			}
		})
		.join('/');
}

When a skipped (undefined) argument precedes the final one, the filtered array is shorter than paths.length, so the real last segment no longer matches i === paths.length - 1. It falls into the else branch and gets trimSlashes(), which strips its trailing slash — even though joinPaths is supposed to preserve the trailing slash of the last segment (that is exactly what removeLeadingForwardSlash does, as opposed to trimSlashes).

Reproduction

joinPaths('/base', undefined, 'docs/setup/'); // → '/base/docs/setup'  (expected '/base/docs/setup/')
joinPaths('/foo', 'bar/', undefined);         // → '/foo/bar'          (expected '/foo/bar/')
joinPaths(undefined, 'pl', 'docs/setup/');    // → 'pl/docs/setup'     (expected 'pl/docs/setup/')

The invariant being violated: a skipped argument should yield the same result as omitting it. joinPaths(a, undefined, b) must equal joinPaths(a, b).

The fix

Compute the filtered array once and base the last-segment check on its length:

export function joinPaths(...paths: (string | undefined)[]) {
	const segments = paths.filter(isString);
	return segments
		.map((path, i) => {
			if (i === 0) {
				return removeTrailingForwardSlash(path);
			} else if (i === segments.length - 1) {
				return removeLeadingForwardSlash(path);
			} else {
				return trimSlashes(path);
			}
		})
		.join('/');
}

Calls where every argument is a string (the vast majority of call sites, e.g. joinPaths(base, route)) are unchanged, since segments.length === paths.length in that case.

This is the underlying utility bug behind the prependWith-with-trailing-slash case noted in the root-cause analysis of #17034 (where getLocaleRelativeUrl calls joinPaths(base, prependWith, locale, path) with prependWith possibly undefined).

Tests

Added a joinPaths test block to packages/internal-helpers/test/path.test.ts covering simple joins, inner-slash trimming, and the skipped-argument regression. The new assertions fail on main and pass with this change.

  • pnpm --filter @astrojs/internal-helpers test → 64 passing
  • astro i18n unit tests (test/units/i18n/astro_i18n.test.ts) → 37 passing, unchanged

Related: #17034

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.4.7

Patch Changes

  • #17035 197e50e Thanks @astrobot-houston! - Fixes getRelativeLocaleUrl, getAbsoluteLocaleUrl, and getAbsoluteLocaleUrlList to strip trailing slashes when trailingSlash: 'never' is configured

  • #16967 3719765 Thanks @astrobot-houston! - Fixes double URL-encoded paths returning 400 Bad Request on on-demand routes

    Previously, any URL containing a double-encoded character (like %255B, which is [ encoded twice) was unconditionally rejected with a 400 Bad Request before middleware or route handlers could run. This broke embedded tools like Sanity Studio whose client-side router legitimately produces double-encoded URLs.

    The fix replaces the rejection approach with iterative decoding — multi-level percent-encoding is now fully resolved to its canonical form before being passed to middleware and route matching. This preserves the security fix for CVE-2025-66202 (middleware authorization bypass via double encoding) because middleware now always sees the fully decoded path, making bypass impossible. For example, /api/%2561dmin is decoded to /api/admin, which middleware can correctly block.

  • #17066 2f4d92a Thanks @matthewp! - Fixes prerendered redirect targets being incorrectly bundled into the SSR function in hybrid mode, causing massive bundle size inflation

  • #16882 621beb7 Thanks @jettwayio! - fix(render): honour compressHTML when joining head elements

  • #16892 8d753b0 Thanks @astrobot-houston! - Fixes custom elements in MDX having their children's slot attribute stripped by the JSX runtime

    When custom elements (tags with hyphens like <my-element>) are used in MDX files, the slot HTML attribute on their children is now correctly preserved. Previously, the shared JSX runtime would treat slot as an Astro slot assignment and remove it from the output, breaking Shadow DOM named slot distribution for web components.

  • #16957 544ee76 Thanks @thelazylamaGit! - Fixes stale inline CSS in server-rendered HTML after CSS file edits during dev

    When editing a CSS file (.css, .scss, etc.) during development, the inline <style> tags in server-rendered HTML would retain old CSS content instead of updating. This caused a brief flash of old CSS (FOUC) on fresh page loads before Vite's client-side HMR corrected the styles.

    The fix ensures that Astro's per-route dev CSS virtual modules are invalidated in both the SSR module graph and the module runner's evaluation cache when a style file changes, so the next page render picks up the fresh CSS.

  • #17044 2220d22 Thanks @astrobot-houston! - Fixes CSS from client:only islands leaking to unrelated pages when Rollup bundles non-CSS-importing modules into the same chunk as CSS-importing modules

  • #17040 7c4763d Thanks @astrobot-houston! - Fixes HMR not triggering for files inside the src/middleware/ directory during dev

  • #16672 52fc862 Thanks @martinheidegger! - Fixes support for numeric IDs in YAML frontmatter when using content collection references

  • #16762 9de80ae Thanks @alexanderdombroski! - Adds a JSON schema to the Wrangler configuration file generated when running astro add cloudflare

  • #17046 ef771ec Thanks @ematipico! - Improves the diagnostics emitted when Astro parses incorrect .astro files.

withastro/starlight

Description

Follow-up to #3923 (comment), this PR improves the comment describing how we import the Sätteri integration to workaround an Astro limitation.

The comment should be clear enough to explain the issue and the workaround. Regarding a potential fix in Astro for the underlying issue, I would definitelly need to think more about it. So far, the only idea I had is to have the Vite module runner used for loading the config to stay open instead of closing immediately, and return some kind of disposable. The disposable would be used to properly close it only when needed, e.g. after a build or when the dev server is closed, when the dev server is restarted, after a sync, etc. The getViteConfig() case would also need to be handled. On top of the extra cost of keeping the module runner open, it's also a lot of orchestration to pass around such disposable with the current architecture.

A workaround that I only briefly tested for fun is to by-pass Vite and rely on a native dynamic import to load @astrojs/markdown-satteri, e.g. using () => new Function('return import("@astrojs/markdown-satteri")') but that's definitely not something we want to do (even tho it seems to work) 😅

withastro/astro

Changes

  • Validates the request URL origin against allowedDomains in default-handler.ts before constructing the statusURL used to fetch prerendered error pages. When allowedDomains is configured and the host matches, the original origin is used. Otherwise, falls back to localhost.

Testing

  • Added two test cases to error-pages.test.ts: one verifying an unvalidated host is replaced with localhost, and one verifying a validated host is preserved.

Docs

  • No docs update needed.
withastro/astro

Changes

This PR restore our "Needs repro" workflow by using the gh CLI. To note that I used the houston bot token.

Testing

I already created a similar workflow and it should work for us.
Green CI

Docs

N/A

withastro/astro

Changes

  • Prevent @astrojs/upgrade from selecting a requested dist-tag when that tag resolves to a version older than the currently installed package.
  • Preserve prerelease comparison for installed versions like 7.0.0-beta.3 instead of coercing them to stable versions.
  • Add a regression test covering astro upgrading to beta while an integration remains on a newer stable release.

Why

Running @astrojs/upgrade beta could downgrade companion packages when their beta dist-tag pointed to an older prerelease than the currently installed stable version.

Fixes #17024

Testing

  • npx --yes pnpm@11.0.9 -C packages/upgrade build
  • npx --yes pnpm@11.0.9 -C packages/upgrade test
  • npx --yes pnpm@11.0.9 lint
withastro/astro

Changes

  • Validates attribute names in addAttribute() against the HTML spec before interpolating them into output. Keys containing characters invalid in attribute names (", ', >, /, =, or whitespace) are silently dropped.

Testing

  • Added addAttribute rejects invalid attribute keys suite (7 cases) covering keys with ", spaces, >, ', =, plus positive cases for normal names (data-foo, aria-label) and namespaced attributes (on:click, xmlns:happy).
  • Added spreadAttributes rejects invalid attribute keys suite (1 case) verifying invalid keys are dropped while valid siblings are preserved.

Docs

  • No docs update needed. Internal rendering hardening with no user-facing API change.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.4.6

Patch Changes

  • #16765 b10e86e Thanks @fkatsuhiro! - Fixes an issue where renaming an image file while the dev server is running triggers a build error. Now Astro correctly hot-reloads the image without crashing.

  • #17026 add3df1 Thanks @matthewp! - Hardens addAttribute to drop attribute names containing characters that are invalid per the HTML spec (", ', >, /, =, whitespace)

  • #17033 ffda27b Thanks @matthewp! - Validates the request origin against allowedDomains before fetching prerendered error pages. When allowedDomains is configured and the Host header matches, the original origin is used. Otherwise, the fetch falls back to localhost.

@astrojs/netlify@7.0.13

Patch Changes

  • #17018 1310277 Thanks @matthewp! - Hardens remotePatterns regex generation to match canonical wildcard semantics more strictly

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3
withastro/astro

Changes

Prevents an unnecessary page reload during ClientRouter back/forward navigations when the browser's Navigation API handles the transition before popstate fires, making from and to the same URL.

Testing

Update: Added a test case that reproduces the bug by pushing a fake history entry, then pressing back


Manually tested in an Astro project where /work and its sub-routes (e.g. /work/slug-1) run as a SPA kept in sync via the browser's Navigation API. Other routes like /about or / still trigger Astro's View Transitions normally. For /work and its sub-routes:

  1. Listening to astro:before-preparation and calling preventDefault() when both the current and destination URLs are under /work, which stops ClientRouter from fetching new HTML.

  2. Using the browser's Navigation API (event.intercept()) to update the app's own state instead.

Pressing back from /work/slug-1 to /work/ causes two things to happen in sequence:

  1. The browser's Navigation API fires. Our event.intercept() handler runs, updating the app's state and changing the URL to /work/.

  2. popstate fires. The ClientRouter's onPopState calls transition().

At this point the URL is already /work/. But originalLocation was never updated to /work/slug-1 — because that earlier forward navigation was done via nav.navigate(), which the ClientRouter doesn't track. So transition() sees from as /work/ (the stale originalLocation) and to as /work/ (the current URL). They match, the ClientRouter thinks nothing changed, falls through to doPreparation(), and eventually calls location.href = to.href, which triggers an unnecessary full page navigation.

With the guard added to transition(), from.href === to.href returns early before any of that happens, and the full page navigation never occurs.

Docs

No docs needed. Bugfix with no API or behavior change.

withastro/astro

Changes

  • Tightens the regex produced by remotePatternToRegex so wildcard hostname and pathname patterns match strictly:
    • *. requires exactly one subdomain (bare apex no longer matches)
    • **. requires one or more subdomains (zero subdomains no longer matches)
    • /* matches a single path segment only, not deeper paths
  • Anchors the generated regex with $ so partial prefix matches are not possible

Testing

  • Updated existing assertion: *.example.org no longer matches bare example.org
  • Added assertion: /images/* does not match /images/a/b.jpg

Docs

  • No docs update needed -- this is an internal regex generation change with no user-facing API impact.
withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.40.0

Minor Changes

  • #3923 edf2e6b Thanks @Princesseuh! - Adds support for Astro 6.4 and the new Sätteri Markdown processor.

    It is now possible to opt into using Astro's 6.4 Sätteri Markdown processor by installing the @astrojs/markdown-satteri package and configuring it in your astro.config.mjs file:

    // astro.config.mjs
    
    import { defineConfig } from 'astro/config';
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri(),
      },
    });

    ⚠️ BREAKING CHANGE: The minimum supported version of Astro is now v6.4.5.

    Please update Starlight and Astro together:

    npx @astrojs/upgrade

    Community Starlight plugins and Astro integrations may also need to be manually updated to work with Sätteri. If you encounter any issues, please reach out to the plugin or integration author to see if it is a known issue or if an updated version is being worked on.

Patch Changes

withastro/astro

Changes

  • Disables persist-credentials on the checkout step in the issue triage workflow. When credentials are persisted, git always uses the default GITHUB_TOKEN (read-only) for pushes, even when gitPush() explicitly provides FREDKBOT_GITHUB_TOKEN. Disabling it lets the privileged token be used as intended.

Testing

Docs

  • No docs needed — CI-only change.
withastro/astro

Changes

Fixes the version of the logger

Testing

N/A

Docs

/cc @withastro/maintainers-docs for feedback!

withastro/astro

Changes

Remove the source code of deprecated package @astrojs/db. Remove related astro db CLI commands.

Testing

The actions-blog and actions-react-19 e2e fixtures only used @astrojs/db as a data store, so they now use a tiny file-backed JSON store instead.

Docs

Added a Changesets file to mention that some CLI commands like astro db are gone.

withastro/astro

IMPORTANT: THIS MUST BE DONE AS A MERGE COMMIT

Changes

  • Merges main into next to bring in latest stable releases (astro 6.4.5, @astrojs/db 0.21.3, @astrojs/cloudflare 13.7.0, @astrojs/mdx 6.0.3, @astrojs/node 10.1.4, @astrojs/markdown-satteri 0.3.0).
  • Conflict resolution kept next versions for all package.json files and merged CHANGELOG entries (alpha entries first, then stable entries).

Testing

  • No test changes. This is a branch merge with only version/changelog conflicts.

Docs

  • No docs needed. Version merges do not affect user-facing documentation.
withastro/astro

Changes

Closes AST-179

This PR exists from prerelease mode.

Note

To merge on the day of when we merge all PRs, right before the release

Testing

N/A

Docs

N/A

withastro/astro

Changes

  • Update all session.skill() calls to use registered skill names instead of file paths (e.g. triage/reproduce.md -> triage). Flue runtime 0.8 removed support for addressing sub-files within a skill; the old runtime was lax about this.
  • Pass step and instructions via args to direct the agent to the correct sub-skill file within each skill.
  • Add .agents/skills/merge/SKILL.md so the merge skill gets discovered by the runtime (it had no SKILL.md and was invisible).

Testing

  • No test changes. This is a CI workflow fix — validation is the next issue triage run succeeding instead of failing with Skill "triage/reproduce.md" not registered.

Docs

  • No docs needed. Internal CI workflow change only.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

astro@7.0.0-beta.3

Major Changes

  • #17010 0606073 Thanks @ocavue! - Removes the astro db, astro login, astro logout, astro link, and astro init CLI commands.

    The @astrojs/db package is now deprecated. We recommend using a database client (Drizzle, Kysely, etc.) directly instead.

  • #16877 3b7d76e Thanks @matthewp! - Enables advanced routing by default.

    The advanced routing feature introduced behind a flag in v6.3.0 is no longer experimental and is now enabled by default.

    This gives full control over how requests flow through your application, with first-class support for frameworks like Hono.

    Advanced routing now uses src/fetch.ts as default entrypoint instead of src/app.ts.

    If you were previously using this feature without a custom entrypoint, please configure fetchFile or rename your entrypoint to src/fetch.ts, and then remove the experimental flag from your Astro config:

    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      experimental {
    -    advancedRouting: true,
      },
    +  fetchFile: 'app.ts' // optional, you only need this if you cannot rename your entrypoint.
    });

    fetchFile is now a top-level config option instead of being nested under experimental.advancedRouting. If you were using a custom entrypoint, please update your Astro config to move its configuration:

    // astro.config.mjs
    export default defineConfig({
    -  experimental: {
    -    advancedRouting: {
    -      fetchFile: 'my-custom-entrypoint.ts',
    -    },
    -  },
    +  fetchFile: 'my-custom-entrypoint.ts',
    })

    You can also set fetchFile: null to disable the entrypoint if you are using src/fetch.ts for another purpose, or don’t need advanced routing features.

    If you have been waiting for stabilization before using advanced routing, you can now do so.

    Please see the advanced routing guide in docs for more about this feature.

Minor Changes

  • #16998 57dcc31 Thanks @matthewp! - Exposes getFetchState() from astro/hono as a public API

    The getFetchState() function retrieves or lazily creates a FetchState from a Hono context object. This allows third-party packages to build Hono middleware that interacts with Astro's per-request state, giving the astro/hono API the same extensibility as astro/fetch.

    import { Hono } from 'hono';
    import { getFetchState, pages } from 'astro/hono';
    
    const app = new Hono();
    
    app.use(async (context, next) => {
      const state = getFetchState(context);
      state.locals.message = 'Hello from custom middleware';
      await next();
    });
    
    app.use(pages());
    
    export default app;
  • #16996 300641e Thanks @florian-lefebvre! - Adds a subset field to the FontData type exposed via fontData from astro:assets. When using multiple font subsets (e.g., subsets: ["latin", "korean"]), each font data entry now includes the subset name, making it possible to distinguish between font entries for different subsets that share the same weight and style.

  • #16745 f864a80 Thanks @ematipico! - The custom logger feature introduced behind a flag in v6.2.0 is no longer experimental and is available for general use.

    This feature provides better control over Astro's logging infrastructure by allowing you to replace the default console output with custom logging implementations (e.g., structured JSON). This is particularly useful for on-demand rendering when connecting to log aggregation services such as Kibana, Logstash, CloudWatch, Grafana, or Loki.

    Astro provides three built-in log handlers (json, node, and console), and you can also create your own.

    JSON logging

    import { defineConfig, logHandlers } from 'astro/config';
    
    export default defineConfig({
      logger: logHandlers.json({
        pretty: true,
        level: 'warn',
      }),
    });

    Custom logger

    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      logger: {
        entrypoint: '@org/custom-logger',
      },
    });

    Additionally, context.logger is now always available in API routes and middleware, even without a custom logger configured.

    If you were previously using this feature, please remove the experimental flag from your Astro config:

    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
    -  experimental: {
    -    logger: {
    -      entrypoint: '@org/custom-logger',
    -    },
    -  },
    +  logger: {
    +    entrypoint: '@org/custom-logger',
    +  },
    });

    If you have been waiting for stabilization before using custom loggers, you can now do so.

    Please see the Logger docs for more about this feature.

  • #16981 0d6d644 Thanks @ematipico! - Removes the setting experimental.queuedRendering. The new rendering engine is now stable and replaces the old one.

    As part of the stabilization, the queued rendering has been improved, and some features have been removed:

    • The construction of the queue has been removed, instead now Astro uses a streaming approach where components are rendered and flushed as they are encountered.
    • The node polling feature has been removed because it doesn't yield concrete savings.
    • The content cache has been descoped, and how only tag names are cached.
      If you were previously using this experimental feature, you must remove this experimental flag from your configuration as it no longer exists:
    // astro.config.mjs
    import { defineConfig } from "astro/config";
    
    export default defineConfig({
      experimental: {
    -    queuedRendering: {}
      }
    });
withastro/astro

Changes

Exports the existing getFetchState() function from astro/hono so third-party packages and custom Hono middleware can access the per-request FetchState.

Testing

Added 3 unit tests in test/units/hono/index.test.ts:

  • Creates a new FetchState from Hono context
  • Returns the same instance on subsequent calls
  • Custom middleware can set locals via getFetchState

Docs

withastro/docs#14021

withastro/astro

Changes

  • Reverts #16720, which added a Cloudflare-Workers navigator check to the isNode variable in packages/astro/src/runtime/server/render/util.ts. This change caused isNode to return false inside workerd, forcing the rendering pipeline onto renderToReadableStream instead of renderToAsyncIterable. For large prerendered sites on Cloudflare (~14k pages), this caused a ~6x build time regression (6-7 min to 40 min).

Testing

  • No new tests. The regression is workerd-specific and only manifests at scale with the Cloudflare adapter. A preview release should be created so the reporter on #16973 can verify the fix.

Docs

  • No docs needed — this restores previous behavior.
withastro/astro

Changes

Testing

Updated

Docs

withastro/astro

I’ve tested the latest version of the LSP server locally and found a regression in quick fixes.
Related to: #15908

Screenshot 2026-06-08 at 2 31 13 PM Screenshot 2026-06-08 at 2 31 31 PM

Changes

  • Restores the Add import quick fix for unimported .astro components with generated TypeScript names ending in AstroComponent

Testing

  • Added coverage for local, nested, aliased, and generated-name Astro components.
  • Tests verify that imports omit the AstroComponent suffix, keep the original diagnostic, and edit the source .astro file.
withastro/astro

Changes

Fixes #14657.

Allows the Astro grammar to keep looking for lang / type on following lines of <style> and <script> opening tags.

Added grammar fixtures for multiline style languages (scss, less, sass) and script types (ts, application/ld+json, module).

Testing

  • corepack pnpm -C packages/language-tools/vscode test:grammar

Docs

No docs needed, syntax highlighting fix.

withastro/astro

Fixes #16974 so startup message shows on experimental.logger destination, not default terminal.

Currently it fires before any request arrives, always missing the user-configured destination.

Changes

  • Adds getLogger().then() before logListeningOn to correct destination.
  • Makes adapterLogger re-create itself when the underlying logger instance changes, so it always reflects the resolved logger regardless of first access

Testing

Before Implementation

Screenshot 2026-06-08 at 3 07 04 AM

After Implementation

Screenshot 2026-06-08 at 3 11 34 AM

Confirmed the first line of stdout is {"message":"Server listening on ...","label":"@astrojs/node","level":"info"} when manually building standalone server.

Added unit test verifying that adapterLogger re-creates itself when pipeline.getLogger() replaces the underlying logger instance. Both packages build without errors.

Docs

No update needed.

withastro/starlight

This PR fixes a typo I spotted in the first place:

Cummunity-Themes -> Community-Themes

It also makes minor changes to 3 sentences to improve their readability.

withastro/starlight

This PR contains the following updates:

Package Type Update Change
actions/checkout action patch v6.0.2v6.0.3
changesets/action action minor v1.8.0v1.9.0

Release Notes

actions/checkout (actions/checkout)

v6.0.3

Compare Source

changesets/action (changesets/action)

v1.9.0

Compare Source

Minor Changes
Patch Changes
  • #​535 34f64f6 Thanks @​Andarist! - Fixed an issue with GitHub releases not being created for successfully published packages when some packages failed to be published to the registry.

  • #​632 1d54b9e Thanks @​bluwy! - Simplify internal implementation to get changelog entries for a package version

  • #​629 e0c90aa Thanks @​bluwy! - Fix custom version and publish command argument parsing

  • #​645 f9585d9 Thanks @​Andarist! - Improved force-push handling when using commitMode: "github-api" so updating an existing branch no longer temporarily resets the target branch to the base commit, avoiding cases where GitHub closes open pull requests during the update. This should remove a possibility of a GitHub state race that caused the force-pushed PRs not being reopened.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

withastro/astro

Changes

  • defineProviderGetters in FetchState now always defines a session getter on context objects, even when no session provider has been registered. This ensures Astro.session / ctx.session is always a present property regardless of whether the sessions() handler is included in the pipeline.
  • Accessing session without storage configured logs a warning on first access: "no session storage is configured. Either configure the storage manually or use an adapter that provides session storage." This restores the helpful warning that existed in 6.2 before the advanced routing refactor moved session setup into the provider system.

Testing

  • Added two unit tests in test/units/fetch/index.test.ts: one verifying the property is always present (returning undefined), one verifying the warn-once behavior with SpyLogger.

Docs

  • No docs needed. No user-facing API change.
withastro/astro

Changes

Closes AST-166

This PR stabilise queued rendering, however there's a twist! The old queued engine is gone, and it's been replaced with a "streaming engine".

This engine is still queue based, but it renders the nodes on the go, so a two-step engine is now a one step engine. The old strategy didn't work very well, and it had some flaws. The new strategy is simpler and faster than the queue double pass and recursion.

Also, I removed pooling. It doesn't yield concrete benefits.

As for content cache, it is now scoped so the single tag names. The content itself isn't cached, as well as the attributes.

Testing

Green CI

Docs

withastro/docs#14019

withastro/astro

Changes

  • Removes provide, resolve, and finalizeAll from the public AstroFetchState interface. The concrete FetchState class retains them for internal use (session, cache).
  • Removes App.Providers module augmentation interface — nothing depends on it since built-in providers are already explicitly typed on APIContext.
  • Removes extends App.Providers from APIContext.
  • Marks ContextProvider<T> as @internal.

Per the discussion on #16877, we do not want 3rd-party handlers extending the Astro global via FetchState#provide yet. This keeps the machinery working internally while removing it from the public API surface.

Testing

No test changes — existing context provider tests continue to pass since the internal class is unchanged.

Docs

withastro/astro

Summary

Three JSDoc example blocks in errors-data.ts — for InvalidGetStaticPathParam, InvalidGetStaticPathsEntry, and InvalidGetStaticPathsReturn — open with the route file pages/blog/[id].astro but then return params keyed on slug:

{ params: { slug: "blog" } },
{ params: { slug: "about" } }

For a dynamic route [id].astro, getStaticPaths() must return params keyed on the segment name id. A slug key would itself trigger a routing error, so these examples — whose whole purpose is to teach correct getStaticPaths() usage — are internally inconsistent.

These JSDoc blocks also generate the public Error Reference pages, so the mismatch is user-facing (e.g. docs.astro.build/en/reference/errors/invalid-get-static-path-param/).

Change

Replace the slug param key with id in all six lines so each example matches its declared [id].astro route. This matches the neighboring blocks GetStaticPathsExpectedParams and GetStaticPathsInvalidRouteParam, which already use { params: { id: ... } } for an [id] route.

Docs/comment example only — no runtime code changes.

withastro/astro

Summary

Preserve empty request cookie values in Astro.cookies.get().

Related: #16983

User-facing problem

A request with Cookie: foo= currently produces two conflicting results:

  • cookies.has('foo') === true
  • cookies.get('foo') === undefined

That collapses present but empty into the same result as missing.

Expected behavior

For Cookie: foo=, cookies.get('foo')?.value should be ''.

Why this looks like a regression

I opened #16983 with a minimal reproduction and the full history.

Short version: get() used to preserve empty values, then started checking if (value) in c69bf18a4e on 2025-03-18. A later refactor in 018fbe90f4 on 2025-03-26 made the has() / get() mismatch more obvious.

What changed

  • treat '' as a valid parsed cookie value in Astro.cookies.get()
  • add a regression test for cookie: 'foo='

Validation

  • npm exec --yes pnpm@11.5.0 -- exec astro-scripts test "test/units/cookies/*.test.ts" --strip-types --teardown ./test/units/teardown.ts
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/cloudflare@13.7.0

Minor Changes

  • #16571 d4b0cd1 Thanks @MA2153! - Sets immutable cache headers for static assets

    Static assets under _astro can be cached to improve performance. The adapter now automatically injects a Cache-Control header at build time when possible.

Patch Changes

  • #16968 7a5c001 Thanks @astrobot-houston! - Fixes a build crash when using experimental.advancedRouting with a custom fetchFile that statically imports cf from @astrojs/cloudflare/fetch. The circular dependency between @astrojs/cloudflare/fetch and astro/app/entrypoint caused createApp or createGetEnv to be undefined at module evaluation time. Initialization is now deferred to the first cf() call, breaking the cycle.

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/markdown-satteri@0.3.0

Minor Changes

  • #16969 4a31f90 Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Setting markdown.syntaxHighlight to 'prism' now highlights your code blocks with Prism.

    // astro.config.mjs
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri(),
        syntaxHighlight: 'prism',
      },
    });

astro@6.4.5

Patch Changes

  • #16985 4ecff32 Thanks @maximslo! - Fixes the experimental.logger destination not being used for the "Server listening on..." startup message. The logger is now resolved before the server starts listening, and adapterLogger re-creates itself when the underlying logger changes so the startup message uses the correct destination.

  • #16947 e0703a6 Thanks @ematipico! - Fixes Astro.request.url not reflecting validated X-Forwarded-Proto/X-Forwarded-Host headers when security.allowedDomains is configured. Previously, only Astro.url was updated with the forwarded origin while Astro.request.url retained the socket-derived URL, causing the two to diverge behind TLS-terminating proxies.

  • #16997 dc45246 Thanks @matthewp! - Reverts a change to isNode runtime detection that caused a significant build time regression for Cloudflare adapter users with large prerendered sites

@astrojs/db@0.21.3

Patch Changes

  • #16964 b048826 Thanks @Princesseuh! - Deprecates the @astrojs/db integration. We no longer have the bandwidth to maintain this package, and we recommend that users directly use the database client of their choice (Drizzle, Kysely, etc.) in their Astro projects instead.

@astrojs/mdx@6.0.3

Patch Changes

  • #16969 4a31f90 Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Setting markdown.syntaxHighlight to 'prism' now highlights your code blocks with Prism.

    // astro.config.mjs
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri(),
        syntaxHighlight: 'prism',
      },
    });

@astrojs/node@10.1.4

Patch Changes

  • #16985 4ecff32 Thanks @maximslo! - Fixes the experimental.logger destination not being used for the "Server listening on..." startup message. The logger is now resolved before the server starts listening, and adapterLogger re-creates itself when the underlying logger changes so the startup message uses the correct destination.
withastro/astro

Changes

Update a test so that it can pass in the next branch (with @astrojs/compiler-rs).

@astrojs/compiler-rs re-serializes CSS values from rgb(255, 165, 0) to orange to this test didn't pass in the next branch.

Unblock #16962

Testing

rgb(0, 255, 0) CI.

Docs

N/A

withastro/astro

Changes

What the title says, it's easy enough, just needed hooking things up

Testing

Added / updated tests

Docs

N/A, I don't think we documented that it didn't work right now

withastro/astro

Changes

  • Defers createApp() and setGetEnv() initialization in @astrojs/cloudflare/fetch to the first cf() call
  • Prevents circular import crashes when custom fetchFile statically imports cf from @astrojs/cloudflare/fetch
  • Uses lazy initialization pattern via ensureInitialized() helper to break the module evaluation cycle

Testing

  • Added fetch-lazy-init.test.ts covering custom fetchFile scenarios with static imports
  • Verified existing cf-helpers tests continue passing
  • Confirmed no regression with advancedRouting: true (boolean) usage

Docs

  • No docs update needed, this fixes the documented usage pattern that was previously broken

Closes #16956

withastro/astro

Changes

  • Replaces blanket rejection of double-encoded paths with iterative decoding in validateAndDecodePathname(). Fixes 400 errors for legitimate double-encoded URLs like Sanity Studio's %255B/%255D (double-encoded [/]).
  • Ensures middleware always sees the fully decoded canonical path. Attack paths like /api/%2561dmin now decode to /api/admin and middleware correctly blocks them with 401 instead of the previous blanket 400.
  • Maintains CVE-2025-66202 security protections while eliminating false positives for legitimate client router URLs.

Testing

  • Added 25 tests in test/units/util/validate-and-decode-pathname.test.ts covering iterative decoding, Sanity Studio cases, and edge cases.
  • Added 13 tests in test/units/app/double-encoding-bypass.test.ts verifying middleware still blocks security attacks.
  • Updated 2 tests in test/middleware.test.ts for new behavior where middleware sees decoded paths.

Docs

  • No docs update needed, this fixes broken behavior to match existing expectations.

Closes #16960

withastro/astro

Changes

🦀

Testing

All tests should pass, tests relying on unified stuff (remark, rehype plugins, etc) got the unified markdown processor added manually

Docs

withastro/astro

Changes

The newly introduced compressHTML: 'jsx' is now the default option in V7, requested by our steward. The Rust compiler always had support for this unlike the Go one, so it's really just switching up the default.

Testing

Tests should pass, added tests for this specific behavior

Docs

withastro/docs#14076

withastro/astro

Changes

We no longer have the bandwidth to maintain this package and are generally not as interested as we once were regarding maintaining our own DB layer considering how much more mature tools like Drizzle and Prisma have become over time.

This package has also generally caused a lot of headaches for not much considering how little usage it has.

Close #16738
Close #15431

Testing

N/A

Docs

withastro/docs#13985 (ish, probably also needs a main callout if we merge this now..)

withastro/astro

Changes

This pull request adds retries for remote image dimension inference in Astro's image service. Currently, if a transient network error happens during build, the entire build fails, which can lead to several builds failing till it succeeds in some environments, making this a QoL improvement.

I am not sure if this counts as a patch or minor change? It doesn't add new API but kinda changes how builds behave regarding images, so I defaulted to minor for the moment.

Testing

I added new unit tests in remote-probe.test.ts + integration tests in core-image-infersize.test.ts to test this plus a combination of scenarios like redirects, not allowed remote sources, 5xx errors, etc.

Docs

It's probably worth a mention in the main Images guide or Image Service API, I'm not exactly sure where the best place would be so I will wait before making a PR in docs.

/cc @withastro/maintainers-docs

withastro/astro

Changes

astro sync was much slower with @astrojs/cloudflare because type generation started the Cloudflare dev runtime even though no requests are served. During the invocation of the temporary type generation dev server, the adapter now:

  • Clears configureServer from @cloudflare/vite-plugin plugins so the Cloudflare dev runtime never starts.
  • Sets optimizeDeps: { noDiscovery: true, include: [] } on every environment so no dependencies are pre-bundled.
  • dev and preview continue to start the Cloudflare runtime as expected.

Testing

  • Measured astro sync type generation with the MRE:
Adapter astro sync
none 37 ms
@astrojs/node 37 ms
@astrojs/vercel 36 ms
@astrojs/netlify 29 ms
@astrojs/cloudflare (before) 1.16 s
@astrojs/cloudflare (after) 56 ms

Docs

  • No docs needed. This is a build-time performance fix with no user-facing API change.

Closes #16332

withastro/astro

Changes

  • Fixes static file endpoints using dynamic routing where .html extensions in getStaticPaths params caused NoMatchingStaticPathFound build errors
  • Modifies FetchState.#stripHtmlExtension() to only strip .html from page routes, not endpoint routes, by adding this.routeData.type === 'page' guard
  • Resolves v5-to-v6 regression where endpoint routes with .html in param values would fail to build

Testing

  • Added regression test preserves .html in pathname for endpoint routes with dynamic params in packages/astro/test/units/fetch/index.test.ts
  • Verified fix resolves the reproduction case and produces correct dist/file.html output

Docs

  • No docs update needed, this restores expected behavior from v5.

Closes #16941

withastro/astro

Closes: #16780

Changes

  • Invalidate per-route dev CSS virtual modules when CSS files change, preventing stale server-rendered inline styles after HMR.
  • Treat *.css?raw imports as SSR dependencies instead of normal style modules, so raw CSS strings rendered with set:html refresh correctly.
  • Builds on the Astrobot fix from #16783, with additional coverage for the ?raw import path.

Testing

  • Added a package integration test that starts an Astro dev server, edits global CSS, and verifies the server-rendered inline <style> output updates.
  • Added unit coverage for dev CSS virtual module invalidation.
  • Added unit coverage for *.css?raw imports going through SSR invalidation instead of the normal CSS HMR skip path.
  • pnpm -C packages/astro run build:ci
  • pnpm -C packages/astro exec astro-scripts test "test/units/vite-plugin-hmr-reload/hmr-reload.test.ts" --strip-types

Docs

No docs changes. This fixes dev-server HMR behavior without changing user-facing APIs.

withastro/astro

Changes

What the title says, this release fixes some bugs and adds some options to GFM and math

Testing

Tests should pass!

Docs

N/A, we don't document Sätteri options, we just link to its docs.

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.4.4

Patch Changes

  • #16926 1b39ae8 Thanks @narendraio! - Prevents App.match() from throwing on request paths that contain an invalid percent-sequence.

  • #16924 2c0bc94 Thanks @astrobot-houston! - Fixes an issue where editing a client-side component (e.g. with client:idle, client:load, etc.) caused an unnecessary full program reload of the backend during development.

  • #16958 2c1d50f Thanks @fkatsuhiro! - Fixes a bug where static file endpoints using getStaticPaths with .html in dynamic param values (e.g. { path: 'file.html' }) would fail with a NoMatchingStaticPathFound error during build. The .html suffix is no longer incorrectly stripped from endpoint route pathnames.

  • #16855 c610cda Thanks @astrobot-houston! - Fixes dynamic routes returning 500 "TypeError: Missing parameter" when using domain-based i18n routing in SSR.

  • #16946 606c37b Thanks @ematipico! - Fixes Astro.routePattern to preserve original casing of dynamic parameter names from filenames. Previously, a file at src/pages/blog/[postId].astro would return /blog/[postid] for Astro.routePattern due to an internal .toLowerCase() call. It now correctly returns /blog/[postId].

  • #16720 16d49b6 Thanks @thomas-callahan-collibra! - Fix an issue where dynamic routes would return the string [object Object] instead of the expected content, in certain runtimes.

  • #16703 17390a6 Thanks @henrybrewer00-dotcom! - Fixes styles being stripped when the project root is started with a path whose case differs from the actual filesystem case (e.g. running astro dev from d:\dev\app while the folder on disk is D:\dev\app).

  • #16855 c610cda Thanks @astrobot-houston! - Fixes Astro.currentLocale returning the default locale instead of the domain's locale on dynamic routes served from a mapped domain.

@astrojs/mdx@6.0.2

Patch Changes

@astrojs/markdown-satteri@0.2.2

Patch Changes

withastro/astro

Changes

Closes #16945

We create a new request and apply the correct symbols.

Testing

Added new unit tests

Docs

withastro/astro

Changes

Fixes an issue where routePattern was transformed into lower case

Closes #16942

Testing

Tests added by the bot.

Docs

withastro/astro

Fixes #16078

Summary

  • include tsconfig-matched .astro files in the TypeScript plugin project
  • inject the installed Astro package's env.d.ts and astro-jsx.d.ts into the TS plugin host
  • add focused unit and VS Code fixture coverage for Astro.locals references

Verification

  • pnpm -C packages/language-tools/ts-plugin build
  • pnpm -C packages/language-tools/ts-plugin exec mocha --ui tdd --require tsx "test/units/**/*.test.mts"
  • pnpm -C packages/language-tools/vscode build && pnpm -C packages/language-tools/ts-plugin test
withastro/astro

Changes

  • With View Transitions, hashchange events are now emitted when navigating between hash anchors on the same page, like clicking anchor links.
  • The event includes oldURL and newURL properties matching standard browser behavior.
  • This behaviour is useful for apps like documentation sites, where we want to sync the highlighted element in the sidebar to the hash.
  • This follows up on #14746 and handles the browser-parity cases called out in review. Hash removal (/long-page#section to /long-page) is left to the browser so Astro does not emit hashchange for cases where native navigation would not.

Testing

  • Added e2e tests in view-transitions.test.ts:
    • Test that hashchange fires for same-page hash navigation
    • Test that hashchange fires when navigating between hashes on the same page
    • Test that back/forward navigation with hashes does not double-fire events
    • Test that hashchange does NOT fire when removing a hash
    • Test that hashchange does NOT fire when going back after removing a hash
    • Test that hashchange does NOT fire for cross-page navigation
  • Updated the existing View Transitions Rule test so hash removal and the later back/forward path assert native navigation without a ClientRouter transition.

Docs

No docs update needed. This fixes existing ClientRouter behavior to match how hashchange works during normal browser navigation.

withastro/starlight

Description

In the starlight docs, it mentions docsSchema() can modify fields, however it is unable to modify z.enum fields The likely cause is from how ZodIntersection are treated.

Why this happens

The Zod documentation explicitly notes that intersections (a logical "AND") produce a ZodIntersection instance rather than a ZodObject.

From the Zod v4 docs:

When merging object schemas, prefer A.extend(B) over intersections. Using .extend() will give you a new object schema, whereas z.intersection(A, B) returns a ZodIntersection instance which lacks common object methods like pick and omit.

This limitation also affects any tooling that expects to operate on a ZodObject, including schema‑amending utilities like docsSchema().

  • Closes #3935

  • What does this PR change? Give us a brief description.
    This PR replace the and intersection in docsSchema with extend to support the modifying of ZodEnum.

  • Did you change something visual? A before/after screenshot can be helpful.
    Nothing was changed.

withastro/astro

Changes

  • Fixes image 404s when using @astrojs/cloudflare adapter with static output and no explicit imageService config
  • Makes normalizeImageServiceConfig() output-mode-aware: defaults to 'compile' for static output, 'cloudflare-binding' for server output
  • Moves image service normalization into astro:config:setup hook where config.output is available
  • Maintains backward compatibility for all existing configurations

Testing

  • Added packages/integrations/cloudflare/test/units/image-config.test.ts with 7 test cases covering static/server defaults, explicit overrides, and compound configs
  • Verified existing image service test suites pass (static, compile, binding, dev endpoint, external)

Docs

  • No docs update needed, this fixes the default behavior to match user expectations for static sites

Closes #16931

withastro/astro

This PR fix ci error which cause type error from #16929

-------- Following content is cloned from #16929 --------

Changes

  • Fixes HMR for action files during development. Editing files in src/actions/ now takes effect immediately without requiring a dev server restart.
  • Adds clearActions() method to dev-specific pipelines (RunnablePipeline and NonRunnablePipeline) to reset the cached resolved actions, matching the existing clearMiddleware() pattern while ensuring production paths remain completely untouched.
  • Wires up file change detection in the actions Vite plugin to invalidate the virtual module and send an astro:actions-updated HMR event when action files are modified.
  • Adds HMR listeners in both dev app entrypoints to clear the actions cache when the event fires.

Testing

  • Added unit tests in packages/astro/test/units/actions/actions-hmr.test.ts covering cache invalidation behavior.
  • Verified the fix resolves the issue reported in #16913 where action endpoint returned stale values after file edits.
  • Confirmed by @fkatsuhiro that the fix works as expected.

Docs

  • No docs update needed, this restores expected HMR behavior for action files.

Closes #16913

withastro/astro

Changes

App.match() decodes the request pathname with decodeURI(), which throws URIError: URI malformed when the path contains an invalid percent-sequence that is not valid UTF-8 — e.g. %C0%AF (an overlong-UTF-8 encoding of /, extremely common from automated path-traversal / .env scanners).

Because matching runs before App.render(), the error escaped the adapter's request handler as an uncaught exception (HTTP 500) that user middleware could not catch. Adapters such as @astrojs/cloudflare call app.match() directly per request, so the worker fetch handler crashed.

This adds a safeDecodeURI() helper that mirrors the existing guarded decode already used in getPathnameFromRequest(), and uses it at all matchRoute / matchAllRoutes call sites in core/app/base.ts. Malformed paths now fall back to the raw, undecoded pathname and are matched (typically a 404) instead of crashing.

Testing

Added packages/astro/test/units/app/malformed-uri.test.ts:

  • match() does not throw on /%C0%AF and returns undefined
  • render() does not return a 500 for the malformed path
  • valid routes still match

All app unit tests pass locally (the two unrelated csrf/logger unit failures reproduce on main without this change and are environmental).

Closes #16916

withastro/astro

Changes

  • Fixes a v6 regression where editing client-side components (client:idle, client:load, client:visible, etc.) causes a full backend program reload during development
  • Returns empty array from astro:hmr-reload plugin when all processed modules exist only in the client module graph, preventing Vite's default SSR HMR propagation
  • Matches the existing pattern used for style modules (CSS/SCSS) that was fixed in PR #14924

Testing

  • Added unit test returns empty array for client-only modules to prevent unnecessary backend reloads covering the specific case where all modules are client-side
  • Verified fix prevents [vite] program reload and server-side module re-execution when editing client components
  • Confirmed no regression for SSR-only module invalidation

Docs

  • No docs update needed - this fixes a bug to restore expected v5 behavior

Closes #15951

withastro/astro

This PR fixes two typos I spotted in the project.

withastro/astro

Changes

  • Fixes clean URLs (e.g., /about) not resolving to static HTML files when using build.format: 'file' or 'preserve' with the Node.js adapter
  • Adds extensions: ['html'] option to the send() call in serve-static.ts when buildFormat is 'file' or 'preserve', enabling the send library to automatically append .html when resolving extensionless paths
  • Resolves issue where only the index page worked while all other prerendered pages returned 404 despite the .html files existing on disk

Testing

  • Added packages/integrations/node/test/build-format-file.test.ts with 6 test cases covering both 'file' and 'preserve' formats
  • Tests verify clean URL resolution, explicit .html extension handling, and correct file output structure
  • Verified existing tests continue to pass with no regressions

Docs

  • No docs update needed — this fixes existing documented behavior that was broken

Closes #16570

withastro/astro

Description

Simplifies the insertDirective method returned by context.csp (in packages/astro/src/core/fetch/fetch-state.ts).

The previous implementation had a misleading else branch:

insertDirective(payload) {
  if (state?.result?.directives) {
    state.result.directives = pushDirective(state.result.directives, payload);
  } else {
    state?.result?.directives.push(payload);
  }
},

Two problems:

  1. Dead code. state.result.directives is always initialized to an array (see fetch-state.ts:407directives: manifest.csp?.directives ? [...manifest.csp.directives] : []). Because empty arrays are truthy in JS, the if branch fires whenever state.result exists. The else is unreachable in practice.
  2. The else would crash if reached. Calling .push on the value that the if-condition just established is nullish would throw a TypeError. The optional-chaining ?. doesn't help — it only short-circuits up through state?.result?, not the final .push call.

This PR replaces both branches with the same single-statement pattern used by the sibling insertScriptResource / insertStyleResource methods directly below it.

Testing

Pure refactor of unreachable code; behavior under all currently-reachable inputs (i.e. whenever state.result exists, which is the only state the existing if-branch matches) is unchanged.

Docs

Not user-visible; no docs changes needed.

Changeset

Included.

withastro/astro

Changes

  • Adds astro/fetch and astro/hono to the Cloudflare adapter's server environment optimizeDeps.include list. These exports were introduced with advanced routing (#16366, #16729) but never added to the pre-bundle list, causing Vite to discover them at runtime and emit a new dependencies optimized: astro/fetch warning during dev.

Testing

  • No new tests. This is a config-only change to an existing optimizeDeps.include array.

Docs

  • No docs update needed — this is an internal dev-server optimization fix with no user-facing API change.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.4.3

Patch Changes

  • #16900 17a0fbd Thanks @ocavue! - Bumps devalue dependency to v5.8.1

  • #16016 0d85e1b Thanks @felmonon! - Fix a false positive in the dev toolbar accessibility audit for anchors with text inside closed <details> elements.

  • #16911 79c6c46 Thanks @astrobot-houston! - Fixes a bug where experimental.advancedRouting with astro/hono handlers threw TypeError: Cannot read properties of undefined (reading 'route') for unmatched routes instead of rendering the custom 404 page.

  • #16899 239c469 Thanks @matthewp! - Fixes a false "does not call the middleware() handler" warning when using astro() in a custom src/app.ts and the first request is a redirect route.

  • #16887 493acdb Thanks @astrobot-houston! - Fixes redirectToDefaultLocale not working after the Advanced Routing refactoring.

  • #16908 ef53ab9 Thanks @florian-lefebvre! - Improves optimized fallbacks generation when using the Fonts API by using better metrics for bold variants

@astrojs/cloudflare@13.6.1

Patch Changes

  • #16914 4bdd240 Thanks @matthewp! - Fixes astro/fetch and astro/hono being discovered at runtime during dev instead of pre-bundled

  • #16693 9e6edc2 Thanks @ArmandPhilippot! - Fixes a link in the documentation specifying where to open an issue for the adapter.

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/netlify@7.0.12

Patch Changes

  • #16693 9e6edc2 Thanks @ArmandPhilippot! - Fixes a link in the documentation specifying where to open an issue for the adapter.

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/node@10.1.3

Patch Changes

  • #16922 7dce185 Thanks @astrobot-houston! - Fixes prerendered pages returning 404 when using build.format: 'file' or build.format: 'preserve' with the Node adapter in standalone mode.

    Previously, clean URLs like /about would fail to resolve to about.html on disk, because the static file handler only supported the default directory format (about/index.html). Now the handler correctly resolves clean URLs to .html files when the build format produces them.

@astrojs/preact@5.1.5

Patch Changes

@astrojs/react@5.0.7

Patch Changes

withastro/astro

Changes

  • Fixes experimental.advancedRouting with astro/hono handlers crashing on unmatched routes instead of rendering the 404 page
  • Replaces broken component-name comparison in FetchState.#resolveRouteData() with the existing getCustom404Route() helper that matches by route path
  • Ensures the routeData-always-set invariant is maintained, preventing TypeError: Cannot read properties of undefined (reading 'route')

Testing

  • Added 'falls back to the 404 route when no route matches' test to verify routeData resolves to the /404 route for unmatched requests
  • Added 'renders the 404 page for unmatched routes instead of throwing' test to verify the full pages() pipeline renders HTTP 404 with custom 404 content instead of throwing

Docs

  • No docs update needed, this fixes broken functionality in an experimental feature

Closes #16907

withastro/astro

Changes

Close #16744

During running astro dev server, getStaticPaths() results were cached indefinitely, so updates to the content collections API (or any external data source) were never reflected without restarting the dev server. This fix clears the route cache on every request in dev mode, ensuring getStaticPaths() always re-runs with the latest data.

Testing

Before implementation

スクリーンショット 2026-05-29 20 29 26

After implementation

スクリーンショット 2026-05-29 20 31 06

Docs

withastro/astro

Changes

  • Addresses withastro/roadmap#1366
  • Generating fallbacks for bold variants is now improved by using better metrics, eg. "Arial Bold" instead of "Arial"
  • I also tried Italic specific variants but metrics are almost always identical to the normal variant so I removed them
  • Made with the help of AI

Testing

Updated

Docs

Changeset, no docs

withastro/astro

Changes

  • AstroHandler.handle() now eagerly sets all PipelineFeatures bits so the one-shot warnMissingFeatures check never fires a false positive.
  • Fixes false "does not call the middleware() handler" warning when using astro() in a custom src/app.ts and the first request is a redirect.

Testing

  • Added unit test: sends a redirect through astro() with middleware configured and asserts all feature bits are set.
  • Verified the test fails without the fix (usedFeatures = 3, only redirects|sessions) and passes with it (63, all features).

Docs

  • No docs needed.
withastro/astro

Changes

The @astrojs/vercel test suite is occasionally failing in CI with an anonymous suite-level timeout (e.g. this run):

✖ node_modules/.astro/test.mjs (60004ms)  'test timed out after 60000ms'
ℹ tests 40 · pass 36 · cancelled 1 · skipped 3

This PR makes two changes:

  • Raise the suite-level --timeout from 60s to 100s, giving the cumulative run more headroom.
  • Add an explicit { timeout: 30000 } to every test. A single stuck test now fails fast and by name at 30s instead of silently consuming the shared suite budget and surfacing as the anonymous test.mjs timeout. So if we see a timeout error next time, we know exactly which test causes the timeout.

Testing

Green CI.

Docs

N/A

withastro/starlight

Description

It has now technically been more than 3 days since we added proper releases with version tags to withastro/automation, so we can now remove the minimumReleaseAge workaround for withastro/automation updates in Renovate.

This PR also updates the reusable workflow references from the main branch comment to the v1.0.0 version tag comment used by Renovate.

withastro/starlight

Description

  • Closes #3912
  • Closes #3916
  • Updates the minimum version of Pagefind bundled with Starlight to 1.5.2
  • Adds support for the new ranking options added in Pagefind v1.5
  • In the end I decided not to make the config a z.looseObject() as discussed in #3912 (comment). The issue with loose objects is you don’t get as good feedback for typos and I think maybe new properties are rare enough that we can keep up-to-date while providing clear diagnostics for people who typo a key name?
withastro/astro

Changes

  • Fixes custom elements losing their slot HTML attributes when rendered through MDX, restoring proper Shadow DOM slot distribution
  • Adds a guard in extractSlots() to skip slot extraction when the parent is a custom HTML element (contains hyphen)
  • Custom elements still route through the renderer pipeline for SSR while preserving standard HTML slot attributes

Testing

  • Added 'preserves slot attribute on children of custom elements' test case to packages/astro/test/units/render/jsx-custom-elements.test.ts
  • Verified all existing custom element tests pass (renderer routing, fallback behavior)
  • Confirmed MDX slots and children tests remain unaffected

Docs

  • No docs update needed, as this fixes broken behavior to match documented web component standards.

Closes #16891

withastro/astro

Changes

Integrations that are not updated yet for 6.4.0 still read the plugins from the legacy keys, so we need to keep them on the config. At worse, the plugins get dropped, at very bad, the integration doesn't expect the config to not be there anymore and crashes.

Caution, the fix is imo super goofy, but I couldn't find a better way that didn't break some other usages, hopefully anyway this code path gets hits less and less as we go forward

Testing

Added unit tests for the config folding

Docs

N/A

withastro/starlight

Description

While working on #3926, I stumbled upon a todo comment related to Node.js version that this PR addresses as the minimum Node.js version supported by Astro is now 22.12.0 and the windows option for fileURLToPath() is available since 22.1.0.

withastro/astro

Changes

Replace the length > 0 guard and manual loop in renderAllHeadContent with a single join('') call. An empty array already returns '' from join, so the guard and loop are unnecessary.

Testing

No testing required. Behaviour is identical in all cases.

Docs

This change does not affect docs.

Changes

Testing

Docs

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.4.2

Patch Changes

  • #16889 b94bcfd Thanks @Princesseuh! - Fixes a plugins is not iterable crash when using a pre-6.0 @astrojs/mdx alongside integrations (e.g. Starlight) that set markdown.remarkPlugins, markdown.rehypePlugins, or markdown.remarkRehype.

  • #16878 b9f6bb9 Thanks @fkatsuhiro! - Fixes an issue where on-demand (SSR) dynamic routes would return 404 when a prerendered dynamic route with the same URL pattern was sorted first alphabetically. In production builds with @astrojs/node adapter, if [a_prebuild].astro (prerender=true) came before [b_ssr].astro alphabetically, requests to URLs not in the prerendered route's static paths would 404 instead of falling through to the SSR route. The fix adds fallthrough logic so that when a prerendered dynamic route matches but can't serve the request, Astro tries subsequent matching routes.

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.4.1

Patch Changes

  • #16883 eeb064c Thanks @Princesseuh! - Restores the astro/jsx/rehype.js entry point so that older versions of @astrojs/mdx continue to work when used with Astro 6.x. This entry point will be removed in Astro 7.0.

@astrojs/mdx@6.0.1

Patch Changes

  • Updated dependencies [eeb064c]:
    • @astrojs/markdown-satteri@0.2.1

@astrojs/markdown-satteri@0.2.1

Patch Changes

withastro/astro

Changes

In f732f3c I moved this rehype plugin from Astro to the MDX integration, but previous versions of MDX have no reasons to not work, I think, so restore it until Astro 7

Testing

N/A

Docs

N/A

withastro/astro

Changes

In renderAllHeadContent, each group of head elements — styles, links and scripts — is joined with a hardcoded '\n' separator. This ignores the existing result.compressHTML flag that controls whitespace in other parts of the rendering pipeline.

Switch to a conditional separator so compressed output omits newlines between head elements, consistent with doctype injection and other compressHTML-aware rendering paths.

Testing

No testing required. The change only affects whitespace in rendered output and is gated behind the existing compressHTML flag, which is already covered by the test suite.

Docs

This change does not affect docs. compressHTML behaviour is unchanged — this fix brings head element rendering in line with how it is already documented.

withastro/starlight

Description

Follow-up to this comment, this PR configures Renovate to manage Node.js versions in our CI workflows so we can pin them, have some consistency across all Node.js versions used in our workflows, and respect our minimumReleaseAge configuration.

  • All workflows use the latest Node.js LTS version (respect the minimumReleaseAge configuration)
  • Except .github/workflows/ci.yml which uses the latest patch of the minimum Node.js major supported by Astro (matches the current behavior of the CI workflow except versions are now pinned and respect the minimumReleaseAge configuration)

I also configure Node.js Renovate PRs to be roughly every two weeks only, so we don't get too many PRs but open to feedback on this.

I validated the config using npx --yes --package renovate -- renovate-config-validator.

withastro/starlight

Improved and translated missing strings

withastro/astro

Changes

  • Fixes production builds where SSR dynamic routes fail when prerendered dynamic routes sort alphabetically first
  • Adds Router.matchAll() method to find all routes matching a pathname instead of just the first
  • Updates production router logic to fall through from prerendered dynamic routes to SSR routes when the static path doesn't match
  • Preserves existing behavior for static prerendered routes (exact paths)

Testing

  • Added regression test for issue #16834 in packages/astro/test/units/fetch/index.test.ts
  • Added unit tests for Router.matchAll() in packages/astro/test/units/routing/router-match.test.ts
  • Verified all existing routing, fetch, app, and ssr-prerender tests continue passing

Docs

No docs update needed, as this fixes broken documented behavior rather than changing the API
Closes #16834

withastro/astro

Changes

  • Removes the experimental.advancedRouting flag and enables the feature by default.
  • fetchFile is now a top-level config option (string | null, default 'fetch') instead of being nested under experimental.advancedRouting.
  • The vite plugin always resolves src/fetch.ts if present — no flag needed.
  • fetchFile: null disables the entrypoint.

RFC: withastro/roadmap#1344

Testing

  • No new tests. Existing tests continue to pass with the flag removed.

Docs

withastro/starlight

Description

This PR adds support for Astro 6.4 and the Sätteri processor introduced in that version. It does so by checking what (if any) markdown.processor is set, and adding plugins accordingly to that.

Versions earlier than 6.4 are also still supported, when markdown.processor isn't present, Starlight pushes plugins to the now deprecated remarkPlugins / rehypePlugins options instead.

This PR does not remove the hard dependency on @astrojs/markdown-remark, it would be possible to with similar patterns as the ones done for Sätteri, but I figured that it wasn't up to me to decide that.

withastro/astro

Changes

The merge-main-to-next workflow can not work if there are conflicts in any of:

  • package.json: pnpm/action-setup can't read packageManager field from package.json
  • pnpm-lock.yaml: pnpm install --frozen-lockfile errors out
  • .flue/*.ts: flue itself can't parse its own workflow

A broken me can't fix a broken you.



This PR splits them into two Git checkouts:

path role
🩺 Doctor runtime/ clean checkout, hosts flue CLI and .flue/ configs
🤕 Patient operand/ post-merge tree, may be full of conflict markers

Testing

Let's merge this, manually trigger this workflow from the GitHub web UI, and see if it works.

Docs

Internal CI change. No docs needed.

withastro/astro

Changes

  • Adds a session: false config option that opts the project out of session support entirely.
  • Introduces an indirection through core/session/provider.ts that the new astro:session-provider Vite plugin swaps for a disabled stub when session: false, so Rollup can tree-shake core/session/runtime.ts out of the SSR bundle.
  • Registers a throw-on-access shim under session: false so Astro.session (and context.session) surfaces a clear SessionDisabledError instead of being undefined, per maintainer guidance in withastro/roadmap#1352 (comment).
  • Adapter changes (@astrojs/cloudflare, @astrojs/netlify, @astrojs/node): skip auto-wiring the default session driver when session: false.
import { defineConfig } from 'astro/config';

export default defineConfig({
  session: false,
});

Projects that do not set session: false see no behavior change.

Closes the design intent of withastro/roadmap#1352.

Why

Astro core unconditionally bundles the session runtime (AstroSession + unstorage) into SSR output, and adapters auto-wire a default driver. There is no first-class opt-out today. For projects that never use sessions, this carries cold-start parse cost (especially on serverless/edge), latent attack surface in cookie/session-storage code paths, and a larger dependency tree.

A note on the RFC: it was written before the recent Pipeline Features / virtual:astro:session-driver refactor. The phrase "core/render-context.js has an unconditional import { AstroSession }" is no longer literally true on mainrender-context.ts does not import the session runtime any more. The actual remaining boundary is at core/session/handler.ts:3, which is statically imported by core/fetch/index.ts, core/routing/handler.ts, and core/errors/default-handler.ts. That static import keeps runtime.ts and unstorage in the SSR bundle even though their work is gated at runtime. The fix in this PR replaces that static-import boundary with a virtual-module indirection.

Design

  • The three hot-path callers (core/fetch/index.ts, core/routing/handler.ts, core/errors/default-handler.ts) now import provideSession from core/session/provider.ts instead of core/session/handler.ts directly.
  • provider.ts is a one-line re-export from handler.ts (the default case). It is a real file so Node's ESM loader resolves it in environments without Vite (library tooling, test loaders).
  • A new Vite plugin astro:session-provider intercepts resolution of provider.js and redirects it to provider-disabled.ts (a tiny stub that does not import runtime.ts or unstorage) when settings.config.session === false. This is structurally similar to how the existing virtual:astro:session-driver plugin chooses driver code, just applied to a real file path because the static-import boundary lives inside Astro's own source rather than user-emitted code.
  • routing/handler.ts runs provideSession when sessions are configured or explicitly disabled, so the throw-on-access shim is registered in the disabled case and Astro.session access surfaces SessionDisabledError instead of returning undefined.
  • sessionsDisabled: true is added to SSRManifest so the routing handler can distinguish "no driver configured" from "explicitly disabled."
  • Each adapter that auto-wires a default driver gains a short-circuit at the top of its session block: if (session !== false) { /* auto-wire */ }.

Decisions called out

  • Throw on access vs. silent no-op. Throw — explicit maintainer guidance in #1352. Astro.session.X under session: false surfaces SessionDisabledError with a hint pointing back at the config.
  • Tree-shake via Vite resolveId swap vs. define flag. The resolveId swap, structurally similar to the existing virtual:astro:session-driver pattern. A define/early-return approach does not remove the static import of AstroSession and so does not actually drop unstorage from the bundle. Real-file (vs. virtual:) because the indirection lives in Astro's own source — virtual: schemes don't survive through astro-scripts build into dist/ and can't be loaded by Node's ESM loader in non-Vite contexts (library tooling, the node:test loader used here).
  • experimental.cache interaction. Out of scope. experimental.cache also uses unstorage. If a user sets session: false and enables experimental.cache, unstorage stays in the bundle. Treating them independently to keep this PR focused.

Testing

  • Unit (test/units/sessions/session-false.test.ts):
    • SessionSchema parses false, rejects other falsy values.
    • sessionConfigToManifest(false) returns undefined; sessionsDisabled(false) returns true.
    • Disabled provider registers a 'session' key whose create() throws SessionDisabledError.
  • Integration (test/session-false.test.ts):
    • Routes that never read Astro.session are unaffected.
    • Routes that read Astro.session under session: false get a 500 (the throw propagates).
    • The built SSR output contains no createStorage (top-level unstorage export) and no class AstroSession.

Bundle-size measurement

Built test/fixtures/session-false with the test adapter, with and without session: false:

Mode Files Total bytes unstorage in bundle AstroSession class in bundle
default (no session: false) 8 408,348 (398.8 KiB) yes yes
session: false 8 395,490 (386.2 KiB) no no
delta −12,858 (−12.6 KiB, −3.1%)

This is the floor of the savings — measured against a fixture where no adapter auto-wires a driver. On Cloudflare/Netlify/Node, the adapter would otherwise also wire its driver (Cloudflare KV / Netlify Blobs / fs-lite) into the bundle, so the real-world delta on those targets is larger.

Docs

Will open a follow-up PR against withastro/docs covering:

  • The session: false option on the astro.config reference page.
  • A short note on the session API page (Astro.session) describing the disabled-mode error.

The Astro core changeset captures user-facing wording.

Out of scope / non-goals

  • Removing experimental.cache's unstorage dependency (see above).
  • Vercel/Deno adapters: neither auto-wires a default session driver today, so no adapter change is needed there.
  • Dev-server behavior for session: false is the same as production (the virtual module routes identically in both).
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/mdx@6.0.0

Major Changes

  • #16848 f732f3c Thanks @Princesseuh! - Adds a new markdown.processor configuration option, allowing you to choose an alternative Markdown processor.

    Websites with many Markdown/MDX files tend to be slow to build because the unified ecosystem (e.g., remark, rehype) is slow to process. This feature introduces the ability to replace this part of the build pipeline with another processor.

    The default processor is unified(). This means that existing configurations remain unchanged and your remark/rehype plugins continue to work.

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { unified } from '@astrojs/markdown-remark';
    import remarkToc from 'remark-toc';
    
    export default defineConfig({
      markdown: {
        processor: unified({
          remarkPlugins: [remarkToc],
        }),
      },
    });

    In addition to this new configuration option, Astro provides a new alternative processor based on Rust: Sätteri. You can choose to use it now by installing @astrojs/markdown-satteri, importing the satteri() processor, and adapting your existing configuration:

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri({
          features: { directive: true },
        }),
      },
    });

    This processor does not support the remark and rehype plugins. This means you may need to convert them to MDAST or HAST plugins to retain your current functionality.

    The existing top-level markdown.remarkPlugins, markdown.rehypePlugins, markdown.remarkRehype, markdown.gfm, and markdown.smartypants options still work, but are now deprecated and will be removed in a future major update. The matching remarkPlugins, rehypePlugins, and remarkRehype options on the MDX integration are also deprecated for the same reason. To anticipate their removal, move them onto unified({...}) (or your preferred plugin processor) :

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import remarkToc from 'remark-toc';
    import rehypeSlug from 'rehype-slug';
    + import { unified } from '@astrojs/markdown-remark';
    
    export default defineConfig({
      markdown: {
    +    processor: unified({
    +      remarkPlugins: [remarkToc],
    +      rehypePlugins: [rehypeSlug],
    +      remarkRehype: true,
    +      gfm: true,
    +      smartypants: true,
    +    }),
    -    remarkPlugins: [remarkToc],
    -    rehypePlugins: [rehypeSlug],
    -    remarkRehype: true,
    -    gfm: true,
    -    smartypants: true,
      },
    });

    For more information on enabling and using this feature in your project, see our Markdown guide. To give feedback on this new Rust processor, see the Native Markdown / MDX parsing and processing RFC.

Minor Changes

  • #16848 f732f3c Thanks @Princesseuh! - Adds support for using @astrojs/markdown-satteri to parse .mdx files.

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import mdx from '@astrojs/mdx';
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri({
          features: { directive: true },
        }),
      },
      integrations: [mdx()],
    });

    Note that the recmaPlugins option is not supported when using Sätteri as your MDX processor. If you would like to use Sätteri for Markdown files, but still use Unified for MDX, you can pass a different Markdown processor to the MDX integration:

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import mdx from '@astrojs/mdx';
    import { satteri } from '@astrojs/markdown-satteri';
    import { unified } from '@astrojs/markdown-remark';
    import myPlugin from './my-recma-plugin.js';
    
    export default defineConfig({
      markdown: {
        processor: satteri({
          features: { directive: true },
        }),
      },
      integrations: [
        mdx({
          recmaPlugins: [myPlugin],
          processor: unified(),
        }),
      ],
    });

Patch Changes

  • Updated dependencies [f732f3c, f732f3c, f732f3c]:
    • @astrojs/internal-helpers@0.10.0
    • @astrojs/markdown-remark@7.2.0
    • @astrojs/markdown-satteri@0.2.0

astro@6.4.0

Minor Changes

  • #16468 4cff3a1 Thanks @matthewp! - Adds a new preserveBuildServerDir adapter feature

    Adapters can now set preserveBuildServerDir: true in their adapter features to keep the dist/server/ directory structure for static builds, mirroring the existing preserveBuildClientDir option. This is useful for adapters that require a consistent dist/client/ and dist/server/ layout regardless of build output type.

    setAdapter({
      name: 'my-adapter',
      adapterFeatures: {
        buildOutput,
        preserveBuildClientDir: true,
        preserveBuildServerDir: true,
      },
    });
  • #16848 f732f3c Thanks @Princesseuh! - Adds a new markdown.processor configuration option, allowing you to choose an alternative Markdown processor.

    Websites with many Markdown/MDX files tend to be slow to build because the unified ecosystem (e.g., remark, rehype) is slow to process. This feature introduces the ability to replace this part of the build pipeline with another processor.

    The default processor is unified(). This means that existing configurations remain unchanged and your remark/rehype plugins continue to work.

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { unified } from '@astrojs/markdown-remark';
    import remarkToc from 'remark-toc';
    
    export default defineConfig({
      markdown: {
        processor: unified({
          remarkPlugins: [remarkToc],
        }),
      },
    });

    In addition to this new configuration option, Astro provides a new alternative processor based on Rust: Sätteri. You can choose to use it now by installing @astrojs/markdown-satteri, importing the satteri() processor, and adapting your existing configuration:

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri({
          features: { directive: true },
        }),
      },
    });

    This processor does not support the remark and rehype plugins. This means you may need to convert them to MDAST or HAST plugins to retain your current functionality.

    The existing top-level markdown.remarkPlugins, markdown.rehypePlugins, markdown.remarkRehype, markdown.gfm, and markdown.smartypants options still work, but are now deprecated and will be removed in a future major update. The matching remarkPlugins, rehypePlugins, and remarkRehype options on the MDX integration are also deprecated for the same reason. To anticipate their removal, move them onto unified({...}) (or your preferred plugin processor) :

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import remarkToc from 'remark-toc';
    import rehypeSlug from 'rehype-slug';
    + import { unified } from '@astrojs/markdown-remark';
    
    export default defineConfig({
      markdown: {
    +    processor: unified({
    +      remarkPlugins: [remarkToc],
    +      rehypePlugins: [rehypeSlug],
    +      remarkRehype: true,
    +      gfm: true,
    +      smartypants: true,
    +    }),
    -    remarkPlugins: [remarkToc],
    -    rehypePlugins: [rehypeSlug],
    -    remarkRehype: true,
    -    gfm: true,
    -    smartypants: true,
      },
    });

    For more information on enabling and using this feature in your project, see our Markdown guide. To give feedback on this new Rust processor, see the Native Markdown / MDX parsing and processing RFC.

Patch Changes

  • #16468 4cff3a1 Thanks @matthewp! - Skips the static preview server when an adapter provides its own previewEntrypoint, allowing the adapter to handle both static and dynamic routes

  • #16811 e0e26db Thanks @matthewp! - Fixes X-Forwarded-Host and X-Forwarded-Proto headers being ignored when set in a custom src/app.ts fetch handler before creating FetchState

  • #16468 4cff3a1 Thanks @matthewp! - Fixes the static preview server to respect preserveBuildClientDir, serving files from build.client instead of outDir when the adapter requires it

  • #16770 1e2aa11 Thanks @matthewp! - Fixes a race condition where the Vite dep optimizer could lose React dependencies in dev mode when using Astro Actions

  • #16468 4cff3a1 Thanks @matthewp! - Exempts internal routes (e.g. server islands) from getStaticPaths() validation, fixing server island rendering on static sites

  • #16468 4cff3a1 Thanks @matthewp! - Fixes preview for static sites that contain non-prerendered routes. Previously, the preview command ignored SSR routes discovered during route scanning and always used the static preview server.

  • Updated dependencies [f732f3c, f732f3c]:

    • @astrojs/internal-helpers@0.10.0
    • @astrojs/markdown-remark@7.2.0

@astrojs/cloudflare@13.6.0

Minor Changes

  • #16729 01aa164 Thanks @matthewp! - Adds @astrojs/cloudflare/fetch and @astrojs/cloudflare/hono exports for composing Cloudflare-specific setup with Astro's advanced routing handlers.

    @astrojs/cloudflare/fetch

    For use with astro/fetch in a custom fetch handler:

    import { astro, FetchState } from 'astro/fetch';
    import { cf } from '@astrojs/cloudflare/fetch';
    
    export default {
      async fetch(request: Request, env: Env, ctx: ExecutionContext) {
        const state = new FetchState(request);
        const asset = await cf(state, env, ctx);
        if (asset) return asset;
        return astro(state);
      },
    };

    @astrojs/cloudflare/hono

    For use with astro/hono as Hono middleware:

    import { Hono } from 'hono';
    import { actions, middleware, pages, i18n } from 'astro/hono';
    import { cf } from '@astrojs/cloudflare/hono';
    
    const app = new Hono<{ Bindings: Env }>();
    
    app.use(cf());
    app.use(actions());
    app.use(middleware());
    app.use(pages());
    app.use(i18n());
    
    export default app;

    Both handlers configure SESSION KV bindings, static asset serving via the ASSETS binding, locals.cfContext, client address, waitUntil, and prerendered error page fetch.

Patch Changes

  • #16868 f9bae95 Thanks @helio-cf! - Fixes user options passed to cloudflare({...}) (remoteBindings, inspectorPort, persistState, configPath, auxiliaryWorkers) being silently ignored during astro preview. The adapter now resolves the full @cloudflare/vite-plugin config once at integration setup time and reuses that single resolved value across the dev/build plugin, the prerenderer's preview server, and the astro preview entrypoint, so user options can no longer be dropped at one of the call sites.

  • #16468 4cff3a1 Thanks @matthewp! - Fixes static Cloudflare builds with server islands or image endpoints that failed at preview time due to mismatched output directories.

  • Updated dependencies [f732f3c]:

    • @astrojs/internal-helpers@0.10.0
    • @astrojs/underscore-redirects@1.0.3

@astrojs/internal-helpers@0.10.0

Minor Changes

  • #16848 f732f3c Thanks @Princesseuh! - Adds markdown, frontmatter, and shiki helper modules, shared by Astro's content pipeline.

@astrojs/markdown-remark@7.2.0

Minor Changes

  • #16848 f732f3c Thanks @Princesseuh! - Adds a new markdown.processor configuration option, allowing you to choose an alternative Markdown processor.

    Websites with many Markdown/MDX files tend to be slow to build because the unified ecosystem (e.g., remark, rehype) is slow to process. This feature introduces the ability to replace this part of the build pipeline with another processor.

    The default processor is unified(). This means that existing configurations remain unchanged and your remark/rehype plugins continue to work.

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { unified } from '@astrojs/markdown-remark';
    import remarkToc from 'remark-toc';
    
    export default defineConfig({
      markdown: {
        processor: unified({
          remarkPlugins: [remarkToc],
        }),
      },
    });

    In addition to this new configuration option, Astro provides a new alternative processor based on Rust: Sätteri. You can choose to use it now by installing @astrojs/markdown-satteri, importing the satteri() processor, and adapting your existing configuration:

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri({
          features: { directive: true },
        }),
      },
    });

    This processor does not support the remark and rehype plugins. This means you may need to convert them to MDAST or HAST plugins to retain your current functionality.

    The existing top-level markdown.remarkPlugins, markdown.rehypePlugins, markdown.remarkRehype, markdown.gfm, and markdown.smartypants options still work, but are now deprecated and will be removed in a future major update. The matching remarkPlugins, rehypePlugins, and remarkRehype options on the MDX integration are also deprecated for the same reason. To anticipate their removal, move them onto unified({...}) (or your preferred plugin processor) :

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    import remarkToc from 'remark-toc';
    import rehypeSlug from 'rehype-slug';
    + import { unified } from '@astrojs/markdown-remark';
    
    export default defineConfig({
      markdown: {
    +    processor: unified({
    +      remarkPlugins: [remarkToc],
    +      rehypePlugins: [rehypeSlug],
    +      remarkRehype: true,
    +      gfm: true,
    +      smartypants: true,
    +    }),
    -    remarkPlugins: [remarkToc],
    -    rehypePlugins: [rehypeSlug],
    -    remarkRehype: true,
    -    gfm: true,
    -    smartypants: true,
      },
    });

    For more information on enabling and using this feature in your project, see our Markdown guide. To give feedback on this new Rust processor, see the Native Markdown / MDX parsing and processing RFC.

Patch Changes

  • Updated dependencies [f732f3c]:
    • @astrojs/internal-helpers@0.10.0

@astrojs/markdown-satteri@0.2.0

Minor Changes

  • #16848 f732f3c Thanks @Princesseuh! - Adds @astrojs/markdown-satteri, a Markdown processor based on Sätteri, a fast Markdown pipeline written in Rust.

    Sätteri is much faster than the default Remark-based processor, and supports a wide range of Markdown features out of the box, without requiring additional plugins. In the future, we plan to make this the default Markdown processor in Astro.

    npm install @astrojs/markdown-satteri
    // astro.config.mjs
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri(),
      },
    });

    Note that this processor currently does not support Prism syntax highlighting, and require using syntaxHighlight: 'shiki' or disabling syntax highlighting altogether for now.

Patch Changes

  • Updated dependencies [f732f3c]:
    • @astrojs/internal-helpers@0.10.0

@astrojs/markdoc@1.0.6

Patch Changes

  • #16848 f732f3c Thanks @Princesseuh! - Removes @astrojs/markdown-remark from @astrojs/markdoc's dependencies in favour of Astro's internal markdown utilities now that Astro's Markdown support is processor agnostic.

  • Updated dependencies [f732f3c]:

    • @astrojs/internal-helpers@0.10.0

@astrojs/netlify@7.0.11

Patch Changes

  • Updated dependencies [f732f3c]:
    • @astrojs/internal-helpers@0.10.0
    • @astrojs/underscore-redirects@1.0.3

@astrojs/node@10.1.2

Patch Changes

  • Updated dependencies [f732f3c]:
    • @astrojs/internal-helpers@0.10.0

@astrojs/preact@5.1.4

Patch Changes

  • Updated dependencies [f732f3c]:
    • @astrojs/internal-helpers@0.10.0

@astrojs/react@5.0.6

Patch Changes

  • Updated dependencies [f732f3c]:
    • @astrojs/internal-helpers@0.10.0

@astrojs/vercel@10.0.8

Patch Changes

  • Updated dependencies [f732f3c]:
    • @astrojs/internal-helpers@0.10.0
withastro/astro

User options like remoteBindings, inspectorPort, persistState, configPath, and auxiliaryWorkers were silently dropped during astro preview because the preview entrypoint only received the adapter-computed half of the config.

Fixes #16867

Changes

  • Fixes user options passed to cloudflare({...}) (remoteBindings, inspectorPort, persistState, configPath, auxiliaryWorkers) being silently dropped during astro preview. The preview entrypoint was only receiving the adapter-computed half of the @cloudflare/vite-plugin config via globalThis.
  • Restructures the adapter to resolve the full @cloudflare/vite-plugin config exactly once at astro:config:setup time. All three call sites (dev/build plugin, prerenderer preview server, astro preview entrypoint) now spread a single pre-merged cfPluginConfig. The unmerged adapter-only half is a local const that goes out of scope, so no future call site can accidentally drop user options.
  • Same bug class as #16705 (fixed for the prerenderer in #16707). This closes the equivalent gap on the astro preview path and structurally prevents it from recurring.
  • Changeset included.

Testing

I've tested it with local npm patch, let me know if we have some e2e tests for these

Docs

No docs added, just a fix

withastro/astro

Changes

  • moved some files inside unit/ because they are unit tests
  • moved many tests to unit tests
  • removed those files that don't have assertions anymore (and related fixtures)
  • moved some assertions to unit test

Testing

Green CI

Docs

withastro/astro

Changes

This PR fixes an outdated link in README.md of the Partytown integration.

withastro/astro

Changes

  • Include mod in the SSR-branch cache entry in callGetStaticPaths so the new cached.mod === mod fast-path actually hits on subsequent SSR requests for the same component.

Before this patch, the SSR branch wrote { ...cached, staticPaths: entry } without mod. The fast-path added in #16776 (cached?.staticPaths && cached.mod === mod) therefore always failed on the second request, control fell back into the SSR branch, routeCache.set() ran again, and RouteCache.set() emitted Internal Warning: route cache overwritten at the top of the function — once per dynamic-route component per worker isolate, on every request after the first, in production.

The static branch already stores mod so it was unaffected.

Closes #16863.

Testing

Added a unit test in packages/astro/test/units/routing/getstaticpaths-cache.test.ts:

does not log "route cache overwritten" on repeated SSR requests for the same module

It constructs a RouteCache in production mode with a capturing logger destination, calls callGetStaticPaths three times with ssr: true, prerender: false, the same mod, and asserts no route cache overwritten message was logged. The existing HMR and prerendered-route tests continue to pass.

Docs

No docs changes — fixes an internal warning, no public API change.

withastro/starlight

Description

This PR bumps the Node.js version used in the Lunaria workflow to the latest Node.js 22 version like in our other workflows instead of the specific 22.12.0 version.

We recently updated to pnpm 11 which requires Node.js 22.13 or higher and would error out with the following message when running the workflow:

ERROR: This version of pnpm requires at least Node.js v22.13
The current version of Node.js is v22.12.0
withastro/astro

Changes

Fixes Astro's dev image endpoint when adapters use a prerender-only dev server, such as Cloudflare static output with a custom image service.

The internal image endpoint is now treated as prerendered in dev so the prerender handler can serve it, while preserving its query string for image transform parameters.

Refs #16752

withastro/astro

Changes

  • Content-collection markdown images no longer emit an empty srcset="" attribute when using the noop/passthrough image service. The updateImageReferencesInBody function in packages/astro/src/content/runtime.ts now conditionally includes srcset only when the image service returns actual values (image.srcSet.values.length > 0), matching the existing guard in packages/astro/src/vite-plugin-markdown/images.ts.

Testing

  • Added test/passthrough-image-content.test.ts — builds a content collection with a local markdown image using passthroughImageService, asserts srcset is absent and the image src is valid.
  • Existing astro-assets.test.ts (sharp service) and passthrough-image-service.test.ts both pass with no regressions.

Docs

  • No docs update needed — this is a validation fix for existing behavior.
withastro/astro

Changes

  • The getActions() method in base-pipeline.ts was missing an assignment to this.resolvedActions.
  • This caused the actions module to be dynamically re-imported on every single action invocation instead of returning the cached module.
  • This PR adds the missing assignment to properly cache the result and improve performance on action requests, matching the existing caching pattern used in getMiddleware() and getSessionDriver().
  • Included a changeset via pnpm changeset.

Testing

No new tests were added. Because this is purely an internal backend caching optimization, it doesn't change the output or behavior of the pipeline, only the execution time of repeated action calls. A standard reproduction link is not applicable here. The bug and fix can be verified statically by inspecting the previous code:

} else if (this.actions) {
    return this.actions(); // <- missing assignment to this.resolvedActions
}

Docs

No docs added. This is a purely internal server-side performance optimization. It does not affect any public APIs, configuration options, or user-facing behavior, so no documentation updates are required.

withastro/astro

Changes

Adds a new Markdown processor based on https://github.com/bruits/satteri, it's fast! It supports all of Astro's features, apart from Prism syntax highlighting in this first version.

Testing

Added tests to the package

Docs

withastro/docs#13919

withastro/astro

Changes

The markdown.processor part of withastro/roadmap#1364, please see the RFC for details.

Testing

Updated tests that relied on specific remark plugins and what not. Added tests using a custom processor to make sure it works from e2e

Docs

withastro/docs#13919

withastro/astro

Changes

  • Upgrades @flue/cli and @flue/runtime from 0.3.10 to 0.8.0, migrating to the new createAgent() + run() API and the agent/workflow directory split.
  • Scopes GITHUB_TOKEN to read-only (contents: read) in all flue workflow permissions blocks. The agent sandbox only receives this read-only token for gh CLI reads (issue views, searches, etc.).
  • Moves all write operations (git push, post comment, add/remove labels, create PR) out of the agent sandbox and into orchestrator-level code that uses FREDKBOT_GITHUB_TOKEN directly. The agent never sees the write-capable token.
  • Adds gitPush() and gitDeleteBranch() helpers in .flue/lib/github.ts that run via child_process.exec outside the sandbox with the privileged token.
  • Splits merge-fix/github.ts into readHeaders() (base token) and writeHeaders() (privileged token) so API reads and writes use separate credentials.

The security model is now: the agent can read anything via gh but cannot write to the repo, post comments, or push code on its own. All mutations go through controlled orchestrator functions.

Testing

  • No test changes — these are internal CI agent infrastructure changes.

Docs

  • No docs update needed — no user-facing changes.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to 5-legacy, this PR will be updated.

Releases

astro@5.18.2

Patch Changes

  • #16813 8f7d8c4 Thanks @matthewp! - Populates styles in the SSR manifest for prerendered routes. Previously, prerendered routes had styles: [] in the manifest, making it impossible for workers or middleware to discover which CSS files a prerendered page uses.
withastro/astro

Fixes #16838.

Changes

The problem (#16838): @astrojs/sitemap writes per-URL <lastmod> into the child sitemaps but never into the <sitemap> entries of sitemap-index.xml. The index gets a <lastmod> only if you set the global lastmod option, and then every entry carries the same date. So the index cannot tell a crawler which child sitemap actually changed — even though the freshness data is already computed and sitting in the child sitemaps.

This PR derives each index entry's <lastmod> from the child sitemap it points to:

  • Each <sitemap> entry is stamped with the most recent <lastmod> among the URLs that land in that file. URLs are written in source order, limit per file, so the date is computed from items.slice(i * limit, (i + 1) * limit).
  • Works for both chunked (chunks) and non-chunked output, and stays accurate when a sitemap overflows into multiple numbered files.
  • When a child sitemap has no per-URL lastmod, the entry falls back to the configured lastmod option — existing behaviour preserved.
  • customSitemaps entries keep using the global lastmod (there are no items to derive a date from).

Before / after, for the reproduction in #16838:

<!-- before -->
<sitemap><loc>https://example.com/sitemap-0.xml</loc></sitemap>

<!-- after -->
<sitemap><loc>https://example.com/sitemap-0.xml</loc><lastmod>2024-09-15T00:00:00.000Z</lastmod></sitemap>

The changeset is patch. It is a behaviour change for anyone setting per-URL lastmod via serialize (their index now carries accurate per-file dates), so happy to bump to minor if preferred.

Testing

New test/index-lastmod.test.ts:

  • Chunked — distinct lastmod values across blog/glossary chunks; asserts each index entry surfaces the newest date in its child sitemap, and that a chunk with no per-URL lastmod falls back to the configured lastmod.
  • Non-chunked, multiple filesentryLimit: 1 so each URL gets its own file; asserts every index entry's lastmod equals the date in the child sitemap it points to (exercises the per-file slicing for i > 0).

Full @astrojs/sitemap suite passes (40/40). biome, eslint, knip, and tsc -b are clean.

Docs

No docs change needed — this refines the existing lastmod behaviour with no new or changed API surface. The lastmod option keeps working as a fallback for child sitemaps without per-URL dates.

withastro/astro

Changes

Document on config reference page that compressHTML: "jsx" is only available starting Astro 6.2.0.

Without this reference, it looks like the option has always been available but users of older versions (e.g. 6.1.9) will see the following message that contradicts current documentation:

[config] Astro found issue(s) with your configuration:

! compressHTML: Expected type "boolean", received "string"

I tried to follow the writing style from i18n.routing which also introduced a new option, by starting the sentence similarly.

From some past PRs I checked, doesn't seem like minor doc text change requires changeset, please correct me otherwise.

Testing

Not tested since it's a doc text change.

Docs

This is a doc change itself, which I previously mistakenly tried to do on the generated doc: withastro/docs#13920

withastro/starlight

Description

This PR disables the minimum release age check of 3 days recently added for withastro/automation updates.

While reviewing #3903, and even after retrying the PR, I was confused why the renovate/stability-days check was still pending, even though all the updates were released around 10 days ago (last change for withastro/automation - last release for pnpm/action-setup).

I'm not a Renovate expert, but I think I finally understand the reason:

  • The withastro/automation is a digest update as we're using the latest commit SHA on the main branch in workflow files without a version tag (@<sha> # main).
  • As documented here, for digest updates, having a release timestamp that can have minimumReleaseAge enforced is "Generally not supported".
  • In Renovate 42, the absence of a release timestamp will be treated as if the release is not yet past the timestamp, which provides a safer default.

I'm not 100% sure, but I think this is the reason why the renovate/stability-days check is still pending, and will probably be always pending, preventing us from merging other updates.

A few extra points:

  • If merging this PR and retrying #3903 does not resolve the issue, we can revert this PR and investigate further.
  • If this works, it could still be a short-term solution and maybe a more long term solution would be to add proper releases with tags to withastro/automation.
  • No matter what we end up doing, we will probably need to do the same to the Docs repo as I think withastro/docs#13873 may have the same issue.
withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.39.3

Patch Changes

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.8

Patch Changes

  • #16830 f2bf3cb Thanks @matthewp! - Fixes 404s for dynamically imported JS chunks when using an adapter with assetQueryParams (e.g. Vercel skew protection)

  • #16831 ace96ba Thanks @astrobot-houston! - Fixes a misleading GetStaticPathsRequired error when a redirect is configured from a dynamic route to a static (or less-dynamic) destination. For example, '/project/[slug]': '/' previously produced a confusing error pointing at index.astro. Astro now detects the parameter mismatch at config validation time and throws a clear InvalidRedirectDestination error naming the missing parameters.

  • #16702 b7d1758 Thanks @matthewp! - Fixes scoped styles from .astro components being dropped when rendered inside MDX content (<Content /> from render(entry)) passed through a named slot using <Fragment slot="X">. The Fragment component now eagerly evaluates its slot contents to ensure propagating components register their styles before head content is flushed.

  • #16823 3df6a45 Thanks @astrobot-houston! - Fixes missing CSS for conditionally rendered Svelte components in production builds

  • #16836 3d7adfa Thanks @LongYC! - Document compressHTML: "jsx" config is only available since Astro v6.2.0

  • #16864 334ce13 Thanks @cheets! - Fixes a false-positive Internal Warning: route cache overwritten logged on every SSR request for dynamic routes

@astrojs/cloudflare@13.5.5

Patch Changes

  • #16607 98297af Thanks @alexanderflodin! - Fixes incorrect assets.directory in the generated wrangler.json when a base path is configured

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/sitemap@3.7.3

Patch Changes

  • #16837 783c4a6 Thanks @jdevalk! - Improves <lastmod> accuracy in the sitemap index. Each <sitemap> entry in sitemap-index.xml is now stamped with the most recent lastmod of the URLs in the child sitemap it points to, instead of repeating a single global date on every entry. When a child sitemap has no per-URL lastmod, the entry falls back to the lastmod option as before. This gives search engines a per-file freshness signal, so they can tell which child sitemaps actually changed without refetching all of them.

@astrojs/svelte@8.1.2

Patch Changes

  • #16496 4d79750 Thanks @fkatsuhiro! - Fixed an issue where type errors occurred during testing library type checks because Astro overrides Svelte 5 component types.

@astrojs/language-server@2.16.10

Patch Changes

  • #16827 90ee151 Thanks @matthewp! - Fixes a crash in the language server and astro check when using TypeScript project references with .vue or .svelte files
withastro/astro

Changes

  • Fixes misleading GetStaticPathsRequired error when redirects from dynamic routes to static destinations are configured (e.g., '/project/[slug]': '/')
  • Adds parameter validation in createRedirectRoutes() to check that destination routes have all required parameters from source routes
  • Extends InvalidRedirectDestination error message to clearly identify missing parameters, preventing confusion about which file is causing the issue

Testing

  • Added unit test for dynamic origin redirecting to static destination (/project/[slug]/)
  • Added unit test for dynamic origin with more params than destination (/old/[id]/[page]/posts/[id])
  • Both tests verify the error is InvalidRedirectDestination with clear parameter information, not the misleading GetStaticPathsRequired

Docs

  • No docs update needed, as this fixes an existing error case to be clearer rather than introducing new functionality.

Closes #16482

withastro/astro

Changes

  • Moves plugin-chunk-imports from renderChunk to generateBundle so it runs after Vite's CSS plugin cleans up pure CSS wrapper chunks. Previously, appending ?dpl=... query params during renderChunk broke Vite's regex-based /* empty css */ replacement, leaving dangling imports to deleted chunks that 404 at runtime.

Fixes #16520

Testing

  • New css-pure-chunk-query-params.test.ts with a Vue fixture where two async components share the same CSS import, forcing Rollup to create a shared pure CSS chunk. Verifies no JS imports reference deleted files, and that query params are still appended to surviving imports.

Docs

  • No docs needed — internal build pipeline fix with no user-facing API change.
withastro/astro

Changes

  • Adds GH_TOKEN to the "Remove Preview Label" step in the preview release workflow. PR #16810 replaced the actions-ecosystem/action-remove-labels action with a gh CLI call but did not set the token — gh requires GH_TOKEN explicitly, unlike GitHub Actions which inject auth automatically for actions.

Testing

  • No test changes. This is a CI-only fix — the step was failing with exit code 4 on every preview release since #16810 merged.

Docs

  • No docs needed — internal CI fix.
withastro/astro

Fixes #16817

When a tsconfig uses project references that include .vue or .svelte files, TypeScript's getOutputDeclarationFileName returns the input path unchanged (since changeExtension doesn't recognize these extensions). This creates self-referencing entries in the redirect maps (Hello.vueHello.vue), causing infinite recursion in findSourceFile.

The fix provides getParsedCommandLine on the language service host, which is TypeScript's official hook for customizing how referenced tsconfigs are parsed. Our implementation parses them normally but filters .vue/.svelte/.astro files from the file list, preventing the self-referencing redirect entries. Project references remain fully functional for all standard TS/JS files.

withastro/astro

Changes

  • Fixes CSS for conditionally rendered Svelte components being missing from production builds when the condition is false during SSR
  • During the SSR build, tracks which component exports were actually rendered (ssrRenderedExports on BuildInternals)
  • Saves CSS assets before deletion during client build deduplication and restores them only when the cssScopeTo target component was not rendered in SSR — meaning the styles were genuinely tree-shaken and missing from the page. CSS for components that were rendered in SSR (and already on the page) stays deleted to avoid duplicate stylesheets.
  • Updates test fixture to use deterministic condition ($state(false) with $effect()) instead of random condition that was passing by coincidence

Testing

  • Updated packages/integrations/svelte/test/fixtures/conditional-rendering/src/components/Parent.svelte to use $state(false) instead of Math.random() > 0.5 for deterministic reproduction
  • Existing conditional-rendering.test.ts now consistently verifies that conditionally rendered component CSS is included in production builds
  • Verified that the existing 0-css.test.ts "remove unused styles from client:load components" test continues to pass (CSS for normally rendered client:load components is still correctly deduplicated)

Docs

  • No docs update needed — this fixes existing documented behavior rather than introducing new functionality

Closes #16251

withastro/astro

Changes

Adds styleDirective.unsafeInline opt-in flag to Astro's CSP configuration (fixes #14798).

Per the CSP spec, browsers silently ignore unsafe-inline when hashes or nonces are present in the same directive. This meant users who needed unsafe-inline for styles (e.g. third-party libraries, inline style attributes) were forced to abandon Astro's CSP feature entirely — losing script hashing in the process.

When styleDirective.unsafeInline: true is set:

  • Astro emits unsafe-inline in style-src instead of style hashes
  • Script hashing is unaffected

Testing

Unit tests added in test/units/csp/rendering.test.ts covering:

  • unsafe-inline is emitted and style hashes are omitted when the flag is set
  • Script hashes are unaffected when the flag is set
  • Default behavior (hashes emitted, no unsafe-inline) is unchanged

Docs

The unsafeInline property is documented inline in config.ts alongside the existing hashes and resources properties. The CspStyleDirective exported type is also updated.

/cc @withastro/maintainers-docs for feedback!

withastro/astro

Changes

  • Fixes Node adapter in middleware mode incorrectly JSON-stringifying Buffer bodies from serverless-http
  • Adds binary data check (ArrayBuffer.isView() and instanceof ArrayBuffer) before generic object branch in makeRequestBody
  • Buffer, Uint8Array, and other typed arrays now pass through directly as valid BodyInit values

Testing

  • Added 5 new test cases in test/units/app/node.test.ts covering Buffer, Uint8Array, ArrayBuffer, plain objects, and strings
  • Verifies binary data passes through unchanged while preserving existing string/object handling
  • All existing tests pass with no regressions

Docs

  • No docs update needed, this restores expected behavior for a specific integration pattern

Closes #16820

withastro/astro

Changes

  • Custom elements (tags with hyphens like <my-element>) in MDX files now go through the renderer pipeline, matching .astro file behavior
  • Adds hyphen check in JSX runtime so custom elements fall through to renderComponentToString() instead of renderElement()
  • Preserves backward compatibility — custom elements with no registered renderer still render as raw HTML

Testing

  • Added unit tests in packages/astro/test/units/render/jsx-custom-elements.test.ts covering renderer pipeline integration and fallback behavior
  • Verified standard HTML elements continue using renderElement() path
  • Confirmed no regressions in existing MDX and render test suites

Docs

  • No docs update needed — this fixes existing documented behavior rather than introducing new functionality

Closes #16273

withastro/astro

This change is so that users can look at manifest.routes[].styles and add Preload link headers. Previously we didn't include this information for prerendered routes because we didn't think it was necessary.

Changes

  • Populates external stylesheet URLs in the SSR manifest for prerendered routes. Previously these had styles: [], making it impossible for workers to discover which CSS files belong to a prerendered page. Inline styles are still omitted (already in the HTML).

Testing

  • No test changes — this is a data-only change to the serialized manifest.

Docs

  • No docs needed.
withastro/astro

Changes

This PR pin the exact version used by the merge-main-to-next workflow to match the same version we use in the repository. I should fix the failures.

Testing

Merge and wait once it's triggered again

Docs

N/A

withastro/astro

tldr, this basically allows for:

export default {
  async fetch(request: Request): Promise<Response> {
    console.log('Request URL:', request.url); // https://localhost:4321
    const state = new FetchState(request);
    return await astro(state);
  },
} satisfies Fetchable;

Changes

  • Moves X-Forwarded-Proto/Host/Port resolution into FetchState's constructor, so headers set by user code in src/app.ts before creating FetchState are reflected in Astro.url and Astro.clientAddress. Previously these headers were resolved before the Fetchable handler's fetch() was called, making user modifications invisible.
  • Removes the now-redundant forwarded header processing from the dev server's handleRequest() in vite-plugin-app/app.ts. The production-side createRequest() in node.ts is kept as-is for backward compatibility with third-party adapters that call it directly without going through FetchState.
  • clientAddress is also resolved from X-Forwarded-For inside FetchState when the host is trusted, unless already provided via render options.

Closes #16797

Testing

  • 11 new unit tests in test/units/fetch/index.test.ts covering: ignored headers without allowedDomains, proto/host/port applied individually and together, rejected untrusted hosts, clientAddress from X-Forwarded-For (trusted/untrusted/pre-set), the core issue scenario (headers set in fetch() before FetchState), and a full pipeline render with forwarded headers.

Docs

  • No docs update needed — this is a bug fix to existing behavior with experimental.advancedRouting.
withastro/astro

Changes

This PR adds:

  • zizmor to our CI to check for incorrect usage of our workflows. As for now, I tuned the tool to target high confidence and high severity
  • I run the tool in our actions, and applied the suggestions
    • removed an action from our preview release
    • removed a possible template injection

There's still a warning

warning[secrets-inherit]: secrets unconditionally inherited by called workflow
  --> .github/workflows/format.yml:12:11
   |
12 |     uses: withastro/automation/.github/workflows/format.yml@497c9268ad4267c842a8f6c4d830ad0182d6f6b3 # main
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this reusable workflow
...
15 |     secrets: inherit
   |     ---------------- inherits all parent secrets
   |
   = note: audit confidence → High

However it's not easy fixable at the moment

Note

I plan to address even warnings, but for now I'll focus on high profile errors.

Testing

Green CI

Docs

withastro/astro

Changes

Closes #16781

It hardens and fixes the double encoding algorithm.

Testing

Added a bunch of unit tests, and an integration test.

Docs

N/A

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.7

Patch Changes

  • #16821 9c76b12 Thanks @astrobot-houston! - Fixes request body handling in the Node adapter when req.body is a Buffer, Uint8Array, or ArrayBuffer. Previously, binary body data was incorrectly JSON-stringified (producing {"type":"Buffer","data":[...]}) instead of being passed through directly. This affected libraries like serverless-http that set req.body to a Buffer.

  • #16785 de96360 Thanks @astrobot-houston! - Fixes vite.build.minify, vite.build.sourcemap, and vite.build.rollupOptions.output (e.g. compact) being ignored for client-side builds. These top-level Vite build options are now properly forwarded to the client environment, with environment-specific overrides (vite.environments.client.build.*) taking priority when set.

  • #16819 b5dd8f1 Thanks @astrobot-houston! - Fixes custom elements in MDX files bypassing the renderer pipeline. Custom elements (tags containing hyphens like <my-element>) in .mdx files are now routed through registered renderers for SSR, matching the behavior of .astro files. If no renderer claims the element, it falls back to rendering as raw HTML.

  • #16808 765896c Thanks @ematipico! - Fixes dynamic routes returning 400 Bad Request when the URL contains a literal % character, such as paths built with encodeURIComponent('%?.pdf')

  • #16804 90d2aca Thanks @jp-knj! - Fixes a v6 regression where astro:i18n could not be imported from client <script> blocks.

@astrojs/cloudflare@13.5.4

Patch Changes

  • #16769 428cb1b Thanks @astrobot-houston! - Forwards user-provided optimizeDeps settings (exclude, include, esbuildOptions.loader) to SSR/prerender environments. Previously, top-level vite.optimizeDeps in the Astro config was silently ignored for server environments because Vite 6 scopes it to client-only and the adapter's configEnvironment hook did not forward it. This caused packages with non-standard file types (e.g. .data files) to fail during dev-mode dependency optimization with errors like "No loader is configured for '.data' files".

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3
withastro/astro

Adds guardrails to the triage skill so the agent bails out after 2 failed server starts instead of looping until timeout.

Prompted by this run where the agent confirmed the bug early but wasted ~10 minutes fighting a stale server process and ran out of time before writing its report.

Changes:

  • SKILL.md: General "don't get stuck" rule at the top level
  • reproduce.md: Server Management Rules subsection (bail after 2 failures, stop before restart, one repro run is enough, prefer build over dev/preview)
  • diagnose.md: Brief server management reminder in the instrumentation step
withastro/astro

Changes

It seems that emdash-cms/emdash#1116 was caused by #16708

This PR reverts it

Testing

Preview release

Docs

withastro/astro

Changes

  • Adds 5-legacy to the branches filter on the preview release workflow so PRs targeting 5-legacy can get preview releases via the pr preview label.

Testing

  • No test changes.

Docs

  • No docs needed.
withastro/astro

Changes

  • Re-enables all Svelte e2e tests that were disabled when the monorepo upgraded to Vite 8. @sveltejs/vite-plugin-svelte@6.2.4 now has experimental Vite 8 support, and all previously-skipped tests pass.
  • Uncomments @astrojs/svelte integration setup and Svelte component usage across 25 e2e fixture files (configs + pages), and removes .skip from 11 e2e test files.
  • The Cloudflare svelte-rune-deps test still fails (500 in dev mode) so it remains skipped with an updated reason. The async-rendering test is a separate Svelte upstream issue and was not touched.

Testing

  • No new tests added — this PR re-enables ~20 existing test cases across svelte-component, nested-in-svelte, nested-in-react, nested-in-vue, nested-in-preact, nested-in-solid, nested-recursive, client-only, csp-client-only, errors, and view-transitions e2e suites.

Docs

  • No docs needed — test-only change.

Closes AST-87

withastro/astro

Changes

This PR fixes this failure: https://github.com/withastro/astro/actions/runs/25864427348/job/76002567202#step:7:13

It's caused by conflicts=true, which leaves possible conflict markers in package.json. Then the pnpm action comes, and tries to use the version from the manifest, which is broken.

This PR pins the pnpm version to use in the GH action

Testing

Green CI. We will see if it works after the next release.

Docs

withastro/astro

Changes

  • Before creating a PR, the fix-verification agent now checks if one already exists for the flue/fix-{issueNumber} branch. If a PR exists, it posts a comment linking to it instead of failing with a 422 from the GitHub API.
  • Adds a findPullRequest helper to .flue/lib/github.ts that queries open PRs by head branch.

Testing

  • No automated tests; this is CI workflow glue code. Verified the logic matches the GitHub Pulls API behavior.

Docs

  • No docs needed — internal CI change only.
withastro/astro

Changes

Update esbuild and tsx to the latest versions. This reduces the vite instances installed under node_modules/ and resolves the test failure in Examples astro check / astro check CI job.

Before:

We have 3 vite instances installed. astro and @example/with-vitest use different vite.

$ git status
On branch chore/merge-main-into-next

$ pnpm -r why vite | egrep ' astro@|vite@8|@example|Found'
vite@8.0.13 peer#4033 (3 variations)
│   └── astro@7.0.0-alpha.1 (devDependencies)
├── astro@7.0.0-alpha.1 (dependencies)
│ └── astro@7.0.0-alpha.1 (dependencies)
vite@8.0.13 peer#5d0f (3 variations)
vite@8.0.13 peer#96d1 (3 variations)
│ ├── @example/with-tailwindcss@0.0.1 (dependencies)
│   ├── @example/container-with-vitest@0.0.1 (dependencies)
│   └── @example/with-vitest@0.0.1 (dependencies)

Found 1 version, 3 instances of vite

After:

We have 2 vite instances installed. astro and @example/with-vitest share the same vite.

$ git status
On branch ocavue/chore/merge-main-into-next

$ pnpm -r why vite | egrep ' astro@|vite@8|@example|Found'
vite@8.0.13 peer#1781 (2 variations)
│ ├── @example/with-tailwindcss@0.0.1 (dependencies)
│   ├── @example/container-with-vitest@0.0.1 (dependencies)
│   ├── astro@7.0.0-alpha.1 (devDependencies)
│   └── @example/with-vitest@0.0.1 (dependencies)
├── astro@7.0.0-alpha.1 (dependencies)
│ ├── astro@7.0.0-alpha.1 (dependencies)
vite@8.0.13 peer#ade4 (2 variations)

Found 1 version, 2 instances of vite

Testing

Docs

withastro/astro

Changes

  • Fixes vite.build.minify, vite.build.sourcemap, and rollup output options being ignored when configured through the top-level vite.build in astro.config.ts
  • Adds proper fallback chains so client environment config inherits from top-level Vite config, with environment-specific overrides taking highest priority
  • Resolves regression from Vite Environment API migration (PR #14306) where these options were hardcoded instead of respecting user configuration

Testing

  • Added 8 new unit tests in packages/astro/test/units/build/vite-build-config.test.ts covering minify, sourcemap, and rollup output inheritance scenarios
  • Tests verify environment-specific config takes priority over top-level config, which takes priority over defaults
  • Existing sourcemap integration tests continue to pass

Docs

  • No docs update needed, as this restores documented behavior that was broken by the Environment API migration

Closes #16268

withastro/astro

Changes

This PR improves the printed time stamp of the build

Testing

Added unit tests

Docs

N/A

withastro/starlight

Description

Fixes #2697.

When the mobile menu is open on narrow viewports, Tab navigation currently escapes the menu after the last focusable element (the theme switcher) and lands on the underlying page content.

This PR sets the native inert attribute on .main-frame and the skip-to-content link while the mobile menu is expanded, so the browser keeps keyboard focus inside the header and sidebar without a hand-maintained list of focusable selectors. A matchMedia('(min-width: 50em)') listener collapses the menu and clears inert automatically if the viewport grows past the mobile breakpoint — so rotating or resizing a device cannot leave the page locked behind a stale trap.

Test plan

Verified manually against pnpm --filter starlight-docs dev at mobile (600×900) and desktop (1024×900) widths:

  • Open the menu: aria-expanded="true", inert set on both .main-frame and .sl-skip-link.
  • Tab through the menu: focus visits sidebar <summary> group toggles (e.g. "Start Here", "Guides") and never lands inside .main-frame. Shift+Tab from the first sidebar item returns to the toggle/close button.
  • Programmatic .focus() on a link inside .main-frame is refused by the browser while inert.
  • Resize to 1024px wide while open: aria-expanded drops to "false", inert is cleared, article links are interactive again.
  • Press Escape: menu closes and focus returns to the toggle (regression check).

Per maintainer request, no dedicated e2e tests are added in this PR — if we add coverage later, it should live in __e2e__/basics.test.ts so it shares the basics fixture build.

pnpm lint, pnpm typecheck, and pnpm --filter @astrojs/starlight test (519/519) all pass.

Changeset

Patch bump for @astrojs/starlight included.

withastro/astro

Changes

  • Adds npm and npx as allowed commands in the triage agent's flue sandbox. The agent uses npx stackblitz-clone to clone issue reproductions, but npx wasn't a permitted command, causing the agent to waste time hunting for tools and ultimately fail (run).

Testing

  • No test changes. This is a configuration-only change to the flue agent definition.

Docs

  • No docs needed — internal CI tooling change.
withastro/astro

Changes

  • The HMR reload plugin now invalidates all recursively-invalidated modules in the SSR module runner's evaluatedModules cache, not just the directly changed file. moduleGraph.invalidateModule() already walks importers and populates an invalidatedModules set, but the runner cache only cleared the leaf module. Barrel files (e.g. index.ts re-exporting components) stayed cached, so await import("../components") returned stale exports even after a full page reload.

Fixes #16000

Testing

  • New unit test in hmr-reload.test.ts: "invalidates importers in the module graph for dynamic import chains" -- sets up a component with a barrel file importer and verifies the invalidation propagates through the mock module graph.
  • Updated mock invalidateModule to simulate Vite's recursive importer walk by adding importers to the seen set.

Docs

  • No docs changes needed. This is a bug fix restoring behavior from Astro 5.
withastro/astro

Changes

  • The route cache (callGetStaticPaths) now stores the module reference alongside cached static paths and compares identity on lookup. After HMR, mod is a new object from a fresh import(), so the cache treats the old entry as stale and re-calls getStaticPaths() with updated component references. This fixes the case where components passed as props via getStaticPaths() served stale content in dev — even on manual refresh.

Fixes #16522

Testing

  • New unit test in getstaticpaths-cache.test.ts: "re-calls getStaticPaths when module identity changes (HMR)" — calls callGetStaticPaths with one module, confirms cache hit on same module, then passes a different module object and asserts the cache is bypassed and fresh props are returned.

Docs

  • No docs changes needed. This is a bug fix restoring existing behavior from Astro 5.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.6

Patch Changes

  • #16774 8f77583 Thanks @astrobot-houston! - Fixes markdown images with empty alt text (![](image.jpg)) in content collections dropping the alt attribute entirely. The alt="" attribute is now correctly preserved in the rendered HTML output, which is important for accessibility (indicating decorative images).

  • #16776 3d10b5e Thanks @matthewp! - Fixes HMR serving stale content when components are passed as props via getStaticPaths()

  • #16784 7453860 Thanks @ematipico! - Improved the printing of the build time if it goes over the 60 seconds.

  • #16665 3dbbcee Thanks @Princesseuh! - Fixes remote SVG sources erroring with dangerouslyProcessSVG after the v6.3 SVG-processing gate. The default Sharp service now resolves the output format from the source up-front when it can (URL extension, data: MIME, ESM metadata), and from the actual buffer at request time when it can't, so SVG sources pass through untouched without needing to set image.dangerouslyProcessSVG: true or an explicit format="svg".

    The error message has also been updated to point at format="svg" as the simpler workaround when an SVG source is encountered without dangerouslyProcessSVG enabled.

  • #16777 1754b91 Thanks @matthewp! - Fixes HMR serving stale content for dynamically imported components through barrel files

  • #16730 068d924 Thanks @harshagarwalnyu! - Fixes an issue where the file() content loader did not generate a valid JSON Schema for collections whose JSON or YAML data is a top-level array instead of an object.

@astrojs/cloudflare@13.5.3

Patch Changes

  • #16801 d619277 Thanks @ematipico! - Reverts a change to the esbuild dep-scan plugin that caused astro check and astro build to fail by making esbuild incorrectly bundle virtual: modules (e.g. from expressive-code)

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3
withastro/astro

Changes

  • Fixes markdown images with empty alt text (![](image.jpg)) in content collections to render alt="" instead of dropping the attribute entirely
  • Replaces JavaScript truthiness check with explicit value != null filtering to preserve empty string attributes
  • Only affects content collections markdown path — pages-based markdown already handled this correctly

Testing

  • Added test 'content collection images with empty alt preserve the alt attribute' in packages/astro/test/core-image.test.ts to verify empty alt attributes are preserved
  • Verified all existing image, markdown, and content collection tests still pass

Docs

  • No docs update needed, this fixes a regression to match existing expected behavior

Closes #16621

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.5

Patch Changes

  • #16771 07c8805 Thanks @ematipico! - Fixes position prop on <Image> and <Picture> components breaking Content Security Policy (CSP).

  • #16593 50924ce Thanks @yanthomasdev! - Improves error messages with more consistent and correct writing.

  • #16757 5d661cd Thanks @astrobot-houston! - Fixes dev server serving stale content when SSR-only modules change (e.g. .astro files outside the project root in a monorepo, or dynamically imported components).

    Previously, the astro:hmr-reload plugin returned an empty array after detecting SSR-only module changes, which prevented Vite's updateModules from propagating the invalidation to the SSR module runner. The runner's evaluated module cache stayed stale, so subsequent requests continued returning old content.

    Now the plugin returns the SSR-only modules so Vite can process them through updateModules, which properly invalidates the module runner's cache and ensures fresh content on the next request.

withastro/starlight

Summary

  • fix a typo in the Romanian previous page label
  • translate remaining English strings in packages/starlight/translations/ro.json
  • add a changeset for the user-facing translation fix
withastro/astro

Changes

Closes #16656

The solution of #16236 doesn't work with CSP.

In this PR we extend the solution we already have by taking position into account too.

Testing

Added a bunch of unit tests and a couple of integration tests.

Docs

N/A

withastro/astro

Changes

  • Uses the server to do injectScript entrypoint discover to prevent a race condition with the dep optimizer.

Closes #16766. Related: #16387.

Testing

  • User confirmed the fix
  • Reproduced locally against npm-installed astro using the reporter's reproduction. Before fix: _metadata.json contained 1 entry. After fix: 10 entries including all React deps.
  • No new test — the race condition requires packages installed from npm (not workspace links) and timing-dependent optimizer behavior.

Docs

  • No docs changes needed.
withastro/astro

Changes

  • Forwards user-provided optimizeDeps.exclude, optimizeDeps.include, and optimizeDeps.esbuildOptions.loader from the Astro config to SSR/prerender environments in the Cloudflare adapter
  • Fixes packages with non-standard file extensions (like .data) failing during dev-mode dependency optimization
  • Resolves issue where top-level vite.optimizeDeps configuration was silently ignored for server environments due to Vite 6's client-scoped design

Testing

  • Added packages/integrations/cloudflare/test/user-optimize-deps.test.ts with a test that imports a package with .data files to verify the exclude configuration works
  • Test uses a fake-data-pkg fixture that would fail without the fix but succeeds when optimizeDeps.exclude is properly forwarded

Docs

  • No docs update needed, this fix restores expected behavior where user Vite config applies to all environments.

Closes #16491

withastro/astro

Root cause

The suggestion of Astro bot is included in #16124 . This fixing is clear the cache of disc. But it didn't solve the issue.
It seems the root cause is remaining cache of vite runner eveluatedModules cache.

Changes

Close #13149

Implemented invalidation for file paths and cache clearing in /packages/astro/src/content/vite-plugin-content-virtual-mod.ts.

Testing

Before Implementation

スクリーンショット 2026-05-17 0 00 19

After Implementation

スクリーンショット 2026-05-17 20 55 52
withastro/starlight

Description

This pull request, adds a simple new page template called blank that allows the end-user to fully customize what's rendered in the ContentPanel without having to worry about CSS hacks to hide things like the PageTitle which are present in the splash template. For more information see #3906

Additionally, it refactors a small section in the Page.astro that handles conditional rendering for pages with a hero image. Hopefully it improves visibility and maintainability in comparison to the previous version.

This pull request does not impact the default Starlight look.

withastro/astro

Changes

There is a json schema file included in the wrangler npm package used to specify the structure of the cloudflare wrangler configurations.

This changes the astro add cloudflare command to include the schema reference when generating the wrangler.jsonc file.

  • I included a changeset

Testing

Doesn't look like there are any existing tests for the astro add command.

Tested it locally instead.

Screenshot 2026-05-16 at 1 31 44 PM

Docs

Sufficient documentation exists for the add command and the CLI walks the user through it.

withastro/astro

Changes

  • Handles CSS @property rules inside Astro <style> blocks before delegating to VS Code's generic CSS grammar.
  • Prevents the tokenizer from staying in CSS context after </style>, so following <style> and <script> blocks keep correct highlighting.

Testing

  • Adds a grammar fixture covering @property descriptors followed by additional style and script blocks.
  • Adds a snapshot assertion that the closing style tags and following script block keep Astro scopes.

Docs

  • No docs update needed because this fixes editor syntax highlighting behavior only.
withastro/astro

Changes

  • Fixes HMR for SSR-only modules in monorepo setups where shared components live outside the Astro project root
  • Changes astro:hmr-reload plugin to return SSR-only modules from hotUpdate instead of an empty array, allowing Vite's updateModules() to properly invalidate the SSR module runner cache
  • Resolves regression from Astro 4/5 where external file changes required dev server restart to be visible

Testing

  • Added comprehensive unit tests in packages/astro/test/units/vite-plugin-hmr-reload/hmr-reload.test.ts covering the key scenarios:
    • SSR-only modules are returned (not []) — the critical test that fails without the fix
    • Non-SSR environments are skipped correctly
    • Style-only modules continue to return [] as expected
    • Modules that exist in both client and SSR environments are excluded

Docs

  • No docs update needed, this fixes existing functionality to work as documented

Closes #16754

withastro/astro

Changes

  • Fixes getSSREnvironment() in module loader to return the passed ssrEnvironment parameter instead of hardcoded viteServer.environments['ssr']
  • Resolves missing styles from Markdoc/MDX custom components in <head> when using Cloudflare adapter with prerenderEnvironment: 'node' and wrapper components
  • The bug occurred because component metadata crawling used the wrong environment's module graph (empty workerd vs populated prerender)

Testing

  • Added packages/astro/test/units/dev/module-loader.test.ts with 2 unit tests confirming getSSREnvironment() returns the correct environment
  • Verified the fix resolves the issue in the reporter's reproduction case

Docs

  • No docs update needed, this fixes a bug with no API changes

Closes #16013

withastro/astro

Changes

Reverts the two-job build/publish split from #16710 back to a single job. The split caused artifact upload timeouts and workspace config mismatches after the pnpm v11 upgrade. Skipping the pnpm cache instead eliminates the cache poisoning vector without needing to shuttle artifacts between jobs.

Testing

CI-only workflow change, validated by triggering a preview release.

Docs

No docs needed — internal CI change.

withastro/astro

Changes

The hardened preview-release workflow (#16710) combined with the pnpm v11 upgrade broke preview releases. This generates a synthetic minimal workspace for the publish artifact and uses pnpm dlx to run pkg-pr-new without triggering workspace resolution. Also excludes test/src/e2e directories from the artifact to prevent upload timeouts.

Testing

  • Added smoke tests for the staging script (scripts/stage-preview-publish.test.ts).

Docs

No docs needed — internal CI change.

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

astro@7.0.0-alpha.2

Major Changes

  • #16610 c63e7e4 Thanks @matthewp! - Adds background dev server management for AI coding agents.

    When an AI coding agent is detected, astro dev now automatically starts the dev server as a detached background process. This prevents the dev server from blocking the agent's terminal and allows it to continue working while the server runs.

    A lock file (.astro/dev.json) is written when the dev server starts, recording the server's URL, port, and PID. This prevents duplicate servers from being started for the same project.

    New flag and subcommands

    • astro dev --background — Start the dev server as a background process (this is what runs automatically when an agent is detected).
    • astro dev stop — Stop a running background dev server.
    • astro dev status — Check if a dev server is running and display its URL, PID, and uptime.
    • astro dev logs — View logs from a background dev server. Use --follow (-f) to stream new output as it's written.

    These allow you to start and manage dev servers programmatically and were designed with AI coding agents in mind.

    What should I do?

    No action is required. If you are not using an AI coding agent, astro dev behaves exactly as before. If you are using an agent, background mode is enabled automatically — the agent will receive the server URL and PID, and can use astro dev stop to shut it down.

    To opt out of automatic background mode when an agent is detected, set the environment variable ASTRO_DEV_BACKGROUND=0 before running astro dev.

  • #16725 10229f7 Thanks @ArmandPhilippot! - Removes deprecated APIs exported from astro:transitions.

    In Astro 6.x, some helpers available in astro:transitions and astro:transitions/client were deprecated.

    In Astro 7.0, the following APIs can no longer be used in your project:

    • TRANSITION_BEFORE_PREPARATION
    • TRANSITION_AFTER_PREPARATION
    • TRANSITION_BEFORE_SWAP
    • TRANSITION_AFTER_SWAP
    • TRANSITION_PAGE_LOAD
    • isTransitionBeforePreparationEvent()
    • isTransitionBeforeSwapEvent()
    • createAnimationScope()

    What should I do?

    Remove any occurrence of createAnimationScope():

    -import { createAnimationScope } from 'astro:transitions';

    Replace any occurrence of the other APIs using the lifecycle event names directly:

    -import {
    -	TRANSITION_AFTER_SWAP,
    -	isTransitionBeforePreparationEvent,
    -} from 'astro:transitions/client';
    
    -console.log(isTransitionBeforePreparationEvent(event));
    +console.log(event.type === 'astro:before-preparation');
    
    -console.log(TRANSITION_AFTER_SWAP);
    +console.log('astro:after-swap');

    Learn more about all utilities available in the View Transitions Router API Reference.

Patch Changes

  • #16980 1f07343 Thanks @matthewp! - Removes state.provide(), state.resolve(), state.finalizeAll(), and App.Providers from the public advanced routing API. These context provider extension points are now internal-only. If you were using them in an integration, use locals to share per-request state instead.

  • #16982 1e000e2 Thanks @matthewp! - Improves the warning when accessing Astro.session without session storage configured. The session property is now always defined on the context object, and accessing it without configuration logs a helpful message instead of silently returning undefined.

  • #16990 ebeb830 Thanks @ocavue! - Fixes Astro.request.url not reflecting validated X-Forwarded-Proto/X-Forwarded-Host headers when security.allowedDomains is configured. Previously, only Astro.url was updated with the forwarded origin while Astro.request.url retained the socket-derived URL, causing the two to diverge behind TLS-terminating proxies.

@astrojs/svelte@9.0.0-alpha.2

Minor Changes

  • #16549 9d9d516 Thanks @ocavue! - Updates @sveltejs/vite-plugin-svelte to v7. No user action is necessary.

@astrojs/markdown-satteri@0.3.0-alpha.0

Minor Changes

  • #16969 4a31f90 Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Setting markdown.syntaxHighlight to 'prism' now highlights your code blocks with Prism.

    // astro.config.mjs
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri(),
        syntaxHighlight: 'prism',
      },
    });

@astrojs/cloudflare@14.0.0-alpha.1

Patch Changes

  • #16990 ebeb830 Thanks @ocavue! - Fixes a build crash when using experimental.advancedRouting with a custom fetchFile that statically imports cf from @astrojs/cloudflare/fetch. The circular dependency between @astrojs/cloudflare/fetch and astro/app/entrypoint caused createApp or createGetEnv to be undefined at module evaluation time. Initialization is now deferred to the first cf() call, breaking the cycle.

  • #16671 fd926fd Thanks @alexanderniebuhr! - Removes deprecations warnings added in Astro v6 for Cloudflare specific Astro.locals properties.

  • Updated dependencies [1f07343, 1e000e2, c63e7e4, ebeb830, 10229f7]:

    • astro@7.0.0-alpha.2
    • @astrojs/underscore-redirects@1.0.3

@astrojs/mdx@6.0.0-alpha.1

Patch Changes

  • #16969 4a31f90 Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Setting markdown.syntaxHighlight to 'prism' now highlights your code blocks with Prism.

    // astro.config.mjs
    import { satteri } from '@astrojs/markdown-satteri';
    
    export default defineConfig({
      markdown: {
        processor: satteri(),
        syntaxHighlight: 'prism',
      },
    });
  • Updated dependencies [4a31f90]:

    • @astrojs/markdown-satteri@0.3.0-alpha.0
withastro/astro

Changes

The hardening split (#16710) combined with the pnpm v11 upgrade broke preview releases — the publish job's minimal artifact was missing patches/ and had unresolvable workspace:* deps. This generates synthetic workspace configs in the staging step and uses pnpm dlx instead of pnpm exec to skip workspace resolution entirely.

Testing

Note, this is me, the human. Preview releases are currently broken after we split up the job for security reasons (so installing code not have access to the id token).

I ran the script in this change and verified it does pack all of the things it's supposed to. I can't run a preview release to verify that works.

Also added some unit tests to the script itself.

Docs

No docs needed — internal CI change.

withastro/astro

Changes

  • Fixes the dev prerender/SSR handoff for Cloudflare prerenderEnvironment: 'node' by delaying request body reads until AstroServerApp.handleRequest() finishes route resolution in prerenderOnly mode. If the resolved route falls through to SSR, the POST body is still intact when the SSR handler receives it.
  • Adds a comment next to the prerender middleware gate explaining why the matchAllRoutes() check is intentional: the final dev routing decision still needs the deeper route resolution in handleRequest(), so future changes do not replace that gate with matchRoute() again.

Testing

  • Added a Cloudflare regression test in packages/integrations/cloudflare/test/prerender-node-env.test.ts that exercises POST /_actions/hello with a prerendered catch-all route.
  • The same fixture continues to cover the surrounding prerenderEnvironment: 'node' behavior for prerendered pages, SSR pages, and server islands.

Docs

  • No docs update needed, because this fixes broken dev behavior rather than changing the supported API.

Closes #16746

withastro/astro

Changes

Closes AST-44

This PR stabilise the logger feature

Testing

Green CI.

Docs

withastro/docs#13907

withastro/astro

Changes

  • Adds a sparse checkout step to the publish job in the preview release workflow so pnpm/action-setup can read the packageManager field from package.json. The publish job previously had no checkout, so pnpm setup failed with "No pnpm version is specified."

Testing

  • No test changes. This is a CI workflow fix — validation is the next preview release run succeeding.

Docs

  • No docs needed — internal CI change only.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.4

Patch Changes

  • #16723 0f10bfe Thanks @matthewp! - Adds fetchFile option to experimental.advancedRouting to customize or disable the entrypoint file

    export default defineConfig({
      experimental: {
        advancedRouting: {
          fetchFile: 'fetch.ts',
        },
      },
    });
  • #16723 0f10bfe Thanks @matthewp! - Fixes Hono cache() middleware to follow the standard wrapper pattern

  • #16723 0f10bfe Thanks @matthewp! - Adds App.Providers interface for typing custom context providers on Astro and ctx

    declare namespace App {
      interface Providers {
        oauth: import('./lib/oauth').OAuthSession;
      }
    }
  • #16723 0f10bfe Thanks @matthewp! - Adds FetchState.response property, set automatically after pages() or middleware() completes

    const response = await middleware(state, (s) => pages(s));
    console.log(state.response === response); // true
  • #16723 0f10bfe Thanks @matthewp! - Adds Fetchable type export for typing the advanced routing entrypoint

    import type { Fetchable } from 'astro';
    
    export default {
      async fetch(request) {
        return new Response('ok');
      },
    } satisfies Fetchable;
  • #16572 4a5a077 Thanks @DORI2001! - Suppresses [WARN] Vite warning: unused imports from "@astrojs/internal-helpers/remote" during prerender builds. The package is now bundled alongside astro in the prerender environment, matching how it is handled in the SSR environment.

  • #16756 b6ee23d Thanks @astrobot-houston! - Fixes styles from Markdoc/MDX custom components not being extracted to <head> in the dev server when using the Cloudflare adapter with prerenderEnvironment: 'node' and rendering content through a wrapper component.

  • #16747 904d19a Thanks @astrobot-houston! - Fixes Astro action requests failing in astro dev when using the Cloudflare adapter with prerenderEnvironment: 'node' alongside a prerendered catch-all route such as [...page].astro.

    Actions and other SSR POST endpoints now continue to work in dev instead of returning an HTTP 500 error.

  • #16701 3495ce4 Thanks @demaisj! - Fix Map and Set instances saved in a content collection being broken when retrieving entries.

  • #16614 fca1c32 Thanks @Eptagone! - Fixes entry.data type inference when a live collection is configured without a schema.

  • #16661 03b8f7f Thanks @ocavue! - Updates typescript to v6. No changes are needed from users.

  • #16681 c22770a Thanks @dotnetCarpenter! - Fixes an issue where SVG images with width="0" or height="0" incorrectly threw a NoImageMetadata error instead of being treated as valid dimensions.

@astrojs/db@0.21.2

Patch Changes

@astrojs/cloudflare@13.5.2

Patch Changes

  • #16708 bb709ff Thanks @fkatsuhiro! - Fixed a bug where a cascade of reloads would cause the page to crash during the first visit when building or developing with Cloudflare SSR in Astro v6 due to dependency loading issues.

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/netlify@7.0.10

Patch Changes

  • #16661 03b8f7f Thanks @ocavue! - Updates typescript to v6. No changes are needed from users.

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/language-server@2.16.9

Patch Changes

  • #16661 03b8f7f Thanks @ocavue! - Updates typescript to v6. No changes are needed from users.

  • Updated dependencies [03b8f7f]:

    • @astrojs/yaml2ts@0.2.4

@astrojs/ts-plugin@1.10.9

Patch Changes

  • #16661 03b8f7f Thanks @ocavue! - Updates typescript to v6. No changes are needed from users.

  • Updated dependencies [03b8f7f]:

    • @astrojs/yaml2ts@0.2.4

astro-vscode@2.16.16

Patch Changes

@astrojs/yaml2ts@0.2.4

Patch Changes

withastro/astro

Changes

  • Extracts the Vite InlineConfig assembly from buildEnvironments() into a new createViteBuildConfig() function in vite-build-config.ts. The function takes settings, viteConfig, routes, plugins, builder, and isRollupInput, and returns the config object without executing any build.
  • The buildApp callback (which depends on internals) is passed in as a parameter, keeping the function free of build-time state.

Testing

  • New test/units/build/vite-build-config.test.ts with 12 unit tests covering: user rollup output overrides (assetFileNames, chunkFileNames, entryFileNames) preserved in top-level, client, and prerender environments; Astro defaults applied when no override; build.assets prefix flowing into rollup templates; base, envPrefix, and cssMinify behavior.
  • Removed 5 integration tests + 3 fixtures replaced by unit tests:
    • custom-assets-name.test.ts + fixture (user assetFileNames function override)
    • entry-file-names.test.ts + fixture (client entryFileNames override)
    • astro-assets-dir.test.ts + fixture (build.assets prefix)
    • config-vite.test.ts first describe block (prerender rollup output override)
    • astro-css-bundling.test.ts custom assetFileNames section (prerender assetFileNames override)

Docs

  • No docs needed — internal refactor with no user-facing behavior change.
withastro/astro

Changes

  • Replaces the astro-global integration test suite with unit tests using createTestApp. Covers Astro.site, Astro.url, Astro.routePattern, Astro.isPrerendered, and middleware locals propagation.
  • Removes the integration test file, fixture directory, and all associated fixture files (21 files including a 7MB penguin image).

Testing

  • New unit test file at test/units/render/astro-global.test.ts with 10 tests covering the same Astro globals behavior. Runs in ~330ms vs seconds for the integration tests.
  • Dropped 4 tests that provided no meaningful coverage: 3 tested import.meta.glob() (Vite behavior, not Astro globals) and 1 was accidentally asserting a 404 response.

Docs

  • No docs needed — test-only change.
withastro/astro

Changes

Replaces the flaky integration test added in d2e25c6, which fetched a remote image from kaleidoscopic-biscotti-6fe98c.netlify.app.

The replacement is a set of offline unit tests for propsToFilename that cover the same filename-sanitization logic without any network calls.

Testing

See changed tests

Docs

N/A

withastro/astro

Changes

  • Fixes a reflected XSS where slot names on hydrated (client:*) components were interpolated raw into data-astro-template and astro-slot name HTML attributes during SSR. An attacker could break out of the attribute context and inject arbitrary HTML when a slot name is derived from user input. Applies escapeHTML() to the slot name key at both interpolation points in component.ts.

Testing

  • Added slot-name-escape.astro fixture page that uses a slot name containing "><img src=x onerror=alert(1)> on a client:load component
  • Added test case in astro-children.test.ts verifying no injected <img> element exists and the attribute value contains properly escaped entities

Docs

  • No docs update needed — this is an internal security fix with no user-facing API change.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.3

Patch Changes

  • #16737 bd84f33 Thanks @matthewp! - Fixes a reflected XSS vulnerability where slot names on hydrated components were not HTML-escaped in SSR output

@astrojs/mdx@5.0.6

Patch Changes

withastro/astro

Changes

This PR addresses a flaky test shown in this run: https://github.com/withastro/astro/actions/runs/25853010563/job/75964157111?pr=16734

    Retry #2 ───────────────────────────────────────────────────────────────────────────────────────

    Error: There should be 3 page loads (for page one & three), and an additional loads for the back navigation

    expect(received).toEqual(expected) // deep equality

    Expected: 3
    Received: 2

      206 | 			loads.length,
      207 | 			'There should be 3 page loads (for page one & three), and an additional loads for the back navigation',
    > 208 | 		).toEqual(3);
          | 		  ^
      209 | 	});
      210 |
      211 | 	test('Declarative Shadow DOM elements are attached after transitions', async ({
        at D:\a\astro\astro\packages\astro\e2e\view-transitions.test.ts:208:5

    Error Context: test-results\e2e-view-transitions-View--20f1e--ClientRouter-w-back-button-Firefox-Stable-retry2\error-context.md

Particularly, I used expect.poll and expect.toPass to refactor some tests with a magic timeout.

// Bad pattern
doSomething()
await page.waitForTimeout(500);  // <-- A magic timeout number whether is too big (too slow) on a fast machine or too small (too fast) on a slow machine
expect(getResult()).toBe(expectedResult)

// Good pattern
doSomething()
await page.poll(getResult).toBe(expectedResult)

// Good pattern
doSomething()
await expect(() => { expect(getResult()).toBe(expectedResult) }).toPass()

Testing

CI should pass

Docs

N/A

withastro/astro

Changes

Previous, if generateImagesForPath() fails, an uncaught error will be thrown. This is an issue because it doesn't break the build() function itself, causing flaky tests.

For example, in this job run, the await fixture.build() returns a resolved promise, but the build is not actually successful.

astro:test: ✖ astro:image (37126.090234ms)
astro:test: ℹ Error: Test hook "before" at test/core-image.test.ts:992:3 generated asynchronous activity after the test ended. This activity created the error "TypeError: fetch failed" and would have caused the test to fail, but instead triggered an unhandledRejection event.

Later, when we want to check out the built result, we hit errors:

astro:test: ✖ failing tests:
astro:test: 
astro:test: test at test/core-image.test.ts:1020:3
astro:test: ✖ writes out allowed remote images (1.309057ms)
astro:test:   Error: ENOENT: no such file or directory, open '/home/runner/work/astro/astro/packages/astro/test/fixtures/core-image-ssg/dist/core-image-build-ssg/_astro/sponsors_1Wl70z.webp'
astro:test:       at async open (node:internal/fs/promises:640:25)
astro:test:       at async Object.readFile (node:internal/fs/promises:1283:14)
astro:test:       at async TestContext.<anonymous> (file:///home/runner/work/astro/astro/packages/astro/test/core-image.test.ts:1025:17)
astro:test:       at async Test.run (node:internal/test_runner/test:1125:7)
astro:test:       at async Suite.processPendingSubtests (node:internal/test_runner/test:787:7) {
astro:test:     errno: -2,
astro:test:     code: 'ENOENT',
astro:test:     syscall: 'open',
astro:test:     path: '/home/runner/work/astro/astro/packages/astro/test/fixtures/core-image-ssg/dist/core-image-build-ssg/_astro/sponsors_1Wl70z.webp'
astro:test:   }
astro:test: 

Testing

CI should pass

Docs

This change doesn't affect users, so no changeset is needed. The error message is silently improved.

If I have a 404 image in the repo, here is what I get before and after when running astro build:

Before:

$ ./node_modules/.bin/astro build
21:21:59 [content] Syncing content
21:21:59 [content] Synced content
21:21:59 [types] Generated 253ms
21:21:59 [build] output: "static"
21:21:59 [build] mode: "static"
21:21:59 [build] directory: /Users/ocavue/code/github/astro/packages/astro/test/fixtures/core-image-ssg/dist/
21:21:59 [build] Collecting build info...
21:21:59 [build] ✓ Completed in 259ms.
21:21:59 [build] Building static entrypoints...
21:21:59 [vite] ✓ built in 350ms
21:21:59 [vite] ✓ built in 6ms
21:21:59 [build] Rearranging server assets...

 generating static routes
21:21:59   ├─ /remote/index.html (+8ms)
21:21:59   ├─ /srcset/index.html (+1ms)
21:21:59 ✓ Completed in 16ms.

 generating optimized images
21:21:59   ▶ /_astro/image 1.ZqYaJ2Hs_Zkuiyp.webp (reused cache entry) (+9ms) (1/3)
21:21:59   ▶ /_astro/image 1.ZqYaJ2Hs_2gIxxN.webp (reused cache entry) (+0ms) (2/3)
21:22:00 ✓ Completed in 1.21s.  <-- ⚠️⚠️⚠️ Notice this "Completed in 1.21s" message

file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/remote.js:10
    throw new Error(
          ^

Error: Failed to load remote image https://astro.build/this_img_does_not_exist.png. The request did not return a 200 OK response. (received 404))
    at loadRemoteImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/remote.js:10:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
    at async loadImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:237:12)
    at async generateImageInternal (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:154:23)
    at async generateImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:69:28)
    at async generateImagesForPath (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:53:5)
    at async file:///Users/ocavue/code/github/astro/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/index.js:394:36

Node.js v24.11.1
$ echo $?
1 <-- ⚠️⚠️⚠️ `astro build` exits with code 1

After:

$  ./node_modules/.bin/astro build
21:27:17 [content] Syncing content
21:27:17 [content] Synced content
21:27:17 [types] Generated 258ms
21:27:17 [build] output: "static"
21:27:17 [build] mode: "static"
21:27:17 [build] directory: /Users/ocavue/code/github/astro/packages/astro/test/fixtures/core-image-ssg/dist/
21:27:17 [build] Collecting build info...
21:27:17 [build] ✓ Completed in 263ms.
21:27:17 [build] Building static entrypoints...
21:27:17 [vite] ✓ built in 377ms
21:27:17 [vite] ✓ built in 5ms
21:27:17 [build] Rearranging server assets...

 generating static routes
21:27:17   ├─ /remote/index.html (+8ms)
21:27:17   ├─ /srcset/index.html (+1ms)
21:27:17 ✓ Completed in 16ms.

 generating optimized images
21:27:17   ▶ /_astro/image 1.ZqYaJ2Hs_Zkuiyp.webp (reused cache entry) (+10ms) (1/3)
21:27:17   ▶ /_astro/image 1.ZqYaJ2Hs_2gIxxN.webp (reused cache entry) (+0ms) (2/3)
21:27:18 [WARN] [build] Unable to generate optimized image for https://astro.build/this_image_does_not_exist.png: Error: Failed to load remote image https://astro.build/this_image_does_not_exist.png. The request did not return a 200 OK response. (received 404))
Error generating image for https://astro.build/this_image_does_not_exist.png: Error: Failed to load remote image https://astro.build/this_image_does_not_exist.png. The request did not return a 200 OK response. (received 404))
  Stack trace:
    at file:///Users/ocavue/code/github/astro/packages/astro/dist/core/build/generate.js:195:21
  Caused by:
  Failed to load remote image https://astro.build/this_image_does_not_exist.png. The request did not return a 200 OK response. (received 404))
    at loadRemoteImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/remote.js:10:11)
    at async loadImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:237:12)
    at async generateImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:69:28)
    at async file:///Users/ocavue/code/github/astro/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/index.js:394:36
$ echo $?
1 <-- ⚠️⚠️⚠️ `astro build` still exits with code 1

Notice that after this PR, you no longer see the Completed in 1.21s message in the shell because the build() function itself throws the error and never finishes. This should be a minor improvement for users.

withastro/astro

Changes

  • file() loader JSON Schema generation now emits an anyOf schema instead of a plain record-only schema
  • Branch 1: { type: 'array', items: <itemSchema> } — validates top-level array JSON/YAML files (e.g. [{ name: 'red', color: '#f00' }])
  • Branch 2: { type: 'object', additionalProperties: <itemSchema>, properties: { $schema: string } } — validates record-style files and preserves the $schema property
  • Before: VS Code flagged top-level array files as invalid against the generated schema
  • After: Both array and record shapes pass validation
  • Fixes #16602
  • Changeset included (.changeset/tangy-chairs-smile.md)

Testing

Updated packages/astro/test/content-intellisense.test.ts — renamed and expanded the existing test to assert:

  1. schema.anyOf exists with exactly 2 branches
  2. Array branch: type: 'array' with items.properties matching the collection item schema
  3. Object branch: type: 'object' with additionalProperties.properties matching the item schema and a $schema property present

Run with:

pnpm test packages/astro/test/content-intellisense.test.ts

Docs

No docs change needed — this is an internal schema generation improvement. The file() loader public API is unchanged; users who already use file() get correct IntelliSense automatically.

withastro/astro

@astrojs/cloudflare/fetch

import { astro, FetchState } from 'astro/fetch';
import { cf } from '@astrojs/cloudflare/fetch';

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const state = new FetchState(request);
    const asset = await cf(state, env, ctx);
    if (asset) return asset;
    return astro(state);
  }
}

@astrojs/cloudflare/hono

import { Hono } from 'hono';
import { actions, middleware, pages, i18n } from 'astro/hono';
import { cf } from '@astrojs/cloudflare/hono';

const app = new Hono<{ Bindings: Env }>();

app.use(cf());
app.use(actions());
app.use(middleware());
app.use(pages());
app.use(i18n());

export default app;

Changes

  • Adds @astrojs/cloudflare/fetch exporting a cf(state, env, ctx) function that applies Cloudflare-specific setup to a FetchState — session KV binding injection, static asset serving via ASSETS, locals.cfContext, client address from cf-connecting-ip, waitUntil, and prerendered error page fetch. Returns a Response for asset hits, undefined otherwise.
  • Adds @astrojs/cloudflare/hono exporting a cf() Hono middleware that does the same setup, reading env and executionCtx from the Hono context automatically (no arguments needed).
  • Extracts shared helpers from the existing handler.ts into cf-helpers.ts and cf.ts, then refactors handler.ts to use them. Zero duplication between the handle() entrypoint and the new exports.

Testing

  • Adds cf-helpers.test.ts with 18 unit tests covering createLocals, getClientAddress, matchStaticAsset, fallbackToAssets, and createErrorPageFetch. Tests use mock Env and ExecutionContext objects — no fixtures or build required.
  • All 207 existing Cloudflare adapter tests continue to pass after the handler.ts refactor.

Docs

withastro/astro

Changes

  • Reverts npm ci back to npm i in the VSCode extension publish step of the release workflow. npm ci requires a package-lock.json which does not exist in packages/language-tools/vscode/ (and is globally gitignored). This was broken by #16711.

Testing

  • No test changes. This is a CI workflow fix — validated by the next release that publishes the VSCode extension.

Docs

  • No docs needed — internal CI change only.
withastro/astro

Astro v7 PR

Changes

Removes the astro:transitions APIs deprecated in #14989 and scheduled to be removed in Astro v7.

Testing

Green CI.

Docs

Changeset added.
Docs PR: withastro/docs#13886

/cc @withastro/maintainers-docs for feedback!

withastro/astro

Changes

  • When the triage bot publishes a preview release, the issue now gets a fix pending verification label and the comment asks the reporter to confirm the fix works
  • A new fix-verification workflow listens for comments on issues with that label. A flue agent (Sonnet) classifies whether the comment is a positive confirmation
  • If verified: removes the label, generates a PR title/body via the astro-pr-writer skill, creates the PR from the existing flue/fix-{N} branch, and adds a fix verified label to the PR

This closes the loop on the triage bot fix pipeline: reproduce → diagnose → fix → test → preview release → reporter verification → PR creation.

Testing

  • No automated tests — this is CI/agent infrastructure. Will be validated by the next triage bot run that produces a fix with a preview release.

Docs

  • No docs needed — internal CI automation, not user-facing.
withastro/astro

Changes

  • Fetchable type: Exported from astro so users can type src/app.ts with satisfies Fetchable.
  • FetchState.response: Set by PagesHandler and AstroMiddleware after rendering so callers can read state.response.
  • App.Providers: New extendable interface for typing custom context providers on ctx and Astro.
  • fetchFile config: Lets users rename the entrypoint away from src/app.ts, or set null to disable.
  • Hono cache() wrapper: Now follows the standard middleware pattern like the other Hono wrappers.

Testing

Added 6 unit tests in test/units/fetch/index.test.ts:

  • state.response is set after pages() and middleware()
  • Context provider provide/resolve lazy creation and caching
  • finalizeAll runs finalize for resolved providers, skips unresolved ones

Docs

Added here: withastro/docs#13890

withastro/astro

Changes

Fixes dynamic routes returning the string [object Object] under the following conditions:

  1. when used with Cloudflare Workers with the nodejs_compat flag. The affected projects all use compatibility dates of 2025-04-01 or newer, not sure if older dates are affected.
  2. only for large, complex projects where Rollup splits code in a way that triggers it. The exact level of complexity/size required is unknown. The fix should work for projects of all sizes though.

This was a very difficult issue to track down. Static routes were fine, and in the Worker logs I could see that the dynamic routes were being processed correctly (I see things like logs indicating success for loading external content and other expected internal logging from the build process) but the returned response was just the string [object Object] regardless of which page or what content was expected -- no error messages of any kind.

I did not open an issue for this because by the time I had enough detail to do so, Claude Code had provided a fix, so I'm just going straight to a PR.

See included Claude Code analysis below for full details.

Testing

I did not add tests because I'm not sure how to provide a sufficiently complicated test project -- this problem only surfaces in larger projects. I'm happy to assist adding tests if I can get some help.

I do have this fix running in 3 separate projects ranging in size and complexity from a stripped down copy of the Astro blog starter template for Workers (where the problem never exhibited but this fix does not cause any issues), up to a 1000-page hybrid site with upwards of 100 Astro components and a mix of dynamic and static routes (where the problem surfaced and the fix works).

Claude Code's explanation of the issue and proposed fix were arrived at after several rounds of analysis and refinements, and its explanation makes sense and matches what I've been experiencing. I specifically asked it to check that the changes do not affect anything for Deno or Node prerendering environments, only workerd.

Docs

I don't believe docs updates are needed because this fixes an issue with expected functionality with no changes needed by the user.

Claude's analysis, RCA, and proposed fix, which I've implemented in this PR

If you're interested, here's what my AI agent found This analysis was done with Astro 6.2.x and Cloudflare 13.3.x, but I've confirmed the problem still exists in the current versions. I'm barred by work policy from installing packages newer than 7 days so I cannot test the latest versions directly, but the problem code still exists in the latest version, unchanged.

This analysis was done by comparing two projects:

  • astro-blog-starter-template which is a copy of the Astro starter for Workers, stripped of all but one dynamic and one static test page
  • web-dev-collibra-astro which is our largest, most complicated project where the problem surfaced

This is its final summary, verbatim:

Fix Summary — Astro isNode false-positive in workerd

Symptom

In an Astro 6.2.x project deployed to Cloudflare Workers via @astrojs/cloudflare 13.3.x with nodejs_compat, any route with prerender: false returns an HTTP 200, content-type: text/html response whose body is the 15-byte string [object Object]. Static / prerendered routes are unaffected. Reproducible locally with astro build && astro preview.

Root cause

astro/dist/runtime/server/render/util.js defines:

const isNode = typeof process !== "undefined"
  && Object.prototype.toString.call(process) === "[object process]";

astro/dist/runtime/server/render/page.js then chooses the response body shape:

if (isNode && !isDeno) {
  body = await renderToAsyncIterable(...);   // Node-only async iterable
} else {
  body = await renderToReadableStream(...);  // standard ReadableStream
}

With nodejs_compat, workerd's process reports Object.prototype.toString.call(process) === "[object process]" until the @cloudflare/unenv-preset polyfill replaces it with a plain object. The output of renderToAsyncIterable is an object exposing Symbol.asyncIterator. Node's undici accepts async iterables as a Response body; workerd's Response does not, and falls back to String(body)"[object Object]".

Why the bug surfaces in some projects but not others

The bug is a module-initialization-order race that only manifests once Rollup decides to code-split Astro's shared runtime.

Both the failing project and the working starter contain the same two pieces of code in the worker bundle:

// A — unenv polyfill assignment (replaces workerd's raw process)
globalThis.process = _process;

// B — Astro's isNode evaluation (reads process)
const isNode = typeof process !== "undefined"
  && Object.prototype.toString.call(process) === "[object process]";

The order in which A and B run depends on whether Rollup keeps them in one chunk or splits them across two:

astro-blog-starter-template (2 pages, almost no Astro internals reused) — Rollup keeps everything in one chunk, worker-entry_DEEjL72T.mjs:

line  558:  globalThis.process = _process;       ← A runs first
line 5864:  const isNode = ...                   ← B sees the unenv polyfill

Single module body, top-down execution → process is the plain-object unenv polyfill by the time isNode runs → isNode = falserenderToReadableStream → works.

web-dev-collibra-astro (many pages and components importing from astro/runtime/server/*) — Rollup factors Astro's shared runtime into its own chunk:

transition_DTdhAtUt.mjs:814    const isNode = ...                ← B
worker-entry_2ArJYird.mjs:1282 globalThis.process = _process;    ← A

worker-entry imports transition. ES Module spec executes imported module bodies before the importer's body, so B runs before A. isNode is evaluated against workerd's raw nodejs_compat process, latches to true, and renderToAsyncIterable is selected → broken response.

The threshold is whatever pushes Rollup over its code-splitting heuristic for shared dependencies. Any project with enough reuse of astro/runtime/server/* across pages or components will eventually trip it — it's not specific to Vue, custom integrations, or middleware.

I confirmed module init order is the differentiator by logging Object.prototype.toString.call(process) at module init vs. request time in the failing project: "[object process]" at init, "[object Object]" at request time — proving the polyfill assignment happens between the two, after isNode has already been frozen.

The fix

One line in packages/astro/src/runtime/server/render/util.ts:

const isNode = typeof process !== "undefined"
  && Object.prototype.toString.call(process) === "[object process]"
  && !(typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers");

Workerd's navigator.userAgent === "Cloudflare-Workers" is documented, stable, and present from t=0 — so the check is immune to the module-init-order race that broke the original process-only detection.

Behavior matrix

Environment process toString navigator.userAgent isNode before isNode after Behavior change
Node.js "[object process]" undefined / Node ua true true None
Deno "[object process]" (Node compat shim) "Deno/x.y.z" true true None — also gated by !isDeno in renderPage
Browser undefined various, never "Cloudflare-Workers" false false None
Cloudflare Workers (workerd) without nodejs_compat undefined "Cloudflare-Workers" false false None
Cloudflare Workers (workerd) with nodejs_compat, polyfill already installed before isNode eval "[object Object]" "Cloudflare-Workers" false false None (incidentally-correct case — the starter)
Cloudflare Workers (workerd) with nodejs_compat, polyfill not yet installed "[object process]" "Cloudflare-Workers" true (wrong) false (correct) Bug fixed

The change only flips behavior in the row where the original heuristic is wrong.

Cross-checks against the prerender flow

@astrojs/cloudflare exposes prerenderEnvironment: "node" | "workerd" (default "workerd").

  • prerenderEnvironment: "node": prerender runs in real Node. No navigator global, original isNode = true preserved. Same as before. ✓
  • prerenderEnvironment: "workerd" (default): prerender runs in workerd. New check correctly returns false. The renderToReadableStream path produces a ReadableStream body, which Astro's prerender pipeline consumes the same way it consumes an async iterable. ✓

Verification performed

  1. Reproduced locally on web-dev-collibra-astro with astro build + astro previewcurl /dev/dynamic returns [object Object] (15 bytes).
  2. Confirmed the path by logging isNode, streaming, and body.constructor.name inside the bundled renderPageisNode=true, body constructor is Object (the async iterable), not ReadableStream.
  3. Confirmed module-init-order theory by logging Object.prototype.toString.call(process) both at module init ("[object process]") and at request time ("[object Object]") — proves the polyfill assignment happens between the two.
  4. Confirmed astro-blog-starter-template evaluates isNode = false because the unenv polyfill assignment (line 558) precedes the isNode evaluation (line 5864) in the same chunk.
  5. Applied the patch via build-scripts/patch-astro-renderer.mjs; re-ran preview; dynamic route returns <!DOCTYPE html>...<div>TEST</div>, static route unchanged.
Details
withastro/astro

Changes

Bug reported: #16667
FIXES #16667

The Astro extension was causing MDX highlighting errors, but only when the code snippets were indented:

BEFORE FIX:

Image

AFTER FIX:

Screenshot 2026-05-12 at 16 09 35

Testing

  1. pnpm install
  2. pnpm -C packages/language-tools/vscode build
  3. In Cursor: Run and Debug → Launch Client
  4. Opened an MDX file and tested an indented astro codeblock

Docs

No docs necessary

withastro/astro

Changes

  • Updates pnpm to v11
  • Removes minimum release age exceptions that are no longer required
  • Cleans up deps in a couple of fixtures that were accidentally using npm versions of monorepo packages
  • Explicitly disallows postinstall scripts for all dependencies that have them
  • Enables pnpm’s trustPolicy which prevents installing versions of packages that have reduced their publishing provenance. This required applying some limited exceptions to continue installing our currently installed packages.

Some semi-user-facing changes:

  • Updated deps in the Netlify adapter
  • Dropped support for older versions of VS Code so we can test with Node ≥22 which is pnpm 11’s minimum required version

Testing

Existing tests should pass

Docs

n/a — monorepo hardening only

withastro/starlight

Description

  • Companion to #3902
  • Tells Renovate to wait for 3 days before applying updates to match our pnpm policy
  • In practice not super important as we mainly use Renovate for GitHub Actions, which don’t involve pnpm, but still good to keep these in sync
withastro/starlight

This PR contains the following updates:

Package Type Update Change
pnpm/action-setup action patch v6.0.7v6.0.8
pnpm/action-setup action patch v6.0.5v6.0.8
withastro/automation action digest 3754a74290b374

Release Notes

pnpm/action-setup (pnpm/action-setup)

v6.0.8

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

withastro/starlight

Description

This PR updates to pnpm 11 and configures a 3-day minimum release age for dependencies to mirror the main astro monorepo: https://github.com/withastro/astro/blob/3abcc86c092ca2a3455fc0ffc72b6b7ca300f1a2/pnpm-workspace.yaml#L32

This helps defend a little against supply chain attacks, but also avoids us installing deps that then cause issues in the astro monorepo CI because they’re newer than their config allows.

withastro/astro

Summary

  • Adds permissions: {} at the top level of every workflow that was missing it
  • Adds explicit job-level permissions to jobs that previously inherited the default token permissions
  • This ensures no workflow accidentally gets broad GITHUB_TOKEN access

Affected workflows: ci.yml, scripts.yml, check.yml, semgrep.yml, congrats.yml, continuous_benchmark.yml, issue-triage.yml, merge-fix.yml, merge-main-to-next.yml.

Part of supply chain hardening in response to the TanStack/Mini Shai-Hulud compromise.

withastro/astro

Summary

  • Removes pnpm store cache (cache: "pnpm") from the release workflow
  • Removes Turbo remote cache (TURBO_TOKEN/TURBO_TEAM)
  • Adds --force to the build step to skip any local Turbo cache
  • Adds --frozen-lockfile to ensure the lockfile is the source of truth

The release workflow now does a fully clean install and build from scratch every time. This eliminates cache poisoning as an attack vector for the publish pipeline.

Part of supply chain hardening in response to the TanStack/Mini Shai-Hulud compromise.

withastro/astro

Summary

Hardens the preview-release workflow against the attack vector used in the TanStack/Mini Shai-Hulud compromise.

Two changes:

  • Block fork PRs from preview releases. Adds a head.repo.full_name == github.repository check so only branches within withastro/astro can trigger preview builds. This prevents a malicious fork from running code in a privileged context.
  • Split into two jobs to isolate OIDC token. The build job runs pnpm install and pnpm build but has no id-token: write. The publish job has id-token: write but only downloads build artifacts and runs pkg-pr-new — it never executes user code. This ensures the OIDC token is never available during code execution, even for non-fork PRs.

The build job uploads only the minimal files needed for publishing (root workspace files + affected package directories), not the entire workspace.

withastro/starlight

Description

This PR contains various improvements to the E2E tests mostly focusing on CI performance.

Here is a table summarizing some of the most impactful changes comparing the before, before average, and after times on my personal machine and CI:

Task Host Before1 Before average After
E2E tests Personal Mac 43.6s N/A 28.9s
E2E tests CI - Linux 2m 27s 2m 34s 1m 29s
E2E tests CI - Windows 5m 9s 5m 11s 3m 11s
A11y tests Personal Mac 1m 1s N/A 41s
A11y tests CI - Linux 2m 39s 2m 43s 1m 47s

You can find below a review were I try to comment on the reasoning behind most of the changes.

Footnotes

  1. For CI times, the "before" times are from merging to main the recent autogenerated sidebar groups refactor.

withastro/astro

Summary

  • Pins actions/labeler from mutable @v4 tag to commit SHA ac9175f8a1f3625fd0d4fb234536d26811351594 (v4.3.0)
  • This was the only action in the repo not pinned to SHA
  • This workflow runs on pull_request_target with write permissions, so a tag hijack would affect every PR including forks

Part of supply chain hardening in response to the TanStack/Mini Shai-Hulud compromise.

withastro/astro

Changes

Close #16248

Root cause

Dependencies imported within .astro file frontmatter are not detected during Vite's initial Dependency Optimization phase. This leads to several issues.

Solution

I have added an onResolve handler to the astro-frontmatter-scan plugin to ensure proper dependency discovery

Testing

Before Implemented

スクリーンショット 2026-05-11 8 11 09

After Implemented

スクリーンショット 2026-05-12 21 44 31

Docs

withastro/astro

Summary

Fixes #16705

Fixes remoteBindings: false being ignored during astro build. The Cloudflare prerenderer's internal Vite preview server now receives the user's adapter options, so remote-flagged bindings (e.g. a D1 database with remote: true in wrangler.toml) are emulated locally during build, matching the existing astro dev behavior.

Changes

The fix is accomplished by forwarding cloudflareOptions (which include remoteBindings setting) to createCloudflarePrerenderer()

withastro/astro

Fixes #14013.

On case-insensitive filesystems (Windows, macOS), starting astro dev from a cwd whose case differs from the actual filesystem case (e.g. d:\dev\app vs. D:\dev\app) made the project root and Vite-resolved module ids disagree on case. normalizeFilename then failed its commonAncestorPath check on the absolute id, treated it as a server-relative URL, and concatenated it onto the root — producing a bogus path that missed the compile metadata cache and stripped scoped styles.

The lookup now falls back to a case-insensitive ancestor check, so absolute paths inside the project are recognized regardless of case and the cache hit succeeds.

withastro/astro

Changes

  • Content entry styles (?astroPropagatedAssets) are now collected at render time instead of being baked into the module at transform time. The old approach captured a snapshot of the Vite module graph during the transform hook — if the graph was incomplete at that moment, styles were permanently missing. A new dev-style-collector.ts registry (shared via globalThis Symbol.for to bridge the Vite plugin and SSR runner module caches) lets the Content component in runtime.ts call getStylesForURL fresh on each render.
  • Adds templateDepth guards to head/maybe-head instructions in common.ts so that maybeRenderHead() never fires inside an inert <template> element in layouts (e.g. theme-provider icon sprites).

Closes #16181

Testing

  • test/content-collections-dev-css.test.ts — dev server integration test verifying scoped styles from .astro components in MDX content entries are present across multiple collections and with component indirection.
  • test/units/render/inert-head-guard.test.ts — verifies templateDepth guard suppresses head rendering inside templates.
  • test/units/render/head-in-template.test.ts — simulates Layout → ThemeProvider → <template> → Icon pipeline, verifying styles land in <head>.

Docs

  • No docs update needed — bug fix restoring expected behavior.
withastro/astro

The updateImageReferencesInData function uses Traverse.map which uses a custom cloning algorithm and as such does not copy correctly instances of native objects such as Map and Set rendering them unusable.

Instead Travserse.forEach can be used after cloning the data object beforehand with the native structuredClone function.

Closes #16682

Changes

  • Use Travserse.forEach instead of Traverse.map in updateImageReferencesInData
  • Clone data before updating image references
  • Update getEntry to not mutate data in store when updating image references

Testing

  • 2 new tests added to cover Map/Set regressions

Docs

  • no docs changes, this is a bug fix
withastro/astro

fixes: #16688

Changes

There was an issue where passing the entry property, returned by the getLiveEntry function imported from astro:content, to the render function caused a type error.
This pull request adds tests and a changeset to the fix originally authored by Houston bot.

Houston bot comment:

The render() function type in packages/astro/templates/content/types.d.ts only has an overload for regular content collection entries (DataEntryMap[C][string]), but no overload for LiveDataEntry. When a project uses only live.config.ts, DataEntryMap is empty, so render() accepts never. The live collections RFC and the existing test fixture both show render(entry) being used with live entries — the type overload was simply missed when live collections were added in PR #13685.

I found a potential fix for this issue. The fix adds a render() overload for LiveDataEntry<LiveLoaderDataType<C>> to the type template. It's a 4-line change that brings the type declarations in line with the runtime behavior. All existing tests (live loaders, content collections, type inference, render) continue to pass.

Testing

Added a test using the astro check command to verify that no type errors occur.
No changes were made to the test fixtures themselves. The error was originally occurring at the following location.

import { getLiveEntry, render } from "astro:content";
const { entry } = await getLiveEntry("liveStuff", "123");
if (!entry) {
return new Response(null, { status: 404 });
}
const { Content } = await render(entry);

src/pages/index.astro:10:34 - error ts(2345): Argument of type 'LiveDataEntry<{ ... }>' is not assignable to parameter of type 'never'.

10 const { Content } = await render(entry);
                                    ~~~~~

Docs

N/A, bug fix

withastro/astro

Changes

  • When the triage bot produces a high-confidence fix (fix verified + unit test created), it now publishes a preview release via pkg.pr.new so reporters can immediately test the fix with npm i https://pkg.pr.new/astro@<sha>.
  • The fix skill now generates a changeset file (.changeset/<slug>.md) for every successful fix, making the branch PR-ready with proper changelog entries.
  • Preview URLs are included in the triage bot issue comment under a "Try this fix" section, giving reporters a one-liner install command.

Testing

  • No test changes — this modifies CI agent infrastructure (skill instructions, Flue orchestrator, GHA workflow).

Docs

  • No docs needed — this is internal CI tooling, not user-facing.
withastro/starlight

This PR contains the following updates:

Package Type Update Change
pnpm/action-setup action patch v6.0.6v6.0.7

Release Notes

pnpm/action-setup (pnpm/action-setup)

v6.0.7

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

withastro/astro

Changes

Testing

Docs

withastro/astro

Changes

This PR removes a bunch of tests and assertions that are already checked against unit tests, so there's so reason to use a dev server for that.

The few that were still needed, they are checked against the build because it's the same behaviour

Testing

Green CI

Docs

withastro/astro

Changes

  • Adds ASTRO_VITE_ENVIRONMENT_NAMES.astro to isAstroServerEnvironment() in packages/astro/src/environments.ts. The internal 'astro' environment is a Node.js fallback used when the 'ssr' environment is not runnable (e.g. Cloudflare adapter with workerd). Without this, Vite plugins like the script loader served actual client-side script content in this environment, causing HTMLElement is not defined crashes on HMR reload.

Closes #16626

Testing

  • New unit test packages/astro/test/units/environments.test.ts covering isAstroServerEnvironment and isAstroClientEnvironment for all four environment names (ssr, prerender, astro, client).

Docs

  • No docs update needed — this is an internal bug fix with no API change.
withastro/astro

Changes

This PR targets the integration tests that user the dev server. The vast majority of the logic is already tested in a unit test environment, so they were removed.

some functions that were used in the dev server as middleware, they are extracted as a unit test function and tested.

Testing

Removed some fixtures. Green CI.

Docs

withastro/astro

Changes

I encountered the following flaky test:

https://github.com/withastro/astro/actions/runs/25663263442/job/75329416110#step:8:7300

Error: EPERM: operation not permitted, rename 'D:\a\astro\astro\packages\astro\test\fixtures\astro-assets-prefix\node_modules\.astro\data-store.json.tmp' -> 'D:\a\astro\astro\packages\astro\test\fixtures\astro-assets-prefix\node_modules\.astro\data-store.json'

I investigated this error. Two test files test/asset-query-params.test.ts and test/astro-assets-prefix.test.ts are using the same fixture ./fixtures/astro-assets-prefix/. Although these two tests have different outDir as added in #16382, they still share the same cache directory ./fixtures/astro-assets-prefix/node_module/.astro/. This seems to be the reason for the flaky test above.

In this PR, I provide a unique cacheDir for every fixture that is used in multiple test files.

15 fixtures are affected:

Fixture (test/fixtures/…) Test files
astro pages test/astro-pages.test.ts
test/static-build-page-dist-url.test.ts
test/static-build-vite-plugins.test.ts
astro-assets test/asset-query-params.test.ts
test/astro-assets.test.ts
astro-assets-prefix test/asset-query-params.test.ts
test/astro-assets-prefix.test.ts
astro-basic test/astro-basic.test.ts
test/astro-sync.test.ts
test/config-mode.test.ts
astro-dev-headers test/astro-dev-headers.test.ts
test/chrome-devtools-workspace.test.ts
css-order-layout test/css-order-layout.test.ts
test/css-order.test.ts
dev-render test/config-format.test.ts
test/dev-base.test.ts
test/dev-render-chunk.test.ts
test/dev-render-components.test.ts
test/integration-route-setup-hook.test.ts
endpoint-routing test/endpoint-response.test.ts
test/endpoint-routing.test.ts
test/endpoint-runtime.test.ts
fonts test/asset-query-params.test.ts
test/fonts.test.ts
middleware space test/featuresSupport.test.ts
test/middleware.test.ts
multiple-jsx-renderers test/asset-query-params.test.ts
test/multiple-jsx-renderers.test.ts
server-islands/hybrid test/csp-server-islands.test.ts
test/server-islands.test.ts
server-islands/ssr test/csp-server-islands.test.ts
test/server-islands.test.ts
ssr-assets test/ssr-assets.test.ts
test/units/app/logger.test.ts
ssr-request test/ssr-adapter-build-config.test.ts
test/ssr-request.test.ts

Testing

CI should pass.

Docs

N/A

withastro/astro

Changes

  • Applied @typescript-eslint/no-floating-promises rule to all tests;
  • Fixed all issues to pass ESLint. Check my code comments / PR comments for details.
  • This PR is the first step. I eventually want to apply the rule to all code, including package sources.

Testing

CI should pass

Docs

N/A

withastro/astro

Awesome work by @OfirHaf and @fkatsuhiro! Fixing bug #16633 🎉

This PR is a curated combination of #16674 and #16676.

What does this fix?

Closes #16633

SVG elements with explicit width="0" or height="0" attributes threw NoImageMetadata: Could not process image metadata instead of being imported normally. This is a valid and common pattern for SVG filter containers hidden from screen readers:

<svg width="0" height="0" aria-hidden="true">
  <defs>...</defs>
</svg>

Changes

issus: #16633

Root cause

Two falsy-zero checks along the metadata extraction path:

vendor/image-size/types/svg.ts — the calculate() function:

// Before: 0 is falsy, so an SVG with width="0" height="0" fell through
if (attrs.width && attrs.height) { ... }

// After: explicit null check allows 0 as a valid dimension
if (attrs.width != null && attrs.height != null) { ... }

assets/utils/metadata.ts — post-probe validation:

// Before: !0 === true, triggering the error for zero dimensions
if (!result.height || !result.width || !result.type) { ... }

// After: only null/undefined signals a missing value
if (result.height == null || result.width == null || !result.type) { ... }

Changes

  • packages/astro/src/assets/utils/vendor/image-size/types/svg.ts: replace truthiness check with null check in calculate()
  • packages/astro/src/assets/utils/metadata.ts: replace truthiness check with null check in imageMetadata()

Both changes are minimal and surgical — they only affect the specific falsy-zero edge case while leaving all other behavior unchanged. SVGs with missing dimensions (null/undefined) still throw the appropriate error.

Testing

Befor implement testing

スクリーンショット 2026-05-10 21 18 10

After implement testing

スクリーンショット 2026-05-10 21 13 38
withastro/astro

Changes

Fixes a regression introduced in #16366, where Astro.cache was undefined when experimental.cache was not configured.

The previous documented behaviour is for Astro.cache to always be defined as a no-op shim: cache.set() warns once, cache.invalidate() throws and cache.enabled can be used to gate. This allows library and user code can call cache methods without conditional checks. The cache provider registration was being gated at the call site on experimental.cache being configured, which meant the disabled shim branch inside the provider was unreachable and the Astro.cache getter was never attached to the context.

Testing

Added a regression test

Docs

Bug fix

withastro/astro

Summary

createStaticHandler in @astrojs/node looks up per-route static headers (e.g. Content-Security-Policy) using header.pathname.includes(baselessPathname). The headers map is keyed by exact prerendered route pathnames written by core/build/generate.ts (routeToHeaders.set(pathname, ...)), and the comment immediately above the lookup states that headers are "keyed by base-less route paths" — so the intent is keyed lookup, not substring matching.

Substring matching causes the wrong headers to be applied whenever one prerendered route's path is a substring of another's:

  • Two prerendered routes /users and /users/profile, each with a different CSP.
  • /users/profile is inserted into routeToHeaders first → it ends up first in the JSON array.
  • A request for /users matches the route correctly via app.match, but headersMap.find(h => h.pathname.includes("/users")) returns the /users/profile entry.
  • The wrong CSP is sent on the /users response.

The fix is a one-line change to use === instead of .includes().

Changes

  • packages/integrations/node/src/serve-static.ts: match header.pathname by equality.
  • Changeset: @astrojs/node patch.

Test plan

  • pnpm -C packages/integrations/node test passes.
  • Manual: build a project with two prerendered routes where one path is a prefix of the other, both emitting different CSPs via the static-headers feature; confirm each route receives its own CSP under @astrojs/node standalone.
withastro/astro

Changes

This PR adjusts the reference zod support to allow numbers that are transformed to strings.

This has been an issue with collection-A markdown content that references collection-B entries with 1234 in the frontmatter as the yaml is parsed to numbers.

Testing

Added a small unit test that tests the case.

Docs

/cc @withastro/maintainers-docs I am not if documentation for this is needed.

withastro/starlight

Description

Opposite of:

but the goal is the same: to silence the Postinstall Scripts Unapproved notification when pnpm install.

References:

Package URL Quote
esbuild evanw/esbuild#4085 (comment) The install script still optimizes the command by replacing the JavaScript shim by the actual binary executable, so that running the esbuild command directly doesn't have to run node first just to shell out to the executable. So it's not necessary for correctness (and you're welcome to disable the install script if you'd like to and if it still works for you) but it might result in esbuild being slower than it would have been otherwise.
sharp https://sharp.pixelplumbing.com/install/ When using pnpm, add sharp to ignoredBuiltDependencies1 to silence warnings.

Changesets are NOT required.

Footnotes

  1. equivalent to allowBuilds = false in pnpm v10.26+.

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.

⚠️⚠️⚠️⚠️⚠️⚠️

Releases

astro@7.0.0-alpha.1

Patch Changes

withastro/starlight

Description

Tweak a comment: "non-Firefox" → "Chromium-based"

Safari has already supported the syntax :lang(zh, ja).

image

The position of Opera is the culprit.

This change is so trivial that we should ship it with other changes.

withastro/starlight

This PR contains the following updates:

Package Type Update Change
pnpm/action-setup action patch v6.0.5v6.0.6
withastro/automation action digest c8a2e8b3754a74

Release Notes

pnpm/action-setup (pnpm/action-setup)

v6.0.6

Compare Source

What's Changed
  • fix: bin_dest output points to self-updated pnpm, not bootstrap by @​zkochan in #​249

Full Changelog: pnpm/action-setup@v6.0.5...v6.0.6


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

withastro/astro

Changes

  • Adds a new "Write a Unit Test" step to the triage bot's fix skill, so it produces a regression test alongside every fix attempt.
  • Creates reference/unit-testing.md documenting unit test conventions, file placement, and the shared test utilities/mocks available in the repo. Referenced from AGENTS.md so any agent (not just triage) can use it.
  • Adds a Unit Test line to the triage bot's GitHub comment template.

Testing

  • No test changes — this PR only modifies agent instructions and reference docs.

Docs

  • No user-facing docs needed. reference/unit-testing.md is agent-facing only.
withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.39.2

Patch Changes

  • #3890 2d05e18 Thanks @tats-u! - Fixes CSS selector for text-autospace styles in Chromium browsers
withastro/starlight

Description

:lang(zh, ja) used in #3872 shamefully has not been supported Chromium and Safari. As a result, text-autospace has been enabled only in Firefox. Replaced it with the combination of :lang(zh) and :lang(ja).

Screenshot in Chrome (Pay attention to the slight presence of whitespace around "Astro" and "Starlight"):

Before (https://starlight.astro.build/ja/getting-started/):

image

After:

image
withastro/astro

Changes

Since the creation of astro:assets, there has been many lingering issues regarding default output formats. The way the pipeline has to work for SSG / SSR, perf reasons etc, means that we typically don't know the format of the image for real if its remote, which is mostly fine except for one case: SVGs. Since we don't want to convert SVGs to WebP by default, and also the reverse, we often need to know what we're working with to set the default format.

Previously, this wasn't that much of an issue, because imo, it's kinda edge casey and being remote images meant that anyway it's up to the user to authorize. However, now in 6.3 with dangerouslyProcessSVG, it's actually problematic to not really know what we're dealing it, because it could mean a SVG go through when it shouldn't, but also the reverse!

This PR tries to fix this through a few methods:

  • Do a best effort guess at the format early on, allowing ourselves to fail sometimes
  • ... If we failed and we're not converting, and we're in SSG, use the service's way of inferring formats to craft the proper file type, or let the service handle it (as it should, since images could anyway come from a random request!)
  • Harden the service against these problems in various ways

Testing

Updated and added tests for certain of the newly covered cases

Docs

N/A

withastro/astro

Changes

Updates the attribute escaping logic to use named entities (&amp; and &quot;) instead of numeric entities (&#38; and &#34;).

Currently, <title> tags render named entities, while other meta tags render numeric entities. Although both formats are semantically equivalent, this results in inconsistent raw HTML output.

This change aligns the escaping behavior across all head and meta tags so they consistently render named entities.

Closes #16657

Testing

Updates the test cases that rendered &#38; and &#34; to instead render &amp; and &quot;.

Docs

N/A

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.2

Patch Changes

  • #16675 11d4592 Thanks @ascorbic! - Fixes a regression where Astro.cache was undefined when experimental.cache was not configured.

    The previous documented behavior is for Astro.cache to always be defined as a no-op shim: cache.set() warns once, cache.invalidate() throws and cache.enabled can be used to gate. This allows library and user code can call cache methods without conditional checks. The cache provider registration was being gated at the call site on experimental.cache being configured, which meant the disabled shim branch inside the provider was unreachable and the Astro.cache getter was never attached to the context.

  • #16691 0f0a4ce Thanks @matthewp! - Fixes HTMLElement is not defined error during HMR when using components with client-side scripts (e.g. Starlight <Tabs>) and the Cloudflare adapter

  • #16562 07529ec Thanks @matthewp! - Fixes non-prerendered routes failing when a dynamic prerendered route exists in the same project with prerenderEnvironment: 'node'

  • #16638 272185b Thanks @ematipico! - Fixes a bug where the Astro compiler wasn't freed at the end of the build. After the fix, the memory used by the compiler is now correctly freed at the end of the build.

  • #16544 d365c97 Thanks @matthewp! - Tightens isRemotePath() to reject control characters after a leading slash and fixes the dev image endpoint origin check

  • #16685 889e748 Thanks @farrosfr! - Improve validation messages for security.csp.directives when script-src or style-src are incorrectly placed in the directives array.

  • #16605 772f13a Thanks @rururux! - Fixes assetsPrefix not being available on build from astro:config/server.

  • #16556 f38dec7 Thanks @matthewp! - Rejects double-encoded URL paths with a 400 response instead of silently falling back to partial decoding

  • #16659 38bcb25 Thanks @jsparkdev! - Fixes & characters appearing as raw entity strings (e.g. &#38;) in <meta> tags when viewed in link previews or raw HTML.

  • Updated dependencies [d365c97, 9256345]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/markdown-remark@7.1.2

@astrojs/prism@4.0.2

Patch Changes

  • #15723 9256345 Thanks @rururux! - Fixes an issue where the <Prism /> component failed to work in Cloudflare Workers.

@astrojs/cloudflare@13.5.1

Patch Changes

  • #16707 2ff3f8f Thanks @helio-cf! - Fixes remoteBindings: false being ignored during astro build. The Cloudflare prerenderer's internal Vite preview server now receives the user's adapter options, so remote-flagged bindings (e.g. a D1 database with remote: true in wrangler.toml) are emulated locally during build, matching the existing astro dev behavior.

  • #16652 98c32cc Thanks @greatjourney589! - Fixes user-declared KV namespace bindings being duplicated in the generated dist/server/wrangler.json, which caused wrangler validation to fail with " assigned to multiple KV Namespace bindings." The Astro Cloudflare config customizer now returns only the auto-injected SESSION binding and lets @cloudflare/vite-plugin merge it with the user's wrangler config, instead of pre-merging the user's bindings into the output.

  • #16272 4f9521e Thanks @barry3406! - Fixes .astro files failing with No matching export in "html:..." for import "default" when default-imported from a .ts file

  • #15723 9256345 Thanks @rururux! - Fixes an issue where the <Prism /> component failed to work in Cloudflare Workers.

  • Updated dependencies [d365c97]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/underscore-redirects@1.0.3

@astrojs/markdoc@1.0.5

Patch Changes

  • #15723 9256345 Thanks @rururux! - Updates internal type usage from @astrojs/prism.

  • Updated dependencies [d365c97, 9256345, 9256345]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/markdown-remark@7.1.2
    • @astrojs/prism@4.0.2

@astrojs/mdx@5.0.5

Patch Changes

  • Updated dependencies [9256345]:
    • @astrojs/markdown-remark@7.1.2

@astrojs/netlify@7.0.9

Patch Changes

  • #16716 04fdbb2 Thanks @delucis! - Updates internal dependencies

  • Updated dependencies [d365c97]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/underscore-redirects@1.0.3

@astrojs/node@10.1.1

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1

@astrojs/preact@5.1.3

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1

@astrojs/react@5.0.5

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1

@astrojs/svelte@8.1.1

Patch Changes

@astrojs/vercel@10.0.7

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1

@astrojs/internal-helpers@0.9.1

Patch Changes

  • #16544 d365c97 Thanks @matthewp! - Tightens isRemotePath() to reject control characters after a leading slash and fixes the dev image endpoint origin check

@astrojs/ts-plugin@1.10.8

Patch Changes

astro-vscode@2.16.15

Patch Changes

@astrojs/markdown-remark@7.1.2

Patch Changes

  • #15723 9256345 Thanks @rururux! - Updates internal type usage from @astrojs/prism.

  • Updated dependencies [d365c97, 9256345]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/prism@4.0.2
withastro/astro

Closes #16590

Changes

  • Fixes a regression introduced in #16555 where user-declared KV namespace bindings (e.g. RATE_LIMIT, CACHE) appear twice in the generated dist/server/wrangler.json, causing wrangler to fail with "<binding> assigned to multiple KV Namespace bindings.".
  • The Astro Cloudflare config customizer now returns only the auto-injected SESSION binding from kv_namespaces, instead of pre-merging the user's existing bindings into the output.
  • @cloudflare/vite-plugin already merges the customizer's output with the user's wrangler config, so echoing the user's bindings caused the duplication.
  • Removed the now-unused withSessionKVBinding() helper.
  • Added a changeset (@astrojs/cloudflare patch).

Affected versions reported in the issue: @astrojs/cloudflare@13.3.0–13.3.1 + astro@6.2.x. Last working combination was astro@6.1.9 + @astrojs/cloudflare@13.2.1, which matches the pre-#16555 behavior this PR restores.

Testing

  • Updated two existing assertions in packages/integrations/cloudflare/test/wrangler.test.ts that locked in the buggy merged-output shape.
  • Added a dedicated regression test does not include user-declared KV bindings in the output (regression #16590) covering the multi-binding scenario from the issue (RATE_LIMIT + CACHE declared by the user) and asserting the customizer returns only [{ binding: 'SESSION' }].
  • All 25 tests in wrangler.test.ts pass locally.

Docs

No documentation changes needed. The fix restores documented and previously-shipped behavior — the public API and configuration surface (sessionKVBindingName, automatic SESSION binding provisioning) are unchanged.

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.1

Patch Changes

  • #16646 15fbc41 Thanks @matthewp! - Fixes local images returning 404 on non-prerendered pages when using the generic image endpoint
withastro/astro

Changes

  • Restores the FREDKBOT_GITHUB_TOKEN env var mapping that was accidentally dropped in the flue 0.3 upgrade (#16441). Without it, postGitHubComment fails at the end of every triage run.

Testing

  • No test changes. Verified by inspecting the failing run.

Docs

  • No docs needed — CI-only change.
withastro/starlight

Description

Adds generic clock, desktop, mobile phone, app window, database, server, git branch, notes, question mark, analytics, and padlock icons. Also adds a new logo icon for SolidJS. The mobile icon has a bit of a funny name — mobile-android — but I kept it to match the source library’s name.

Closes #3881
Closes #3863

Which icons to include built-in is always a bit subjective, but I went through our icon library and tried to add a handful that might meet some more common needs on docs sites given I was already adding a couple based on user requests.

For the SolidJS logo, I asked on their Discord server to confirm which style they preferred for a monochrome icon (as it deviates from their more common blue gradient style).

withastro/astro

Changes

  • The generic image endpoint (assets/endpoint/generic.ts) self-fetches local images from the same origin. #16519 added an isRemoteAllowed check on the response URL, but that check rejects local URLs (e.g. http://host/_astro/image.png) since they aren't in image.domains or image.remotePatterns. This caused local images on non-prerendered pages to 404.
  • Extracted the fetch logic into loadImage.ts with an isRemote flag that gates the isRemoteAllowed check. Local images skip it — they're already protected by the same-origin guard in the caller.

Fixes #16644

Testing

  • Added test/units/assets/endpoint-load-image.test.ts with 4 cases: local image succeeds, unauthorized remote rejected, allowed remote succeeds, fetch failure handled. The local image test fails without the fix.

Docs

  • No docs needed — this is a regression fix restoring prior behavior.
withastro/astro

Changes

A flaky test was detected in astro-assets-prefix.test.ts in CI.
https://github.com/withastro/astro/actions/runs/25502409719/job/74839082587

The root cause is likely that tests are now running in parallel, and both astro-assets-prefix.test.ts and astro-assets-prefix-multi-cdn.test.ts share the same fixture while also calling fixture.clean() in their after() hooks, which deletes the cache folder.
When the test fails, the log shows "The collection "blog" does not exist or is empty. Please check your content config file for errors." and as shown here, the getDataStoreFile function references the .astro folder, which is exactly what clean() removes.

export function getDataStoreFile(settings: AstroSettings, isDev: boolean) {
return new URL(DATA_STORE_FILE, isDev ? settings.dotAstroDir : settings.config.cacheDir);
}

clean: async () => {
await fs.promises.rm(config.outDir, {
maxRetries: 10,
recursive: true,
force: true,
});
const astroCache = new URL('./node_modules/.astro', config.root);
if (fs.existsSync(astroCache)) {
await fs.promises.rm(astroCache, {
maxRetries: 10,
recursive: true,
force: true,
});
}
},

To fix this, I merged the two test files into one.
An alternative would have been to give each test its own dedicated fixture, but since the two test cases are similar in nature, I went with the merge approach instead.

Testing

Green CI

Docs

N/A

withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.39.1

Patch Changes

  • #3885 010eed1 Thanks @ArmandPhilippot! - Fixes the version mentioned in an error message related to autogenerated sidebar groups support.

  • #3887 b3c6990 Thanks @delucis! - Adds 13 new icons: clock, desktop, mobile-android, window, database, server, code-branch, notes, question, question-circle, analytics, padlock, and solidjs.

withastro/starlight

Description

The Starlight version mentioned in the error related to autogenerated sidebar groups support is wrong:

Support for autogenerated sidebar groups was removed in Starlight v0.38.0.

#3618 has been released earlier in 0.39.0, not 0.38.0.

I added a changeset because we need a new version for such a small change. 😅

withastro/astro

Changes

Migrate a js test file to ts.

Testing

CI should pass.

Docs

N/A

withastro/starlight

This PR contains the following updates:

Package Type Update Change
changesets/action action minor v1.7.0v1.8.0

Release Notes

changesets/action (changesets/action)

v1.8.0

Compare Source

Minor Changes
  • #​258 f5dbf72 Thanks @​tom-sherman! - Support draft version PR modes with a new prDraft input. Use create to create new version PRs as drafts, or always to also convert existing version PRs back to draft when updating them.
Patch Changes

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

withastro/astro

Changes

A follow-up to #16471 and #16472

svelte2tsx@0.7.55 supports typescript v6, so we can also update the typescript peer dependency range in @astrojs/svelte.

Testing

CI should pass

Docs

A changeset file is added because we need to release a new version for @astrojs/svelte.

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/cloudflare@13.5.0

Minor Changes

@astrojs/node@10.1.0

Minor Changes

withastro/astro

Changes

Thank you @TheOtterlord for catching this

Testing

Green CI

Docs

withastro/astro

Changes

The compiler was never torn down at the end of the build. This PR fixes it.

Testing

Manually tested.

Docs

N/A

withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.39.0

Minor Changes

  • #3618 dcf6d09 Thanks @HiDeoo! - ⚠️ BREAKING CHANGE: This release changes how autogenerated links work in Starlight’s sidebar configuration.

    If you have sidebar groups using the autogenerate key, you must now wrap that configuration in an items array:

    {
        label: 'My group',
    -   autogenerate: { directory: 'some-dir' },
    +   items: [{ autogenerate: { directory: 'some-dir' } }],
    }

    This change unlocks the possibility to mix autogenerated links and other links in a single group, for example:

    {
      label: 'Mixed group',
      items: [
        'example-page',
        { autogenerate: { directory: 'examples' } },
        { label: 'More examples', link: 'https://example.com' },
      ],
    }

    This release also updates the shape of autogenerated sidebar entries in route data. Autogenerated links and groups in Astro.locals.starlightRoute.sidebar now include an autogenerate object with the configured directory value:

    {
      type: 'link',
      label: 'Example',
      href: '/examples/example/',
      isCurrent: false,
      autogenerate: { directory: 'examples' }
    }
  • #3618 dcf6d09 Thanks @HiDeoo! - ⚠️ BREAKING CHANGE: This release changes the default collapsed state of autogenerated sidebar subgroups.

    Autogenerated subgroups no longer inherit the collapsed value from their parent group. They are now expanded by default unless explicitly configured with autogenerate.collapsed.

    If your sidebar configuration relies on a collapsed parent group to also collapse its autogenerated subgroups, update your configuration to set autogenerate.collapsed to true:

    {
      label: 'Reference',
      collapsed: true,
      items: [
    -   { autogenerate: { directory: 'reference' } },
    +   { autogenerate: { directory: 'reference', collapsed: true } },
      ],
    }
  • #3845 4d755f5 Thanks @delucis! - Adds a <link rel="alternate" hreflang="x-default" href="..."> tag pointing to the default locale in multilingual sites. The x-default alternate is used as a signal of which language to fall back to if no other is available. Learn more in Google’s SEO localization docs.

  • #3862 ec70630 Thanks @itrew! - Makes spacing of items in nested lists more consistent

  • #3872 417a66c Thanks @tats-u! - Enables the CSS property text-autospace in Chinese and Japanese documents.

    If you would prefer to disable autospacing in Chinese and Japanese pages, you can add the following custom CSS to your site:

    [lang]:where(:lang(zh, ja)) {
      text-autospace: initial;
    }
  • #3797 9764ebd Thanks @delucis! - Avoids the risk of layout shift when users expand and collapse sidebar groups

    This release can introduce additional padding to the site sidebar on certain devices to reserve space for scrollbars. You may wish to inspect your site sidebar visually when upgrading.

    If you would prefer to keep the previous styling, you can add the following custom CSS to your site:

    .sidebar-pane {
      scrollbar-gutter: auto;
    }
  • #3858 6672c35 Thanks @delucis! - Updates i18next, used for Starlight’s localization APIs, from v23 to v26

    There should not be any user-facing changes from this update

withastro/astro

Description

Adds flush() and close() methods to AstroIntegrationLogger, mirroring AstroLogger. Both delegate to the underlying destination's optional flush / close hooks.

AstroLogger already exposes flush() and close() (added in #16404). AstroIntegrationLogger did not, so any code path that placed an AstroIntegrationLogger where an AstroLogger was expected — e.g. App.#prepareResponse calling this.logger.flush() — would crash with TypeError: this.logger.flush is not a function. The two logger classes now have a symmetric public surface.

Closes #16622.

Changes

  • packages/astro/src/core/logger/core.ts: add flush() and close() to AstroIntegrationLogger, identical shape to the methods on AstroLogger.
  • packages/astro/test/units/logger/logger.test.ts: cover AstroIntegrationLogger.flush() / .close() (delegation, no-throw, fork inheritance).
  • changeset: patch.

Testing

pnpm --filter astro build:ci
pnpm --filter astro exec astro-scripts test test/units/logger/logger.test.ts --strip-types --teardown ./test/units/teardown.ts
# 10 pass, 0 fail

Docs

No public API docs change required — AstroIntegrationLogger.flush() / .close() mirror the existing AstroLogger methods (no behaviour change for destinations that don't define flush/close).

withastro/astro

Changes

Fixes #16612

Testing

Should pass! We unfortunately cannot test this in the monorepo because TypeScript is of course always available

Docs

N/A

withastro/astro

Changes

Some tests in core-image.test.ts have been flaky in Astro's CI.
https://github.com/withastro/astro/actions/runs/25425893932/job/74579340641?pr=16579
https://github.com/withastro/astro/actions/runs/25407064617/job/74520525804?pr=16614

After investigating, I found that the removeDir function, which is used to clean up the cache directory before tests, was not being awaited despite returning a Promise.

This PR adds the missing await to address the potential race condition.

Testing

Green CI

Docs

N/A

withastro/astro

Changes

One of TypeScript's biggest strengths is static analysis. In practice, we rely on it in three places:

  1. In editors (e.g. vscode / TypeScript language service)
  2. During local and CI builds via tsc
  3. During local and CI linting via eslint with type-aware linting

All three ultimately depend on TypeScript project configuration through tsconfig.

Before #16241, Astro used three different tsconfig setups for these workflows:

  1. Editors used an effectively empty root tsconfig.json, so the language service fell back to its default behavior.
  2. tsc used per-package configs that only included src/, excluding files like bin/ and test/.
  3. eslint used a separate tsconfig.eslint.json.

This inconsistency created a poor developer experience. For example, vscode could report type errors while CI remained green, making editor diagnostics unreliable.

This PR completes the TypeScript project references work introduced in previous PRs. After this PR, all three workflows share the same source of truth: the root tsconfig.json.

The root tsconfig.json now includes all src/, test/, scripts, and configuration files (e.g. prettier.config.mjs). It doesn't include SFC files (.astro, .svelte, etc.) and test fixtures, though.

Also, note that if a JS/TS file is included in eslint but not in the root tsconfig.json, eslint will show the following errors. This is beneficial because it ensures that we have all files added to tsconfig.json, thus these files are covered by tsc.

/astro/packages/astro/e2e/astro-island-hydration-error.test.js
  0:0  error  Parsing error: /astro/packages/astro/e2e/astro-island-hydration-error.test.js was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject

Testing

CI should pass

Docs

N/A

withastro/astro

Changes

Migrate some tests from JS to TS.

These tests come from an old pull request, which was merged into main after the TypeScript migration was completed.

Testing

N/A

Docs

N/A

withastro/starlight

This PR contains the following updates:

Package Type Update Change
actions/labeler action minor v6.0.1v6.1.0

Release Notes

actions/labeler (actions/labeler)

v6.1.0

Compare Source

Enhancements

  • Add changed-files-labels-limit and max-files-changed configuration options to cap the number of labels added by @​bluca in #​923

Bug Fixes

Dependency Updates

New Contributors

Full Changelog: actions/labeler@v6...v6.1.0


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

withastro/astro

Changes

The Houston bot for the triage workflow is currently not working.
https://github.com/withastro/astro/actions/runs/25424727887/job/74575139340

The log shows the following:

pnpm exec flue run issue-triage \
    --target node \
    --session-id "issue-triage-$ISSUE_NUMBER" \
    --payload "{\"issueNumber\": $ISSUE_NUMBER}"

Unknown argument: --session-id

Looking into the flue repository, I couldn't find the --session-id argument anywhere.
Instead, I found that --id is defined as the correct argument.
https://github.com/withastro/flue/blob/808b34ee154b94025d5cb2b45288d88b7766ae0f/packages/cli/bin/flue.ts#L95-L108

I also confirmed that commit 8388ed1 in the flue repository changed --session-id to --id.
Based on this, I concluded that --id should be used instead of --session-id, and updated it accordingly.

Testing

I don't know how to do it 😔

Docs

N/A

withastro/astro

Changes

Right now, types for entry.data field returned by getLiveEntry() are not working.

---
import { getLiveEntry } from "astro:content";

const { entry } = await getLiveEntry("example", "hello-world");
const data = entry?.data; // type `any` instead of defined type from loader.
---

The cause is that the utility type ExtractDataType used here is missing, so entry.data is always of type any.

This pr adds the missing utility type to the file.

Testing

Tested by manually adding the missing utility type to the generated .astro/content.d.ts in my projects and verifing the expected types to work.

Docs

It adds missing typing.

withastro/astro

Fixes the smoke test failure caused by astro-expressive-code@0.42.0 being too recently published to pass the 3-day minimumReleaseAge gate. Since this is a Starlight dependency used by our docs smoke test, it should always be installable regardless of release age.

withastro/starlight

Another PR updating depedencies. Expressive Code is the main motivator as the latest release updates to Shiki v4 (thanks again @ocavue!) so Starlight will share this with Astro instead of having v3 and v4 in the tree.

Prod dependencies

  • Updates astro-expressive-code in Starlight (0.41.7 => 0.42.0)

Dev depdendencies

  • Updates astro across monorepo (6.1.9 => 6.2.2)
  • Updates @astrojs/check (0.9.8 => 0.9.9)
  • Updates pnpm (10.33.2 => 10.33.3)
  • Updates linters
    • eslint (10.2.1 => 10.3.0)
    • globals (17.5.0 => 17.6.0)
    • typescript-eslint (8.59.0 => 8.59.2)

Haven’t bothered with a changeset as we already have an unreleased “update deps” patch changeset in the pipeline. Should see no user-facing change in any case.

withastro/astro

Changes

This PR adds the missing * line from a code sample in JSDoc, which basically removed the whitespace between the imports and the code.

This doesn't need a changeset, AFAIK

Testing

Docs

That's all it is.

withastro/astro

Changes

Implements RFC: astro dev --background. When an AI coding agent is detected (via am-i-vibing), astro dev automatically starts the dev server as a detached background process so it doesn't block the agent's terminal.

  • Adds astro dev --background flag and astro dev stop, astro dev status, and astro dev logs subcommands for managing background dev servers
  • Writes a lock file (.astro/dev.json) on server start to prevent duplicate servers and enable the subcommands to find the running instance
  • Adds a /_astro/status health endpoint for programmatic readiness checks
  • astro dev logs --follow streams new log output as it's written, exits when the server dies
  • astro dev stop and astro dev --background --force escalate to SIGKILL if the process doesn't exit within 5 seconds of SIGTERM
  • Reports detected agent id, name, and type in ASTRO_CLI_SESSION_STARTED telemetry events for all CLI commands

No action required from users. If no agent is detected, astro dev behaves exactly as before.

Testing

  • Unit tests for lock file parsing, serialization, stale detection, and CLI output formatters (test/units/dev/lockfile.test.ts, test/units/dev/dev-output.test.ts)

Docs

withastro/astro

Changes

Fixes a regression from #16277 where the generated wrangler.json sets
assets.directory to include the base prefix (e.g. "../client/blog"
instead of "../client").

Cloudflare's asset binding resolves the full request URL path (including
the base) against the directory. With the prefixed path, requests would
resolve to a double-nested location (e.g. client/blog/blog/file.js),
causing 404s for all static assets.

Related: #16276

Testing

Added a test to with-base.test.ts verifying the assets directory in
the generated wrangler.json points to the un-prefixed client root.

Docs

withastro/astro

fixes: #16604

Changes

As reported in the issue, the migration guide and deprecation comments recommended using build.assetsPrefix from astro:config/server as a replacement for the deprecated import.meta.env.ASSETS_PREFIX, but astro:config/server was not actually exporting build.assetsPrefix.

/**
* The prefix for Astro-generated asset links if the build.assetsPrefix config option is set. This can be used to create asset links not handled by Astro.
* @deprecated This will be removed in a future major version of Astro. Use `build.assetsPrefix` from `astro:config/server` instead.
*/
readonly ASSETS_PREFIX: string | Record<string, string>;

This PR fixes the implementation so that build.assetsPrefix is correctly exported from astro:config/server.

Testing

Added tests to verify that build.assetsPrefix is properly exported and that its value can be read correctly.

Docs

The "Upgrade to Astro v6" page documents this under "Deprecated: import.meta.env.ASSETS_PREFIX" and recommends using build.assetsPrefix instead, but the "Imports from astro:config/server" reference page does not yet list build.assetsPrefix.

A follow-up PR to update the docs will be submitted separately.

withastro/astro

Changes

  • Moves conflict resolution and changeset cleanup into the merge-main-to-next action so the branch is clean and buildable before CI runs. AI only runs when there are actual conflicts; clean merges skip it entirely.
  • Strips the merge-fix action down to just test fixing — no more conflict stripping, no --no-frozen-lockfile. Uses --frozen-lockfile since the lockfile is already correct.
  • Adds critical rules to the fix-tests skill based on analysis of a 1-hour timeout: never run pnpm install, time-box investigation to 5 min, diff before deep-reading source, batch bash commands.

Testing

  • No test changes — these are CI workflow and skill instruction changes.

Docs

  • No docs needed — internal CI automation only.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@6.3.0

Minor Changes

  • #16366 d69f858 Thanks @matthewp! - Adds a new experimental.advancedRouting option that lets you take full control of Astro's request handling pipeline by creating a src/app.ts file in your project.

    Today, Astro handles every incoming request through a fixed internal pipeline: trailing slash normalization, redirects, actions, middleware, page rendering, i18n, and so on. That pipeline works great for most sites, but as projects grow you often want to run your own logic between those steps — an auth check before rendering, a rate limiter before actions, custom logging around the whole stack. Advanced routing gives you that control.

    When enabled, Astro looks for a src/app.ts file in your project. If it finds one, that file becomes the entrypoint for all server-rendered requests. You compose the pipeline yourself using the handlers Astro provides, and you can slot your own logic anywhere in the chain.

    Enabling advanced routing

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      experimental: {
        advancedRouting: true,
      },
    });

    Two ways to build your pipeline

    Astro ships two entrypoints for advanced routing: astro/fetch and astro/hono.

    astro/fetch is a low-level, framework-free API built on the Web Fetch standard. You create a FetchState from the incoming request, then call handler functions in sequence. Each handler takes the state, does its work, and returns a Response (or undefined to pass through). This is the core primitive that everything else is built on:

    // src/app.ts
    import {
      FetchState,
      trailingSlash,
      redirects,
      actions,
      middleware,
      pages,
      i18n,
    } from 'astro/fetch';
    
    export default {
      async fetch(request: Request) {
        const state = new FetchState(request);
    
        // Early exits — these return a Response only when they apply.
        const slash = trailingSlash(state);
        if (slash) return slash;
    
        const redirect = redirects(state);
        if (redirect) return redirect;
    
        const action = await actions(state);
        if (action) return action;
    
        // Middleware wraps page rendering; i18n post-processes the response.
        const response = await middleware(state, () => pages(state));
        return i18n(state, response);
      },
    };

    astro/hono wraps the same handlers as Hono middleware, so you can mix Astro's pipeline with Hono's ecosystem of middleware (logger, CORS, JWT, rate limiting, etc.) using the app.use() pattern you already know:

    // src/app.ts
    import { Hono } from 'hono';
    import { getCookie } from 'hono/cookie';
    import { logger } from 'hono/logger';
    import { actions, middleware, pages, i18n } from 'astro/hono';
    
    const app = new Hono();
    
    app.use(logger());
    
    // Auth gate — only runs for /dashboard routes.
    app.use('/dashboard/*', async (c, next) => {
      const session = getCookie(c, 'session');
      if (!session) return c.redirect('/login');
      return next();
    });
    
    app.use(actions());
    app.use(middleware());
    app.use(pages());
    app.use(i18n());
    
    export default app;

    Both approaches give you the same power — pick whichever fits your project. If you don't need a framework, astro/fetch keeps things minimal. If you want a rich middleware ecosystem, astro/hono gets you there with one import.

    For more information on enabling and using this feature in your project, see the experimental advanced routing docs. To give feedback, or to keep up with its development, see the advanced routing RFC for more information and discussion.

  • #16366 d69f858 Thanks @matthewp! - Adds a consume() instance method to AstroCookies. This method marks the cookies as consumed and returns the Set-Cookie header values. After consumption, any subsequent set() calls will log a warning, since the headers have already been sent.

    Previously this was only available as a static method AstroCookies.consume(cookies). The static method is now deprecated but kept for backward compatibility with existing adapters.

  • #16412 ba2d2e3 Thanks @0xbejaxer! - Add retry and error event handling for astro-island hydration import failures to reduce unrecoverable hydration errors on transient network failures.

  • #16582 885cd31 Thanks @Princesseuh! - Adds a new image.dangerouslyProcessSVG flag to optionally enable processing SVG inputs. For security reasons, Astro will no longer rasterizes SVG image sources by default in its default image service and endpoint.

    Set image.dangerouslyProcessSVG: true to opt back into processing SVG inputs.

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      // ...
      image: {
        dangerouslyProcessSVG: true,
      },
    });

    Note that this is a breaking change for users who were previously relying on Astro's default image service to rasterize SVG inputs, but it is a necessary change to improve security and prevent potential vulnerabilities.

  • #16519 1b1c218 Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.

    Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in image.remotePatterns or a domain in image.domains, Astro will fail with a helpful error message.

    In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:

    export default defineConfig({
      image: {
        domains: ['example.com', 'cdn.example.com'],
      },
    });
    {
      /* Redirects to https://cdn.example.com/assets/image.png: */
    }
    <Image
      src="https://example.com/assets/image.png"
      width="1920"
      height="1080"
      alt="An example image."
    />;
    
    {
      /* Redirects to https://malicious.com/image.png: */
    }
    <Image
      src="https://example.com/bad-image.png"
      width="1920"
      height="1080"
      alt="An example image."
    />;

    In cases where all redirects to HTTPS hosts should be trusted, the following configuration for image.remotePatterns can be used:

    export default defineConfig({
      image: {
        remotePatterns: [
          {
            protocol: 'https',
          },
        ],
      },
    });

Patch Changes

  • #16592 9c6efc5 Thanks @matthewp! - Escapes interpolated values in the dev server redirect HTML template, consistent with how the 404 template already handles them

  • #16585 78f305e Thanks @web-dev0521! - Fixes z.array(z.boolean()) in form actions incorrectly coercing the string "false" to true. Boolean array elements now use the same 'true'/'false' string comparison as single z.boolean() fields, so submitting ["false", "true", "false"] correctly parses as [false, true, false].

  • #16567 12a03f2 Thanks @matthewp! - Fixes deleted content collection entries persisting in getCollection() results during dev

  • #16595 ce9b25c Thanks @web-dev0521! - Fixes pushDirective in the CSP runtime duplicating the new directive once per existing non-matching directive. Calling insertDirective() (or otherwise pushing a directive whose name is not yet in the list) now appends it exactly once, and a directive that merges with a later existing entry no longer leaves an unmerged copy behind.

  • #16600 94e4b7c Thanks @web-dev0521! - Fixes Astro.preferredLocale returning the wrong value when i18n.locales mixes object-form entries ({ path, codes }) with string entries that normalize to the same locale. The first matching code in the configured locales order is now selected, matching the documented behavior.

  • #16591 cce20f7 Thanks @matthewp! - Uses a consistent generic error message in the image endpoint across all adapters

  • #16629 f54be80 Thanks @g-taki! - Fixes a bug where SSR responses in astro dev could crash with TypeError: this.logger.flush is not a function.

  • #16589 3740b24 Thanks @ArmandPhilippot! - Fixes an outdated code snippet in the documentation for session storage configuration.

  • Updated dependencies [354e231]:

    • @astrojs/telemetry@3.3.2

@astrojs/cloudflare@13.4.0

Minor Changes

  • #16519 1b1c218 Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.

    Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in image.remotePatterns or a domain in image.domains, Astro will fail with a helpful error message.

    In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:

    export default defineConfig({
      image: {
        domains: ['example.com', 'cdn.example.com'],
      },
    });
    {
      /* Redirects to https://cdn.example.com/assets/image.png: */
    }
    <Image
      src="https://example.com/assets/image.png"
      width="1920"
      height="1080"
      alt="An example image."
    />;
    
    {
      /* Redirects to https://malicious.com/image.png: */
    }
    <Image
      src="https://example.com/bad-image.png"
      width="1920"
      height="1080"
      alt="An example image."
    />;

    In cases where all redirects to HTTPS hosts should be trusted, the following configuration for image.remotePatterns can be used:

    export default defineConfig({
      image: {
        remotePatterns: [
          {
            protocol: 'https',
          },
        ],
      },
    });

Patch Changes

  • Updated dependencies []:
    • @astrojs/underscore-redirects@1.0.3

@astrojs/language-server@2.16.8

Patch Changes

  • #16627 5778cb7 Thanks @Princesseuh! - Fixes unintended dependency on the typescript package being available to the language server

@astrojs/telemetry@3.3.2

Patch Changes

  • #16260 354e231 Thanks @gameroman! - Refactors internal config logic to remove the dlv dependency in favor of native logic
withastro/astro

Changes

  • Fix pushDirective in the CSP runtime duplicating the new directive once per existing non-matching directive.
  • Root cause: the else branch inside the loop pushed newDirective on every non-match instead of once after the loop. Replaced the deduplicated flag with a matched flag and moved the newDirective push outside the loop, guarded by !matched.
  • Closes #16594.

Before: pushDirective(["img-src 'self'", "style-src 'self'"], "script-src 'self'")["img-src 'self'", "script-src 'self'", "style-src 'self'", "script-src 'self'"] (duplicated + interleaved)
After:["img-src 'self'", "style-src 'self'", "script-src 'self'"]

Also fixes Mode 2: a directive that merges with a later existing entry no longer leaves an unmerged copy in front of the merged result.

Don't forget a changeset! Run pnpm changeset. — Added: .changeset/fix-csp-push-directive-duplicate.md

Testing

  • Added a regression: issue #16594 suite in packages/astro/test/units/csp/runtime.test.ts covering:
    • Mode 1 — non-matching new directive appended exactly once.
    • Mode 2 — merge with a later match, no unmerged copy left behind.
    • Empty input list edge case.
    • Many non-matches: directive-name uniqueness asserted via a Map count.
    • Table-driven sweep across "non-match", "match in middle", "match at end" shapes, asserting both length and Set-based name uniqueness.
  • All 11 tests in csp/runtime.test.ts pass locally (node --test test/units/csp/runtime.test.ts).

Docs

No docs change required — pushDirective is internal CSP runtime; public insertDirective() semantics are unchanged (this restores them to the documented behavior).

withastro/astro

Changes

This PR does an overhaul of all error messages to make them more consistent with each other, namely:

  • fixing missing full stop at the end of messages.
  • adding missing backticks to code excerpts.
  • adding () to differentiate functions from simple properties/objects.
  • improving error titles/messages with more context.

Testing

I am not sure if adding backticks in a few places could break rendering, we'll be sure once we get the automated PR on docs.

Docs

That's all it is.

withastro/astro

Changes

  • The redirect template in 3xx.ts interpolated URL values directly into HTML without escaping. The sibling 4xx.ts template already escapes its values using html-escaper. This applies the same pattern to the redirect template for consistency.

Testing

  • No new tests. The change adds escape() calls around existing interpolations — the same approach 4xx.ts already uses.

Docs

  • No docs update needed.
withastro/astro

Changes

  • The Node image endpoint (node.ts) returns a generic 'Internal Server Error' string on failure, but the generic adapter endpoint (generic.ts) was interpolating the error object into the response. This aligns generic.ts with node.ts so all adapters use the same consistent error response.

Testing

  • No new tests. Single-line string replacement in an error path — existing image endpoint tests cover the surrounding behavior.

Docs

  • No docs update needed.
withastro/starlight

This PR contains the following updates:

Package Type Update Change
withastro/automation action digest e27ec6dc8a2e8b

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

withastro/starlight

Description

Especially in Chinese and Japanese, the CSS property text-autospace: normal is preferred for better looking like books by automatically inserting spaces with the moderate width.

Without this, some users and AIs try to insert such spaces manually as workarounds but they are too wide and prevent keywords from searched.

Japanese:

Before:

image

After:

image

Note: the current Chinese content of Starlight translation is not affected because it took the workarounds like manually inserting spaces.

The other languages are not affected.

withastro/astro

Changes

  • Fix z.array(z.boolean()) in form actions incorrectly coercing the string "false" to true.
  • handleFormDataGetAll was using entries.map(Boolean), but FormData values are always strings and Boolean("false") === true. Replaced with the same 'true' / 'false' comparison already used for single z.boolean() fields in handleFormDataGet.
  • Closes #16584.

Before: ["false", "true", "false"][true, true, true]
After: ["false", "true", "false"][false, true, false]

Testing

  • Added a new boolean arrays suite in packages/astro/test/units/actions/form-data-to-object.test.ts:
    • should preserve "false" string values in boolean arrays — direct regression test for the reported case.
    • should coerce mixed boolean array values correctly — table-driven coverage across all-true / all-false / mixed inputs.
  • All 25 tests in formDataToObject pass locally (node --test test/units/actions/form-data-to-object.test.ts).

Docs

No docs change required — this is a behavior fix bringing array-element parsing in line with the already-documented single-boolean coercion behavior.

withastro/astro

fixes #13297

Changes

In the current implementation, enabling prefetch settings causes links to be collected via document.getElementsByTagName('a') on the initial page load.
While this approach works for most cases, it fails to cover certain scenarios, such as the one reported in issue #13297.

The root cause of issue #13297 is that when a component with the server:defer attribute uses top-level await, its links are rendered after the Promise resolves, meaning they are not captured by the initial document.getElementsByTagName('a') call.
More broadly, the same issue occurs whenever links are dynamically added to the DOM after the initial page load.

To address this, this pull request adds the observeDynamicLinks option, which uses a MutationObserver to watch for dynamically added link elements.
This ensures that links added after the initial render are properly captured.

This feature is opt-in.

Why MutationObserver?

Investigation process
  • First, I checked the code around server:defer to see if it used any event notifications.
  • It did not, but I found that the prefetch code [uses the astro:page-load event].
    document.addEventListener('astro:page-load', () => {
  • When I looked into whether astro:page-load could be used here, I found that the constant defining this event name is marked as @deprecated, so I decided to look for a different approach.
    /** @deprecated This will be removed in Astro 7 */
    export const TRANSITION_PAGE_LOAD = 'astro:page-load';
  • I also considered that the root cause of issue #13297 is not specific to server:defer, the same problem can occur in other situations where links are added to the DOM dynamically. For this reason, I wanted an approach that resolves the issue within the prefetch code itself, rather than adding prefetch-dependent logic to the server:defer side.
  • As a result, I concluded that MutationObserver is the best approach, as it is well-suited for observing DOM changes and allows the fix to be fully self-contained within the prefetch implementation.

Testing

Added tests to verify that the observeDynamicLinks option works correctly in the following scenarios:

  • Links dynamically added via server:defer, as reported in issue #13297
  • Links dynamically added by a toggle component (ToggleButton.jsx)

Docs

/cc @withastro/maintainers-docs for feedback!

withastro/astro

Changes

SVGs are always troublesome in the end and wanting to actually process them is so uncommon that it's best to leave it to the user.

I decided to put it on a global image option so that other services could also use it, but I'd be okay with it being a service config as well, it's ok

Testing

Added a test

Docs

WIP

withastro/astro

Changes

Minor adjustment to @astrojs/mdx so smartypants object options are passed to remark-smartypants, matching Astro's markdown behavior since v6.1.

Testing

Added a regression test that verifies smartypants object options (e.g. dashes: "oldschool") are applied in MDX. The test fails without this change (e.g., for dashes: "oldschool" an em-dash is expected while the default configuration produces an en-dash) and passes with it.

withastro/starlight

This PR contains the following updates:

Package Type Update Change
pnpm/action-setup action patch v6.0.4v6.0.5

Release Notes

pnpm/action-setup (pnpm/action-setup)

v6.0.5

Compare Source

What's Changed
  • fix: append (not prepend) action node dir to PATH for npm bootstrap by @​zkochan in #​241

Full Changelog: pnpm/action-setup@v6.0.4...v6.0.5


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

lin-stephanie/astro-antfustyle-theme

Description

withastro/astro

Changes

Fixes the Vite build warning reported in #15957:

[WARN] Plugin vite:reporter: Unused imports from "@astrojs/internal-helpers/remote":
 – "matchHostname", "matchPathname", "matchPort", "matchProtocol"

plugin-internals.ts sets noExternal: ['astro'] for the prerender environment, which bundles astro but leaves @astrojs/internal-helpers as an external. Rollup then sees that those four helpers are imported by the bundled astro code but not consumed inside the bundle boundary, which triggers the unused-import warning.

Adding @astrojs/internal-helpers to noExternal alongside astro co-bundles it in the prerender environment, eliminating the warning. This is a first-party workspace package and the same pattern is already used elsewhere.

Testing

The warning is emitted by Vite/Rollup during the build phase and is not directly observable in the integration test suite without capturing stderr. Verified manually by running a project build with output: 'hybrid' before and after the change — the warning disappears with the fix applied.

Docs

No user-facing behavior change — no docs update needed.

Fixes #15957.

withastro/astro

Fixes #16692

Changes

  • Automatically injects Cache-Control: public, max-age=31536000, immutable for hashed Astro assets (/_astro/*) into Cloudflare's _headers file at build time, so browsers cache assets across deploys without extra user configuration.
  • Skips injection when build.assetsPrefix is set (assets are served from a different origin).
  • Skips injection when the user's existing _headers already has a Cache-Control rule whose URL pattern matches the assets path — Cloudflare merges duplicate matching rules' headers with a comma, which would produce contradictory cache directives.
  • Respects base config: the injected pattern is prefixed accordingly (e.g. /blog/_astro/*).
  • Injection block is prepended before any existing user-defined headers so rule ordering in the file is predictable.
  • Atomic _headers write (write to .tmp, then rename) to avoid leaving the file truncated on a mid-write crash.

Testing

  • Added unit tests for the new headersFileHasCacheControlForPath utility in packages/integrations/cloudflare/test/headers.test.ts, covering: empty files, exact patterns, global splat (/*), ! Cache-Control detach, non-matching rules, placeholder patterns (:name), host-prefixed patterns, comments/blank lines, base-prefixed paths, and case-insensitivity.
  • Extended the existing with-base integration test to assert the injected block is present with the correct base prefix, that user-defined headers are preserved, and that the injected block is placed before user headers.

Docs

No documentation changes needed — this is an internal build-time improvement for Cloudflare deployments. The behavior is automatic and the skip conditions (logged via logger.info) are observable in build output. No new configuration options were added.

Other Info

Live preview of the fix: https://with-fix-astro-assets-cf-repro.ma2153.workers.dev/

withastro/astro

Changes

  • Invalidates the astro:data-layer-content virtual module in the SSR module runner's evaluatedModules cache when the data store changes, not just in the server-side module graph. The existing invalidateModule call only clears the server's transformResult, but a subsequent transformRequest (triggered during module resolution) re-populates it before the runner's fetchModule check, causing a false cache hit that returns stale data.

Closes #16561

Testing

  • No new tests added. Manually verified with a content collection setup: deleting entries correctly updates getCollection() results on the next page load in dev.

Docs

  • No docs update needed; this is a bug fix with no API change.
withastro/astro

Changes

  • Fixes #16564data-astro-prefetch="tap" silently failing when the user clicks a nested child element (e.g. <span>, <img>, <svg>, Astro <Image />) inside an anchor
  • e.target on touchstart/mousedown is the deepest element under the pointer, not necessarily the <a> — use closest('a') to walk up to the anchor before checking the strategy
  • Particularly impactful on slow connections / data-saver mode, where all strategies collapse to tap — meaning zero prefetching was happening for any link with nested content for the users who would benefit most

Testing

  • Added e2e fixture link <a data-astro-prefetch="tap"><span>tap nested</span></a> and a corresponding page
  • Added e2e test in prefetch.test.ts that clicks the inner <span> and asserts the prefetch request fires

Docs

No docs change needed — this is a bug fix restoring already-documented behavior, not a new feature or API change.

withastro/astro

Closes #16563

Summary

Calling session.delete(key) as the first mutation in a request (no prior get, set, has, keys, etc.) did not write back to session storage. The session stayed dirty in memory, but [PERSIST_SYMBOL]() skipped the save path because #data was still undefined, so the backing store kept the old value and the next request could still read the “deleted” key.

Root cause

  • set() initializes the in-memory map with this.#data ??= new Map(); delete() only did this.#data?.delete(key), so #data could remain undefined.
  • Persistence gated saves on if (this.#dirty && this.#data), so delete-only flows never called setItem and #toDelete was never applied to storage.

Fix

  • Initialize the map in delete() the same way as in set(): this.#data ??= new Map() before removing the key, so the persist path runs and #ensureData() can merge, apply deletions, and serialize to the driver.

Testing

  • Added a unit test that pre-seeds backing storage, calls only delete() then persist, and asserts a new AstroSession with the same storage/session id no longer returns the deleted key.
  • Adjusted an existing persistence test so the follow-up session’s storage get returns parsed JSON (matching real unstorage behavior) after setItem writes a devalue JSON string.

Docs

No documentation change required; behavior now matches what the session API already promises.

Contributor checklist (see CONTRIBUTING.md)

  • pnpm exec changeset for the astro package (user-facing bugfix)
  • Tests: pnpm -C packages/astro exec astro-scripts test "test/units/sessions/astro-session.test.ts"
withastro/astro

Fixes #16553

Changes

  • Fixes non-prerendered routes (e.g. SSR endpoints using cloudflare:workers) failing when a dynamic prerendered route like [page].astro exists in the same project with prerenderEnvironment: 'node'.
  • The dev prerender middleware was using matchAllRoutes(), which returns every route matching a pathname. A request to /ssr would match both ssr.astro (non-prerendered) and [page].astro (prerendered), and since one match was prerendered, the request was incorrectly routed to the Node handler. Replaced with matchRoute(), which returns only the highest-priority match.

Testing

  • Added a dynamic prerendered route ([page].astro with getStaticPaths) to the existing prerender-node-env fixture. The existing "renders SSR page through workerd" test now exercises this scenario.

Docs

  • No docs needed.
withastro/starlight

This PR contains the following updates:

Package Type Update Change
withastro/automation action digest 497c926e27ec6d

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

withastro/astro

Changes

  • Updates ./test-utils.js imports to ./test-utils.ts in three .test.js files that were missed when #16492 renamed test-utils.jstest-utils.ts.

Testing

  • No new tests. This fixes the existing test:integration:js suite which fails immediately with ERR_MODULE_NOT_FOUND.

Docs

  • No docs needed — test-only change.
withastro/astro

Changes

Simplify db's tsconfig by removing tsconfig.virtual.json.

Its only purpose is to prevent incorrect auto-imports from editor hints and doesn’t affect the runtime output, so it should be relatively safe to remove it.

This file makes it tricky for the TypeScript project references refactor.

Testing

Green CI

Docs

N/A

withastro/astro

Changes

  • Double-encoded URL paths like /api/%2561dmin/users could bypass middleware auth checks because the normalization fallback left ctx.url.pathname half-decoded.
  • Adds MultiLevelEncodingError as a distinct error type so callers can distinguish it from generic decode failures.
  • #createNormalizedUrl now re-throws MultiLevelEncodingError instead of falling back. Other decode errors (truly malformed URLs) still fall back gracefully.
  • BaseApp.render() catches MultiLevelEncodingError and returns 400 Bad Request.

Testing

  • Added test/units/app/double-encoding-bypass.test.ts — 7 tests using a catch-all /api/[...path] endpoint behind auth middleware. Covers the direct path (401), single-encoded (401), double-encoded (400), multiple encoded segments (400), public routes (200), and non-protected API routes (200).

Docs

  • No docs update needed — no user-facing API changes.
withastro/astro

Changes

  • Fixes a bug in @astrojs/cloudflare where custom KV namespace bindings (e.g. MY_KV, CACHE) were silently removed when Astro's session functionality was enabled.
  • The root cause was in packages/integrations/cloudflare/src/wrangler.ts: when injecting the SESSION KV binding, the code returned a fresh single-item array instead of merging with the user's existing kv_namespaces.
  • Extracted a withSessionKVBinding helper that copies existing namespaces and appends the SESSION binding, preserving all user-defined bindings.
  • Fix applies to both the top-level config and the previews config (same code path).
  • Updated the existing test that was asserting the broken behavior, and added a new test covering the previews case.

Closes #16554

Testing

  • Updated packages/integrations/cloudflare/test/wrangler.test.ts: fixed the assertion in "adds SESSION binding when other KV bindings exist but not the session one" to verify OTHER_KV is preserved alongside SESSION.
  • Added a new test "preserves existing previews KV bindings when adding SESSION binding" covering the same scenario in the previews config.
  • No integration test needed: the bug and fix are fully exercised by the unit test suite for cloudflareConfigCustomizer.

Docs

No docs change needed. This is a bug fix restoring behavior that users already rely on — their existing kv_namespaces entries in wrangler.toml now survive when sessions are enabled.

withastro/astro

Changes

  • Fixes a regex bug where return was incorrectly rewritten inside string literals, template literals, and comments during esbuild's dep-scan / frontmatter error-check phase
  • Replaces the two-pass \breturn\b regex with a single-pass state-machine regex that skips over all string/comment tokens before rewriting bare return statements
  • Fixes the primary report case: gen.return(value) → was producing gen.throw (value) (syntax error); now preserved correctly
  • Extracts a shared replaceTopLevelReturns() helper into utils.ts to deduplicate logic between the cloudflare esbuild plugin and compile.ts

Affected files:

  • packages/integrations/cloudflare/src/esbuild-plugin-astro-frontmatter.ts
  • packages/astro/src/vite-plugin-astro/utils.ts
  • packages/astro/src/vite-plugin-astro/compile.ts

Closes #16551

Testing

The fix is a pure regex change with no new dependencies. Verified manually against the cases from the issue:

Input Before After
gen.return(val) gen.throw (val) ← syntax error unchanged ✓
"return value" "throw value" unchanged ✓
// return foo // throw foo unchanged ✓
`return ${x}` `throw ${x}` unchanged ✓
return foo return foo throw foo
return; return; throw 0;

No new tests added — the two affected code paths are internal-only (esbuild dep scan and compile-error enhancement), exercised by the existing integration test suite.

Docs

No user-facing behavior change — this only affects internal dep scanning and error reporting during compilation. No docs update needed.

withastro/astro

Changes

fixes #16524.

with vite.css.transformer: 'lightningcss', scoped styles using a nested & selector inside :where(...) silently produce css where the scope attribute lands on the matched child instead of the intended parent. tailwind v4's space-x-*, space-y-*, and divide-* all expand to this shape and tailwind v4 ships with lightningcss in the loop, so any astro 6 + tailwind v4 project using these utilities in scoped <style> blocks targeting children from another component gets broken spacing in production while the css rule still looks present in the bundle.

what's happening: lightningcss runs inside vite's preprocessCSS() and lowers nesting before @astrojs/compiler injects scope attributes. by the time the compiler sees the css, .parent is buried inside :where(...) and isn't a top-level compound anymore, so the injector prepends [data-astro-cid-X] as a new leading compound — which constrains the wrong element.

fix in packages/astro/src/core/compile/style.ts: when the user has css.transformer: 'lightningcss', call preprocessCSS() with a shallow-cloned vite config whose css.lightningcss.exclude ORs in Features.Nesting, so lightningcss skips its nesting-lowering pass for this preprocess call. vite's final pipeline still lowers nesting for the bundle so output stays compatible with the user's targets. clone is non-mutating per call so it's safe under parallel .astro compiles. lightningcss is resolved from the user's project root via createRequire — same instance vite uses, no new dep added to packages/astro. falls back to the original config if lightningcss can't be resolved.

i think a better fix would live in @astrojs/compiler itself, teach the scope injector to recognize :where(<simple-compound>, ...) as a leading-compound wrapper and attach the cid to the inner compound instead of prepending a new one. similar in spirit to withastro/compiler#1153 but extended to cover the :where(...) case, and it'd cover any future css transform that produces a similarly shaped selector not just lightningcss.

Testing

added a regression fixture under packages/astro/test/:

  • lightningcss-scoped-nesting.test.ts — compiles the reporter's exact shape with vite.css.transformer: 'lightningcss' and asserts the scope id binds to .parent, not as a leading compound on the matched child.
  • fixture under packages/astro/test/fixtures/lightningcss-scoped-nesting/ — single page using the reporter's :where(& > :not(:last-child)) selector inside a scoped <style> block.

reporter's reproduction repo: https://github.com/rklos/astro-css-bug-repro

Docs

no docs changes. silent regression in css output; behavior after the fix matches what the docs already describe for scoped styles + nested selectors.

withastro/astro

Changes

upgrade/package.json has "build": "astro-scripts build \"src/index.ts\" --bundle && tsc". The output dist/index.js should be emitted by astro-scripts, while tsc should only emit .d.ts files.

In #16493, tsc emits both .js and .d.ts files, which overrides the bundled dist/index.js file that was just emitted by astro-scripts.

This PR updates upgrade/tsconfig.json to ensure that tsc won't emit any .js files.

Testing

Before this PR:

$ pnpm -w build
$ tree ./packages/upgrade/dist/
./packages/upgrade/dist/
├── actions
│   ├── context.d.ts
│   ├── context.js
│   ├── help.d.ts
│   ├── help.js
│   ├── install.d.ts
│   ├── install.js
│   ├── verify.d.ts
│   └── verify.js
├── index.d.ts
├── index.js
├── messages.d.ts
├── messages.js
├── shell.d.ts
└── shell.js

2 directories, 14 files

After this PR:

$ pnpm -w build
$ tree ./packages/upgrade/dist/
./packages/upgrade/dist/
├── actions
│   ├── context.d.ts
│   ├── help.d.ts
│   ├── install.d.ts
│   └── verify.d.ts
├── index.d.ts
├── index.js
├── messages.d.ts
└── shell.d.ts

2 directories, 8 files

After this PR, only one bundled dist/index.js file is emitted in dist/.

Docs

The previous and afterward outputs can both work, so there is no behavior change. No docs are needed.


Last fetched:  |  Scheduled refresh: Every Saturday

See astro-loaders documentation to configure your own

Inspired by prs.atinux.com