Skip to content

AstroEco is Contributing…

Display your GitHub pull requests using astro-loader-github-prs

withastro/astro

Changes

@astrojs/cloudflare@14.3.0 imports renderForPrerender from astro/app, but its astro peer range is still ^7.2.0. That export was added in Astro 7.3.0, so npm/pnpm install 14.3.0 on top of Astro 7.2.x with no peer warning, and the build fails:

[MISSING_EXPORT] "renderForPrerender" is not exported by "node_modules/astro/dist/core/app/entrypoints/index.js".
   ╭─[ node_modules/@astrojs/cloudflare/dist/utils/prerender.js:1:10 ]
 1 │ import { renderForPrerender } from "astro/app";

Verified against the published tarballs:

package renderForPrerender
astro@7.2.4, astro@7.2.10 not exported from dist/core/app/entrypoints/index.js
astro@7.3.0 exported
@astrojs/cloudflare@14.2.6 not imported
@astrojs/cloudflare@14.3.0 imported in dist/utils/prerender.js

Both sides were introduced together in #17795 (core + adapter) and released as astro@7.3.0 / @astrojs/cloudflare@14.3.0, but the peer range wasn't bumped. CI doesn't catch this because the monorepo resolves astro as workspace:*, so the declared range is never exercised.

This is easy to hit in practice: wrangler deploy auto-config runs astro add cloudflare on a project whose lockfile pins Astro 7.2.x, installs 14.3.0, and the deploy fails.

Testing

Peer-range-only change; no runtime behavior affected.

Docs

No docs change needed.

withastro/astro

Changes

  • A dev toolbar app's UI is destroyed on the first client-side navigation and never restored — silently, with no error and app.status still ready.
  • DevToolbarCanvas.connectedCallback() assigns this.shadowRoot.innerHTML, which replaces rather than appends, discarding whatever the app rendered into its canvas.
  • The canvas reconnects on every navigation.
  • Nothing rebuilds it — initApp() sits behind the hasBeenInitialized guard, so app.init() never runs a second time.
  • Fix: write the style once in the constructor and drop connectedCallback, which had no other job.
  • Only DevToolbarCanvas is affected. DevToolbarWindow projects through a <slot> and DevToolbarTooltip rebuilds from its attributes.
  • Changeset included (patch).
Before After
before-fix after-fix

Testing

  • Added third-party app content survives a client-side navigation to packages/astro/e2e/dev-toolbar.test.ts. No new fixture needed — the existing dev-toolbar fixture already has a third-party app, a <ClientRouter /> layout, and linked view-transition-a/b pages.
  • Confirmed the test catches the bug: passes with the fix, fails without it (toHaveCount: Expected 1, Received 0), passes again once restored.

Docs

withastro/astro

Changes

  • Adds the :disableDependencyDashboard preset to .github/renovate.json5. config:recommended enables the dashboard by default. So this disables it.
  • Issue not needed, you can go to https://developer.mend.io/

Testing

  • N/A

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.3.1

Patch Changes

  • #17899 0389640 Thanks @ematipico! - Fixes an error that prevented projects using astro:assets from starting or building
withastro/astro

Changes

Closes #17897
Closes #17898

We were using an internal path in our runtime. Removed it in favour of an internal runtime object.

Testing

Tested locally in one of the reproductions. Will do a preview release too.

I update our review skill to check for those.

Docs

withastro/astro

Changes

  • MDX rendering treated any plain-string <script>/<style> child as raw, unescaped markup, including a dynamic value like <script>{value}</script>.
    • Now this is escaped by default.
  • Literals get collapsed into a set:html attribute at compile time. This allows remark plugins to continue to work.

Testing

  • Adds an MDX fixture/tests (script-style-dynamic.mdx) covering dynamic <script>/<style> children

Docs

  • N/A, bug fix.
withastro/starlight

This PR is aimed to temporarily fix formatting.

It’s currently broken due to a bug in Prettier’s parsing of the PageFrame.astro component. It does not like the JS class inside a <script> inside a conditional expression here:

