From b4946e293f94136f7d167982591f6fbd6cf7664c Mon Sep 17 00:00:00 2001 From: "helio.semedo" Date: Tue, 26 May 2026 15:24:49 +0100 Subject: [PATCH 1/3] fix(cloudflare): resolve vite plugin config once across call sites User options like remoteBindings, inspectorPort, persistState, configPath, and auxiliaryWorkers were silently dropped during astro preview because the preview entrypoint only received the adapter-computed half of the config. Resolve the full @cloudflare/vite-plugin config once at integration setup time so every call site (dev/build, prerenderer, preview) spreads a single merged object and cannot accidentally drop user options. --- .../cloudflare-preview-remote-bindings.md | 5 ++++ .../cloudflare/src/entrypoints/preview.ts | 2 +- packages/integrations/cloudflare/src/index.ts | 27 +++++++++++-------- .../cloudflare/src/prerenderer.ts | 10 +------ packages/integrations/cloudflare/virtual.d.ts | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) create mode 100644 .changeset/cloudflare-preview-remote-bindings.md diff --git a/.changeset/cloudflare-preview-remote-bindings.md b/.changeset/cloudflare-preview-remote-bindings.md new file mode 100644 index 000000000000..fb3f5ae176e7 --- /dev/null +++ b/.changeset/cloudflare-preview-remote-bindings.md @@ -0,0 +1,5 @@ +--- +'@astrojs/cloudflare': patch +--- + +Fixes user options passed to `cloudflare({...})` (`remoteBindings`, `inspectorPort`, `persistState`, `configPath`, `auxiliaryWorkers`) being silently ignored during `astro preview`. The adapter now resolves the full `@cloudflare/vite-plugin` config once at integration setup time and reuses that single resolved value across the dev/build plugin, the prerenderer's preview server, and the `astro preview` entrypoint, so user options can no longer be dropped at one of the call sites. diff --git a/packages/integrations/cloudflare/src/entrypoints/preview.ts b/packages/integrations/cloudflare/src/entrypoints/preview.ts index ce9141b9b4ae..35eda7401502 100644 --- a/packages/integrations/cloudflare/src/entrypoints/preview.ts +++ b/packages/integrations/cloudflare/src/entrypoints/preview.ts @@ -49,7 +49,7 @@ const createPreviewServer: CreatePreviewServer = async ({ allowedHosts, }, plugins: [ - cfVitePlugin({ ...globalThis.astroCloudflareOptions, viteEnvironment: { name: 'ssr' } }), + cfVitePlugin({ ...globalThis.astroCloudflareConfig, viteEnvironment: { name: 'ssr' } }), ], }); } catch (err) { diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index 6e697caa0bfa..39ec3502b80d 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -175,7 +175,7 @@ export default function createIntegration({ const usesContentCollections = hasContentCollectionsConfig(config.srcDir); const prebundleContentRuntime = command === 'dev' && usesContentCollections; - cfPluginConfig = { + const adapterPluginConfig: Partial = { config: cloudflareConfigCustomizer({ needsSessionKVBinding, sessionKVBindingName, @@ -203,11 +203,21 @@ export default function createIntegration({ }, }), }; - - // The preview entrypoint uses Cloudflare's vite plugin and so it needs access - // to the config. But there's no proper API for this so we use globalThis. + // Resolve the full `@cloudflare/vite-plugin` config exactly once by merging + // the user's `cloudflare({...})` options (e.g. `remoteBindings`, + // `inspectorPort`, `persistState`, `configPath`, `auxiliaryWorkers`) with + // the adapter's computed bindings/wrangler wiring. Downstream call sites + // (the dev/build plugin instance, the prerenderer's preview server, and + // the `astro preview` entrypoint) then just spread `cfPluginConfig` and + // cannot accidentally drop user options (see #16705 and related CHANGELOG + // entries). + cfPluginConfig = { ...cloudflareOptions, ...adapterPluginConfig }; + + // The preview entrypoint uses Cloudflare's vite plugin and so it needs + // access to the resolved config. There's no proper API for this so we + // use globalThis. if (command === 'preview') { - globalThis.astroCloudflareOptions = cfPluginConfig; + globalThis.astroCloudflareConfig = cfPluginConfig; } // Including prismjs files in `optimizeDeps.includes` when `@astrojs/prism` is not installed @@ -241,11 +251,7 @@ export default function createIntegration({ ...(prerenderEnvironment === 'node' && command === 'dev' ? [createNodePrerenderPlugin()] : []), - cfVitePlugin({ - ...cloudflareOptions, - ...cfPluginConfig, - viteEnvironment: { name: 'ssr' }, - }), + cfVitePlugin({ ...cfPluginConfig, viteEnvironment: { name: 'ssr' } }), { name: '@astrojs/cloudflare:cf-imports', enforce: 'pre', @@ -447,7 +453,6 @@ export default function createIntegration({ if (prerenderEnvironment === 'workerd') { setPrerenderer( createCloudflarePrerenderer({ - cloudflareOptions, root: _config.root, serverDir: _config.build.server, clientDir: _config.build.client, diff --git a/packages/integrations/cloudflare/src/prerenderer.ts b/packages/integrations/cloudflare/src/prerenderer.ts index cf9dd807cf4e..f95c38eb4b2d 100644 --- a/packages/integrations/cloudflare/src/prerenderer.ts +++ b/packages/integrations/cloudflare/src/prerenderer.ts @@ -21,7 +21,6 @@ import { } from './utils/prerender-constants.js'; interface CloudflarePrerendererOptions { - cloudflareOptions: Partial; root: AstroConfig['root']; serverDir: AstroConfig['build']['server']; clientDir: AstroConfig['build']['client']; @@ -36,7 +35,6 @@ interface CloudflarePrerendererOptions { * This allows prerendering to happen in the same runtime that will serve the pages. */ export function createCloudflarePrerenderer({ - cloudflareOptions, root, serverDir, clientDir, @@ -84,13 +82,7 @@ export function createCloudflarePrerenderer({ port: 0, // Let the OS pick a free port open: false, }, - plugins: [ - cfVitePlugin({ - ...cloudflareOptions, - ...cfPluginConfig, - viteEnvironment: { name: 'prerender' }, - }), - ], + plugins: [cfVitePlugin({ ...cfPluginConfig, viteEnvironment: { name: 'prerender' } })], }); const address = previewServer.httpServer.address(); diff --git a/packages/integrations/cloudflare/virtual.d.ts b/packages/integrations/cloudflare/virtual.d.ts index 45c757c342d4..6c8b7e0d3f0d 100644 --- a/packages/integrations/cloudflare/virtual.d.ts +++ b/packages/integrations/cloudflare/virtual.d.ts @@ -20,4 +20,4 @@ interface Env extends Cloudflare.Env {} type ImagesBinding = import('@cloudflare/workers-types').ImagesBinding; type Fetcher = import('@cloudflare/workers-types').Fetcher; -declare var astroCloudflareOptions: import('@cloudflare/vite-plugin').PluginConfig; +declare var astroCloudflareConfig: import('@cloudflare/vite-plugin').PluginConfig; From 7dea3a047f7f1b2d6e300bdeb90ee08ad6f7862f Mon Sep 17 00:00:00 2001 From: "helio.semedo" Date: Tue, 26 May 2026 17:28:13 +0100 Subject: [PATCH 2/3] test(cloudflare): add regression test for remoteBindings during astro preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verifies that astro preview honors remoteBindings: false when a wrangler binding is flagged remote: true. Confirmed to fail before the fix in b4946e29 — wrangler.maybeStartOrUpdateRemoteProxySession throws from the @cloudflare/vite-plugin preview path because the option is dropped. --- .../preview-remote-bindings/astro.config.mjs | 10 ++++++ .../src/pages/index.astro | 3 ++ .../preview-remote-bindings/wrangler.jsonc | 15 ++++++++ .../test/preview-remote-bindings.test.ts | 34 +++++++++++++++++++ 4 files changed, 62 insertions(+) create mode 100644 packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/astro.config.mjs create mode 100644 packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/src/pages/index.astro create mode 100644 packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/wrangler.jsonc create mode 100644 packages/integrations/cloudflare/test/preview-remote-bindings.test.ts diff --git a/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/astro.config.mjs b/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/astro.config.mjs new file mode 100644 index 000000000000..cdea3f8f1226 --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/astro.config.mjs @@ -0,0 +1,10 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare'; + +export default defineConfig({ + output: 'server', + adapter: cloudflare({ + remoteBindings: false, + }), +}); diff --git a/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/src/pages/index.astro b/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/src/pages/index.astro new file mode 100644 index 000000000000..97d9fec08b3b --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/src/pages/index.astro @@ -0,0 +1,3 @@ +--- +--- +

