Skip to content

AstroEco is Contributing…

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

withastro/starlight

Description

Tweak a comment: "non-Firefox" → "Chromium-based"

Safari has already supported the syntax :lang(zh, ja).

image

The position of Opera is the culprit.

This change is so trivial that we should ship it with other changes.

withastro/starlight

This PR contains the following updates:

Package Type Update Change
pnpm/action-setup action patch v6.0.5v6.0.6

Release Notes

pnpm/action-setup (pnpm/action-setup)

v6.0.6

Compare Source

What's Changed
  • fix: bin_dest output points to self-updated pnpm, not bootstrap by @​zkochan in #​249

Full Changelog: pnpm/action-setup@v6.0.5...v6.0.6


Configuration

📅 Schedule: (UTC)

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

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

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

🔕 Ignore: Close this PR and you won't be reminded about this update again.


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

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

withastro/astro

Changes

  • Adds a new "Write a Unit Test" step to the triage bot's fix skill, so it produces a regression test alongside every fix attempt.
  • Creates reference/unit-testing.md documenting unit test conventions, file placement, and the shared test utilities/mocks available in the repo. Referenced from AGENTS.md so any agent (not just triage) can use it.
  • Adds a Unit Test line to the triage bot's GitHub comment template.

Testing

  • No test changes — this PR only modifies agent instructions and reference docs.

Docs

  • No user-facing docs needed. reference/unit-testing.md is agent-facing only.
withastro/starlight

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

Releases

@astrojs/starlight@0.39.2

Patch Changes

  • #3890 2d05e18 Thanks @tats-u! - Fixes CSS selector for text-autospace styles in Chromium browsers
withastro/starlight

Description

:lang(zh, ja) used in #3872 shamefully has not been supported Chromium and Safari. As a result, text-autospace has been enabled only in Firefox. Replaced it with the combination of :lang(zh) and :lang(ja).

Screenshot in Chrome (Pay attention to the slight presence of whitespace around "Astro" and "Starlight"):