{
hasSidebar && (
<nav class="sidebar print:hidden" aria-label={Astro.locals.t('sidebarNav.accessibleLabel')}>
<MobileMenuToggle />
<script>
// The mobile menu uses the popover API to open and close without JavaScript. We enhance this
// behaviour with focus trapping while the menu is open.
class StarlightSidebarPane extends HTMLElement {
constructor() {
super();
// Close the menu when the viewport is resized between mobile and desktop sizes.
matchMedia('(min-width: 50em)').addEventListener('change', () => this.hidePopover());
// Update the menu state when the sidebar popover is toggled.
this.addEventListener('toggle', ({ newState }) => this.trapFocus(newState === 'open'));
}
/** Trap keyboard focus inside the header and menu when the menu is expanded. */
trapFocus(shouldTrap: boolean) {
document.querySelectorAll<HTMLElement>('.main-frame, .sl-skip-link').forEach((el) => {
el.toggleAttribute('inert', shouldTrap);
});
}
}
customElements.define('sl-sidebar-pane', StarlightSidebarPane);
</script>
<sl-sidebar-pane popover="" id="starlight__sidebar" class="sidebar-pane">
<div class="sidebar-content sl-flex">
<slot name="sidebar" />
</div>
</sl-sidebar-pane>
</nav>
)
}

As a workaround, this PR adds PageFrame.astro to our .prettierignore so it is skipped by formatting. We can remove this later in #4175.

withastro/astro

