From 0d288d32bee17d2a801dc535650c514629afb525 Mon Sep 17 00:00:00 2001 From: "factory[bot]" Date: Tue, 29 Sep 2026 20:38:56 +0000 Subject: [PATCH 1/2] fix(cloudflare): only add process shim banner to server environments --- .changeset/cloudflare-client-process-banner.md | 5 +++++ packages/integrations/cloudflare/src/index.ts | 17 ++++++++++++++--- .../test/fixtures/static/src/pages/index.astro | 3 +++ .../integrations/cloudflare/test/static.test.ts | 17 +++++++++++++++-- 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 .changeset/cloudflare-client-process-banner.md diff --git a/.changeset/cloudflare-client-process-banner.md b/.changeset/cloudflare-client-process-banner.md new file mode 100644 index 000000000000..b79b42867d32 --- /dev/null +++ b/.changeset/cloudflare-client-process-banner.md @@ -0,0 +1,5 @@ +--- +'@astrojs/cloudflare': patch +--- + +Fixes the `globalThis.process` shim being prepended to client-side scripts. The shim is now only added to the server output. diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index 3445a70736f0..3af0aa0b0e16 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -661,12 +661,23 @@ export default function createIntegration({ vite.build ||= {}; vite.build.rolldownOptions ||= {}; - vite.build.rolldownOptions.output ||= {}; vite.build.rolldownOptions.external = ['sharp']; + // Scoped to the server environments: top-level `build` options are inherited by the + // client environment, which must not ship this shim to browsers. See https://github.com/withastro/astro/issues/18182 + // A user-provided top-level banner takes precedence and is inherited by the server environments as-is. // @ts-expect-error - vite.build.rolldownOptions.output.banner ||= - 'globalThis.process ??= {}; globalThis.process.env ??= {};'; + if (!vite.build.rolldownOptions.output?.banner) { + for (const name of ['ssr', 'prerender']) { + vite.environments ??= {}; + const environment = (vite.environments[name] ??= {}) as Record; + environment.build ??= {}; + environment.build.rolldownOptions ??= {}; + environment.build.rolldownOptions.output ??= {}; + environment.build.rolldownOptions.output.banner ||= + 'globalThis.process ??= {}; globalThis.process.env ??= {};'; + } + } // Cloudflare env is only available per request. This isn't feasible for code that access env vars // in a global way, so we shim their access as `process.env.*`. This is not the recommended way for users to access environment variables. But we'll add this for compatibility for chosen variables. Mainly to support `@astrojs/db` diff --git a/packages/integrations/cloudflare/test/fixtures/static/src/pages/index.astro b/packages/integrations/cloudflare/test/fixtures/static/src/pages/index.astro index 06d6094d506b..804500a44476 100644 --- a/packages/integrations/cloudflare/test/fixtures/static/src/pages/index.astro +++ b/packages/integrations/cloudflare/test/fixtures/static/src/pages/index.astro @@ -5,5 +5,8 @@

Static Site

This is a fully static Astro site using the Cloudflare adapter.

+ diff --git a/packages/integrations/cloudflare/test/static.test.ts b/packages/integrations/cloudflare/test/static.test.ts index 216730fc93b0..b9ab20d9e32f 100644 --- a/packages/integrations/cloudflare/test/static.test.ts +++ b/packages/integrations/cloudflare/test/static.test.ts @@ -1,4 +1,4 @@ -import { describe, it } from 'node:test'; +import { before, describe, it } from 'node:test'; import { type Fixture, loadFixture } from './test-utils.ts'; import assert from 'node:assert/strict'; import { existsSync } from 'node:fs'; @@ -6,15 +6,28 @@ import { fileURLToPath } from 'node:url'; describe('Static output', () => { let fixture: Fixture; - it('should not output a _worker.js directory for fully static sites', async () => { + + before(async () => { fixture = await loadFixture({ root: './fixtures/static', }); await fixture.build(); + }); + it('should not output a _worker.js directory for fully static sites', async () => { const workerExists = existsSync(fileURLToPath(new URL('_worker.js', fixture.config.outDir))); assert.ok(!workerExists, '_worker.js directory should not exist for static sites'); }); + + it('does not prepend the process shim banner to client scripts', async () => { + const html = await fixture.readFile('/client/index.html'); + assert.ok(html.includes('data-ready'), 'Expected the client script to be in the output'); + assert.ok(!html.includes('globalThis.process')); + for (const file of await fixture.glob('client/_astro/*.js')) { + const js = await fixture.readFile('/' + file); + assert.ok(!js.includes('globalThis.process'), `${file} should not contain the process shim`); + } + }); }); From 48e40574b38224c7bb4418a91607f3fee30e7df6 Mon Sep 17 00:00:00 2001 From: "factory[bot]" Date: Fri, 2 Oct 2026 20:00:49 +0000 Subject: [PATCH 2/2] test(cloudflare): assert server entry keeps the process shim banner --- .../cloudflare/test/server-entry.test.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/integrations/cloudflare/test/server-entry.test.ts b/packages/integrations/cloudflare/test/server-entry.test.ts index 0e662f4a8836..2a99415667e8 100644 --- a/packages/integrations/cloudflare/test/server-entry.test.ts +++ b/packages/integrations/cloudflare/test/server-entry.test.ts @@ -1,4 +1,4 @@ -import { describe, it } from 'node:test'; +import { before, describe, it } from 'node:test'; import { type Fixture, loadFixture } from './test-utils.ts'; import assert from 'node:assert/strict'; import { existsSync } from 'node:fs'; @@ -6,16 +6,27 @@ import { fileURLToPath } from 'node:url'; describe('Server entry', () => { let fixture: Fixture; - it('should load the custom entry when using legacy entrypoint', async () => { + + before(async () => { fixture = await loadFixture({ root: './fixtures/server-entry', output: 'server', }); await fixture.build(); + }); + it('should load the custom entry when using legacy entrypoint', async () => { const itExits = existsSync(fileURLToPath(new URL('server/custom.mjs', fixture.config.outDir))); assert.ok(itExits); }); + + it('prepends the process shim banner to the server entry', async () => { + const entry = await fixture.readFile('/server/custom.mjs'); + assert.ok( + entry.startsWith('globalThis.process ??= {};'), + 'Expected the process shim at the top of the server entry', + ); + }); });