Before (https://starlight.astro.build/ja/getting-started/):

image

After:

image
withastro/astro

Changes

Updates the attribute escaping logic to use named entities (& and ") instead of numeric entities (& and ").

Currently, <title> tags render named entities, while other meta tags render numeric entities. Although both formats are semantically equivalent, this results in inconsistent raw HTML output.

This change aligns the escaping behavior across all head and meta tags so they consistently render named entities.

Closes #16657

Testing

Updates the test cases that rendered &#38; and &#34; to instead render &amp; and &quot;.

Docs

N/A

withastro/astro

Changes

  • When imageService uses compile-time behavior (compile or { build: 'compile', ... }), the adapter no longer always replaces the user’s image.service with the workerd stub. It only swaps in @astrojs/cloudflare/image-service-workerd when the project is on Astro’s default Sharp service; a custom entrypoint is left intact.
  • The Vite plugin now receives the resolved image service entrypoint (so static image handling matches the configured service instead of hard-coding the workerd stub).
  • The Cloudflare workerd prerenderer only temporarily swaps in Sharp for the Node-side static image pass when that workerd stub is in use; with a custom service it leaves globalThis.astroAsset.imageService unset so the custom transform runs.

Testing

  • Adds compile-custom-image-service.test.ts with a fixture that sets imageService: { build: 'compile' } and a custom image service; asserts prerendered HTML uses the custom getURL() output and custom image attributes.

Docs

  • No docs update: behavior now matches the existing imageService docs expectation that a custom image.service is respected when compatible with the adapter.
withastro/astro

Changes

  • Fixes #16201 — the Cloudflare adapter silently replaced a user-defined image.service with @astrojs/cloudflare/image-service-workerd whenever imageService was unset (default 'cloudflare-binding') or set to 'compile'/'cloudflare-binding'/'cloudflare'/'passthrough'. Custom services (e.g., a third-party CDN service) are now preserved across all modes.
  • Added a guard at the top of setImageConfig that detects a non-default image.service (entrypoint ≠ astro/assets/services/sharp) and returns the config untouched, mirroring the precedent in the existing fallback default: branch and the explicit 'custom' mode.
  • Emits a single logger.info line when this preservation kicks in, telling the user the override was skipped and pointing them at imageService: 'custom' to silence the notice.
  • Added a .changeset (@astrojs/cloudflare patch).

Testing

  • New unit test: packages/integrations/cloudflare/test/image-config.test.ts (10 cases) covering preservation across undefined, 'passthrough', 'cloudflare', 'cloudflare-binding', 'compile', and the compound { build: 'compile' } / { build: 'compile', runtime: 'cloudflare-binding' } configs. Also asserts the existing override of default sharp in 'cloudflare-binding' and 'compile' is preserved (no regression) and that explicit 'custom' continues to work.
  • Existing image-related fixture tests run clean: external-image-service.test.ts (2/2), binding-image-service.test.ts (7/7), compile-image-service.test.ts (5/5).
  • pnpm run build succeeds; tsc -b clean.

Docs

No user-facing docs changes needed. Behavior aligns with what the existing docs already imply (a custom image.service should be respected); this PR brings the Cloudflare adapter into line. Worth a brief note in the Cloudflare adapter README that custom services are preserved alongside imageService modes — happy to follow up in withastro/docs if maintainers prefer.

Closes #16201

withastro/astro

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

Releases

astro@6.3.2

Patch Changes

  • #16638 272185b Thanks @ematipico! - Fixes a bug where the Astro compiler wasn't freed at the end of the build. After the fix, the memory used by the compiler is now correctly freed at the end of the build.

  • #16544 d365c97 Thanks @matthewp! - Tightens isRemotePath() to reject control characters after a leading slash and fixes the dev image endpoint origin check

  • Updated dependencies [d365c97, 9256345]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/markdown-remark@7.1.2

@astrojs/prism@4.0.2

Patch Changes

  • #15723 9256345 Thanks @rururux! - Fixes an issue where the <Prism /> component failed to work in Cloudflare Workers.

@astrojs/cloudflare@13.5.1

Patch Changes

  • #16652 98c32cc Thanks @greatjourney589! - Fixes user-declared KV namespace bindings being duplicated in the generated dist/server/wrangler.json, which caused wrangler validation to fail with " assigned to multiple KV Namespace bindings." The Astro Cloudflare config customizer now returns only the auto-injected SESSION binding and lets @cloudflare/vite-plugin merge it with the user's wrangler config, instead of pre-merging the user's bindings into the output.

  • #15723 9256345 Thanks @rururux! - Fixes an issue where the <Prism /> component failed to work in Cloudflare Workers.

  • Updated dependencies [d365c97]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/underscore-redirects@1.0.3

@astrojs/markdoc@1.0.5

Patch Changes

  • #15723 9256345 Thanks @rururux! - Updates internal type usage from @astrojs/prism.

  • Updated dependencies [d365c97, 9256345, 9256345]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/markdown-remark@7.1.2
    • @astrojs/prism@4.0.2

@astrojs/mdx@5.0.5

Patch Changes

  • Updated dependencies [9256345]:
    • @astrojs/markdown-remark@7.1.2

@astrojs/netlify@7.0.9

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1
    • @astrojs/underscore-redirects@1.0.3

@astrojs/node@10.1.1

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1

@astrojs/preact@5.1.3

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1

@astrojs/react@5.0.5

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1

@astrojs/vercel@10.0.7

Patch Changes

  • Updated dependencies [d365c97]:
    • @astrojs/internal-helpers@0.9.1

@astrojs/internal-helpers@0.9.1

Patch Changes

  • #16544 d365c97 Thanks @matthewp! - Tightens isRemotePath() to reject control characters after a leading slash and fixes the dev image endpoint origin check

@astrojs/markdown-remark@7.1.2

Patch Changes

  • #15723 9256345 Thanks @rururux! - Updates internal type usage from @astrojs/prism.

  • Updated dependencies [d365c97, 9256345]:

    • @astrojs/internal-helpers@0.9.1
    • @astrojs/prism@4.0.2
withastro/astro

Closes #16590

Changes

  • Fixes a regression introduced in #16555 where user-declared KV namespace bindings (e.g. RATE_LIMIT, CACHE) appear twice in the generated dist/server/wrangler.json, causing wrangler to fail with "<binding> assigned to multiple KV Namespace bindings.".
  • The Astro Cloudflare config customizer now returns only the auto-injected SESSION binding from kv_namespaces, instead of pre-merging the user's existing bindings into the output.
  • @cloudflare/vite-plugin already merges the customizer's output with the user's wrangler config, so echoing the user's bindings caused the duplication.
  • Removed the now-unused withSessionKVBinding() helper.
  • Added a changeset (@astrojs/cloudflare patch).

Affected versions reported in the issue: @astrojs/cloudflare@13.3.0–13.3.1 + astro@6.2.x. Last working combination was astro@6.1.9 + @astrojs/cloudflare@13.2.1, which matches the pre-#16555 behavior this PR restores.

Testing

  • Updated two existing assertions in packages/integrations/cloudflare/test/wrangler.test.ts that locked in the buggy merged-output shape.
  • Added a dedicated regression test does not include user-declared KV bindings in the output (regression #16590) covering the multi-binding scenario from the issue (RATE_LIMIT + CACHE declared by the user) and asserting the customizer returns only [{ binding: 'SESSION' }].
  • All 25 tests in wrangler.test.ts pass locally.

Docs

No documentation changes needed. The fix restores documented and previously-shipped behavior — the public API and configuration surface (sessionKVBindingName, automatic SESSION binding provisioning) are unchanged.

withastro/astro

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

Releases

astro@6.3.1

Patch Changes

  • #16646 15fbc41 Thanks @matthewp! - Fixes local images returning 404 on non-prerendered pages when using the generic image endpoint
withastro/astro

Changes

  • Restores the FREDKBOT_GITHUB_TOKEN env var mapping that was accidentally dropped in the flue 0.3 upgrade (#16441). Without it, postGitHubComment fails at the end of every triage run.

Testing

  • No test changes. Verified by inspecting the failing run.

Docs

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

Description

Adds generic clock, desktop, mobile phone, app window, database, server, git branch, notes, question mark, analytics, and padlock icons. Also adds a new logo icon for SolidJS. The mobile icon has a bit of a funny name — mobile-android — but I kept it to match the source library’s name.

Closes #3881
Closes #3863

Which icons to include built-in is always a bit subjective, but I went through our icon library and tried to add a handful that might meet some more common needs on docs sites given I was already adding a couple based on user requests.

For the SolidJS logo, I asked on their Discord server to confirm which style they preferred for a monochrome icon (as it deviates from their more common blue gradient style).

withastro/astro

Changes

After #16402 migrated every test under packages/astro/test to TypeScript, two scripts in packages/astro/package.json were left pointing at non-existent .test.js globs:

"test:match": "astro-scripts test \"test/**/*.test.js\" --match",
"test:cli":   "astro-scripts test \"test/**/cli.test.js\"",

Because astro-scripts test wraps the matched files in a temp module before invoking node:test (see scripts/cmd/test.js:90-102), an empty match still produces a passing run — node:test counts the empty wrapper as a single passing "test" — so the breakage was silent. pnpm test:cli reports tests 1, pass 1, suites 0 and exits in ~100ms without ever executing the real CLI tests in test/cli.test.ts.

This PR:

  • Updates the globs to .test.ts so they match real files (test/cli.test.ts, test/units/**/*.test.ts, test/*.test.ts).
  • Adds --strip-types so node:test can load the TypeScript files, matching the existing test:unit and test:integration scripts on the surrounding lines.
  • Restores test:match as the documented "run a subset of tests by name pattern" command referenced in CONTRIBUTING.md lines 130-132.

Testing

Verified locally before/after the change:

Beforepnpm test:cli (phantom pass, no real tests run):

> astro-scripts test "test/**/cli.test.js"
✔ /workspaces/astro/packages/astro/node_modules/.astro/test.mjs (90ms)
ℹ tests 1   ℹ suites 0   ℹ pass 1

Afterpnpm test:cli --match "astro --version" (real cli.test.ts test):

> astro-scripts test "test/**/cli.test.ts" --strip-types --match 'astro --version'
▶ astro cli
  ✔ astro --version (338ms)
✔ astro cli (339ms)
ℹ tests 1   ℹ suites 1   ℹ pass 1

Note the suite count flips from 0 → 1, confirming cli.test.ts is actually loaded and the describe('astro cli') suite runs.

Docs

No docs changes needed — CONTRIBUTING.md:130-132 already documents pnpm test:match "<pattern>"; this PR makes that documented behaviour work again.

No changeset added — internal-only package.json script change, no published-package surface affected.

withastro/astro

Changes

  • Renames packages/astro/e2e/astro-island-hydration-error.test.js to .test.ts and adds minimal type annotations to align with the e2e test convention established in #16402.
  • This file was introduced in #16412 (after #16402 had already migrated every other e2e test to TypeScript), so it landed using the prior .js convention. It is the only remaining .test.js file in packages/astro/e2e/.
  • As a side effect, the file is now matched by playwright.config.js (which uses testMatch: 'e2e/*.test.ts'), so its two regression tests for astro:hydration-error recovery (added in #16412) now actually execute in CI. They were silently skipped before this rename.

Testing

  • pnpm exec tsc --noEmit -p tsconfig.test.json passes against the converted file.
  • The diff is a near-mechanical port of the existing test (12 insertions / 6 deletions), preserving identical test semantics. git recognises the change as a rename (83% similarity).
  • The Window.__hydrationErrorEvents typing follows the same declare global { interface Window {...} } pattern already used by e2e/view-transitions.test.ts.

Docs

No docs changes required — internal test refactor with no public-API surface.

No changeset added — internal-only test file rename, no published-package behaviour changes.

withastro/astro

Changes

  • The generic image endpoint (assets/endpoint/generic.ts) self-fetches local images from the same origin. #16519 added an isRemoteAllowed check on the response URL, but that check rejects local URLs (e.g. http://host/_astro/image.png) since they aren't in image.domains or image.remotePatterns. This caused local images on non-prerendered pages to 404.
  • Extracted the fetch logic into loadImage.ts with an isRemote flag that gates the isRemoteAllowed check. Local images skip it — they're already protected by the same-origin guard in the caller.

Fixes #16644

Testing

  • Added test/units/assets/endpoint-load-image.test.ts with 4 cases: local image succeeds, unauthorized remote rejected, allowed remote succeeds, fetch failure handled. The local image test fails without the fix.

Docs

  • No docs needed — this is a regression fix restoring prior behavior.
withastro/astro

Changes

A flaky test was detected in astro-assets-prefix.test.ts in CI.
https://github.com/withastro/astro/actions/runs/25502409719/job/74839082587

The root cause is likely that tests are now running in parallel, and both astro-assets-prefix.test.ts and astro-assets-prefix-multi-cdn.test.ts share the same fixture while also calling fixture.clean() in their after() hooks, which deletes the cache folder.
When the test fails, the log shows "The collection "blog" does not exist or is empty. Please check your content config file for errors." and as shown here, the getDataStoreFile function references the .astro folder, which is exactly what clean() removes.

export function getDataStoreFile(settings: AstroSettings, isDev: boolean) {
return new URL(DATA_STORE_FILE, isDev ? settings.dotAstroDir : settings.config.cacheDir);
}

clean: async () => {
await fs.promises.rm(config.outDir, {
maxRetries: 10,
recursive: true,
force: true,
});
const astroCache = new URL('./node_modules/.astro', config.root);
if (fs.existsSync(astroCache)) {
await fs.promises.rm(astroCache, {
maxRetries: 10,
recursive: true,
force: true,
});
}
},

To fix this, I merged the two test files into one.
An alternative would have been to give each test its own dedicated fixture, but since the two test cases are similar in nature, I went with the merge approach instead.

Testing

Green CI

Docs

N/A

withastro/starlight

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

Releases

@astrojs/starlight@0.39.1

Patch Changes

  • #3885 010eed1 Thanks @ArmandPhilippot! - Fixes the version mentioned in an error message related to autogenerated sidebar groups support.

  • #3887 b3c6990 Thanks @delucis! - Adds 13 new icons: clock, desktop, mobile-android, window, database, server, code-branch, notes, question, question-circle, analytics, padlock, and solidjs.

withastro/starlight

Description

The Starlight version mentioned in the error related to autogenerated sidebar groups support is wrong:

Support for autogenerated sidebar groups was removed in Starlight v0.38.0.

#3618 has been released earlier in 0.39.0, not 0.38.0.

I added a changeset because we need a new version for such a small change. 😅

withastro/astro

Changes

Migrate a js test file to ts.

Testing

CI should pass.

Docs

N/A

withastro/starlight

This PR contains the following updates:

Package Type Update Change
changesets/action action minor v1.7.0v1.8.0

Release Notes

changesets/action (changesets/action)

v1.8.0

Compare Source

Minor Changes
  • #​258 f5dbf72 Thanks @​tom-sherman! - Support draft version PR modes with a new prDraft input. Use create to create new version PRs as drafts, or always to also convert existing version PRs back to draft when updating them.
Patch Changes

Configuration

📅 Schedule: (UTC)

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

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

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

🔕 Ignore: Close this PR and you won't be reminded about this update again.


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

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

withastro/astro

Changes

A follow-up to #16471 and #16472

svelte2tsx@0.7.55 supports typescript v6, so we can also update the typescript peer dependency range in @astrojs/svelte.

Testing

CI should pass

Docs

A changeset file is added because we need to release a new version for @astrojs/svelte.

withastro/astro

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

Releases

@astrojs/cloudflare@13.5.0

Minor Changes

@astrojs/node@10.1.0

Minor Changes

withastro/astro

Changes

Thank you @TheOtterlord for catching this

Testing

Green CI

Docs

withastro/astro

Changes

The compiler was never torn down at the end of the build. This PR fixes it.

Testing

Manually tested.

Docs

N/A

withastro/starlight

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

Releases

@astrojs/starlight@0.39.0

Minor Changes

  • #3618 dcf6d09 Thanks @HiDeoo! - ⚠️ BREAKING CHANGE: This release changes how autogenerated links work in Starlight’s sidebar configuration.

    If you have sidebar groups using the autogenerate key, you must now wrap that configuration in an items array:

    {
        label: 'My group',
    -   autogenerate: { directory: 'some-dir' },
    +   items: [{ autogenerate: { directory: 'some-dir' } }],
    }

    This change unlocks the possibility to mix autogenerated links and other links in a single group, for example:

    {
      label: 'Mixed group',
      items: [
        'example-page',
        { autogenerate: { directory: 'examples' } },
        { label: 'More examples', link: 'https://example.com' },
      ],
    }

    This release also updates the shape of autogenerated sidebar entries in route data. Autogenerated links and groups in Astro.locals.starlightRoute.sidebar now include an autogenerate object with the configured directory value:

    {
      type: 'link',
      label: 'Example',
      href: '/examples/example/',
      isCurrent: false,
      autogenerate: { directory: 'examples' }
    }
  • #3618 dcf6d09 Thanks @HiDeoo! - ⚠️ BREAKING CHANGE: This release changes the default collapsed state of autogenerated sidebar subgroups.

    Autogenerated subgroups no longer inherit the collapsed value from their parent group. They are now expanded by default unless explicitly configured with autogenerate.collapsed.

    If your sidebar configuration relies on a collapsed parent group to also collapse its autogenerated subgroups, update your configuration to set autogenerate.collapsed to true:

    {
      label: 'Reference',
      collapsed: true,
      items: [
    -   { autogenerate: { directory: 'reference' } },
    +   { autogenerate: { directory: 'reference', collapsed: true } },
      ],
    }
  • #3845 4d755f5 Thanks @delucis! - Adds a <link rel="alternate" hreflang="x-default" href="..."> tag pointing to the default locale in multilingual sites. The x-default alternate is used as a signal of which language to fall back to if no other is available. Learn more in Google’s SEO localization docs.

  • #3862 ec70630 Thanks @itrew! - Makes spacing of items in nested lists more consistent

  • #3872 417a66c Thanks @tats-u! - Enables the CSS property text-autospace in Chinese and Japanese documents.

    If you would prefer to disable autospacing in Chinese and Japanese pages, you can add the following custom CSS to your site:

    [lang]:where(:lang(zh, ja)) {
      text-autospace: initial;
    }
  • #3797 9764ebd Thanks @delucis! - Avoids the risk of layout shift when users expand and collapse sidebar groups

    This release can introduce additional padding to the site sidebar on certain devices to reserve space for scrollbars. You may wish to inspect your site sidebar visually when upgrading.

    If you would prefer to keep the previous styling, you can add the following custom CSS to your site:

    .sidebar-pane {
      scrollbar-gutter: auto;
    }
  • #3858 6672c35 Thanks @delucis! - Updates i18next, used for Starlight’s localization APIs, from v23 to v26

    There should not be any user-facing changes from this update

withastro/astro

Description

Adds flush() and close() methods to AstroIntegrationLogger, mirroring AstroLogger. Both delegate to the underlying destination's optional flush / close hooks.

AstroLogger already exposes flush() and close() (added in #16404). AstroIntegrationLogger did not, so any code path that placed an AstroIntegrationLogger where an AstroLogger was expected — e.g. App.#prepareResponse calling this.logger.flush() — would crash with TypeError: this.logger.flush is not a function. The two logger classes now have a symmetric public surface.

Closes #16622.

Changes

  • packages/astro/src/core/logger/core.ts: add flush() and close() to AstroIntegrationLogger, identical shape to the methods on AstroLogger.
  • packages/astro/test/units/logger/logger.test.ts: cover AstroIntegrationLogger.flush() / .close() (delegation, no-throw, fork inheritance).
  • changeset: patch.

Testing

pnpm --filter astro build:ci
pnpm --filter astro exec astro-scripts test test/units/logger/logger.test.ts --strip-types --teardown ./test/units/teardown.ts
# 10 pass, 0 fail

Docs

No public API docs change required — AstroIntegrationLogger.flush() / .close() mirror the existing AstroLogger methods (no behaviour change for destinations that don't define flush/close).

withastro/astro

Changes

Fixes #16612

Testing

Should pass! We unfortunately cannot test this in the monorepo because TypeScript is of course always available

Docs

N/A

withastro/astro

Changes

This PR improves integration tests:

  • remove large-array, as we already use that in our benchmarks
  • moves some fixtures at higher level so that they aren't built multiple times
  • removes the custom rollup thing because it's already tested elsewhere

Testing

Green CI

Docs

N/A

withastro/astro

Changes

Some tests in core-image.test.ts have been flaky in Astro's CI.
https://github.com/withastro/astro/actions/runs/25425893932/job/74579340641?pr=16579
https://github.com/withastro/astro/actions/runs/25407064617/job/74520525804?pr=16614

After investigating, I found that the removeDir function, which is used to clean up the cache directory before tests, was not being awaited despite returning a Promise.

This PR adds the missing await to address the potential race condition.

Testing

Green CI

Docs

N/A

withastro/astro

Changes

One of TypeScript's biggest strengths is static analysis. In practice, we rely on it in three places:

  1. In editors (e.g. vscode / TypeScript language service)
  2. During local and CI builds via tsc
  3. During local and CI linting via eslint with type-aware linting

All three ultimately depend on TypeScript project configuration through tsconfig.

Before #16241, Astro used three different tsconfig setups for these workflows:

  1. Editors used an effectively empty root tsconfig.json, so the language service fell back to its default behavior.
  2. tsc used per-package configs that only included src/, excluding files like bin/ and test/.
  3. eslint used a separate tsconfig.eslint.json.

This inconsistency created a poor developer experience. For example, vscode could report type errors while CI remained green, making editor diagnostics unreliable.

This PR completes the TypeScript project references work introduced in previous PRs. After this PR, all three workflows share the same source of truth: the root tsconfig.json.

The root tsconfig.json now includes all src/, test/, scripts, and configuration files (e.g. prettier.config.mjs). It doesn't include SFC files (.astro, .svelte, etc.) and test fixtures, though.

Also, note that if a JS/TS file is included in eslint but not in the root tsconfig.json, eslint will show the following errors. This is beneficial because it ensures that we have all files added to tsconfig.json, thus these files are covered by tsc.

/astro/packages/astro/e2e/astro-island-hydration-error.test.js
  0:0  error  Parsing error: /astro/packages/astro/e2e/astro-island-hydration-error.test.js was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject

Testing

CI should pass

Docs

N/A

withastro/astro

Changes

Migrate some tests from JS to TS.

These tests come from an old pull request, which was merged into main after the TypeScript migration was completed.

Testing

N/A

Docs

N/A

withastro/starlight

This PR contains the following updates:

Package Type Update Change
actions/labeler action minor v6.0.1v6.1.0

Release Notes

actions/labeler (actions/labeler)

v6.1.0

Compare Source

Enhancements

  • Add changed-files-labels-limit and max-files-changed configuration options to cap the number of labels added by @​bluca in #​923

Bug Fixes

Dependency Updates

New Contributors

Full Changelog: actions/labeler@v6...v6.1.0


Configuration

📅 Schedule: (UTC)

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

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

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

🔕 Ignore: Close this PR and you won't be reminded about this update again.


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

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

withastro/astro

Changes

  • Fixes ModuleLoader.getSSREnvironment() to return the ssrEnvironment parameter the loader was constructed with, instead of always looking up viteServer.environments.ssr.
  • With adapters that register a separate prerender Vite dev environment (notably @astrojs/cloudflare with prerenderEnvironment: 'node'), createViteLoader builds a dedicated loader per environment. Every other method already uses the constructor parameter - only getSSREnvironment() ignored it, so the prerender handler's loader returned the unrelated ssr environment and getComponentMetadata() walked the wrong module graph in dev. With Cloudflare's default workerd ssr, that env is also non-runnable, making the as RunnableDevEnvironment cast unsafe.
  • Removes the now-unused ASTRO_VITE_ENVIRONMENT_NAMES import.
  • Changeset: astro: patch (.changeset/fix-module-loader-prerender-env.md).
  • Closes #16436. Follow-up to #16292, which fixed the visible style-into-<template> symptom in astro:head-metadata but did not touch this separate correctness bug in the per-environment module loader.

Testing

  • tsc --noEmit on packages/astro passes cleanly.
  • Existing regression test packages/astro/test/head-propagation-prerender-env.test.js still passes (1/1) - confirms no regression on the astro:head-metadata path that #16292 fixed.
  • No new test added: loader.getSSREnvironment() has a single caller (getComponentMetadata), and when only the ssr env exists ssrEnvironment === viteServer.environments.ssr, so the new behaviour is a strict superset of the old correct behaviour. Happy to extend the head-propagation-prerender-env fixture with a prerendered route to exercise the prerender handler's loader directly if maintainers prefer.

Docs

No docs change needed - this is a dev-server-internal correctness fix with no user-facing API or behaviour change. Users on adapters with a separate prerender env (e.g. @astrojs/cloudflare with prerenderEnvironment: 'node') will simply see correct head metadata for prerendered routes in dev.

withastro/astro

Changes

The Houston bot for the triage workflow is currently not working.
https://github.com/withastro/astro/actions/runs/25424727887/job/74575139340

The log shows the following:

pnpm exec flue run issue-triage \
    --target node \
    --session-id "issue-triage-$ISSUE_NUMBER" \
    --payload "{\"issueNumber\": $ISSUE_NUMBER}"

Unknown argument: --session-id

Looking into the flue repository, I couldn't find the --session-id argument anywhere.
Instead, I found that --id is defined as the correct argument.
https://github.com/withastro/flue/blob/808b34ee154b94025d5cb2b45288d88b7766ae0f/packages/cli/bin/flue.ts#L95-L108

I also confirmed that commit 8388ed1 in the flue repository changed --session-id to --id.
Based on this, I concluded that --id should be used instead of --session-id, and updated it accordingly.

Testing

I don't know how to do it 😔

Docs

N/A

withastro/astro

Changes

issue : #15609

  • Added a fallback path in handleAction for contexts that lack a pipeline.
  • If neither a pipeline nor ACTION_API_CONTEXT_SYMBOL is present on the context ActionCalledFromServerError is still thrown as before.

Testing

Before implementation

スクリーンショット 2026-05-06 6 51 54

After implementation

スクリーンショット 2026-05-06 6 55 00

Docs

withastro/astro

Changes

Right now, types for entry.data field returned by getLiveEntry() are not working.

---
import { getLiveEntry } from "astro:content";

const { entry } = await getLiveEntry("example", "hello-world");
const data = entry?.data; // type `any` instead of defined type from loader.
---

The cause is that the utility type ExtractDataType used here is missing, so entry.data is always of type any.

This pr adds the missing utility type to the file.

Testing

Tested by manually adding the missing utility type to the generated .astro/content.d.ts in my projects and verifing the expected types to work.

Docs

It adds missing typing.

withastro/astro

Fixes the smoke test failure caused by astro-expressive-code@0.42.0 being too recently published to pass the 3-day minimumReleaseAge gate. Since this is a Starlight dependency used by our docs smoke test, it should always be installable regardless of release age.

withastro/starlight

Another PR updating depedencies. Expressive Code is the main motivator as the latest release updates to Shiki v4 (thanks again @ocavue!) so Starlight will share this with Astro instead of having v3 and v4 in the tree.

Prod dependencies

  • Updates astro-expressive-code in Starlight (0.41.7 => 0.42.0)

Dev depdendencies

  • Updates astro across monorepo (6.1.9 => 6.2.2)
  • Updates @astrojs/check (0.9.8 => 0.9.9)
  • Updates pnpm (10.33.2 => 10.33.3)
  • Updates linters
    • eslint (10.2.1 => 10.3.0)
    • globals (17.5.0 => 17.6.0)
    • typescript-eslint (8.59.0 => 8.59.2)

Haven’t bothered with a changeset as we already have an unreleased “update deps” patch changeset in the pipeline. Should see no user-facing change in any case.

withastro/astro

Changes

This PR adds the missing * line from a code sample in JSDoc, which basically removed the whitespace between the imports and the code.

This doesn't need a changeset, AFAIK

Testing

Docs

That's all it is.

withastro/astro

Changes

Fixes a regression from #16277 where the generated wrangler.json sets
assets.directory to include the base prefix (e.g. "../client/blog"
instead of "../client").

Cloudflare's asset binding resolves the full request URL path (including
the base) against the directory. With the prefixed path, requests would
resolve to a double-nested location (e.g. client/blog/blog/file.js),
causing 404s for all static assets.

Related: #16276

Testing

Added a test to with-base.test.ts verifying the assets directory in
the generated wrangler.json points to the un-prefixed client root.

Docs

withastro/astro

fixes: #16604

Changes

As reported in the issue, the migration guide and deprecation comments recommended using build.assetsPrefix from astro:config/server as a replacement for the deprecated import.meta.env.ASSETS_PREFIX, but astro:config/server was not actually exporting build.assetsPrefix.

/**
* The prefix for Astro-generated asset links if the build.assetsPrefix config option is set. This can be used to create asset links not handled by Astro.
* @deprecated This will be removed in a future major version of Astro. Use `build.assetsPrefix` from `astro:config/server` instead.
*/
readonly ASSETS_PREFIX: string | Record<string, string>;

This PR fixes the implementation so that build.assetsPrefix is correctly exported from astro:config/server.

Testing

Added tests to verify that build.assetsPrefix is properly exported and that its value can be read correctly.

Docs

The "Upgrade to Astro v6" page documents this under "Deprecated: import.meta.env.ASSETS_PREFIX" and recommends using build.assetsPrefix instead, but the "Imports from astro:config/server" reference page does not yet list build.assetsPrefix.

A follow-up PR to update the docs will be submitted separately.

withastro/astro

Fixes #16491

Context: In Vite 6, top-level vite.optimizeDeps only applies to the client environment. The Cloudflare adapter owns SSR/prerender dep optimization via its configEnvironment hook, so user-configured optimizeDeps.exclude and optimizeDeps.esbuildOptions.loader were silently ignored for server environments.

Changes

  • Fix: capture the user's config.vite.optimizeDeps in astro:config:setup and merge it into the adapter's configEnvironment return value for server environments.
  • Added integration test: (test/custom-loader.test.ts) with a fixture containing a fake package that imports a .data file, reproduces the esbuild "No loader is configured for .data files" error when the fix is absent.
  • Extended test/ssr-deps.test.ts with a dynamically-created fake-data-pkg that imports a .data file, reproduces the esbuild "No loader is configured for .data files" error when the fix is absent.

Testing

  1. Run NODE_OPTIONS="--experimental-strip-types --no-warnings" pnpm test in packages/integrations/cloudflare.
  2. If the new test fails locally with a miniflare compatibility date error, that is a pre-existing local env issue (same as ssr-deps.test.ts) CI is the source of truth

Docs

Fix doesn't introduce any new config options or change any public API. No docs update needed.

withastro/astro

Changes

  • Moves conflict resolution and changeset cleanup into the merge-main-to-next action so the branch is clean and buildable before CI runs. AI only runs when there are actual conflicts; clean merges skip it entirely.
  • Strips the merge-fix action down to just test fixing — no more conflict stripping, no --no-frozen-lockfile. Uses --frozen-lockfile since the lockfile is already correct.
  • Adds critical rules to the fix-tests skill based on analysis of a 1-hour timeout: never run pnpm install, time-box investigation to 5 min, diff before deep-reading source, batch bash commands.

Testing

  • No test changes — these are CI workflow and skill instruction changes.

Docs

  • No docs needed — internal CI automation only.
withastro/astro

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

Releases

astro@6.3.0

Minor Changes

  • #16366 d69f858 Thanks @matthewp! - Adds a new experimental.advancedRouting option that lets you take full control of Astro's request handling pipeline by creating a src/app.ts file in your project.

    Today, Astro handles every incoming request through a fixed internal pipeline: trailing slash normalization, redirects, actions, middleware, page rendering, i18n, and so on. That pipeline works great for most sites, but as projects grow you often want to run your own logic between those steps — an auth check before rendering, a rate limiter before actions, custom logging around the whole stack. Advanced routing gives you that control.

    When enabled, Astro looks for a src/app.ts file in your project. If it finds one, that file becomes the entrypoint for all server-rendered requests. You compose the pipeline yourself using the handlers Astro provides, and you can slot your own logic anywhere in the chain.

    Enabling advanced routing

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      experimental: {
        advancedRouting: true,
      },
    });

    Two ways to build your pipeline

    Astro ships two entrypoints for advanced routing: astro/fetch and astro/hono.

    astro/fetch is a low-level, framework-free API built on the Web Fetch standard. You create a FetchState from the incoming request, then call handler functions in sequence. Each handler takes the state, does its work, and returns a Response (or undefined to pass through). This is the core primitive that everything else is built on:

    // src/app.ts
    import {
      FetchState,
      trailingSlash,
      redirects,
      actions,
      middleware,
      pages,
      i18n,
    } from 'astro/fetch';
    
    export default {
      async fetch(request: Request) {
        const state = new FetchState(request);
    
        // Early exits — these return a Response only when they apply.
        const slash = trailingSlash(state);
        if (slash) return slash;
    
        const redirect = redirects(state);
        if (redirect) return redirect;
    
        const action = await actions(state);
        if (action) return action;
    
        // Middleware wraps page rendering; i18n post-processes the response.
        const response = await middleware(state, () => pages(state));
        return i18n(state, response);
      },
    };

    astro/hono wraps the same handlers as Hono middleware, so you can mix Astro's pipeline with Hono's ecosystem of middleware (logger, CORS, JWT, rate limiting, etc.) using the app.use() pattern you already know:

    // src/app.ts
    import { Hono } from 'hono';
    import { getCookie } from 'hono/cookie';
    import { logger } from 'hono/logger';
    import { actions, middleware, pages, i18n } from 'astro/hono';
    
    const app = new Hono();
    
    app.use(logger());
    
    // Auth gate — only runs for /dashboard routes.
    app.use('/dashboard/*', async (c, next) => {
      const session = getCookie(c, 'session');
      if (!session) return c.redirect('/login');
      return next();
    });
    
    app.use(actions());
    app.use(middleware());
    app.use(pages());
    app.use(i18n());
    
    export default app;

    Both approaches give you the same power — pick whichever fits your project. If you don't need a framework, astro/fetch keeps things minimal. If you want a rich middleware ecosystem, astro/hono gets you there with one import.

    For more information on enabling and using this feature in your project, see the experimental advanced routing docs. To give feedback, or to keep up with its development, see the advanced routing RFC for more information and discussion.

  • #16366 d69f858 Thanks @matthewp! - Adds a consume() instance method to AstroCookies. This method marks the cookies as consumed and returns the Set-Cookie header values. After consumption, any subsequent set() calls will log a warning, since the headers have already been sent.

    Previously this was only available as a static method AstroCookies.consume(cookies). The static method is now deprecated but kept for backward compatibility with existing adapters.

  • #16412 ba2d2e3 Thanks @0xbejaxer! - Add retry and error event handling for astro-island hydration import failures to reduce unrecoverable hydration errors on transient network failures.

  • #16582 885cd31 Thanks @Princesseuh! - Adds a new image.dangerouslyProcessSVG flag to optionally enable processing SVG inputs. For security reasons, Astro will no longer rasterizes SVG image sources by default in its default image service and endpoint.

    Set image.dangerouslyProcessSVG: true to opt back into processing SVG inputs.

    // astro.config.mjs
    import { defineConfig } from 'astro/config';
    
    export default defineConfig({
      // ...
      image: {
        dangerouslyProcessSVG: true,
      },
    });

    Note that this is a breaking change for users who were previously relying on Astro's default image service to rasterize SVG inputs, but it is a necessary change to improve security and prevent potential vulnerabilities.

  • #16519 1b1c218 Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.

    Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in image.remotePatterns or a domain in image.domains, Astro will fail with a helpful error message.

    In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:

    export default defineConfig({
      image: {
        domains: ['example.com', 'cdn.example.com'],
      },
    });
    {
      /* Redirects to https://cdn.example.com/assets/image.png: */
    }
    <Image
      src="https://example.com/assets/image.png"
      width="1920"
      height="1080"
      alt="An example image."
    />;
    
    {
      /* Redirects to https://malicious.com/image.png: */
    }
    <Image
      src="https://example.com/bad-image.png"
      width="1920"
      height="1080"
      alt="An example image."
    />;

    In cases where all redirects to HTTPS hosts should be trusted, the following configuration for image.remotePatterns can be used:

    export default defineConfig({
      image: {
        remotePatterns: [
          {
            protocol: 'https',
          },
        ],
      },
    });

Patch Changes

  • #16592 9c6efc5 Thanks @matthewp! - Escapes interpolated values in the dev server redirect HTML template, consistent with how the 404 template already handles them

  • #16585 78f305e Thanks @web-dev0521! - Fixes z.array(z.boolean()) in form actions incorrectly coercing the string "false" to true. Boolean array elements now use the same 'true'/'false' string comparison as single z.boolean() fields, so submitting ["false", "true", "false"] correctly parses as [false, true, false].

  • #16567 12a03f2 Thanks @matthewp! - Fixes deleted content collection entries persisting in getCollection() results during dev

  • #16595 ce9b25c Thanks @web-dev0521! - Fixes pushDirective in the CSP runtime duplicating the new directive once per existing non-matching directive. Calling insertDirective() (or otherwise pushing a directive whose name is not yet in the list) now appends it exactly once, and a directive that merges with a later existing entry no longer leaves an unmerged copy behind.

  • #16600 94e4b7c Thanks @web-dev0521! - Fixes Astro.preferredLocale returning the wrong value when i18n.locales mixes object-form entries ({ path, codes }) with string entries that normalize to the same locale. The first matching code in the configured locales order is now selected, matching the documented behavior.

  • #16591 cce20f7 Thanks @matthewp! - Uses a consistent generic error message in the image endpoint across all adapters

  • #16629 f54be80 Thanks @g-taki! - Fixes a bug where SSR responses in astro dev could crash with TypeError: this.logger.flush is not a function.

  • #16589 3740b24 Thanks @ArmandPhilippot! - Fixes an outdated code snippet in the documentation for session storage configuration.

  • Updated dependencies [354e231]:

    • @astrojs/telemetry@3.3.2

@astrojs/cloudflare@13.4.0

Minor Changes

  • #16519 1b1c218 Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.

    Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in image.remotePatterns or a domain in image.domains, Astro will fail with a helpful error message.

    In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:

    export default defineConfig({
      image: {
        domains: ['example.com', 'cdn.example.com'],
      },
    });
    {
      /* Redirects to https://cdn.example.com/assets/image.png: */
    }
    <Image
      src="https://example.com/assets/image.png"
      width="1920"
      height="1080"
      alt="An example image."
    />;
    
    {
      /* Redirects to https://malicious.com/image.png: */
    }
    <Image
      src="https://example.com/bad-image.png"
      width="1920"
      height="1080"
      alt="An example image."
    />;

    In cases where all redirects to HTTPS hosts should be trusted, the following configuration for image.remotePatterns can be used:

    export default defineConfig({
      image: {
        remotePatterns: [
          {
            protocol: 'https',
          },
        ],
      },
    });

Patch Changes

  • Updated dependencies []:
    • @astrojs/underscore-redirects@1.0.3

@astrojs/language-server@2.16.8

Patch Changes

  • #16627 5778cb7 Thanks @Princesseuh! - Fixes unintended dependency on the typescript package being available to the language server

@astrojs/telemetry@3.3.2

Patch Changes

  • #16260 354e231 Thanks @gameroman! - Refactors internal config logic to remove the dlv dependency in favor of native logic
withastro/astro

Changes

  • Fix pushDirective in the CSP runtime duplicating the new directive once per existing non-matching directive.
  • Root cause: the else branch inside the loop pushed newDirective on every non-match instead of once after the loop. Replaced the deduplicated flag with a matched flag and moved the newDirective push outside the loop, guarded by !matched.
  • Closes #16594.

Before: pushDirective(["img-src 'self'", "style-src 'self'"], "script-src 'self'")["img-src 'self'", "script-src 'self'", "style-src 'self'", "script-src 'self'"] (duplicated + interleaved)
After:["img-src 'self'", "style-src 'self'", "script-src 'self'"]

Also fixes Mode 2: a directive that merges with a later existing entry no longer leaves an unmerged copy in front of the merged result.

Don't forget a changeset! Run pnpm changeset. — Added: .changeset/fix-csp-push-directive-duplicate.md

Testing

  • Added a regression: issue #16594 suite in packages/astro/test/units/csp/runtime.test.ts covering:
    • Mode 1 — non-matching new directive appended exactly once.
    • Mode 2 — merge with a later match, no unmerged copy left behind.
    • Empty input list edge case.
    • Many non-matches: directive-name uniqueness asserted via a Map count.
    • Table-driven sweep across "non-match", "match in middle", "match at end" shapes, asserting both length and Set-based name uniqueness.
  • All 11 tests in csp/runtime.test.ts pass locally (node --test test/units/csp/runtime.test.ts).

Docs

No docs change required — pushDirective is internal CSP runtime; public insertDirective() semantics are unchanged (this restores them to the documented behavior).

withastro/astro

Changes

This PR does an overhaul of all error messages to make them more consistent with each other, namely:

  • fixing missing full stop at the end of messages.
  • adding missing backticks to code excerpts.
  • adding () to differentiate functions from simple properties/objects.
  • improving error titles/messages with more context.

Testing

I am not sure if adding backticks in a few places could break rendering, we'll be sure once we get the automated PR on docs.

Docs

That's all it is.

withastro/astro

Changes

  • The redirect template in 3xx.ts interpolated URL values directly into HTML without escaping. The sibling 4xx.ts template already escapes its values using html-escaper. This applies the same pattern to the redirect template for consistency.

Testing

  • No new tests. The change adds escape() calls around existing interpolations — the same approach 4xx.ts already uses.

Docs

  • No docs update needed.
withastro/astro

Changes

  • The Node image endpoint (node.ts) returns a generic 'Internal Server Error' string on failure, but the generic adapter endpoint (generic.ts) was interpolating the error object into the response. This aligns generic.ts with node.ts so all adapters use the same consistent error response.

Testing

  • No new tests. Single-line string replacement in an error path — existing image endpoint tests cover the surrounding behavior.

Docs

  • No docs update needed.
withastro/starlight

This PR contains the following updates:

Package Type Update Change
withastro/automation action digest e27ec6dc8a2e8b

Configuration

📅 Schedule: (UTC)

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

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

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

🔕 Ignore: Close this PR and you won't be reminded about this update again.


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

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

withastro/starlight

Description

Especially in Chinese and Japanese, the CSS property text-autospace: normal is preferred for better looking like books by automatically inserting spaces with the moderate width.

Without this, some users and AIs try to insert such spaces manually as workarounds but they are too wide and prevent keywords from searched.

Japanese:

Before:

image

After:

image

Note: the current Chinese content of Starlight translation is not affected because it took the workarounds like manually inserting spaces.

The other languages are not affected.

withastro/astro

Changes

  • During astro build, createTempViteServer (in packages/astro/src/core/sync/index.ts) creates a Vite dev server purely to resolve virtual modules for type generation. The server never handles HTTP requests, but every plugin's configureServer hook still fires — including adapter plugins that start heavy runtimes (notably @astrojs/cloudflare@cloudflare/vite-plugin → miniflare/workerd, ~1–3.5 s of overhead).
  • Additionally, top-level optimizeDeps: { noDiscovery: true } only applies to the client environment under Vite 7's Environment API, so adapters' configEnvironment hooks re-injected their optimizeDeps.include lists into ssr / astro / prerender and forced unnecessary pre-bundling.
  • Two changes in createTempViteServer:
    • Per-environment optimizeDeps: { noDiscovery: true, include: [] } for all four environments.
    • Inline astro:sync:strip-server-hooks plugin that removes configureServer from non-astro: plugins in configResolved.
  • Net effect on a representative project: Types Generated drops from ~3.6 s to ~125 ms; total build from ~13 s to ~9.2 s.
  • Closes #16332.
  • Changeset added (patch for astro).

This is the fix astrobot-houston suggested on the issue, applied verbatim.

Testing

  • pnpm exec astro-scripts test test/astro-sync.test.ts --strip-types — all 9 existing sync tests pass.
  • Verified end-to-end against the MRE from #16332 (https://github.com/adamchal/astro-sync-perf): with @astrojs/cloudflare on the adapter, Types Generated drops from ~2.3 s to ~100 ms and miniflare no longer starts during sync.
  • I have been running this same patch (applied as a postinstall monkey-patch) on a production Astro/Cloudflare site for the past few weeks with no regressions in dev or build.

Docs

No user-facing API changes — this is a build-time perf fix. Adapter authors who relied on configureServer firing during sync would notice, but that path was unintentional and has no documented contract.

withastro/astro

Changes

  • Fix z.array(z.boolean()) in form actions incorrectly coercing the string "false" to true.
  • handleFormDataGetAll was using entries.map(Boolean), but FormData values are always strings and Boolean("false") === true. Replaced with the same 'true' / 'false' comparison already used for single z.boolean() fields in handleFormDataGet.
  • Closes #16584.

Before: ["false", "true", "false"][true, true, true]
After: ["false", "true", "false"][false, true, false]

Testing

  • Added a new boolean arrays suite in packages/astro/test/units/actions/form-data-to-object.test.ts:
    • should preserve "false" string values in boolean arrays — direct regression test for the reported case.
    • should coerce mixed boolean array values correctly — table-driven coverage across all-true / all-false / mixed inputs.
  • All 25 tests in formDataToObject pass locally (node --test test/units/actions/form-data-to-object.test.ts).

Docs

No docs change required — this is a behavior fix bringing array-element parsing in line with the already-documented single-boolean coercion behavior.

withastro/astro

fixes #13297

Changes

In the current implementation, enabling prefetch settings causes links to be collected via document.getElementsByTagName('a') on the initial page load.
While this approach works for most cases, it fails to cover certain scenarios, such as the one reported in issue #13297.

The root cause of issue #13297 is that when a component with the server:defer attribute uses top-level await, its links are rendered after the Promise resolves, meaning they are not captured by the initial document.getElementsByTagName('a') call.
More broadly, the same issue occurs whenever links are dynamically added to the DOM after the initial page load.

To address this, this pull request adds the observeDynamicLinks option, which uses a MutationObserver to watch for dynamically added link elements.
This ensures that links added after the initial render are properly captured.

This feature is opt-in.

Why MutationObserver?

Investigation process
  • First, I checked the code around server:defer to see if it used any event notifications.
  • It did not, but I found that the prefetch code [uses the astro:page-load event].
    document.addEventListener('astro:page-load', () => {
  • When I looked into whether astro:page-load could be used here, I found that the constant defining this event name is marked as @deprecated, so I decided to look for a different approach.
    /** @deprecated This will be removed in Astro 7 */
    export const TRANSITION_PAGE_LOAD = 'astro:page-load';
  • I also considered that the root cause of issue #13297 is not specific to server:defer, the same problem can occur in other situations where links are added to the DOM dynamically. For this reason, I wanted an approach that resolves the issue within the prefetch code itself, rather than adding prefetch-dependent logic to the server:defer side.
  • As a result, I concluded that MutationObserver is the best approach, as it is well-suited for observing DOM changes and allows the fix to be fully self-contained within the prefetch implementation.

Testing

Added tests to verify that the observeDynamicLinks option works correctly in the following scenarios:

  • Links dynamically added via server:defer, as reported in issue #13297
  • Links dynamically added by a toggle component (ToggleButton.jsx)

Docs

/cc @withastro/maintainers-docs for feedback!

withastro/astro

Changes

SVGs are always troublesome in the end and wanting to actually process them is so uncommon that it's best to leave it to the user.

I decided to put it on a global image option so that other services could also use it, but I'd be okay with it being a service config as well, it's ok

Testing

Added a test

Docs

WIP

withastro/astro

This PR contains the following updates:

Package Change Age Confidence
@markdoc/markdoc (source) ^0.5.4^0.5.7 age confidence
devalue ^5.6.3^5.8.0 age confidence
preact (source) ^10.28.4^10.29.1 age confidence

Release Notes

markdoc/markdoc (@​markdoc/markdoc)

v0.5.7

Compare Source

What's Changed
  • Make table-syntax validation error location more precise by @​yue-stripe in #​605
    • the validate() method now returns more location/line info than before
    • Build output now includes ES2022 syntax. The esbuild version used to build markdoc was upgraded from 0.13 to 0.25, which raises the effective output target from ~ES2020 to ES2022+. If your build pipeline transpiles @​markdoc/markdoc, you may need to ensure your toolchain supports ES2022 features such as static class blocks.

Full Changelog: markdoc/markdoc@0.5.6...0.5.7

v0.5.6

Compare Source

What's Changed

  • Add validation for invalid Markdoc table syntax by @​yue-stripe in #​603
    • Markdoc.validate will now raise errors for invalid syntax within {% table %} tags.

Full Changelog: markdoc/markdoc@0.5.5...0.5.6

v0.5.5

Compare Source

Added support for providing a list of conditional tags in addition to if. (['if'] is set by default). No breaking changes.

#​600

sveltejs/devalue (devalue)

v5.8.0

Compare Source

Minor Changes
  • c5115b0: feat: add stringifyAsync for async serialization

v5.7.1

Compare Source

Patch Changes
  • 8becc7c: fix: handle regexes consistently in uneval's value and reference formats

v5.7.0

Compare Source

Minor Changes
  • df2e284: feat: use native alternatives to encode/decode base64
  • 498656e: feat: add DataView support
  • a210130: feat: whitelist Float16Array
  • df2e284: feat: simplify TypedArray slices
Patch Changes
  • 5590634: fix: get uneval type handling up to parity with stringify
  • 57f73fc: fix: correctly support boxed bigints and sentinel values
preactjs/preact (preact)

v10.29.1

Compare Source

Fixes

Maintenance


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

Minor adjustment to @astrojs/mdx so smartypants object options are passed to remark-smartypants, matching Astro's markdown behavior since v6.1.

Testing

Added a regression test that verifies smartypants object options (e.g. dashes: "oldschool") are applied in MDX. The test fails without this change (e.g., for dashes: "oldschool" an em-dash is expected while the default configuration produces an en-dash) and passes with it.

withastro/astro

Changes

issue : #16273

Fixed issue where custome elements in MDX bypass the renderer pipline. By detecting hyphens in tag names within the JSX runtime, custom elements are now correctly routed to the renderer for SSR, ensuring parity with .astro files.

Testing

Before fixing bug

Test result after implement all test.

スクリーンショット 2026-05-05 23 33 57

After fixing bug

スクリーンショット 2026-05-05 23 24 59

Docs

withastro/starlight

This PR contains the following updates:

Package Type Update Change
pnpm/action-setup action patch v6.0.4v6.0.5

Release Notes

pnpm/action-setup (pnpm/action-setup)

v6.0.5

Compare Source

What's Changed
  • fix: append (not prepend) action node dir to PATH for npm bootstrap by @​zkochan in #​241

Full Changelog: pnpm/action-setup@v6.0.4...v6.0.5


Configuration

📅 Schedule: (UTC)

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

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

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

🔕 Ignore: Close this PR and you won't be reminded about this update again.


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

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

lin-stephanie/astro-antfustyle-theme

Description

withastro/astro

Changes

Fixes prerendered pages returning 404 when build.format: 'file' is set in the Node standalone adapter.

When build.format is 'file', pages are emitted as flat .html files (about.html) instead of about/index.html. The send library's extensions option tells it to try appending .html before giving up, so a request to /about resolves correctly on disk instead of falling through to the SSR handler as a 404.

One-line change in serve-static.tsapp.manifest.buildFormat is already available in scope (same as app.manifest.trailingSlash used just above).

Testing

Added two cases to the existing prerender.test.ts under a new "build.format: 'file'" describe block:

  • prerendered page is served correctly via its clean URL
  • SSR route still works alongside it

Docs

No user-facing behavior change beyond bug fix — no docs update needed.

Fixes #16570.

withastro/astro

Changes

Fixes the Vite build warning reported in #15957:

[WARN] Plugin vite:reporter: Unused imports from "@astrojs/internal-helpers/remote":
 – "matchHostname", "matchPathname", "matchPort", "matchProtocol"

plugin-internals.ts sets noExternal: ['astro'] for the prerender environment, which bundles astro but leaves @astrojs/internal-helpers as an external. Rollup then sees that those four helpers are imported by the bundled astro code but not consumed inside the bundle boundary, which triggers the unused-import warning.

Adding @astrojs/internal-helpers to noExternal alongside astro co-bundles it in the prerender environment, eliminating the warning. This is a first-party workspace package and the same pattern is already used elsewhere.

Testing

The warning is emitted by Vite/Rollup during the build phase and is not directly observable in the integration test suite without capturing stderr. Verified manually by running a project build with output: 'hybrid' before and after the change — the warning disappears with the fix applied.

Docs

No user-facing behavior change — no docs update needed.

Fixes #15957.

withastro/astro

Changes

Automatically injects Cache-Control: public, max-age=31536000, immutable into Cloudflare's _headers file for hashed Astro assets (/_astro/*), skipping when build.assetsPrefix is set or when the user already has a matching Cache-Control rule.

Testing

Unit tests.

Docs

/cc @withastro/maintainers-docs for feedback!

withastro/astro

Changes

  • Invalidates the astro:data-layer-content virtual module in the SSR module runner's evaluatedModules cache when the data store changes, not just in the server-side module graph. The existing invalidateModule call only clears the server's transformResult, but a subsequent transformRequest (triggered during module resolution) re-populates it before the runner's fetchModule check, causing a false cache hit that returns stale data.

Closes #16561

Testing

  • No new tests added. Manually verified with a content collection setup: deleting entries correctly updates getCollection() results on the next page load in dev.

Docs

  • No docs update needed; this is a bug fix with no API change.
withastro/astro

Changes

  • Fixes #16564data-astro-prefetch="tap" silently failing when the user clicks a nested child element (e.g. <span>, <img>, <svg>, Astro <Image />) inside an anchor
  • e.target on touchstart/mousedown is the deepest element under the pointer, not necessarily the <a> — use closest('a') to walk up to the anchor before checking the strategy
  • Particularly impactful on slow connections / data-saver mode, where all strategies collapse to tap — meaning zero prefetching was happening for any link with nested content for the users who would benefit most

Testing

  • Added e2e fixture link <a data-astro-prefetch="tap"><span>tap nested</span></a> and a corresponding page
  • Added e2e test in prefetch.test.ts that clicks the inner <span> and asserts the prefetch request fires

Docs

No docs change needed — this is a bug fix restoring already-documented behavior, not a new feature or API change.

withastro/astro

Closes #16563

Summary

Calling session.delete(key) as the first mutation in a request (no prior get, set, has, keys, etc.) did not write back to session storage. The session stayed dirty in memory, but [PERSIST_SYMBOL]() skipped the save path because #data was still undefined, so the backing store kept the old value and the next request could still read the “deleted” key.

Root cause

  • set() initializes the in-memory map with this.#data ??= new Map(); delete() only did this.#data?.delete(key), so #data could remain undefined.
  • Persistence gated saves on if (this.#dirty && this.#data), so delete-only flows never called setItem and #toDelete was never applied to storage.

Fix

  • Initialize the map in delete() the same way as in set(): this.#data ??= new Map() before removing the key, so the persist path runs and #ensureData() can merge, apply deletions, and serialize to the driver.

Testing

  • Added a unit test that pre-seeds backing storage, calls only delete() then persist, and asserts a new AstroSession with the same storage/session id no longer returns the deleted key.
  • Adjusted an existing persistence test so the follow-up session’s storage get returns parsed JSON (matching real unstorage behavior) after setItem writes a devalue JSON string.

Docs

No documentation change required; behavior now matches what the session API already promises.

Contributor checklist (see CONTRIBUTING.md)

  • pnpm exec changeset for the astro package (user-facing bugfix)
  • Tests: pnpm -C packages/astro exec astro-scripts test "test/units/sessions/astro-session.test.ts"
withastro/astro

Fixes #16553

Changes

  • Fixes non-prerendered routes (e.g. SSR endpoints using cloudflare:workers) failing when a dynamic prerendered route like [page].astro exists in the same project with prerenderEnvironment: 'node'.
  • The dev prerender middleware was using matchAllRoutes(), which returns every route matching a pathname. A request to /ssr would match both ssr.astro (non-prerendered) and [page].astro (prerendered), and since one match was prerendered, the request was incorrectly routed to the Node handler. Replaced with matchRoute(), which returns only the highest-priority match.

Testing

  • Added a dynamic prerendered route ([page].astro with getStaticPaths) to the existing prerender-node-env fixture. The existing "renders SSR page through workerd" test now exercises this scenario.

Docs

  • No docs needed.
withastro/starlight

This PR contains the following updates:

Package Type Update Change
withastro/automation action digest 497c926e27ec6d

Configuration

📅 Schedule: (UTC)

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

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

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

🔕 Ignore: Close this PR and you won't be reminded about this update again.


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

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

withastro/astro

Changes

  • Updates ./test-utils.js imports to ./test-utils.ts in three .test.js files that were missed when #16492 renamed test-utils.jstest-utils.ts.

Testing

  • No new tests. This fixes the existing test:integration:js suite which fails immediately with ERR_MODULE_NOT_FOUND.

Docs

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

Changes

Simplify db's tsconfig by removing tsconfig.virtual.json.

Its only purpose is to prevent incorrect auto-imports from editor hints and doesn’t affect the runtime output, so it should be relatively safe to remove it.

This file makes it tricky for the TypeScript project references refactor.

Testing

Green CI

Docs

N/A

withastro/astro

Changes

  • Double-encoded URL paths like /api/%2561dmin/users could bypass middleware auth checks because the normalization fallback left ctx.url.pathname half-decoded.
  • Adds MultiLevelEncodingError as a distinct error type so callers can distinguish it from generic decode failures.
  • #createNormalizedUrl now re-throws MultiLevelEncodingError instead of falling back. Other decode errors (truly malformed URLs) still fall back gracefully.
  • BaseApp.render() catches MultiLevelEncodingError and returns 400 Bad Request.

Testing

  • Added test/units/app/double-encoding-bypass.test.ts — 7 tests using a catch-all /api/[...path] endpoint behind auth middleware. Covers the direct path (401), single-encoded (401), double-encoded (400), multiple encoded segments (400), public routes (200), and non-protected API routes (200).

Docs

  • No docs update needed — no user-facing API changes.
withastro/astro

Changes

  • Fixes a bug in @astrojs/cloudflare where custom KV namespace bindings (e.g. MY_KV, CACHE) were silently removed when Astro's session functionality was enabled.
  • The root cause was in packages/integrations/cloudflare/src/wrangler.ts: when injecting the SESSION KV binding, the code returned a fresh single-item array instead of merging with the user's existing kv_namespaces.
  • Extracted a withSessionKVBinding helper that copies existing namespaces and appends the SESSION binding, preserving all user-defined bindings.
  • Fix applies to both the top-level config and the previews config (same code path).
  • Updated the existing test that was asserting the broken behavior, and added a new test covering the previews case.

Closes #16554

Testing

  • Updated packages/integrations/cloudflare/test/wrangler.test.ts: fixed the assertion in "adds SESSION binding when other KV bindings exist but not the session one" to verify OTHER_KV is preserved alongside SESSION.
  • Added a new test "preserves existing previews KV bindings when adding SESSION binding" covering the same scenario in the previews config.
  • No integration test needed: the bug and fix are fully exercised by the unit test suite for cloudflareConfigCustomizer.

Docs

No docs change needed. This is a bug fix restoring behavior that users already rely on — their existing kv_namespaces entries in wrangler.toml now survive when sessions are enabled.

withastro/astro

Changes

  • Fixes a regex bug where return was incorrectly rewritten inside string literals, template literals, and comments during esbuild's dep-scan / frontmatter error-check phase
  • Replaces the two-pass \breturn\b regex with a single-pass state-machine regex that skips over all string/comment tokens before rewriting bare return statements
  • Fixes the primary report case: gen.return(value) → was producing gen.throw (value) (syntax error); now preserved correctly
  • Extracts a shared replaceTopLevelReturns() helper into utils.ts to deduplicate logic between the cloudflare esbuild plugin and compile.ts

Affected files:

  • packages/integrations/cloudflare/src/esbuild-plugin-astro-frontmatter.ts
  • packages/astro/src/vite-plugin-astro/utils.ts
  • packages/astro/src/vite-plugin-astro/compile.ts

Closes #16551

Testing

The fix is a pure regex change with no new dependencies. Verified manually against the cases from the issue:

Input Before After
gen.return(val) gen.throw (val) ← syntax error unchanged ✓
"return value" "throw value" unchanged ✓
// return foo // throw foo unchanged ✓
`return ${x}` `throw ${x}` unchanged ✓
return foo return foo throw foo
return; return; throw 0;

No new tests added — the two affected code paths are internal-only (esbuild dep scan and compile-error enhancement), exercised by the existing integration test suite.

Docs

No user-facing behavior change — this only affects internal dep scanning and error reporting during compilation. No docs update needed.

withastro/astro

Changes

fixes #16524.

with vite.css.transformer: 'lightningcss', scoped styles using a nested & selector inside :where(...) silently produce css where the scope attribute lands on the matched child instead of the intended parent. tailwind v4's space-x-*, space-y-*, and divide-* all expand to this shape and tailwind v4 ships with lightningcss in the loop, so any astro 6 + tailwind v4 project using these utilities in scoped <style> blocks targeting children from another component gets broken spacing in production while the css rule still looks present in the bundle.

what's happening: lightningcss runs inside vite's preprocessCSS() and lowers nesting before @astrojs/compiler injects scope attributes. by the time the compiler sees the css, .parent is buried inside :where(...) and isn't a top-level compound anymore, so the injector prepends [data-astro-cid-X] as a new leading compound — which constrains the wrong element.

fix in packages/astro/src/core/compile/style.ts: when the user has css.transformer: 'lightningcss', call preprocessCSS() with a shallow-cloned vite config whose css.lightningcss.exclude ORs in Features.Nesting, so lightningcss skips its nesting-lowering pass for this preprocess call. vite's final pipeline still lowers nesting for the bundle so output stays compatible with the user's targets. clone is non-mutating per call so it's safe under parallel .astro compiles. lightningcss is resolved from the user's project root via createRequire — same instance vite uses, no new dep added to packages/astro. falls back to the original config if lightningcss can't be resolved.

i think a better fix would live in @astrojs/compiler itself, teach the scope injector to recognize :where(<simple-compound>, ...) as a leading-compound wrapper and attach the cid to the inner compound instead of prepending a new one. similar in spirit to withastro/compiler#1153 but extended to cover the :where(...) case, and it'd cover any future css transform that produces a similarly shaped selector not just lightningcss.

Testing

added a regression fixture under packages/astro/test/:

  • lightningcss-scoped-nesting.test.ts — compiles the reporter's exact shape with vite.css.transformer: 'lightningcss' and asserts the scope id binds to .parent, not as a leading compound on the matched child.
  • fixture under packages/astro/test/fixtures/lightningcss-scoped-nesting/ — single page using the reporter's :where(& > :not(:last-child)) selector inside a scoped <style> block.

reporter's reproduction repo: https://github.com/rklos/astro-css-bug-repro

Docs

no docs changes. silent regression in css output; behavior after the fix matches what the docs already describe for scoped styles + nested selectors.

withastro/astro

Changes

upgrade/package.json has "build": "astro-scripts build \"src/index.ts\" --bundle && tsc". The output dist/index.js should be emitted by astro-scripts, while tsc should only emit .d.ts files.

In #16493, tsc emits both .js and .d.ts files, which overrides the bundled dist/index.js file that was just emitted by astro-scripts.

This PR updates upgrade/tsconfig.json to ensure that tsc won't emit any .js files.

Testing

Before this PR:

$ pnpm -w build
$ tree ./packages/upgrade/dist/
./packages/upgrade/dist/
├── actions
│   ├── context.d.ts
│   ├── context.js
│   ├── help.d.ts
│   ├── help.js
│   ├── install.d.ts
│   ├── install.js
│   ├── verify.d.ts
│   └── verify.js
├── index.d.ts
├── index.js
├── messages.d.ts
├── messages.js
├── shell.d.ts
└── shell.js

2 directories, 14 files

After this PR:

$ pnpm -w build
$ tree ./packages/upgrade/dist/
./packages/upgrade/dist/
├── actions
│   ├── context.d.ts
│   ├── help.d.ts
│   ├── install.d.ts
│   └── verify.d.ts
├── index.d.ts
├── index.js
├── messages.d.ts
└── shell.d.ts

2 directories, 8 files

After this PR, only one bundled dist/index.js file is emitted in dist/.

Docs

The previous and afterward outputs can both work, so there is no behavior change. No docs are needed.


Last fetched:  |  Scheduled refresh: Every Saturday

See Customizing GitHub Activity Pages to configure your own

Inspired by prs.atinux.com