Skip to content

AstroEco is Contributing…

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

withastro/astro

Closes #17682

Changes

  • Astro.site is now correctly set when rendering components via the Container API. Previously, astroConfig.site passed to AstroContainer.create() was accepted in the type signature but never read — the value was never forwarded to the internal createManifest() call or written onto the SSRManifest, so Astro.site was always undefined.
  • Wires astroConfig.site through AstroContainer.create() → constructor → createManifest(), and adds 'site' to the AstroContainerManifest Pick type so a pre-built manifest can also carry the value.

Testing

  • Added 'Astro.site reflects astroConfig.site' — verifies that Astro.site matches the URL set in astroConfig.site.
  • Added 'Astro.site is undefined when astroConfig.site is not set' — verifies the default behavior remains unchanged.

Docs

No docs update needed — AstroContainer.create() already documents the astroConfig.site option; this fix makes it work as documented.

withastro/astro

Changes

  • Base stripping now only removes a configured base when the pathname is the base itself or continues at a path-segment boundary. With base: '/docs', a request like /docs-archive/page is treated as outside the base instead of being rewritten to /page. This keeps route matching and context.url.pathname in agreement.
  • Consolidates the three duplicated base-stripping implementations (BaseApp.removeBase, FetchState.#computePathname, and the i18n domain helper) into a single shared stripRequestBase helper in @astrojs/internal-helpers, matching the boundary logic the router already uses in stripBase.

Testing

  • Adds base-prefix-boundary.test.ts covering single-character extensions of the base prefix (/docsX/..., /docs2/..., /docs-/...), asserting they do not resolve to a route under the base.

Docs

  • No docs update needed; this corrects internal pathname handling with no public API change.
withastro/astro

Changes

  • computePreferredLocaleList compared object-form locale codes exactly, while every other locale comparison in i18n/utils.ts normalizes both sides first. That one raw comparison is now normalized like the rest.
  • The result is a self-contradiction on a single request: sortAndFilterLocales filters on normalized codes, so a locale configured as { path: 'english', codes: ['en-us'] } passes the filter when a browser sends Accept-Language: en-US, and is then silently dropped by the exact comparison. Astro.preferredLocale returns 'en-us' while Astro.preferredLocaleList returns [].
  • The two branches of the same loop disagreed: the string-locale branch already normalized (so locales: ['en-us'] works today), the object-form branch did not. Underscore codes such as en_US were affected the same way, since normalizeTheLocale maps _ to -.
  • The configured casing is still what gets returned — the comparison is normalized, but the original code is what's pushed, matching how getLocaleByPath compares normalized and returns the configured value.

Production change is one line:

-  if (code === browserLocale.locale) {
+  if (normalizeTheLocale(code) === normalizeTheLocale(browserLocale.locale)) {

One thing I deliberately left out: computePreferredLocale breaks out of its codes loop on first match (#16600), and this list function doesn't. That's a visible behaviour difference between the pair, but it's a separate change with its own semantics, so I'd rather ask than bundle it — happy to follow up if you want them aligned.

Testing

Added to the existing packages/astro/test/units/i18n/i18n-utils.test.ts, in the style of the surrounding cases:

  • object-form codes matched case-insensitively (codes: ['en-us'] vs en-US) — failed before this change, returning []
  • object-form codes with an underscore separator (codes: ['en_US']) — failed before, returning []
  • object-form matches sorted by quality value — failed before
  • the configured casing is returned for an exact-case object-form code
  • string locales still match case-insensitively
  • a code is returned rather than the path for a multi-code entry

The last three pass both before and after, so they guard against a regression rather than describing the fix.

Locally: 32/32 in that file, and the full i18n unit suite (396 tests), tsc -b, format:ci, biome and eslint all pass. One caveat — astro-scripts test fails on my machine with ERR_UNKNOWN_FILE_EXTENSION for .ts on Node 22.17.1 (the repo's .nvmrc is 24.14.0); it fails identically on files I didn't touch, so I ran the units via the equivalent node --experimental-strip-types --test. CI on Node 24 will run them through the wrapper.

Docs

No docs change needed. Astro.preferredLocaleList is already documented as returning the list of matching locales; this makes the object-form config shape behave as documented, rather than changing any documented behaviour.

withastro/astro

Changes

  • The dep-scan plugins for .astro files rewrite top-level return to throw so esbuild/Rolldown accept the frontmatter as an ES module. They found those return keywords with a single regex whose skip group covered strings, template literals and comments, but not regex literals. A quote inside a regex literal, as in value.replace(/"/g, """), was then read as an opening string delimiter, so quote pairing stayed off by one for the rest of the frontmatter and every later top-level return was left alone. Vite reported Failed to run dependency scan plus one Top-level return cannot be used inside an ECMAScript module per surviving return, and only on a cold node_modules/.vite, which made it look intermittent.
  • replaceTopLevelReturns now scans the frontmatter character by character instead. It skips strings, template literals, line and block comments, and regex literals, and only rewrites a return found in code. A / is treated as the start of a regex literal only when the preceding token allows an expression there, so division is left alone.
  • Moved the helper to src/utils/frontmatter.ts, since the esbuild and Rolldown plugins each carried their own copy of the same tokenizer and the same bug.

Closes #17697

Testing

Added test/frontmatter-returns.test.ts covering the reported case along with division, character classes, member access, strings and comments. The regex-literal case fails on main and passes here. The existing top-level-return integration test still passes.

Docs

No docs needed, this only fixes the dependency scan for frontmatter that was already valid.

withastro/astro

Changes

  • Shallow-clones langAlias before passing it to Shiki's createHighlighter() in packages/internal-helpers/src/shiki.ts. Shiki's Registry.loadLanguage() writes built-in language aliases (e.g. js, cjs, mjs for JavaScript) directly into the langAlias object it receives. Because Astro passed the same object reference from the resolved config, those aliases leaked back into config.markdown.shikiConfig.langAlias. Since computeConfigHash() runs after the Vite build, the hash then reflected which languages appeared in code blocks rather than the user's actual config, causing the incremental build cache to be invalidated on every content change.

Closes #17693

Testing

  • Adds packages/internal-helpers/test/shiki.test.ts with a test that highlights a JavaScript code block and asserts the original langAlias object passed to createShikiHighlighter is not mutated.

Docs

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

Changes

  • Require a directory boundary when deciding whether an MDX file is under src/pages.
  • Prevent sibling directories such as src/pages-old and src/pages2 from receiving page-only automatic charset injection in both the unified and Sätteri processors.

Testing

  • Added focused coverage for real pages and similarly prefixed sibling directories across both processor paths.
  • Ran: pnpm -C packages/integrations/mdx test
  • Ran: pnpm -C packages/integrations/mdx build
  • Ran: pnpm lint:ai

Docs

No docs changes. This corrects internal page classification without changing the public API.

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

Patch Changes

  • #17636 51723b1 Thanks @matthewp! - Fixes the dev server sometimes matching against stale routes after pages were added, removed, or renamed, requiring a dev server restart to pick up the change

  • #17636 51723b1 Thanks @matthewp! - Fixes the composable request helpers (astro/fetch) throwing an error when used on a request that had been rewritten with Astro.rewrite() or next()

  • #17636 51723b1 Thanks @matthewp! - Refactors Astro's internal server-side request handling. This is an internal change: all documented public APIs, including App and NodeApp, keep their existing signatures and behavior.

    The undocumented internal app.pipeline property and the AppPipeline export from astro/app have been removed. Adapters that used app.pipeline.getLogger() to wait for the configured log destination can call the new app.getLogger() instead.

    As a result of this refactor, new FetchState(request) from astro/fetch now works anywhere inside a built Astro server — including custom src/fetch.ts entrypoints — without the request needing to first pass through app.render(). Previously this threw an error, breaking patterns like the Cloudflare adapter's advanced custom-worker setup.

  • #17572 2066f39 Thanks @matthewp! - Fixes a crash when a request arrives with a malformed port in the Host header (for example example.com:65536 or example.com:8080:8080). Such a host made the constructed request URL invalid, and the fallback that was meant to recover reused the same invalid host and threw again. The request URL now degrades to a host the server controls when the incoming host cannot be parsed, so the request is handled instead of erroring.

  • #17685 9f15609 Thanks @astrobot-houston! - Fixes a dev server error where an SSR full reload triggered by a third-party Vite plugin (such as @tailwindcss/vite) could fail with Failed to load url astro:server-app.js

  • #17636 51723b1 Thanks @matthewp! - Improves error handling for custom log destinations. When the configured logger fails to load, Astro now reports the error and continues with the default console logger instead of failing the first request.

  • #17631 cf29bec Thanks @matthewp! - Fixes getCollection() and getEntry() throwing DataCloneError when a collection schema transform returns a Temporal.PlainDate or other class instance.

  • Updated dependencies [8c193f6]:

    • @astrojs/internal-helpers@0.10.3
    • @astrojs/markdown-remark@7.2.3
    • @astrojs/markdown-satteri@0.3.6

@astrojs/cloudflare@14.2.2

Patch Changes

  • Updated dependencies [8c193f6]:
    • @astrojs/internal-helpers@0.10.3
    • @astrojs/underscore-redirects@1.0.4

@astrojs/markdoc@2.0.7

Patch Changes

  • Updated dependencies [8c193f6]:
    • @astrojs/internal-helpers@0.10.3

@astrojs/mdx@7.0.6

Patch Changes

  • Updated dependencies [8c193f6]:
    • @astrojs/internal-helpers@0.10.3
    • @astrojs/markdown-remark@7.2.3

@astrojs/netlify@8.2.2

Patch Changes

  • Updated dependencies [8c193f6]:
    • @astrojs/internal-helpers@0.10.3
    • @astrojs/underscore-redirects@1.0.4

@astrojs/node@11.1.3

Patch Changes

  • #17636 51723b1 Thanks @matthewp! - Updates the adapter to wait for the configured log destination through Astro's new app.getLogger() API. This release requires Astro 7.2.1 or later.

  • Updated dependencies [8c193f6]:

    • @astrojs/internal-helpers@0.10.3

@astrojs/preact@6.0.3

Patch Changes

  • Updated dependencies [8c193f6]:
    • @astrojs/internal-helpers@0.10.3

@astrojs/react@6.0.3

Patch Changes

  • Updated dependencies [8c193f6]:
    • @astrojs/internal-helpers@0.10.3

@astrojs/vercel@11.0.6

Patch Changes

  • #17680 ce9f1da Thanks @astrobot-houston! - Fixes server islands returning 404 responses in Vercel deployments using output: "static"

  • Updated dependencies [8c193f6]:

    • @astrojs/internal-helpers@0.10.3

@astrojs/internal-helpers@0.10.3

Patch Changes

  • #17696 8c193f6 Thanks @astrobot-houston! - Fixes incremental build cache invalidation caused by Shiki mutating the langAlias config object when loading languages

@astrojs/ts-plugin@1.10.11

Patch Changes

  • #17668 bef9db5 Thanks @lazerg! - Fixes Astro's ambient types leaking into unrelated TypeScript projects. In a monorepo with hoisted node_modules, the plugin found the shared astro install from any project and injected env.d.ts and astro-jsx.d.ts into it, which pulled @types/node into projects that never asked for it. The plugin now only injects those types when the project actually depends on astro or has an astro.config.* file.

@astrojs/markdown-remark@7.2.3

Patch Changes

  • Updated dependencies [8c193f6]:
    • @astrojs/internal-helpers@0.10.3

@astrojs/markdown-satteri@0.3.6

Patch Changes

  • Updated dependencies [8c193f6]:
    • @astrojs/internal-helpers@0.10.3
withastro/astro

Changes

  • Require a directory boundary when checking whether a file is inside src/pages.
  • Prevent sibling directories such as src/pages-old and src/pages2 from receiving page-only handling.
  • Add regression coverage for pages, nested pages, endpoints, and sibling-directory false positives.

Testing

  • pnpm --filter astro exec astro-scripts test "test/units/util/*.test.ts" --strip-types (41 passed)
  • pnpm --filter astro build
  • pnpm lint:ai

Docs

No docs changes. This fixes internal path classification without changing the public API.

withastro/starlight

Description

We noticed while working on #4121 that our current size limit checks won’t reflect changes to the page sidebars because we test against the start template’s landing page.

This PR adds one of the example guide pages in the starter template to our size limit checks so we also track changes in size to the more standard documentation page layouts as suggested by @trueberryless.

While there I’ve also slightly reduced the size limit for the HTML pages to be a bit closer to the current output sizes and trigger a failure earlier if those increase.

withastro/astro

Changes

  • Updates the dependency diffing action to 1.7.1. Primarily to fix a bug for PRs from branches that were behind main where they would report changes that were inaccurate as you can see in #17688 (comment) for example. The issue should be fixed by e18e/action-dependency-diff#177 which is included in the 1.7.1 release.
  • Versions 1.6 and 1.7 updated internal dependencies, improved trusted publishing checks, and fixed duplicate scanning (a feature we don’t use) so otherwise should be a safe update.

Testing

Tested upstream, 🤞

Docs

n/a

withastro/starlight

Description

This PR refactors Starlight’s mobile menu toggle to use the Popover API instead of the current JS-powered <button> with aria-expanded pattern.

  • A small amount of JS remains but it’s now only a progressive enhancement for focus trapping. The core functionality of opening/closing the mobile menu and locking body scroll works even if JS breaks for whatever reason.

  • The JS is now in the PageFrame component as a custom element that attaches to the popover body instead of the menu button. It reacts to global events (matchMedia()) and the popover’s "toggle" event so it makes more sense to live alongside the element it attaches to. This also means the menu button component is now easier to override as any button with the correct popovertarget attribute will work.

  • CSS switches from hooking into [aria-expanded] to using the :popover-open pseudo class.

  • We no longer add a data-mobile-menu-expanded attribute to <body> when the menu is open and use body:has(#starlight__sidebar:popover-open) in CSS instead, which works without the JS-managed attribute.

  • While I was updating the button, I switched from aria-label to a visually hidden span of text, which is generally the recommended pattern for labels where possible.

Browser compatibility

Use of the Popover API requires slightly bumping our minimum supported browsers:

  • Firefox 121 (December 2023) => 125 (April 2024)
  • Chromium 111 (March 2023) => 116 (August 2023)
  • Safari 16.4 (March 2023) => 17.0 (September 2023) (for both macOS and iOS)

According to browsersl.ist, comparing before and after shows a drop in global coverage of 1 percentage point. The newest minimum supported browser will be Firefox 125, released 29 months ago.

N.B. that umbrella compatibility measures such as wf-popover show support only arriving later in some browsers. However, IIUC this is due to subfeatures that we are not relying on not being ready yet and the features we require are safe even in these older browser versions:

Safari on iOS had a long-standing bug in versions 17.0–18.2 which prevented tap-away clicks closing popovers as is expected. However, we do not require that behaviour in Starlight mobile menus as there is nowhere a user expects to click away to and the main control is the button.

Accessibility

The button with popovertarget and popover combo has built-in accessible roles equivalent to our previous aria-expanded pattern. I tested with VoiceOver in Firefox, Chrome, and Safari on macOS 26.5.1 and found the behaviour to be a very slight improvement in my opinion compared to the current announcements although both are basically equivalent. Would be great to test in more scenarios!

Demo

Here’s a small screen capture of the live Starlight docs and compared to this branch, showing the menu working without JavaScript after these changes:

popover.mp4
withastro/astro

Changes

  • No code changes, just tests

Testing

  • Uses a local mock rather than going to GitHub, to prevent flakiness caused by GitHub not responding in time.

Docs

N/A, test fix

withastro/astro

Changes

With isr enabled, middlewareMode: 'edge' is silently inert.

The adapter builds and deploys _middleware.func, but every on-demand route's
dest is set to the ISR function, so nothing ever routes to it. Middleware
still runs — but only inside the ISR function, which Vercel skips entirely on a
cache hit. The observable result is middleware that works on a cold entry and
then stops running once the entry is warm, with no error and no log.

This is the ordering problem: middleware has to run before the cache is
consulted, not behind it.

  • Route on-demand pages at _middleware rather than _isr when a middleware
    entry point exists, so the edge function is actually reached.
  • Collect those route patterns and inline them into the generated middleware,
    so next() forwards to /_isr?x_astro_path=… for a route the ISR function
    backs, and /_render for one it doesn't. Cached responses are still served
    from cache; only the entry point moves.
  • _image and _server-islands keep going straight to _render, unchanged.
  • Routes matched by isr.exclude still resolve to _render through next().
  • Prerendered pages are untouched: no route entry, served as static files.

x_astro_path carries the original pathname, so the ISR cache key stays the
request path rather than collapsing to /_isr; x_astro_path_token is the
build token added in #17370. Without isr, or without a middleware file,
nothing about the output changes.

Testing

New: packages/integrations/vercel/test/isr-edge-middleware.test.ts — 16 tests
over two fixtures.

isr-with-edge-middleware asserts against the real build output, since the bug
is entirely a property of the emitted config.json:

  • pages, dynamic routes, endpoints and the 404 resolve to _middleware
  • _image and _server-islands still resolve to _render
  • prerendered pages get no route entry and ship as static HTML
  • configured redirects still resolve ahead of the middleware
  • the ISR prerender config and its expiration survive

and then imports the generated middleware.mjs with fetch stubbed, to check
where next() actually forwards:

  • a cached route → /_isr, with x_astro_path and a token
  • a dynamic route → /_isr?x_astro_path=/cached/42, the real path, because that
    path is the cache key
  • an isr.exclude route → /_render
  • a query string does not leak into x_astro_path
  • the response still reaches the middleware, headers intact

isr-edge-no-middleware covers middlewareMode: 'edge' with no middleware file
present: routes go straight to _isr as before and no middleware function is
built.

Verified as a regression guard by reverting src/ and re-running: 4 of the 16
fail, all of them on dest.

Also ran the full suite — core unit (3307), core integration (1240), all 18
integration packages including @astrojs/vercel (58), language-tools (98), and
e2e in chrome and firefox. No new failures; the pre-existing ones
(test/fonts.test.ts cancellations, a handful of e2e) don't touch the adapter.

One caveat worth stating plainly: this is verified against build output and the
generated module, not against a live Vercel deployment. Confirmation on a real
project would be welcome.

withastro/astro

Changes

  • Removed the .flue folder from the repository. We don't use it anymore.
  • Adds evals.json files and an harness that we can test when want. The harness isn't wired to our testing infra because it spends tokens.

Testing

Tested locally and all assertions pass

Docs

withastro/astro

Closes #17684

Changes

  • Prefixes ASTRO_DEV_SERVER_APP_ID with virtual: (changing it from astro:server-app to virtual:astro:server-app), matching the convention already used by the sibling virtual:astro:app module in the same file. Vite's ModuleGraph._resolveUrl() skips URL normalization for IDs that start with virtual: — without this prefix, Vite appended .js to the stored URL, so full-reload attempts via runner.import("astro:server-app.js") failed to match Astro's resolveId filter.
  • Fixes the error Failed to load url astro:server-app.js that appeared when a third-party Vite plugin (e.g. @tailwindcss/vite) triggered an SSR full reload on a non-page file.

Testing

  • No automated test added — reproducing this requires a live dev server with @tailwindcss/vite triggering an SSR full reload, which is outside the current unit/integration test infrastructure.
  • Fix was manually verified against the minimal reproduction from the issue; editing src/content/test.md now produces [vite] program reload without the astro:server-app.js error, and the server continues serving successfully. Confirmed by the issue reporter (@mavam).

Docs

No docs update needed — this is an internal virtual module ID fix with no user-facing API change.

withastro/astro

Closes #17679

Changes

  • The glob() and file() content loaders now respect prerenderConflictBehavior when a duplicate entry ID is detected. Previously, both loaders always emitted a hardcoded logger.warn() regardless of the config setting.
  • "error" throws DuplicateContentEntrySlugError during content sync; "ignore" suppresses the warning entirely; "warn" (the default) preserves the existing behavior.

Testing

  • Added tests to file-loader.test.ts covering "error" (throws), "warn" (logs), and "ignore" (silent) modes for duplicate IDs in the file() loader.
  • Added tests to glob-loader.test.ts covering the same three modes for duplicate IDs in the glob() loader.

Docs

No docs update needed — prerenderConflictBehavior is already documented; this extends its existing behavior to a new context without changing its API or semantics.

withastro/astro

Changes

  • Fixes server:defer components returning 404 responses on Vercel when using output: "static".
  • Preserves a dedicated server build directory and packages it through the existing Vercel serverless and middleware path only when Astro runs the SSR build for discovered server islands. Fully static sites continue to emit no serverless function, and server build files are not published as static assets.

Closes #17678

Testing

  • Adds a static server-islands fixture that verifies _render.func, its route configuration, and rendering through the packaged function.
  • Verifies the server entry is excluded from static assets and a fully static fixture does not create a serverless function.

Docs

  • No docs update needed because this restores the documented server:defer behavior for static output.
withastro/starlight

Description

  • Replace "Markdown plugins" links

We have recently updated the Markdown guide in Astro Docs (withastro/docs#14297) and the section was renamed "Markdown processor plugins". There was two occurrences in Starlight docs.

  • Replace an example using Astro DB

Astro DB has been removed in Astro 7 (withastro/docs#13985) and the Astro DB guide now redirects to the integration page with an aside saying "Removed". I think it is better to use an example that is still relevant.

withastro/astro

Changes

Remote images whose URL has no file extension fail with 400 Unsupported format: null under the cloudflare-binding image service (the adapter default).

Astro's baseService.validateOptions only sets options.format when it can infer a source format from the URL, and since #16665 deliberately leaves it undefined otherwise so the image service resolves the format from the source bytes instead — which is what the Sharp service does at sharp.ts#L155-L156. With format undefined, no f parameter is emitted onto the /_image URL, and transformStream treated a missing f as a client error.

Extensionless remote images are the common case here — GitHub avatars like https://avatars.githubusercontent.com/u/192622539?s=200&v=4 have no extension to infer from, which is why every theme author avatar on astro.build is currently broken:

$ curl -sD - -o /dev/null 'https://astro.build/_image?href=https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F192622539%3Fs%3D200%26v%3D4&w=300&h=300'
HTTP/1.1 400 Bad Request
Unsupported format: null

# ...the same URL with an explicit format works
$ curl -so /dev/null -w '%{http_code} %{content_type}\n' '...&w=300&h=300&f=webp'
200 image/webp
  • transformStream now takes the source's media type and uses it when f is absent: SVG sources pass through unchanged, everything else is encoded as WebP. This mirrors core's resolveDefaultOutputFormat, which webp-encodes every non-SVG source.
  • Requests that explicitly ask for a format the IMAGES binding cannot produce (e.g. f=tiff) still return 400.
  • This also fixes SVG images, which core requests as f=svg and which previously failed the same way — the IMAGES binding cannot emit SVG, so those bytes are served as-is.

Fixes withastro/astro.build#2610

Testing

Added four cases to test/binding-image-service.test.ts (build + preview, exercising the IMAGES binding for real) covering remote/local sources with no f, and SVG passthrough. A local HTTP server serves images from extensionless paths to reproduce the GitHub avatar shape. Also added the no-f case to test/dev-image-endpoint.test.ts.

All four new binding tests fail on main with 400 and pass with this change; the seven pre-existing tests in that file are unaffected.

Note

Two things worth flagging for maintainers, both pre-existing and left untouched here:

  • caches.default is persisted under test/fixtures/binding-image-service/.wrangler/state/v3/cache, so a previously-cached 200 can mask a genuine failure for any stable /_image URL. I had to clear that directory to see the new tests fail on main.
  • createPreviewServer returns the requested port rather than the bound one (preview.ts#L97), so the whole suite 404s if port 4321 is already taken.

Docs

No docs changes needed. This restores the documented behavior of image.remotePatterns/image.domains for remote images; there's no API or configuration surface change.

withastro/astro

Changes

A deploy that changes rendered output without changing content leaves the validator of a cached page untouched: revalidation resolves to 304, and clients keep HTML that references hashed assets from the previous build (/_astro/<hash>.css is gone after the next build). The cause: every validator the Cloudflare provider can send comes from the caller — cache.set(), CacheHint, routeRules — and describes the content, never the build.

With the CF_VERSION_METADATA binding configured, the provider now reads the Worker version id and

  • adds an astro-version:<id> cache tag, which enables version-specific purging, and
  • folds the id into a weak ETag (W/"<id>:<lastModified-ms>") on responses that already send Last-Modified and do not supply their own etag.

Without the binding, every header is exactly what it was before.

Testing

test/cache-provider.test.ts gains four cases against the preview server:

  • the version tag appears on a cacheable response,
  • the weak ETag carries the same id and the lastModified timestamp,
  • an explicitly supplied etag survives untouched, and
  • a cacheable response without a validator still gets none.

Two fixture pages (/lastmod, /explicit-etag) and the version_metadata binding in the fixture's wrangler.jsonc support them. The prerenderEnvironment: 'node' build path has no automated case because it would need a second fixture build per run; a manual fixture build with a prerendered route and the provider enabled passes.

Docs

The adapter README does not cover route caching, so there is no section to update here. The behavior is worth a paragraph in the Cloudflare adapter guide on docs.astro.build, and I am happy to open that PR.

Details

Responses that carry no validator keep none. Minting W/"<id>" for them would be the wider fix, but it hands a validator to pages whose content changes between deploys, and a cache may then answer 304 until the next deploy. The narrow rule only makes an existing validator deploy-sensitive, which cannot regress a route.

The version id is read through a dynamic import('cloudflare:workers') resolved once at module load, for the same reason invalidate() imports lazily (#16335): a static import breaks the prerenderEnvironment: 'node' build.

onRequest() would be the other place to reach the runtime, but a provider that defines it counts as a runtime cache, and CacheHandler then strips Cache-Tag from the response.

This picks up #17038, which targeted the feat/cdn-cache-providers branch and was closed when that branch was deleted. The version tag that it built on never reached main, so this change adds it.

withastro/astro

Changes

  • Moves satteri from devDependencies to dependencies in @astrojs/mdx. The package has unconditional static from 'satteri' imports across four files under src/satteri/, making it a runtime requirement. Listing it only as a devDependency meant pnpm never linked it into @astrojs/mdx's isolated node_modules, so astro build failed with ERR_MODULE_NOT_FOUND in strict pnpm setups (e.g. hoist: false, Vercel monorepo deploys). The import resolved accidentally in non-strict layouts only because pnpm hoisted satteri as a transitive dep of astro → @astrojs/markdown-satteri.

Testing

  • No new tests added. The bug is a packaging/manifest issue that cannot be covered by unit tests run inside the monorepo (which hoists all deps). The fix was confirmed in an isolated hoist: false pnpm project by @danielmlr, and by the packageExtensions counter-check in their report.

Docs

  • No docs update needed; this is an internal dependency declaration fix with no user-visible API change.

Closes #17371

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

Patch Changes

  • #17611 9bc3207 Thanks @thelazylamaGit! - Fixes component styles rendered from content entries remaining stale until a second save when an adapter uses Astro's fallback development environment

  • #17634 2267eee Thanks @astrobot-houston! - Fixes incremental builds dropping optimized images for cached pages when using a collectStaticImages prerenderer (e.g. @astrojs/cloudflare with compile-time image optimization)

  • #17650 4cdf128 Thanks @astrobot-houston! - Fixes intermittent ImageNotFound errors during build on projects with many images. The build now limits concurrent image file reads to avoid exhausting OS file descriptors (EMFILE) and retries transient I/O errors with backoff. Non-transient errors are no longer silently swallowed.

  • #17683 2378221 Thanks @astrobot-houston! - Fixes prerenderConflictBehavior not applying to content collection duplicate ID warnings in the glob() and file() loaders. Setting it to 'error' now throws during content sync, and 'ignore' suppresses the warning.

  • #17659 90c6ea4 Thanks @astrobot-houston! - Fixes the Fonts API breaking experimental.incrementalBuild caching by embedding a build-local, randomly-assigned server port in generated code used for the dependency hash

  • #17630 fd1d9ee Thanks @ericclemmons! - Fixes incremental builds becoming prohibitively slow for sites with many pages or content entries that share a large dependency graph.

  • #17690 93beecc Thanks @NgoQuocViet2001! - Prevents files in directories whose names start with pages from being treated as page routes

  • #17671 09f0dc7 Thanks @tarikermis! - Fixes astro dev refusing to start after a Docker container restart when an unrelated process reuses the PID from a persisted lock file. Astro now checks the process command across platforms, so stale lock files are cleaned up and --force does not signal the unrelated process.

@astrojs/node@11.1.2

Patch Changes

  • #17400 c1cf110 Thanks @tianrking! - Return a 404 instead of a 500 for unknown parameters that match a prerendered dynamic endpoint.
withastro/astro

Closes #17656

Changes

  • Checks the command for the PID stored in the dev or preview lock file instead of assuming that any live process with the same PID is Astro.
  • Uses an exact PID lookup on Linux, macOS, and Windows. This cleans up stale locks after container restarts and prevents --force from signalling an unrelated process when the command can be verified.

Testing

  • Added Unix and Windows command matching cases, including the Windows .cmd shim and unrelated commands.
  • Added coverage for matching processes, reused PIDs, unavailable process lookups, and stale lock cleanup through the real process lookup.

Docs

No docs update needed because the CLI workflow and user-facing messages stay the same.

withastro/astro

Changes

Testing

Docs

withastro/astro

Changes

This PR adds a configuration file that adds some configuration for the upcoming automated reviews.

Testing

Tested in a different repository

Docs

N/A

withastro/astro

Changes

  • The TS plugin is registered globally, so it runs for every project in a workspace. Since #17269 it calls addAstroTypes() unconditionally, and findAstroPackageDirectory() just walks up the tree looking for node_modules/astro/. With a hoisted node_modules (pnpm nodeLinker: hoisted, npm, classic Yarn) that lookup succeeds from any sibling project, so env.d.ts and astro-jsx.d.ts were injected into projects that have nothing to do with Astro. Those files transitively pull in @types/node, and its global shims then win over lib.dom.d.ts, so "Go to Definition" on Blob, fetch or URL in a browser-only app lands in @types/node.
  • Adds an isAstroProject() guard in front of the injection: the nearest package.json has to list astro, or there has to be an astro.config.* next to it. The language server already does this through getAstroInstall(), the plugin was the one place missing it.

Closes #17667

Testing

  • Three cases in packages/language-tools/ts-plugin/test/units/astro-types.test.mts over a fixture monorepo with a hoisted node_modules: a React project that only reaches astro through the shared root is skipped, a project that depends on astro is detected, and so is one with an astro.config.mjs but no dependency.

Docs

  • No docs change, this only narrows when the plugin injects its own ambient types.
withastro/astro

Changes

Error stack traces printed to the terminal lose every other frame.

formatErrorStackTrace filters the stack with a module-level global regex:

const STACK_LINE_REGEXP = /^\s+at /g;
...
const stackLines = (err.stack || '').split('\n').filter((line) => STACK_LINE_REGEXP.test(line));

RegExp.prototype.test on a /g regex advances lastIndex on every match. The next call starts searching mid-string, where ^ cannot match, so it returns false — and then resets lastIndex to 0, which lets the frame after that match again. The result is that exactly half the frames are dropped:

const STACK_LINE_REGEXP = /^\s+at /g;
const stack = [
  'Error: boom',
  '    at first (/app/src/pages/index.astro:3:1)',
  '    at second (/app/src/lib/a.ts:10:5)',
  '    at third (/app/src/lib/b.ts:2:2)',
  '    at fourth (/app/src/lib/c.ts:4:4)',
];
stack.filter((l) => STACK_LINE_REGEXP.test(l));
// → [ 'at first …', 'at third …' ]     // second and fourth are gone
stack.filter((l) => /^\s+at /.test(l));
// → [ 'at first …', 'at second …', 'at third …', 'at fourth …' ]

IRRELEVANT_STACK_REGEXP on the next line has the same shape — it is also only ever asked whether one line matches, and it feeds findIndex, which decides where the stack gets truncated.

Neither regex needs the g flag, so this drops it from both. Nothing else changes: both are still single-line predicates.

Testing

Added packages/astro/test/units/errors/format-error-message.test.js:

  • every frame of a four-frame stack survives formatErrorMessage()
  • formatting the same error twice produces the same string (the leaked lastIndex is per-regex module state, so repeated calls were order-dependent)

The first test fails on main with missing stack frame: at second (/app/src/lib/a.ts:10:5) and passes with this change.

Docs

Not applicable — no API or behaviour that is documented changes; printed stacks just stop losing lines.

withastro/astro

Changes

  • When 'unsafe-inline' is present in script-src, style-src, script-src-elem, or style-src-elem, Astro no longer emits auto-generated hashes on that directive. Per the CSP spec, browsers silently ignore 'unsafe-inline' when a hash is present in the same directive — so users who need 'unsafe-inline' (e.g. to allow third-party scripts that inject dynamic inline styles) were getting a broken policy with no workaround.
  • Adds a hasUnsafeInline() helper in csp.ts checked at three points: the script-src baseline, the style-src baseline, and inside renderSpecificDirective() for -elem variants. The previous fix (#14798) had only addressed style-src-attr, which happened to never emit hashes anyway.
  • Updates the security.csp config docs to describe this suppression behavior.

Testing

  • 7 new unit tests in packages/astro/test/units/csp/render-csp.test.ts covering: hash suppression on style-src, script-src, style-src-elem, script-src-elem, render-time extra hashes with unsafe-inline, and a guard confirming hashes are not suppressed when 'unsafe-inline' is scoped only to -attr (which doesn't affect the baseline directive).

Docs

  • Inline JSDoc in packages/astro/src/types/public/config.ts updated to document the hash-suppression behavior when 'unsafe-inline' is used.

Closes #17663

withastro/astro

Changes

  • The virtual:astro:assets/fonts/runtime/font-file-url-resolver virtual module was embedding the font HTTP server's AddressInfo (including an OS-assigned ephemeral port) as a JSON literal directly in its compiled source text. Because the port changes on every build, any route that transitively imports <Font /> got a different dependencyHash on every run, silently defeating experimental.incrementalBuild for any project using the Fonts API from a shared layout.
  • Fixes this by assigning the server address to a named variable (__ASTRO_FONTS_SERVER_ADDRESS__) in the generated module, then stripping that variable declaration in resolveAssetPlaceholders() (in plugin-incremental.ts) before the module code is hashed. The stable variable reference remains in the hashed code; only the volatile declaration (with the changing port) is removed. This follows the same pattern already used to normalize asset emit handles before hashing.

Closes #17626

Testing

  • Added packages/astro/test/incremental-build-fonts.test.ts: runs two consecutive builds of a fixture that uses the Fonts API with experimental.incrementalBuild: true and asserts the route's dependencyHash is identical across both builds.
  • Added the corresponding fixture (packages/astro/test/fixtures/incremental-build-fonts/) with a dynamic route using <Font /> and a stable cacheKey from getStaticPaths().

Docs

No docs update needed — this is a bug fix for two experimental features; no user-facing API or behavior contract changed.

withastro/astro

Changes

  • Fixes a MaxListenersExceededWarning that fires after ~11 keep-alive requests when staticHeaders: true and security.csp are both active on the Node adapter. serve-static.ts calls createRequestFromNodeRequest() solely for route matching via app.match(), but that function wires an AbortController close listener on the socket that was never cleaned up. On keep-alive connections the listener count grows by one per request. The fix adds getAbortControllerCleanup(req)?.() immediately after app.match(), using the same cleanup pattern already applied to serve-app.ts in #15735.

Testing

  • Added a 'Static headers listener cleanup' test suite to packages/integrations/node/test/static-headers.test.ts that sends 30 keep-alive requests and asserts no MaxListenersExceededWarning is emitted.

Docs

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

Closes #17657

withastro/astro

Overview

Adds graceful shutdown support to the Node.js standalone adapter. When the process receives SIGTERM or SIGINT, the server stops accepting new connections and waits for all in-flight requests to complete before exiting.

What's new

  • SIGTERM / SIGINT handling — the server closes gracefully on either signal
  • Force-destroy timeout — if connections don't drain within the timeout, remaining connections are force-destroyed. Default is 10s, configurable via ASTRO_NODE_GRACEFUL_SHUTDOWN_TIMEOUT (milliseconds)
  • Opt-out — set ASTRO_NODE_GRACEFUL_SHUTDOWN=disabled to skip signal handler registration entirely

Testing

Adds an integration test suite (graceful-shutdown.test.ts) covering:

  • No new connections accepted after server.close()
  • SIGTERM and SIGINT each independently stop the server (SIGINT tested on a fresh server)
  • Multiple concurrent in-flight requests all complete before closed() resolves
  • Force-destroy fires after timeout when a connection is permanently stalled

Docs

Docs have not been updated yet.

withastro/astro

Changes

Context #17521

  • Replaces semver with the smaller ESM-native verkit package for Astro's Node.js version gate, update checks, integration resolution, and the upgrade CLI.
  • Removes unused semver dependencies from @astrojs/ts-plugin while preserving existing version-handling behavior.

Testing

  • No test changes; the migration preserves the behavior covered by the existing Astro CLI and upgrade tests.

Docs

  • No docs update needed because this refactor does not change public APIs or user-facing behavior.
withastro/astro

Changes

  • emitImageMetadata now uses a concurrency-limited file reader (max 200 simultaneous fs.readFile calls) to prevent exhausting OS file descriptors on projects with tens of thousands of images. Previously, all image imports were read concurrently with no limit, causing EMFILE: too many open files errors — especially after astro check or astro dev already consumed file descriptors.
  • Transient I/O errors (EMFILE, ENFILE, EAGAIN, EBUSY) are retried with exponential backoff instead of failing immediately.
  • The bare catch that silently swallowed all errors (including EMFILE) is replaced: only ENOENT returns undefined; other errors are re-thrown with the real OS error message. This makes ImageNotFound accurate — it now only fires when the file genuinely doesn't exist.

Closes #17649

Testing

  • Added packages/astro/test/units/assets/emit-image-metadata.test.ts covering: undefined id returns undefined, a missing file (ENOENT) returns undefined, and a real JPEG file returns correct width/height/format metadata.

Docs

No docs update needed — this is a build reliability fix with no user-facing API change.

withastro/astro

Changes

  • The adapter injects a SESSION KV binding with no id, which Wrangler treats as a
    request to auto-provision a namespace on deploy. That needs an API token with
    Workers KV Storage: Edit, so deploys without it fail with a generic
    Authentication error [code: 10000] against /storage/kv/namespacesafter the
    build already succeeded
    , and with nothing in the message pointing back at sessions.

  • astro build now warns when that binding is about to be provisioned, naming the
    permission and both escape hatches:

    [@astrojs/cloudflare] The "SESSION" KV binding has no `id`, so `wrangler deploy` will
    provision a new KV namespace. This requires an API token with the "Workers KV Storage:
    Edit" permission.
      To use an existing namespace, add `kv_namespaces: [{ binding: "SESSION", id: "<id>" }]`
      to your Wrangler config.
      To skip sessions entirely, set `session: false` in your Astro config.
    
  • Silent when the user already declared the binding, when session: false, and during
    dev (which never provisions anything). Deduplicated to one warning per build, since
    the customizer runs once per worker (entry, prerender, previews).

  • No change to the emitted Wrangler config. The existing
    !needsSessionKVBinding || hasSessionBinding ternary is extracted into a named
    injectsSessionBinding so the warning condition and the emission condition can't
    drift apart, but the output is identical.

  • The warning lives in cloudflareConfigCustomizer rather than the astro:config:setup
    hook because that's the only place that knows whether an id-less binding is actually
    being emitted — warning from index.ts would also fire for users who correctly
    declared SESSION with an id.

Closes #17640

Testing

Five cases added to the existing test/session-false.test.ts, which already covers the
sibling session: false behavior: warns with both key phrases, dedupes across workers,
silent when user-declared, silent when sessions disabled, and doesn't throw without a
logger.

Being upfront about verification: the suite imports from dist/, so it needs a full
monorepo install and build, which I did not run locally. I verified the logic by
compiling wrangler.ts standalone and running the same assertions plus three
regressions asserting binding emission is unchanged (8/8 passing). The committed test
file itself has therefore not been executed, and tsc -b is unconfirmed — I'd
appreciate CI confirming both.

Docs

No docs change needed: this adds no API or config surface, and the new logger option
is internal to the customizer.

That said, the underlying gotcha — that a default-on adapter feature makes deploy
require Workers KV Storage: Edit — isn't currently called out on the Cloudflare
sessions docs page, and arguably should be.

/cc @withastro/maintainers-docs for feedback!

withastro/astro

The remote image fetch in redirectValidation.ts passes no signal or timeout to fetchFn. A slow or unresponsive image origin server will stall the fetch indefinitely - blocking SSR responses in production and the dev server during development. Added AbortSignal.timeout(10_000) to the fetch options so the call aborts after 10 seconds rather than hanging forever.

withastro/astro

Changes

  • What does this change?

  • Add UT restore issue.

  • Improve cache key handling with a hashing function

  • This doc must be updated if you accept my PR.

Testing

Run node test command:

node ./packages/astro/test/get-static-paths-incremental.test.ts 

Closed issue: #17635

withastro/astro

Changes

  • Primary purpose of this pull-request is to refactor internal requests to get rid of the Pipeline and App classes as used internally. These were essentially "god objects" that held state related to the server-side app.
  • The problem with these objects were that there was no way to pass them into FetchState when access from outside of the App class. For example in Cloudflare you can create a custom worker which is the entrypoint to the application.
  • I realized that the manifest is the one true god-object in SSR, and we could simply derive all state from that. So this new architecture is much more functional. Derived state is created as createManifestMemo and createAsyncManifestMemo which are keyed on the manifest. Anything that needs this state can simply import it now.
  • Everything else in this PR is just conforming to the above.
  • App remains as its the external API for adapters, but mostly just defers to the functional approach now.

Fixes #17591

Testing

  • Mostly refactored existing tests which relied in the Pipeline.

Docs

  • N/A, just a refactor
withastro/astro

Changes

  • Fixes experimental.incrementalBuild dropping optimized images for cached pages when an adapter uses collectStaticImages (e.g. @astrojs/cloudflare with compile-time image optimization). When a cached page and a re-rendered page share the same source image with different transforms, the merge loop in generatePages was replacing the entire entry with .set(path, entry), discarding transforms that restoreStaticImages() had already replayed into the global static image list. The fix merges adapter transforms into existing entries instead of overwriting them, matching the deduplication pattern already used by restoreStaticImages(). Closes #17633.

Testing

  • Added packages/astro/test/units/build/incremental-images.test.ts with two unit tests: one verifying that restored transforms are preserved when adapter images share the same source path, and one verifying that new source paths from adapter images are still added when no restored entry exists.

Docs

  • No docs update needed — this is a bug fix for experimental.incrementalBuild, and no user-facing API or behavior contract changed.
withastro/astro

Changes

  • A collection schema transform returning a Temporal.PlainDate or other class instance no longer throws DataCloneError from getCollection()/getEntry().
  • Gets rid of structuredClone usage, so any types supported by devalue() should work.

Testing

  • image-references.test.ts: reworked, removed old tests
  • mutable-data-store.test.ts: asserts image prefixes are stripped to plain srcs and their paths recorded as imageImports, and that entries without images record nothing.

Docs

  • N/A, bug fix

Alternative to #17596.
Closes #17589

withastro/astro

Changes

Optimizes experimental cache from O(R × G) to O(G × R) using a Merkle-style hashing algorithm to content roots that heavily share dependencies.

  • R = # of roots. (e.g. 8,000 content pages)
  • G = graph size.
flowchart LR
    subgraph Before["Before: O(R × G)"]
        P1["Entry 1"] --> G1["Walk graph"]
        P2["Entry 2"] --> G2["Walk graph"]
        P3["Entry 3"] --> G3["Walk graph"]
    end

    subgraph After["After: O(G + R)"]
        G["Analyze graph once"] --> H["Hash cache"]
        H --> R1["Entry 1: O(1) lookup"]
        H --> R2["Entry 2: O(1) lookup"]
        H --> R3["Entry 3: O(1) lookup"]
    end
Loading

Testing

The performance degradation was discovered on my fork of cloudflare-docs where I enabled incrementalBuild. ~10m builds now timed out at 30 minutes.

  • TODO: Can this be published on pkg.pr.new to validate?
13:01:47.975
> astro build
13:01:47.976
13:01:58.504
18:01:58 [astro-skills] Setting up Agent Skills Discovery routes
13:01:58.504
18:01:58 [astro-skills] Agent Skills Discovery routes configured
13:02:01.502
18:02:01 [build] Waiting for integration "@cloudflare/nimbus-docs", hook "astro:config:setup"...
13:02:14.225
18:02:14 [WARN] [@cloudflare/nimbus-docs] [nimbus-docs] (nimbus/duplicate-slug, warning) 3 routes are served by an explicit src/pages file that shadows a content entry at the same URL:
13:02:14.225
  /ai/models  ←  src/content/docs/ai/models/index.mdx, src/pages/ai/models/index.astro
13:02:14.225
  /ruleset-engine/rules-language/fields/reference  ←  src/content/docs/ruleset-engine/rules-language/fields/reference/index.mdx, src/pages/ruleset-engine/rules-language/fields/reference/index.astro
13:02:14.225
  /workers-ai/models  ←  src/content/docs/workers-ai/models/index.mdx, src/pages/workers-ai/models/index.astro
13:02:14.225
13:02:14.226
Astro serves the page and drops the content route (deterministic). Intended when a content page wraps a custom page component; verify each shadow is intentional.
13:02:18.146
18:02:18 [content] Syncing content
13:02:18.149
18:02:18 [content] Astro config changed
13:02:18.150
18:02:18 [content] Clearing content store
13:02:28.869
18:02:28 [skills-loader] Loaded 11 skill(s) from "skills"
13:02:41.475
18:02:41 [content] Synced content
13:02:41.488
18:02:41 [types] Generated 27.00s
13:02:41.489
18:02:41 [build] output: "static"
13:02:41.490
18:02:41 [build] mode: "static"
13:02:41.490
18:02:41 [build] directory: /opt/buildhome/repo/dist/
13:02:41.490
18:02:41 [build] Collecting build info...
13:02:41.495
18:02:41 [build] ✓ Completed in 43.00s.
13:02:41.498
18:02:41 [build] Building static entrypoints...
13:04:25.008
18:04:25 [astro-icon] Loaded icons from src/icons, ph, simple-icons, vscode-icons
13:31:14.749
Build took too long and was timed out

Docs

withastro/astro

Changes

  • When both security.csp and experimental.clientPrerender are enabled, Astro now injects a single static <script type="speculationrules"> at render time using "source": "document" with a CSS selector (a[data-astro-prefetch] or a when prefetchAll is enabled). This produces a deterministic payload whose hash can be computed at build time and added to the CSP script-src directive.
  • The client-side prefetch code detects existing document-source speculation rules in the DOM and skips dynamic per-URL injection, which would generate unwhitelistable hashes.

Closes #17599

Testing

  • New unit test file packages/astro/test/units/csp/speculation-rules.test.ts covering generateSpeculationRulesContent() output shape and hash determinism.

Docs

  • No docs update needed; this fixes a bug in the interaction between two existing features without changing any user-facing API or configuration.
withastro/astro

Changes

  • Bumps the astro peer dependency in @astrojs/cloudflare from ^7.0.0 to ^7.2.0. The adapter imports beginContentEntryCollection, beginImageCollection, endContentEntryCollection, and endImageCollection from astro/app, which were added in 7.2.0. The wider range allowed npm to silently resolve astro@7.1.x, causing a cryptic MISSING_EXPORT build failure with no install-time warning.

Closes #17622

Testing

  • No new tests added — the fix is a manifest correction; existing adapter tests cover the affected code paths.

Docs

  • No docs update needed; this corrects a peer dependency declaration, not a user-facing API.
withastro/astro

Closes #17624

Changes

  • Entries whose frontmatter slug is an unquoted YAML number (e.g. slug: 20260624) now survive repeated syncs. YAML parses an unquoted number as a JS number, but the untouchedEntries Set holds string keys from the store. The Set.delete call used strict equality, so delete(20260624) was a no-op against "20260624", leaving the entry marked "untouched" and deleted in the cleanup pass on every sync after the first.
  • generateIdDefault() now calls String(data.slug) instead of relying on the as string type assertion, which was compile-time-only and did nothing at runtime.
  • The generateId wrapper also coerces the return value with String() so custom generateId callbacks returning non-string values are handled defensively as well.

Testing

  • Added a unit test in packages/astro/test/units/content-layer/glob-loader.test.ts that calls contentLayer.sync() twice and asserts the numeric-slug entry (id === '20260624') is present after both syncs and that the entry count is stable.
  • Added a fixture file src/content/space/numeric-slug.md with slug: 20260624 to back the new test case.

Docs

No docs update needed — this is a bug fix for an internal ID coercion issue with no API surface change.

withastro/astro

Changes

Update example template to refer to Astro 7.0

Testing

No test changed since this is a simple text update in example template.

Docs

No document changed since this is a simple text update in example template.

withastro/astro

Changes

  • Dynamic redirect routes in createRedirectsFromAstroRoutes now honour the user-configured status code. Previously, the dynamic branch hardcoded 301 (route.type === 'redirect' ? 301 : 200), so a redirect configured as { destination: '/new', status: 302 } would be written as 301 in the _redirects file. The static branch already called getRedirectStatus(route) correctly; this applies the same call to the dynamic branch.
  • Since 301 is cached essentially permanently by browsers, an incorrect status is very difficult to reverse in production.

Closes #17619

Testing

  • Two new test cases in packages/underscore-redirects/test/astro.test.ts: one verifies that a dynamic redirect with { destination, status: 302 } emits 302, the other verifies that a string-form redirect still defaults to 301.

Docs

  • No docs update needed — this restores already-documented behavior (configured status is respected).
withastro/astro

Changes

  • Since #15908 the generated TSX exports a component as BlogPostAstroComponent, so TypeScript never matches <BlogPost /> to it and stops offering "Add all missing imports" or "Add import from …". Completions still worked because they strip the suffix afterwards, while code actions need TypeScript to find the export first.
  • patchTSX now also re-exports the component under its clean name, which keeps the suffixed function name that avoids conflicts with same-name imports inside the file.
  • rewriteAstroImportText turns the resulting import { BlogPost } from './BlogPost.astro' back into a default import. Other named imports from .astro files, such as Props, are left alone.
  • Astro components are now offered once in the auto-import completion list instead of twice.

Fixes #17617

Testing

  • New packages/language-tools/language-server/test/typescript/code-actions.test.ts asks the server for quick fixes on an unimported component and checks the import edit. It fails on main.
  • New patchTSX and rewriteAstroImportText unit tests, plus a count assertion on the existing component auto-import completion test.
  • The @astrojs/language-server and @astrojs/check suites pass.

Docs

None, this is a bug fix with no API change.

withastro/astro

Changes

  • closes #17615
  • With experimental.incrementalBuild, a route that imports more than one asset was sometimes re-rendered even though nothing about it changed. An imported image's module code carries an __ASTRO_ASSET_IMAGE__<handle>__ placeholder, other assets carry Vite's __VITE_ASSET__<handle>__, and the handle comes from emitFile in the order the modules finish transforming, so two builds of the same sources can swap the handles around. The route hash then changes while the output stays byte-for-byte identical.
  • Before hashing a module, the placeholders are replaced with the file name each handle resolves to. Those names are content-hashed, so the hash stays stable across builds and still changes when an asset's contents change. Replacing the placeholder with a fixed token would also stop the churn, but then an image edited without a dimension change would leave the hash untouched and the restored HTML would point at a file name the build no longer emits.

Testing

New unit tests in test/units/build/plugin-incremental.test.ts drive the plugin with swapped emit handles, for images and for other assets, and assert the hash is unchanged, plus the reverse case where a different resolved file name does change it. Also confirmed against the reproduction in the issue, where 50 builds now produce the same hash, and against a variant of it importing a video and a PDF rather than images.

Docs

No user-facing behavior change, so nothing to document.

withastro/astro

Fixes: #17621

When navigating away from a route with ClientRouter and then returning to it, later CSS edits can trigger an HMR update without changing the visible page. A manual reload temporarily restores HMR until another navigation causes the problem again.

Vite keeps references to the <style data-vite-dev-id> elements it creates and updates those same elements during CSS HMR. ClientRouter head swaps can remove a Vite-managed style element and replace it with a new element containing the same CSS. Vite still holds the original element reference, so subsequent updates are applied to a detached element instead of the stylesheet currently in the document.

This can affect any route-specific style managed by Vite, including Svelte and other framework component styles, imported CSS, and Astro component styles. Svelte components make the problem particularly visible because their styles can be inserted asynchronously after hydration. Vue already has special handling for browser-transformed scoped styles, which this change preserves.

Changes

  • Tracks Vite-managed style elements by their data-vite-dev-id and reuses the existing element during ClientRouter head swaps. This preserves the element reference used by Vite’s CSS HMR runtime.
  • Observes styles inserted asynchronously by client framework runtimes so they can also be retained across later navigations.
  • Refreshes the contents of server-generated styles when the same Vite ID represents different CSS on the next route, while preserving Vue scoped styles that have been transformed in the browser.

Testing

  • Adds coverage for navigating away from and back to a route before updating a nested Svelte component style and an imported stylesheet.
  • Verifies that both updates use native CSS HMR without causing a full page reload.
  • Adds coverage using a real Vite-created style node to verify that ClientRouter preserves its identity while applying changed CSS from the incoming document.
  • Existing Vue scoped-style tests continue to cover preservation of browser-transformed CSS.

Docs

No docs changes. This fixes development-only ClientRouter and Vite HMR coordination without changing public APIs or production output.

withastro/astro

Fixes: #17672

When editing a style block in an Astro component rendered from an MDX content entry, the first save triggers a reload but the page can still render the previous CSS. Saving the file again triggers another reload and finally displays the change.

This happens when Astro cannot run the adapter’s SSR environment directly and uses its internal astro environment to collect styles from content entries. In my case this was because I was using the Cloudflare workerd environment.

Astro’s dev CSS plugin does not currently run in the fallback environment, so the first update reloads the page before that environment has collected the latest transformed CSS.

This change enables the existing dev CSS plugin in the fallback astro environment. The updated CSS is then collected during the first file change, allowing the first reload to render the new styles instead of requiring a second save.

Changes

  • Applies the dev CSS collection plugin to Astro’s fallback runnable environment, used when an adapter’s SSR environment cannot be run directly, such as with Cloudflare workerd.
  • Ensures content-rendered styles are refreshed before an SSR reload, instead of requiring a second save for the latest CSS to appear.

Testing

  • Adds coverage verifying that the dev CSS plugin applies to the fallback astro environment.

Docs

No docs changes. This fixes internal development-server CSS collection behavior.

withastro/astro

Changes

Add a test case missed in #17605

Testing

This change is only the addition of a test case

Docs

No docs needed for the addition of a test case

withastro/astro

PR #17383 fixed stale server-rendered CSS during HMR, but its client-module detection included a broad same-file fallback intended to support speculative query variants such as ?used and ?direct.

Those variants are not part of the verified Astro/Vite update path, and another style module associated with the same source file is not necessarily capable of applying the update. It could represent a different component style index or a non-injected CSS import.

This follow-up removes hasClientStyleModuleByFile() and retains only the module matching behavior supported by observed requests and Astro’s CSS collection code.

Changes

  • Removes the same-file fallback previously used to decide whether a client style module could handle an update.
  • Uses exact client module IDs when selecting native Vite CSS HMR. The only normalized difference is the bare inline parameter that Astro adds when loading component CSS text server-side.
  • Keeps .css?raw and .css?inline imports on the SSR invalidation path because they export strings rather than injecting client styles.

Testing

  • Updates coverage to verify that a different style index from the same component does not count as a matching client module.
  • Adds coverage for Astro’s server-side inline component requests mapping to their exact client equivalent.
  • Covers CSS string imports, reordered component-style queries, and incomplete or non-style requests.

Docs

No docs changes. This tightens internal HMR module matching without changing public APIs.

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

Patch Changes

  • #17612 7133730 Thanks @thelazylamaGit! - Fixes CSS hot module replacement after navigating between pages with ClientRouter

  • #17628 4ada248 Thanks @astrobot-houston! - Fixes a CSP violation when using both security.csp and experimental.clientPrerender with data-astro-prefetch links. The dynamically injected <script type="speculationrules"> now uses a static "source": "document" approach with a CSS selector, producing a deterministic payload that is hashed and included in the CSP script-src directive at build time.

  • #17605 89e4647 Thanks @ashleigh-yeoman! - Fixes middleware HMR not responding to changes in imported modules. Previously, only direct edits to the middleware file would trigger a reload.

  • #17582 bd2c1a5 Thanks @astrobot-houston! - Fixes a regression where content collection reference() fields silently accepted entry IDs that don't exist, such as an ID that doesn't match a loader's slugified version of it. Astro now logs an error for references that point to a missing entry after all loaders finish syncing.

  • #17661 97b0cc7 Thanks @ArmandPhilippot! - Improves Markdown options documentation with links to the Markdown guide and official processors.

  • #17349 4328c73 Thanks @astrobot-houston! - Fixes an issue where requests handled by the dev prerender environment (e.g. /_image with @astrojs/cloudflare's prerenderEnvironment: 'node') returned a 500 when a prerendered catch-all route existed, because non-prerendered route modules were imported in an environment where their runtime-specific APIs are unavailable

  • #17603 722eed6 Thanks @astrobot-houston! - Fixes <video> and <audio> elements being non-functional after navigating via view transitions (<ClientRouter />)

  • #17616 3a890d2 Thanks @lazerg! - Fixes experimental.incrementalBuild re-rendering unchanged routes that import more than one asset. The route's dependency hash depended on the order the assets finished building, so two builds of identical sources could produce different hashes. The hash is now based on the file name each asset resolves to.

  • #17547 fba468c Thanks @dmgawel! - Improves getCollection() and getEntry() performance for entries without local image references

  • #17602 16e0d9d Thanks @astrobot-houston! - Fixes a build error caused by hash collisions in generated content collection image import identifiers

@astrojs/cloudflare@14.2.1

Patch Changes

  • #17627 ba6a9f6 Thanks @astrobot-houston! - Fixes the astro peer dependency range from ^7.0.0 to ^7.2.0. The adapter imports symbols (beginContentEntryCollection, beginImageCollection, endContentEntryCollection, endImageCollection) from astro/app that were added in Astro 7.2.0, so earlier versions fail at build time with a MISSING_EXPORT error.

  • Updated dependencies [0891ac9]:

    • @astrojs/underscore-redirects@1.0.4

@astrojs/netlify@8.2.1

Patch Changes

  • Updated dependencies [0891ac9]:
    • @astrojs/underscore-redirects@1.0.4

@astrojs/node@11.1.1

Patch Changes

  • #17658 8b211a5 Thanks @astrobot-houston! - Fixes an EventEmitter memory leak when serving static pages over keep-alive connections with staticHeaders enabled and CSP (security.csp) active

@astrojs/language-server@2.16.14

Patch Changes

  • #17618 2630631 Thanks @lazerg! - Fixes the missing "Add all missing imports" and "Add import from" quick fixes for Astro components

@astrojs/underscore-redirects@1.0.4

Patch Changes

  • #17620 0891ac9 Thanks @astrobot-houston! - Fixes dynamic redirect routes to honour user-configured status codes instead of hardcoding 301. Previously, a redirect configured with { destination: '/new', status: 302 } would be emitted as 301 in the _redirects file when the route was dynamic.
withastro/astro

Changes

Fixes the three @astrojs/cloudflare session-false tests failing on main and blocking #17558.

The adapter's astro:config:setup gained an unguarded read of config.experimental.collectionStorage in #17543 (src/index.ts:289). session-false.test.ts calls that hook with a mock config that has no experimental key, so it threw TypeError: Cannot read properties of undefined (reading 'collectionStorage'). This PR adds the missing key to the mock:

experimental: { collectionStorage: 'single-file' },

A resolved AstroConfig always has it, since the schema defaults it to 'single-file', so the mock was incomplete rather than the adapter.

Testing

  • Fixed the failing Cloudflare tests.
  • Node and Netlify are unaffected since their mocks don't read experimental.

Docs

None needed, test-only change. No changeset for the same reason.

withastro/astro

Changes

Fixes the previous attempt to get HMR working for middleware. Now any imports (and transitive imports) in middleware.ts can trigger HMR for middleware.ts, instead of just itself.

This is meant to be a cleaner and better-tested version of #17597

Closes #17590

Testing

3 tests added to packages/astro/test/middleware.test.ts, covering the 3 main HMR cases

  1. middleware.ts is modified
  2. A module imported by middleware.ts is modified
  3. A transitive dependency (a module imported by a module imported by middleware.ts) is modified

Docs

No docs update needed. This PR fixes a bug in HMR behavior, with no API changes.

withastro/astro

Changes

  • <video> and <audio> elements are now fully functional after navigating to a page via <ClientRouter />. Previously, media controls were permanently disabled and playback was impossible after any client-side navigation.
  • The root cause is DOMParser.parseFromString(), which parses incoming page HTML into an inert document where the browser never initializes the media stack. Moving those elements into the live DOM does not retroactively initialize it. The fix adds a reifyMediaElements() post-swap step (following the existing attachShadowRoots() pattern) that replaces each <video>/<audio> element with a fresh copy created via document.createElement(), forcing the browser to properly initialize the media stack.

Testing

  • No automated test added — the fix is client-side browser code that exercises the browser's media stack initialization, which cannot be meaningfully tested in Node.js.

Docs

  • No docs update needed; this is a bug fix restoring behavior that was always expected to work.

Closes #17601

withastro/astro

Changes

  • Content collection image imports in .astro/content-assets.mjs now use sequential identifiers (__ASTRO_IMAGE_IMPORT_0, __ASTRO_IMAGE_IMPORT_1, …) instead of hash-based names. The previous shorthash()-derived names used a 32-bit hash that could collide for different image paths, causing a PARSE_ERROR at build time. Sequential indices are collision-free by construction.
  • Removes the now-unused importIdToSymbolName export and shorthash import from resolveImports.ts.

Closes #17595

Testing

  • Adds packages/astro/test/units/content-layer/asset-imports.test.ts: verifies that two image paths whose filenames produce identical shorthash() values (imgAa.jpg / imgBB.jpg) are assigned distinct import identifiers.

Docs

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

Changes

  • Fixes #17589
  • getCollection() / getEntry() threw DataCloneError when a collection schema produced values that structuredClone cannot handle — e.g. a Zod transform() returning Temporal.PlainDate or any other class instance.
  • The unconditional structuredClone(data) in updateImageReferencesInData() (introduced in #16701 to preserve Map/Set) is replaced with a selective clone: only plain objects and arrays — the containers whose nested strings may be rewritten into image metadata — are copied. Everything else (Map, Set, class instances, Temporal objects, …) passes through by reference.
  • This is safe because the traversal only ever calls ctx.update() on strings prefixed with __ASTRO_IMAGE_; non-plain values are never mutation targets, so sharing them between the store data and the returned copy cannot leak mutations.
  • Handles circular references among plain containers via a WeakMap, matching structuredClone's behavior there.
  • Includes a changeset (patch for astro).

Testing

  • Added a regression test in packages/astro/test/units/content-collections/image-references.test.ts using a class instance as a stand-in for Temporal.PlainDate (the test first asserts structuredClone rejects it with DataCloneError, then that updateImageReferencesInData preserves it by reference, including when nested).
  • Added a test asserting image resolution still does not mutate the original store data.
  • All 13 tests in image-references.test.ts pass, including the Map/Set preservation tests from #16701; the other content-collections unit suites (get-entry-info, get-entry-type, mutable-data-store) also pass.

Docs

No docs changes needed — this removes an undocumented limitation rather than changing documented behavior.

🤖 Generated with Claude Code

withastro/starlight

Adds https://lilypond.ky.fyi to the Starlight Showcase following the contribution guidelines.

withastro/astro

Changes

  • app.use(cf()) from @astrojs/cloudflare/hono now type-checks correctly in projects that use wrangler types. Previously, HonoCloudflareContextLike declared executionCtx: ExecutionContext, binding it to the global ExecutionContext type. When wrangler types generates worker-configuration.d.ts, that global gains required members (tracing, exports) that Hono's own ExecutionContext doesn't have, making the handler contravariant-incompatible with MiddlewareHandler.
  • Replaces executionCtx: ExecutionContext with an inline structural type listing only the three members actually consumed downstream (waitUntil, passThroughOnException, props). This matches the approach astro/hono already uses for its duck-typed context, and the narrowed type remains assignable to internal callers (cfFetch, createLocals) without any casts.

Testing

  • No new automated test — the mismatch only surfaces when the user's wrangler types-generated ExecutionContext global is in scope, which the package's own test compilation doesn't exercise (it compiles against @cloudflare/workers-types, whose ExecutionContext has fewer/optional members). The fix was verified against the reporter's repro at https://github.com/iseraph-dev/repro-astro-cf-hono-types, where astro check goes from 1 error to 0.

Docs

  • No docs change needed — this is a type-only fix with no behavior or API surface change.

Closes #17593

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

Patch Changes

  • #4114 3e486fb Thanks @delucis! - Fixes processing of code examples in RTL languages when using Astro’s Sätteri Markdown processor
withastro/starlight

Description

  • Fixes our Sätteri plugin for supporting code in RTL documents in newer versions of Astro
  • Fixes the failing test in #4113
  • Sätteri now has a ctx.parent() we can use to get parents so we can remove the more convoluted code for working this out. Strictly speaking these do not work 1:1 — before, we’d skip applying attributes to a <code> element even if it was deeply nested in a <pre> whereas now we only skip if the <code> is a direct child of the <pre>. However, in practice, I think this covers all the scenarios we were intending?
  • Existing tests here should pass, showing that the changes work for people on older versions of Astro. And I ran the changes against #4113 too to ensure it fixes the failing test there.
withastro/starlight

Description

  • Updates monorepo versions of astro to latest, fixes #3991 in our own docs
  • Also updates other @astrojs/* packages
  • Updates sharp to latest in examples and docs
withastro/starlight

Description

While reviewing another PR, I noticed we use pnpm dlx to run pkg-pr-new for preview releases.

This is not recommended because pnpm dlx would always resolves to the latest version which could have unexpected behavior due to some upstream breaking changes but also increases supply-chain risks.

Caution

In CI environments, avoid npx, pnpm dlx, yarn dlx, and bunx for this step. Install pkg-pr-new as a dependency and execute it from the lockfile (npm exec, pnpm exec, yarn, or bun run).

This PR fixes that by installing pkg-pr-new as a dev dependency and running it using pnpm exec.

withastro/astro

Changes

I hit this while following up on #17481. The same header mutation that crashed the Cloudflare adapter on cached image responses also lives in core: applyCacheHeaders() calls response.headers.set() directly on the response a route returns, and CacheHandler deletes CDN-Cache-Control and Cache-Tag from the response a provider returns. Both throw TypeError: immutable when the response headers can't be modified, which is the case for any response returned straight from fetch(). A route that proxies an upstream (return fetch(...) plus cache.set({ maxAge })) gets a 500 on every request. A user already reported this error class when combining cache.set() with a Vary header on Cloudflare (comment on #17408). I haven't verified that path, it needs the Cloudflare dev runtime, but the repro here is adapter free and fails on plain Node.

The fix mirrors what #17481 did in the adapter: try the mutation, and when it throws, rebuild the response with new Response(response.body, response) and apply the headers to the copy. The first .set() throws before changing anything, so applying them again can't duplicate headers. Header stripping now also checks has() first, so provider responses without CDN headers aren't rebuilt for nothing.

One heads up: pnpm install with the pinned pnpm also dropped a stale triage/gh-17583 importer from the lockfile that #17584 left behind. I can split that out if you'd rather keep it.

Testing

Two new fixtures. Without the fix all four new tests fail with a 500 instead of a 200: a memoryCache() route returning a fetch() proxied response, and a provider whose onRequest serves responses with immutable headers, where the CDN header stripping used to crash even when those headers weren't present. With the fix they pass and the second request is served as a cache HIT. The cache unit suite (162 tests) and the neighbouring integration tests still pass. I also reproduced the crash end to end against published astro 7.1.6 with @astrojs/node before writing the fix.

Docs

No docs changes. The changeset describes the fix.

withastro/astro

Changes

Skip the failing type check tests in ecosystem-ci. This will make ecosystem-ci pass.

I think it is better to make the tests pass so we can easily catch regressions. If we want to catch regressions for types, we can enable these tests later when it passes.

I didn't add a changeset as this is completely an internal change.

Testing

I ran ecosystem-ci against my fork by running node ecosystem-ci.ts astro in the local modified ecosystem-ci repo.

Docs

No docs change as this is an internal change.

withastro/astro

Changes

Astro's config schema imported @astrojs/markdown-satteri at the top level, so Sätteri and its optional platform-specific native binaries landed in the module graph on every astro dev/build, even for projects with no Markdown files. When npm skips the unavailable optional binary for the host platform (for example Windows without @bruits/satteri-wasm32-wasi), the bundler then can't resolve it and the build crashes.

This puts the default processor behind a dynamic import, so @astrojs/markdown-satteri is only resolved when Markdown actually gets rendered. A project with no Markdown never pulls it into the graph.

Testing

I added a unit test checking that the default markdown.processor still resolves to the Sätteri processor. The existing markdown.processor integration test already covers rendering with the default, and it plus the full unit suite pass. The Windows build crash from the report needs the missing optional binary, so it can't be reproduced on Linux CI.

Docs

No user-facing behavior change, so no docs needed.

Fixes #17585

withastro/astro

Changes

  • Solid component libraries that ship pre-compiled browser artifacts via the exports.solid condition (e.g. @kobalte/core) were left external during prerendering. Node resolved them via the default condition instead, which picks up browser-only code that calls template() and other APIs stubbed with notSup() in solid-js/web/dist/server.js — crashing astro build with "Client-only API called on the server side".
  • Uses crawlFrameworkPkgs from vitefu to discover all packages that declare solid-js as a peer dependency, then adds them to resolve.noExternal for non-client environments (e.g. prerender). This forces Vite to bundle those packages so it can apply the solid export condition correctly, matching the established pattern from @astrojs/svelte (PR #16210).
  • Adds vitefu to @astrojs/solid-js's dependencies (previously it was only available as a transitive dep via other packages).

Testing

No automated test added — configEnvironmentPlugin is internal and the solid integration has no existing test infrastructure for this; the svelte integration's equivalent fix also lacks a unit test. Fix was confirmed working by the reporter against both a minimal reproduction and a production project (12 pages).

Docs

No docs update needed — this restores expected build behavior with no API changes.

Closes #17583

withastro/astro

Changes

  • reference() fields in content collections no longer silently accept entry IDs that don't exist in the store. After all loaders finish syncing, ContentLayer now walks every entry's data to find reference objects ({ id, collection }) and logs an error for any that point to a missing entry. This catches cases like author: "John-Doe" where the glob() loader slugified the actual entry ID to john-doe.
  • This is a regression from the Zod 4 upgrade (PR #14956), which removed the inline Zod validation that previously caught invalid references. Inline validation can't be restored because loaders run in parallel and the referenced collection may not be populated yet, so validation runs post-sync instead.

Closes #17322

Testing

  • Two new unit tests in packages/astro/test/units/content-layer/data-transforms.test.ts: one verifying that an invalid reference (John-Doe where only john-doe exists) produces an error log, and one verifying that a valid reference produces no error.

Docs

  • No docs update needed; this restores previously documented behavior that was accidentally removed.
withastro/astro

See #2587

withastro/astro

Changes

package-manager-detector recently supported the devEngines field, but for our custom strategies option passing, we redefine the list and didn't include the devEngines-field option, so this PR includes it.

Its default value had devEngines-field.

Testing

Didn't test as should be a simple change

Docs

Added a changeset.

withastro/astro

This PR contains the following updates:

Package Change Age Confidence
@types/semver (source) ^7.7.1^7.8.0 age confidence
@vscode/test-cli ^0.0.12^0.0.15 age confidence
js-yaml ^4.3.0^4.3.1 age confidence
mocha (source) ^11.7.5^11.8.0 age confidence
ovsx (source) ^0.10.10^0.10.12 age confidence
prettier (source) ^3.9.0^3.9.6 age confidence
semver ^7.7.4^7.8.5 age confidence
svelte (source) ^5.55.3^5.56.8 age confidence
tinyglobby (source) ^0.2.16^0.2.17 age confidence
tsx (source) ^4.22.0^4.23.5 age confidence
vscode-languageserver-protocol (source) ^3.17.5^3.18.2 age confidence
yaml (source) ^2.8.3^2.9.0 age confidence
yargs (source) ^18.0.0^18.1.0 age confidence

Release Notes

nodeca/js-yaml (js-yaml)

v4.3.1

Compare Source

mochajs/mocha (mocha)

v11.8.0

Compare Source

v11.7.6

Compare Source

🩹 Fixes
  • make describe().timeout() work (aafe6fd)
  • test: replace wmic usage with native Windows API (#​5694) (73ebdfa)
🧹 Chores
eclipse-openvsx/openvsx (ovsx)

v0.10.12

Compare Source

Dependencies
  • Bump follow-redirects from 1.15.6 to 1.16.0 (#​1759)
  • Bump ip-address from 10.1.0 to 10.2.0 (#​1820)

v0.10.11

Compare Source

Dependencies
  • Bump picomatch from 2.3.1 to 2.3.2 (#​1719)
  • Bump picomatch from 4.0.3 to 4.0.4
  • Bump brace-expansion from 1.1.12 to 1.1.13 (#​1725)
  • Bump brace-expansion from 2.0.2 to 2.0.3
  • Bump brace-expansion from 5.0.4 to 5.0.5
  • Bump lodash from 4.17.23 to 4.18.1 (#​1745)
prettier/prettier (prettier)

v3.9.6

Compare Source

v3.9.5

Compare Source

diff

Markdown: Cap ordered list mark at 999,999,999 (#​19351 by @​tats-u)

CommonMark parsers only support ordered list item numbers up to 999,999,999.

With this change, Prettier now caps the ordered list item number at 999,999,999 to ensure that the output is correctly parsed as an ordered list by CommonMark parsers. Numbers larger than 999,999,999 are not parsed as list item numbers and are left unchanged in the output:

<!-- Input -->
999999998. text
999999998. text
999999998. text
999999998. text

1234567890123456789012) text

<!-- Prettier 3.9.4 -->
999999998. text
999999999. text
1000000000. text
1000000001. text

1234567890123456789012) text

<!-- Prettier 3.9.5 -->
999999998. text
999999999. text
999999999. text
999999999. text

1234567890123456789012) text
Markdown: Avoid corrupting empty link with title (#​19487 by @​andersk)

Do not remove <> from an inline link or image with an empty URL and a title, as this removal would change its interpretation.

<!-- Input -->
[link](<> "title")

<!-- Prettier 3.9.4 -->
[link]( "title")

<!-- Prettier 3.9.5 -->
[link](<> "title")
Less: Remove extra spaces after [ in map lookups (#​19503 by @​kovsu)
// Input
.foo {
  color: #theme[ primary];
  color: #theme[@name];
  color: #theme[@@name];
}

// Prettier 3.9.4
.foo {
  color: #theme[ primary];
  color: #theme[ @name];
  color: #theme[ @@name];
}

// Prettier 3.9.5
.foo {
  color: #theme[primary];
  color: #theme[@name];
  color: #theme[@@name];
}
CSS: Prevent addition space in type() with + (#​19516 by @​bigandy)

This fixes the addition space before + in CSS type() declaration. For example type(<number>+) was being converted into type(<number> +) which is invalid CSS and does not work.

/* Input */
div {
  border-radius: attr(br type(<length>+));
}

/* Prettier 3.9.4 */
div {
  border-radius: attr(br type(<length> +));
}

/* Prettier 3.9.5 */
div {
  border-radius: attr(br type(<length>+));
}
Less: Remove spaces between merge markers and colons (#​19517 by @​kovsu)
// Input
a {
  box-shadow  +  : 0 0 1px #&#8203;000;
}

// Prettier 3.9.4
a {
  box-shadow+  : 0 0 1px #&#8203;000;
}

// Prettier 3.9.5
a {
  box-shadow+: 0 0 1px #&#8203;000;
}
Markdown: Preserve wiki links with aliases (#​19527 by @​kovsu)
<!-- Input -->
[[Foo:Bar]]

<!-- Prettier 3.9.4 -->
[[Foo]]

<!-- Prettier 3.9.5 -->
[[Foo:Bar]]
TypeScript: Fix comments being dropped on shorthand type import/export specifiers (#​19565 by @​kirkwaiblinger)
// Input
export { type /* comment */ T } from "foo";
import { type /* comment */ T } from "foo";

// Prettier 3.9.4
Error: Comment "comment" was not printed. Please report this error!

// Prettier 3.9.5
export { type /* comment */ T } from "foo";
import { type /* comment */ T } from "foo";
Miscellaneous: Preserving comments' placement property (#​19567 by @​Janther)

Prettier@​3.9.0 deleted an undocumented property on comments, which was already used by plugins, comment.placement is now available again after comment attach.

Flow: Stop enforcing empty module declaration to break (#​19568 by @​fisker)
// Input
declare module "foo" {}

// Prettier 3.9.4
declare module "foo" {
}

// Prettier 3.9.5
declare module "foo" {}
Angular: Support expression for exhaustive typechecking (#​19571 by @​fisker)
<!-- Input -->
@switch (state.mode) {
  @default never(state);
}

<!-- Prettier 3.9.4 -->
@switch (state.mode) {
  @default never;
}

<!-- Prettier 3.9.5 -->
@switch (state.mode) {
  @default never(state);
}
TypeScript: Ignore comments inside mapped type when checking type parameter comments (#​19572 by @​fisker)
// Input
foo<{
  // comment
  [key in keyof Foo]: number
}>();

// Prettier 3.9.4
foo<
  {
    // comment
    [key in keyof Foo]: number;
  }
>();

// Prettier 3.9.5
foo<{
  // comment
  [key in keyof Foo]: number;
}>();
Less: Fix adjacent block comments being corrupted (#​19574 by @​kovsu)
// Input
/* a *//* b */
/* a */* {
  color: red;
}

// Prettier 3.9.4
/* a */
/* b */
/* a * {
  color: red;
}

// Prettier 3.9.5
/* a */ /* b */
/* a */
* {
  color: red;
}
JavaScript: Handle dangling comments in SwitchStatement (#​19581 by @​fisker)
// Input
switch (foo) {
 // comment
}

// Prettier 3.9.4
switch (
  foo
  // comment
) {
}

// Prettier 3.9.5
switch (foo) {
  // comment
}
TypeScript: Remove space in comment-only object type (#​19583 by @​fisker)
// Input
var foo = {
  /* comment */
};
type Foo = {
  /* comment */
};

// Prettier 3.9.4
var foo = {/* comment */};
type Foo = { /* comment */ };

// Prettier 3.9.5
var foo = {/* comment */};
type Foo = {/* comment */};

v3.9.4

Compare Source

v3.9.3

Compare Source

v3.9.2

Compare Source

v3.9.1

Compare Source

sveltejs/svelte (svelte)

v5.56.8

Compare Source

Patch Changes
  • fix: call onerror and provide a working reset when hydrating a failed boundary (#​18556)

  • fix: preserve select selection when spread attributes omit value (#​18561)

v5.56.7

Compare Source

Patch Changes
  • chore: provide indent option for print (#​18474)

v5.56.6

Compare Source

Patch Changes
  • perf: skip unnecessary blocker analysis when compiling components without top-level await (#​18548)

  • fix: rerun derived that had an abort controller on reconnection (#​18551)

v5.56.5

Compare Source

Patch Changes
  • chore: drop dead code that make TSGO fail (#​18496)

  • fix: don't (re)connect deriveds when read inside branch/root effects (#​18527)

  • fix: skip unnecessary derived effect in earlier batch (#​18525)

  • fix: avoid declaration tag warning in event handlers (#​18500)

  • fix: abort deriveds own AbortSignal when it disconnects (#​18400)

  • fix: ensure $state.eager() is correctly transormed for SSR output (#​18530)

  • fix: correctly transform declaration tags during SSR (#​18492)

  • fix: transform computed keys in keyed {#each} destructuring patterns (#​18521)

  • fix: chain preprocessor sourcemaps with an empty sources[0] instead of dropping them (#​18518)

  • fix: clear previous_task reference after abort in Tween to prevent memory leak on interrupted tweens (#​18541)

  • fix: don't treat declaration tags as parts inside each blocks (#​18507)

v5.56.4

Compare Source

Patch Changes
  • fix: include wrapping parentheses in {@const} declarator end position (#​18436)

  • fix: always unset reactivity context after restoring it (#​18453)

  • fix: don't notify searchParams subscribers when the URL changes without affecting the search string (#​18425)

  • fix: strip ? from optional parameters in <script lang="ts"> so generated JavaScript is valid (#​18448)

v5.56.3

Compare Source

Patch Changes
  • fix: ignore errors that occur in destroyed effects (#​18384)

  • fix: type BigInts in $state.snapshot(...) return values (#​18388)

v5.56.2

Compare Source

Patch Changes
  • fix: properly track effect end node for async sibling component (#​18371)

  • fix: prevent false-positive reactivity loss warning (#​18373)

  • chore: bump esrap dependency (#​18372)

  • fix: ignore declaration tags for animation directive (#​18366)

  • fix: reject pending async deriveds on discard (#​18308)

v5.56.1

Compare Source

Patch Changes
  • fix: error at compile time on duplicate snippet/declaration tag definitions (#​18351)

  • fix: parse declaration tag contents more robustly (#​18353)

  • fix: correctly transform references to earlier declarators in a declaration tag (e.g. {let a = $state(0), b = $derived(a * 2)}) (#​18348)

  • fix: avoid spurious state_referenced_locally warnings for $derived declarations in declaration tags (#​18348)

  • fix: tolerate whitespace before let/const in declaration tags (#​18348)

  • fix: prevent infinite loop when a tag's expression ends with a trailing / at the end of the input (#​18350)

  • fix: more robust parsing of declaration tags with regards to type (#​18330)

  • fix: preserve newlines in spread input values when the type attribute is applied after value (#​18345)

  • fix: update SvelteURLSearchParams when setting duplicate keys to the same joined value (#​18336)

  • fix: check references for blockers on server, too (#​18352)

v5.56.0

Compare Source

Minor Changes
  • feat: allow declarations in the template (#​18282)
Patch Changes
  • perf: use createElement instead of createElementNS for HTML elements (#​18262)

  • perf: store current_sources as a Set for O(1) membership checks (#​18278)

  • perf: deduplicate identical hoisted templates within a component (#​18320)

  • perf: hoist rest_props exclude list as a module-scope Set (#​18252)

v5.55.10

Compare Source

Patch Changes
  • fix: unlink errored and otherwise finished batch (#​18264)

  • perf: walk composedPath() directly in delegated event propagation (#​18268)

  • fix: transfer effects when merging batches (#​18254)

  • fix: allow $derived(await ...) in disconnected effect roots (#​18273)

  • fix: remove temporary raw-text hydration markers (#​18269)

  • fix: propagate async @const blockers through closure references so template expressions like {(() => host)()} correctly wait for the awaited value (#​18309)

  • fix: properly unlink batches (#​18298)

  • fix: settle discarded batch (#​18290)

  • fix: declare let: directives before {@const} declarations on slotted elements (#​18271)

  • fix: resume outro-ed branches if they were kept around (#​18291)

  • fix: avoid waterfall-warning when async resolves to same value (#​18297)

  • fix: correctly coordinate component-level effects inside async blocks (#​18260)

  • fix: make unnecessary commit work less likely (#​18263)

  • chore: add tag name to a11y_click_events_have_key_events warning (#​18272)

  • fix: catch rejected promises while merging/committing (#​18266)

v5.55.9

Compare Source

Patch Changes
  • fix: don't unset batch when calling {#await ...} promise (#​18243)

  • fix: promise-ify {#await await ...} expressions on the server and correctly hydrate them on the client (#​18243)

  • fix: deduplicate dependencies that are added outside the init/update cycle (#​18243)

  • fix: avoid false-positive batch invariant error (#​18246)

  • fix: inline primitive constants in attribute values during SSR (#​18232)

v5.55.8

Compare Source

Patch Changes
  • fix(print): handle svelte:body and fix keyframe percentage double-printing (#​18234)

  • fix: execute uninitialized derived even if it's destroyed (#​18228)

  • fix: use named symbols everywhere (#​18238)

  • fix: don't run teardown effects when deriveds are unfreezed (#​18227)

  • fix: unset context synchronously in run (#​18236)

v5.55.7

Compare Source

Patch Changes

v5.55.6

Compare Source

Patch Changes
  • fix: leave stale promises to wait for a later resolution, instead of rejecting (#​18180)

  • fix: keep dependencies of $state.eager/pending (#​18218)

  • fix: reapply context after transforming error during SSR (#​18099)

  • fix: don't rebase just-created batches (#​18117)

  • chore: allow null for pending in typings (#​18201)

  • fix: flush eager effects in production (#​18107)

  • fix: rethrow error of failed iterable after calling return() (#​18169)

  • fix: account for proxified instance when updating bind:this (#​18147)

  • fix: ensure scheduled batch is flushed if not obsolete (#​18131)

  • fix: resolve stale deriveds with latest value (#​18167)

  • chore: remove unnecessary increment_pending calls (#​18183)

  • fix: correctly compile component member expressions for SSR (#​18192)

  • fix: reset source.updated stack traces after flush (#​18196)

  • fix: replacing async 'blocking' strategy with 'merging' (#​18205)

  • fix: allow @debug tags to reference awaited variables (#​18138)

  • fix: re-run fallback props if dependencies update (#​18146)

  • fix: abort running obsolete async branches (#​18118)

  • fix: ignore comments when reading CSS values (#​18153)

  • fix: wrap Promise.all in save during SSR (#​18178)

  • fix: ignore false-positive errors of $inspect dependencies (#​18106)

v5.55.5

Compare Source

Patch Changes
  • fix: don't mark deriveds while an effect is updating (#​18124)

  • fix: do not dispatch introstart event with animation of animate directive (#​18122)

v5.55.4

Compare Source

Patch Changes
  • fix: never mark a child effect root as inert (#​18111)

  • fix: reset context after waiting on blockers of @const expressions (#​18100)

  • fix: keep flushing new eager effects (#​18102)

privatenumber/tsx (tsx)

v4.23.5

Compare Source

v4.23.4

Compare Source

Bug Fixes
  • cli: allow async process.once() signal handlers to finish (#​827) (2afc7bb)

This release is also available on:

v4.23.3

Compare Source

Bug Fixes

This release is also available on:

v4.23.2

Compare Source

v4.23.1

Compare Source

Bug Fixes
  • support tsImport after global preload (8d4ffc2)
  • watch: avoid clearing piped output (95d0672)
  • watch: treat script and dependency paths literally (79fddde)
Performance Improvements
  • index transform cache lazily (e818ad6)
  • load esbuild lazily in CLI (d067938)
  • map Node TypeScript formats directly (cdcc623)
  • use sync module hooks on Node v22.22.3+ (f8992f1)

This release is also available on:

v4.23.0

Compare Source

Bug Fixes
Features

This release is also available on:

v4.22.5

Compare Source

Bug Fixes
  • isolate hook state per async module.register() registration (a305f36)

This release is also available on:

v4.22.4

Compare Source

Bug Fixes
  • resolve CommonJS directory requires inside dependencies (#​803) (1ce8463)

This release is also available on:

yargs/yargs (yargs)

v18.1.0

Compare Source

Features
  • ignore bun when getting bin name (b77831c)
Bug Fixes

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • Between 12:00 AM and 03:59 AM, only on Monday (* 0-3 * * 1)
  • 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

  • closes #17574
  • imageService: 'custom' (and the fallback case) left Astro's default dev image endpoint in place, which imports vite and node:fs and cannot load inside workerd, so every /_image request returned 500 in dev
  • use the generic fetch-based endpoint in dev, matching the other image service modes; a user-configured image.endpoint is left untouched
  • warn in dev when imageService: 'custom' resolves to the Sharp service (including when no image.service is configured), since Sharp's native binding cannot run inside workerd in dev or production (see #17574 (comment))

Testing

  • new custom-image-service.test.ts
  • new setImageConfig unit tests in image-config.test.ts
  • existing

Docs

  • none, bug fix
withastro/astro

Closes #17508

Changes

  • Adds neotraverse to the ALWAYS_NOEXTERNAL list in vite-plugin-environment/index.ts, forcing it to be bundled into the prerender output instead of emitted as a bare external import.
  • Before this fix, if any other package in the dependency tree required neotraverse@^0.6.x, npm would hoist that older copy to the project root. The prerender bundle (written to dist/.prerender/, outside node_modules/astro) would then resolve the bare import { forEach } from "neotraverse" to the hoisted 0.6.x copy — which doesn't export forEach — crashing the build. Bundling neotraverse ensures Astro's own copy is always used regardless of what's hoisted in the project.

Testing

  • No new tests added; the fix is a one-line config addition. Existing Content Layer integration tests cover the affected code paths and confirm no regression.

Docs

  • No docs update needed; this is an internal dependency resolution fix with no user-facing API changes.
withastro/astro

Changes

  • Fixes a crash in the Node adapter when a request arrives with a malformed port in the Host header (e.g. example.com:65536, example.com:8080:8080). The bad host made the request URL invalid, and the existing catch fallback rebuilt the URL from the same host and threw again.
  • buildRequestUrl (shared by createRequestFromNodeRequest and createRequest) now degrades in steps — full URL, origin only, then a server-controlled host (localhost, with the listening port when known) — so URL construction never throws.
  • parseHost now rejects a host with more than one hostname:port pair, which previously passed by inspecting only the first two colon-separated segments.

Testing

  • Unit tests in node.test.ts covering malformed hosts through createRequestFromNodeRequest (no throw, parseable URL) plus a valid max-port control, and a createRequest case asserting a duplicated-port host is rejected.
  • An end-to-end @astrojs/node test that drives a standalone server, sends a crafted Host over a raw socket, and asserts a follow-up request still succeeds.

Docs

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

Closes #17329

Changes

  • Cookies set via Astro.cookies.set() inside a custom 404.astro or 500.astro are now correctly included in the final response. Previously they were silently dropped.
  • Two bugs in mergeResponses (packages/astro/src/core/errors/default-handler.ts): (1) when both the original and error page had AstroCookies, the error page's cookies were appended to originalResponse.headers — an object already copied into newHeaders and no longer connected to the merged response — so they went nowhere; fixed by replacing the loop with originalCookies.merge(newCookies). (2) the seen-set guard that deduplicates merged headers treated set-cookie as a single-value header, blocking error-page cookies when the original response already set one; fixed by always appending set-cookie entries.

Testing

  • Added packages/astro/test/error-page-cookies.test.ts with three cases: error page cookies survive when the original page throws, error page cookies survive when no original cookies exist, and both middleware and 404 error page cookies are preserved together.

Docs

  • No docs update needed. This restores the behavior that Astro.cookies.set() is documented to provide; no API surface changed.
withastro/astro

Changes

  • @astrojs/vercel creates .vercel/output/server/ with a plain mkdirSync, so astro build crashes with EEXIST when that directory already exists, for example when two builds run against the same project root. This creates it with { recursive: true }, the same way the static/ directory one line above is already created.
  • It also awaits emptyDir(staticDir), which was fire-and-forget before and could race the copy calls that follow it.

Fixes #17568

Testing

The existing @astrojs/vercel test suite passes. Reproducing the race needs two overlapping builds sharing one project root, which the current integration harness has no way to set up, so I did not add a new test.

Docs

No docs changes. This is an internal bug fix with no change to the public API.

withastro/astro

Changes

  • Fixes fontProviders.googleicons() downloading the entire ~3.9MB Google Icons font instead of only the requested glyphs when experimental.glyphs contains more than one name. The root cause is a bug in unifont (unjs/unifont#336) where glyph names are joined with .join("") (no separator), producing an invalid icon_names query parameter that causes Google's API to silently return the full font. The workaround pre-joins multiple glyphs into a single comma-separated string before passing them to unifont, so unifont's .join("") produces the correct value.

Testing

  • Adds packages/astro/test/units/assets/fonts/googleicons-glyphs.test.ts covering the resolveFont call with multiple glyphs, a single glyph, undefined options, and an empty glyphs array — verifying the patching logic doesn't throw for any of these shapes.

Docs

  • No docs update needed; experimental.glyphs behavior is unchanged from the user's perspective — this restores the documented subsetting behavior.

Closes #17565


Last fetched:  | Scheduled refresh: Every Saturday

See Customizing GitHub Activity Pages to configure your own

Inspired by prs.atinux.com