Changes

  • astro/app/manifest and @astrojs/cloudflare/cache/provider are now pre-declared in the optimizeDeps.include array of the @astrojs/cloudflare:environment plugin. Without these entries, Vite discovers the modules mid-startup or mid-request, triggers a re-optimization, and workerd crashes referencing stale pre-rewrite chunk filenames — resulting in Dev server process exited before becoming ready on every cold start.
  • astro/app/manifest is added unconditionally alongside the existing astro/app entry (regression from PR #17787 which changed the import specifier in vite-plugin-routes). @astrojs/cloudflare/cache/provider is added conditionally when needsWorkerCache is true, mirroring the existing pattern used for astro/logger/json.

Testing

  • Two regression tests added to packages/integrations/cloudflare/test/typegen-phase.test.ts: one asserting astro/app/manifest is always present in the SSR optimizeDeps.include list, and one asserting @astrojs/cloudflare/cache/provider is present when Workers Caching is enabled.

Docs

  • No docs update needed; this is an internal adapter configuration fix with no user-facing API change.

Closes #17893

withastro/starlight

Description

This PR updates the Astro MDX integration to v8, dropping a bunch of dependencies.

It also requires bumping the minimum supported Astro version from 7.2.5 to 7.2.10 and the dependencies for Sätteri/Remark integration too. We were already bumping these for the current release, so I’ve updated that changeset to reflect this.

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-docsearch@0.8.0

Minor Changes

  • #3572 292fb17 Thanks @HiDeoo! - Distributes package as JavaScript files with dedicated type declaration files instead of TypeScript source files.

@astrojs/starlight@0.42.0

Minor Changes

  • #3572 292fb17 Thanks @HiDeoo! - Distributes package as JavaScript files with dedicated type declaration files instead of TypeScript source files.

  • #4121 2623ae6 Thanks @delucis! - Simplifies markup for Starlight’s mobile menu toggle

    ⚠️ Potentially breaking change: If you use a theme plugin, custom styles, or component overrides targeting the MobileMenuToggle button or PageFrame components, you may need to adjust these for the new markup. The button is no longer wrapped in a <starlight-menu-button> custom element and no longer uses the aria-expanded attribute. Instead, you can use the .sl-menu-button class name to target the button and the :popover-open pseudo-class to style the menu open state specifically.

    In the following example, custom styles for the menu button are updated for the new approach:

    - starlight-menu-button button {
    + .sl-menu-button {
      color: var(--sl-color-text);
    }
    
    - starlight-menu-button[aria-expanded='true'] button {
    + .sl-menu-button:has(~ :popover-open) {
      color: var(--sl-color-text-accent-high);
    }

    See MobileMenuToggle.astro and PageFrame.astro on GitHub for the full source code of the updated components.

  • #3572 292fb17 Thanks @HiDeoo! - Removes the tagline configuration option, which was never used.

    If your configuration included a tagline option, you can safely remove it without any replacement.

  • #4134 6135f01 Thanks @HiDeoo! - Updates internal @astrojs/mdx, @astrojs/markdown-satteri, and satteri dependencies.

    ⚠️ BREAKING CHANGE: The following minimum versions are now required:

    • astro v7.2.10 or later
    • @astrojs/markdown-satteri 0.4.0 or later (if you use it)
    • @astrojs/markdown-remark 7.3.0 or later (if you use it)

    Please update Starlight and Astro together:

    npx @astrojs/upgrade
  • #4121 2623ae6 Thanks @delucis! - Refactors Starlight’s mobile menu toggle to work when JavaScript fails or is disabled

    ⚠️ BREAKING CHANGE: This release drops official support for Chromium-based browsers prior to version 116 (released August 2023), Safari-based browsers prior to version 17.0 (released September 2023), and Firefox prior to version 125 (released April 2024). You can find a list of currently supported browsers and their versions using this browserslist query.

    This change also removes the data-mobile-menu-expanded attribute, which was previously added to <body> while the mobile menu is open. If you have custom code that was depending on this attribute, you will need to update it to use a new selector to check if the mobile menu is open.

    In the following example, a custom background colour for the site header while the menu is open is updated for the new approach:

    - [data-mobile-menu-expanded] header {
    + body:has(sl-sidebar-pane:popover-open) header {
      background-color: var(--sl-color-bg);
    }
withastro/astro

Changes

Rewrites @astrojs/solid-js for Solid 2.0 as a new major (v8), following the @astrojs/svelte precedent for major framework transitions: v8 supports Solid 2.0 only (solid-js@^2.0.0-rc.5 + @solidjs/web@^2.0.0-rc.5 — caret ranges, so 2.0 stable satisfies them when it ships); v7 remains for Solid 1.x.

Core (first commit)

  • Compiles through @solidjs/vite-plugin (replacing vite-plugin-solid), with a compiler option for the native (Oxc) or Babel backend.
  • Islands render through Solid 2.0's first-class async model (renderToStream, no Suspense wrapper): async islands settle fully on the server; Errored boundaries contain sync and async errors.
  • Threads an asset manifest into server renders so lazy() boundaries inside islands resolve module URLs, hydrate through serialized asset maps, and get their CSS inlined into island markup (deduped per page) — a live dev resolver in dev, the client build manifest in production (captured before the .vite cleanup for prerendering; persisted next to the server bundle for deployed SSR).
  • check() probe-renders through the stream renderer with per-component caching, rejects foreign framework vnodes explicitly, and is Proxy-safe (fixes the class of issues behind #12354).
  • Drops the solid-devtools option (its 2.0 story is unsettled).
  • All Solid fixtures ported to 2.0; the previously-skipped solid-component suite is re-enabled with new lazy-CSS coverage.
  • astro add solid-js tsconfig preset now writes jsxImportSource: "@solidjs/web" (2.0 moves JSX types to renderer packages).

Server functions (experimental, second commit)

solid({ serverFunctions: true }) compiles "use server" functions in islands to typed RPC calls. The transport endpoint (default /_server) is injected as an Astro route, so requests flow through Astro's middleware pipeline — same auth guards and locals — in dev and production alike; getRequestEvent() inside a function exposes the request, Astro locals, and the API context. Island SSR runs inside a request-event scope, so direct in-process calls work during rendering. Requires an adapter (build fails with a clear error otherwise).

Server components (experimental, third commit)

serverFunctions: { components: true } enables "use server" functions returning components. They SSR inline in islands, are adopted at boot with zero endpoint requests, and later re-evaluations stream over the endpoint and morph in place, preserving client slot state and DOM identity.

Testing

  • e2e: solid-component, solid-circular, solid-recurse, nested-in-solid, plus a new solid-server-functions suite (endpoint round trip, middleware locals through the request event, t=0 server-component adoption + morph) — 26 tests.
  • unit: solid-component (13, includes 3 pre-existing assertion bug fixes), slots-solid, react-and-solid, astro-slots-nested.

Notes for review

  • Versioning: package.json is set to 8.0.0-beta.0 with a major changeset; happy to adjust to whatever release flow you prefer (Solid 2.0 itself is in RC).
  • examples/framework-solid is intentionally untouched: its component code is already 2.0-compatible, and its published-range package.json (create-astro templates install from the registry) should bump at release, adding @solidjs/web (2.0 compiled output imports it).
  • pnpm-workspace.yaml adds @solidjs/*/solid-js to minimumReleaseAgeExclude so freshly published RCs install past the age gate — needs a policy sign-off.
  • No existing Solid 2.0 issues/PRs found in the tracker; this is the first migration work filed.

Made with Cursor

withastro/astro

Changes

  • Fixes the dev server base middleware stripping the configured base from any request whose pathname merely starts with it as a string, ignoring path-segment boundaries.
  • With base: '/s', a request like /src/pages/index.astro was treated as being under the base and rewritten to /rc/pages/index.astro before reaching Vite, so it could never resolve. Any base value that is a non-segment prefix of a served path triggers this (/s vs /src); bases like /docs don't collide and hide the bug.
  • This is the same bug class #17701 fixed for App.removeBase, FetchState, and the i18n domain helper: the dev middleware (evaluateBaseRewrite in vite-plugin-astro-server/base.ts) was the remaining spot still doing a plain startsWith check. It now strips the base only when the pathname is exactly the base or lies under base + '/', matching the policy of stripRequestBase.
  • Only the match condition changes. The rewrite itself (an url.replace that preserves query strings), the exact-base-match behavior (/s/), and the not-found-subpath / not-found / check-public branches are unchanged. stripRequestBase isn't reused directly because it never strips a / base, while this middleware is always installed and must keep passing every request through when base is /.

Reproduction

  1. pnpm create astro@latest (minimal template)

  2. Set base: '/s' in astro.config.mjs

  3. Run astro dev, then:

    curl -i "http://localhost:4321/src/pages/index.astro"

    Before this fix the request 404s because the middleware rewrote it to /rc/pages/index.astro; after it, Vite serves the module. In practice this broke the root-relative requests the dev server itself emits without the base, taking down every page.

Testing

Added unit tests to test/units/dev/base-rewrite.test.ts covering the segment-boundary cases: with base: '/s', /src/... requests are no longer rewritten while /s (exact) and /s/... still are, query strings are preserved, and /docs-archive/page with base: '/docs' falls through to not-found.

Also verified manually against examples/minimal with base: '/s': requests to /src/... now pass through to Vite untouched, pages under /s/ and the base root /s render, public assets under the base resolve, and URLs outside the base still 404.

Docs

No docs needed: this restores the documented behavior of base in dev; no user-facing API changes.

🤖 Generated with Claude Code

withastro/astro

Changes

  • Restores cookies, sessions, cache defaults, asset fallback, and prerendering for Cloudflare custom entrypoints. Fixes #17600.
  • Adds explicit finalize(state, response) handling for astro/fetch;
    • Using the hono middleware this is applied manually.
  • Replaces #17708

Testing

  • Expands custom-entrypoint coverage for Fetch, Hono, errors, assets, sessions, and prerendering.

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@0.41.11

Patch Changes

  • #4167 32a5e29 Thanks @HiDeoo! - Fixes a layout issue and anchor links appearing for hidden headings, e.g. footnote headings, when markdown.headingLinks is enabled or the <AnchorHeading> component is used.

  • #4148 cdfafd8 Thanks @ematipico! - Optimizes sidebar data generation logic to speed up sites with large sidebars

withastro/astro

Changes

  • Skips storing memory-cache responses when Vary contains Cookie or *, rather than dropping those values during cache-key construction.
  • Removes stale entries when background revalidation returns an unsupported variant.

Testing

  • Updates Vary coverage for Cookie, *, and combined header values.
  • Adds regression coverage for stale-while-revalidate entries.

Docs

  • A follow-up docs update is needed to clarify how the memory provider handles these Vary values.
withastro/astro

Changes

This function runs for every single page module and does a few expensive call, we can do them only once and re-use the map for lookups.

On a benchmark where pages are not very expensive, but there's a lot of them, this roughly cut the build time in half.

Testing

Tests should pass

Docs

N/A

withastro/starlight

Description

This PR fixes an issue with anchor links and hidden headings (e.g. footnote headings).

This PR does not use the approach from #4166 to prevent heading links to be added at the Markdown processor level:

  • A user could have some custom CSS or client-side JS to show such headings at some point in time
  • The issue also appears when using the <AnchorHeading> component

Instead, this PR uses CSS to properly hide anchor links for hidden headings.

Remaining tasks

  • Address all TODO(HiDeoo) comments
withastro/astro

Changes

  • Adds a keepAliveTimeout option to @astrojs/node, applied to the standalone server's http.Server before it starts listening.
  • Leaving it unset keeps today's behavior exactly (whatever Node.js defaults to).
export default defineConfig({
  adapter: node({ mode: 'standalone', keepAliveTimeout: 65_000 }),
});

Why

Node.js destroys an idle keep-alive connection after 5 seconds by default (server.keepAliveTimeout), which is shorter than the idle timeout of most reverse proxies and load balancers — an AWS Application Load Balancer, for instance, defaults to 60 seconds (idle_timeout.timeout_seconds). AWS names this exact case in its ALB troubleshooting guide for HTTP 502: the target closed the connection with a TCP RST or FIN while the load balancer still had an outstanding request to it, and the guide tells you to check whether the keep-alive duration of the target is shorter than the load balancer's idle timeout.

The standalone adapter calls http.createServer(listener) and never touches keepAliveTimeout, so every standalone deployment behind a proxy sits on the wrong side of that ordering. The symptom is easy to misread: connections only stay idle long enough to reach the 5 second window when the server is quiet, so the 502 rate goes up as traffic goes down, and a busy deployment can look completely healthy.

There is currently no supported way for a user to configure this:

  • Adapter options are JSON-serialized into a virtual module (vite-plugin-config.ts), so a callback such as configureServer(server) cannot be passed through.
  • In standalone mode the built entry starts the server as a side effect of being imported, so there is no window in which to configure it.
  • What is left is ASTRO_NODE_AUTOSTART=disabled plus a custom launcher that imports startServer() and mutates server.server.keepAliveTimeout — depending on the shape of the adapter's return value, and assigning after listen().

Prior art: Next.js exposes this as a first-class flag for the same reason (next start --keepAliveTimeout <ms>, assigned to server.keepAliveTimeout in its start-server). Node.js itself has since raised the default to 65 seconds on main (nodejs/node#62782) — but that is semver-major and unreleased, and every current release line still ships 5000.

I kept the scope to the single option behind the 502s. Happy to extend it to headersTimeout / requestTimeout if you'd prefer a broader surface.

Validation

The option crosses a JSON boundary on its way to the built entrypoint (vite-plugin-config.ts serializes adapter options into a virtual module), and JSON.stringify maps Infinity and NaN to null. Node then assigns anything to keepAliveTimeout without complaint, and gates on > 0 — so both values would have disabled the timeout, the exact opposite of the option's purpose, with no warning. Measured on Node v24.16.0:

value Keep-Alive response header
unset timeout=5
65000 timeout=65
0 absent — timeout disabled (documented)
Infinity / NaN → serialized as null absent — timeout disabled (now rejected)
-1 timeout=-1 (now rejected)

So the adapter rejects non-finite and negative values at build time, next to the existing mode check. This follows @astrojs/vercel, which validates maxDuration the same way.

Open question

createServer(listener, host, port, keepAliveTimeout?) adds a fourth positional parameter. It keeps preview.ts compiling unchanged, but the obvious follow-ups to this issue (headersTimeout, requestTimeout) would each add another slot. Happy to switch to an options bag — createServer(listener, host, port, { keepAliveTimeout }) — if you'd prefer; I left it positional to keep the diff small.

astro preview deliberately does not apply the option, and the JSDoc says so. Preview isn't sitting behind a load balancer, and threading it through would mean reading the built config out of the imported server module. Say the word if you'd rather have it consistent.

Testing

packages/integrations/node/test/keep-alive-timeout.test.ts builds a fixture in standalone mode and asserts what reaches the wire:

✔ advertises the configured timeout to clients   →  Keep-Alive: timeout=65

It is asserted on the response header rather than on server.keepAliveTimeout so it covers the whole path — adapter option → virtual config module → built entrypoint → http.Server — and because the header is what a reverse proxy actually reads. I verified it fails for the right reason: removing the argument at standalone.ts:32 turns it red with timeout=5.

packages/integrations/node/test/units/keep-alive-timeout.test.ts covers the unit level:

✔ keeps the Node.js default when the option is not set
✔ applies the configured value to the underlying server
✔ rejects -1
✔ rejects Infinity
✔ rejects NaN
✔ allows 0, which disables the timeout

The first case is asserted against a freshly created http.createServer() rather than a hardcoded 5000, so it survives Node changing its own default.

The rest of the package's suite passes (pnpm --filter @astrojs/node test); the only failure in my run was preview-host.test.ts losing port 4321 to an unrelated process on my machine, which fails identically on a clean checkout.

Docs

This adds a user-facing adapter option, so it needs an entry alongside the other @astrojs/node options. I'm glad to open the PR on withastro/docs — just say the word.


Last fetched:  | Scheduled refresh: Every Saturday

See Customizing GitHub Activity Pages to configure your own

Inspired by prs.atinux.com