preview ok

diff --git a/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/wrangler.jsonc b/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/wrangler.jsonc new file mode 100644 index 000000000000..d8a818058fa6 --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/preview-remote-bindings/wrangler.jsonc @@ -0,0 +1,15 @@ +{ + "name": "test-preview-remote-bindings", + "compatibility_date": "2026-01-28", + "assets": { + "directory": "./dist", + "binding": "ASSETS" + }, + "kv_namespaces": [ + { + "binding": "REMOTE_KV", + "id": "abcdef0123456789abcdef0123456789", + "remote": true + } + ] +} diff --git a/packages/integrations/cloudflare/test/preview-remote-bindings.test.ts b/packages/integrations/cloudflare/test/preview-remote-bindings.test.ts new file mode 100644 index 000000000000..d8c213a0b2cc --- /dev/null +++ b/packages/integrations/cloudflare/test/preview-remote-bindings.test.ts @@ -0,0 +1,34 @@ +import assert from 'node:assert/strict'; +import { after, before, describe, it } from 'node:test'; +import { type Fixture, loadFixture, type PreviewServer } from './test-utils.ts'; + +describe('astro preview with remoteBindings: false', () => { + let fixture: Fixture; + let previewServer: PreviewServer | undefined; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/preview-remote-bindings/', + }); + await fixture.build(); + }); + + after(async () => { + await previewServer?.stop(); + }); + + it('starts the preview server without attempting a remote proxy session', async () => { + // The fixture's wrangler config declares a KV namespace flagged `remote: true`, + // and the adapter is configured with `remoteBindings: false`. The cloudflare + // vite plugin must observe `remoteBindings: false` and skip + // `wrangler.maybeStartOrUpdateRemoteProxySession`, which would otherwise fail + // in environments without Cloudflare credentials. + // + // This regresses whenever the `astro preview` entrypoint drops user adapter + // options on the floor — see #16705 for the equivalent bug on the prerenderer + // path. + await assert.doesNotReject(async () => { + previewServer = await fixture.preview(); + }, 'preview server should start without requiring Cloudflare credentials'); + }); +}); From f15bec99bc87c51b562c9d9236a4c4cb46094d92 Mon Sep 17 00:00:00 2001 From: "helio.semedo" Date: Tue, 26 May 2026 17:39:41 +0100 Subject: [PATCH 3/3] chore: cleanup comments --- .../cloudflare/test/preview-remote-bindings.test.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/integrations/cloudflare/test/preview-remote-bindings.test.ts b/packages/integrations/cloudflare/test/preview-remote-bindings.test.ts index d8c213a0b2cc..502cbe32ffd5 100644 --- a/packages/integrations/cloudflare/test/preview-remote-bindings.test.ts +++ b/packages/integrations/cloudflare/test/preview-remote-bindings.test.ts @@ -17,16 +17,8 @@ describe('astro preview with remoteBindings: false', () => { await previewServer?.stop(); }); + // assumption: will reject in CI since since remote proxy session is expected unavailable in CI it('starts the preview server without attempting a remote proxy session', async () => { - // The fixture's wrangler config declares a KV namespace flagged `remote: true`, - // and the adapter is configured with `remoteBindings: false`. The cloudflare - // vite plugin must observe `remoteBindings: false` and skip - // `wrangler.maybeStartOrUpdateRemoteProxySession`, which would otherwise fail - // in environments without Cloudflare credentials. - // - // This regresses whenever the `astro preview` entrypoint drops user adapter - // options on the floor — see #16705 for the equivalent bug on the prerenderer - // path. await assert.doesNotReject(async () => { previewServer = await fixture.preview(); }, 'preview server should start without requiring Cloudflare credentials');