From 389dd27fba023414817b2324ad2362c0ce086a47 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 2 Jul 2026 11:31:19 -0400 Subject: [PATCH] Load wrangler config vars into astro:env at build time --- .changeset/wrangler-vars-astro-env.md | 7 ++ packages/integrations/cloudflare/package.json | 3 +- packages/integrations/cloudflare/src/index.ts | 6 ++ .../cloudflare/src/utils/wrangler-config.ts | 69 +++++++++++++++++++ .../cloudflare/test/astro-env.test.ts | 2 - pnpm-lock.yaml | 60 +++++++++------- 6 files changed, 119 insertions(+), 28 deletions(-) create mode 100644 .changeset/wrangler-vars-astro-env.md create mode 100644 packages/integrations/cloudflare/src/utils/wrangler-config.ts diff --git a/.changeset/wrangler-vars-astro-env.md b/.changeset/wrangler-vars-astro-env.md new file mode 100644 index 000000000000..209499634381 --- /dev/null +++ b/.changeset/wrangler-vars-astro-env.md @@ -0,0 +1,7 @@ +--- +'@astrojs/cloudflare': patch +--- + +Loads `vars` from your Wrangler config so `astro:env` public variables resolve at build time + +Public variables (`access: 'public'`) are inlined at build time, but the adapter only read `.dev.vars` into the environment — variables defined under `vars` in `wrangler.toml`/`wrangler.json`/`wrangler.jsonc` were missing, so direct imports from `astro:env/client` and `astro:env/server` resolved to `undefined` (only `getSecret` worked). The adapter now reads `vars` from your Wrangler config (respecting `CLOUDFLARE_ENV`) so these imports work as documented. `.dev.vars` still takes precedence, matching Wrangler. diff --git a/packages/integrations/cloudflare/package.json b/packages/integrations/cloudflare/package.json index c766454f35f8..8bb1ba709ef0 100644 --- a/packages/integrations/cloudflare/package.json +++ b/packages/integrations/cloudflare/package.json @@ -63,7 +63,8 @@ "cheerio": "1.2.0", "devalue": "^5.8.1", "prismjs": "^1.30.0", - "tinyglobby": "^0.2.15" + "tinyglobby": "^0.2.15", + "wrangler": "^4.83.0" }, "publishConfig": { "provenance": true diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index 567c1675deec..35c65bfb2c44 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -31,6 +31,7 @@ import { parseEnv } from 'node:util'; import { sessionDrivers } from 'astro/config'; import { createCloudflarePrerenderer } from './prerenderer.js'; import cfPrismPlugin from './vite-plugin-prism.js'; +import { loadWranglerVars } from './utils/wrangler-config.js'; const CLOUDFLARE_KV_SESSION_DRIVER_ENTRYPOINT = sessionDrivers.cloudflareKVBinding().entrypoint; @@ -469,6 +470,11 @@ export default function createIntegration({ }, }); + // Assign wrangler config `vars` to process.env so astro:env can find these + // vars at build time. `.dev.vars` is loaded afterwards so it can override + // them, matching wrangler's own precedence. + loadWranglerVars(config.root, cloudflareOptions.configPath, logger); + // QUESTION could be removed based on https://developers.cloudflare.com/workers/configuration/compatibility-flags/#enable-auto-populating-processenv // Assign .dev.vars to process.env so astro:env can find these vars const devVarsPath = new URL('.dev.vars', config.root); diff --git a/packages/integrations/cloudflare/src/utils/wrangler-config.ts b/packages/integrations/cloudflare/src/utils/wrangler-config.ts new file mode 100644 index 000000000000..22b020ca9d1e --- /dev/null +++ b/packages/integrations/cloudflare/src/utils/wrangler-config.ts @@ -0,0 +1,69 @@ +import { existsSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import type { AstroIntegrationLogger } from 'astro'; +import { unstable_readConfig } from 'wrangler'; + +const DEFAULT_WRANGLER_CONFIG_FILES = ['wrangler.toml', 'wrangler.json', 'wrangler.jsonc']; + +function resolveWranglerConfigPath(root: URL, configPath: string | undefined): string | undefined { + if (configPath) { + return fileURLToPath(new URL(configPath, root)); + } + for (const file of DEFAULT_WRANGLER_CONFIG_FILES) { + const candidate = new URL(`./${file}`, root); + if (existsSync(candidate)) { + return fileURLToPath(candidate); + } + } + return undefined; +} + +/** + * Reads the `vars` defined in the project's wrangler config (`wrangler.toml`, + * `wrangler.json`, or `wrangler.jsonc`) and assigns them to `process.env`. + * + * Astro's `astro:env` inlines public variables at build time using Vite's + * `loadEnv()`, which only reads from `process.env` and `.env` files. Cloudflare + * makes wrangler `vars` available at runtime (through `cloudflare:workers`), but + * they are invisible to the build-time step, so direct imports of public + * variables resolve to `undefined`. Surfacing them on `process.env` here lets + * `astro:env` pick them up during the build. + */ +export function loadWranglerVars( + root: URL, + configPath: string | undefined, + logger: AstroIntegrationLogger, +): void { + const resolvedConfigPath = resolveWranglerConfigPath(root, configPath); + if (!resolvedConfigPath) { + return; + } + + try { + const config = unstable_readConfig( + { + config: resolvedConfigPath, + env: process.env.CLOUDFLARE_ENV, + }, + { hideWarnings: true }, + ); + + if (!config.vars) { + return; + } + + for (const [key, value] of Object.entries(config.vars)) { + // `vars` can hold non-string JSON values (numbers, booleans, objects). + // `process.env` only stores strings, matching how `.dev.vars` is handled. + if (value === undefined || value === null) { + continue; + } + process.env[key] = typeof value === 'string' ? value : JSON.stringify(value); + } + } catch (e) { + logger.warn( + `Unable to read wrangler config, variables defined in it will not be available to astro:env at build time.`, + ); + logger.debug(String(e)); + } +} diff --git a/packages/integrations/cloudflare/test/astro-env.test.ts b/packages/integrations/cloudflare/test/astro-env.test.ts index 9a961e399e01..59508a6d1170 100644 --- a/packages/integrations/cloudflare/test/astro-env.test.ts +++ b/packages/integrations/cloudflare/test/astro-env.test.ts @@ -8,8 +8,6 @@ describe('astro:env', () => { let fixture: Fixture; let previewServer: PreviewServer; before(async () => { - process.env.API_URL = 'https://google.de'; - process.env.PORT = '4322'; fixture = await loadFixture({ root: './fixtures/astro-env/', }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97b7892c6105..14c9feac79f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,7 +30,7 @@ importers: version: 2.29.8(@types/node@22.19.19) '@flue/cli': specifier: ^0.8.0 - version: 0.8.1(@cloudflare/workers-types@4.20260527.1)(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(typebox@1.1.38)(typescript@6.0.3)(wrangler@4.95.0)(yaml@2.9.0)(zod-to-json-schema@3.25.2)(zod@4.3.6) + version: 0.8.1(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(typebox@1.1.38)(typescript@6.0.3)(wrangler@4.95.0)(yaml@2.9.0)(zod-to-json-schema@3.25.2)(zod@4.3.6) '@flue/runtime': specifier: ^0.8.0 version: 0.8.1(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(typebox@1.1.38)(typescript@6.0.3)(zod-to-json-schema@3.25.2)(zod@4.3.6) @@ -124,7 +124,7 @@ importers: version: 5.2.0(tinybench@2.9.0)(vite@8.1.0)(vitest@4.1.0) vitest: specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.1.0) benchmark/packages/adapter: dependencies: @@ -243,7 +243,7 @@ importers: version: 18.3.1(react@18.3.1) vitest: specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.1.0) devDependencies: '@types/react': specifier: ^18.3.28 @@ -764,7 +764,7 @@ importers: version: 7.24.8 vitest: specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.1.0) optionalDependencies: sharp: specifier: ^0.34.0 || ^0.35.0 @@ -4094,7 +4094,7 @@ importers: version: link:../../.. vitest: specifier: ^4.1.0 - version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + version: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.1.0) packages/astro/test/fixtures/vue-component: dependencies: @@ -4217,7 +4217,7 @@ importers: version: link:../../underscore-redirects '@cloudflare/vite-plugin': specifier: ^1.39.0 - version: 1.39.0(@cloudflare/workers-types@4.20260527.1)(vite@8.1.0)(workerd@1.20260526.1) + version: 1.39.0(vite@8.1.0)(workerd@1.20260526.1)(wrangler@4.95.0) piccolore: specifier: ^0.1.3 version: 0.1.3 @@ -4252,6 +4252,9 @@ importers: tinyglobby: specifier: ^0.2.15 version: 0.2.17 + wrangler: + specifier: ^4.83.0 + version: 4.95.0(@cloudflare/workers-types@4.20260527.1) packages/integrations/cloudflare/test/fixtures/allowed-hosts: dependencies: @@ -6745,6 +6748,24 @@ importers: specifier: ^4.22.0 version: 4.22.3 + triage/gh-16181: + dependencies: + '@astrojs/mdx': + specifier: workspace:* + version: link:../../packages/integrations/mdx + '@astrojs/node': + specifier: workspace:* + version: link:../../packages/integrations/node + astro: + specifier: workspace:* + version: link:../../packages/astro + + triage/gh-16522: + dependencies: + astro: + specifier: workspace:* + version: link:../../packages/astro + packages: '@anthropic-ai/sdk@0.91.1': @@ -7364,6 +7385,7 @@ packages: resolution: {integrity: sha512-AHC+KSR+3dtGu7Ab7I0Ode4Whx12TxMEmiZt7w+Fc3/2wYNByIzbb6cndWZ78tnveFdO1xhNLv1YaNngxGtOPg==} peerDependencies: vite: ^6.1.0 || ^7.0.0 || ^8.0.0 + wrangler: ^4.95.0 '@cloudflare/workerd-darwin-64@1.20260526.1': resolution: {integrity: sha512-/pR3GH3gfv0PUp7DjI8v0aAIDOqFwibq4bg5xT7TZgcVdBV/cJQWckdXCMqiRtHiawLwogUX00EIOINkYJ1Zqg==} @@ -15477,6 +15499,7 @@ packages: '@vitest/ui': 4.1.0 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -16784,7 +16807,7 @@ snapshots: optionalDependencies: workerd: 1.20260526.1 - '@cloudflare/vite-plugin@1.39.0(@cloudflare/workers-types@4.20260527.1)(vite@8.1.0)(workerd@1.20260526.1)': + '@cloudflare/vite-plugin@1.39.0(vite@8.1.0)(workerd@1.20260526.1)(wrangler@4.95.0)': dependencies: '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260526.1) miniflare: 4.20260526.0 @@ -16793,7 +16816,6 @@ snapshots: wrangler: 4.95.0(@cloudflare/workers-types@4.20260527.1) ws: 8.20.1 transitivePeerDependencies: - - '@cloudflare/workers-types' - bufferutil - utf-8-validate - workerd @@ -16829,7 +16851,7 @@ snapshots: '@codspeed/core': 5.2.0 tinybench: 2.9.0 vite: 8.1.0(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) - vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.1.0) transitivePeerDependencies: - debug @@ -17573,9 +17595,9 @@ snapshots: fastq: 1.20.1 glob: 13.0.3 - '@flue/cli@0.8.1(@cloudflare/workers-types@4.20260527.1)(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(typebox@1.1.38)(typescript@6.0.3)(wrangler@4.95.0)(yaml@2.9.0)(zod-to-json-schema@3.25.2)(zod@4.3.6)': + '@flue/cli@0.8.1(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(typebox@1.1.38)(typescript@6.0.3)(wrangler@4.95.0)(yaml@2.9.0)(zod-to-json-schema@3.25.2)(zod@4.3.6)': dependencies: - '@cloudflare/vite-plugin': 1.39.0(@cloudflare/workers-types@4.20260527.1)(vite@8.1.0)(workerd@1.20260526.1) + '@cloudflare/vite-plugin': 1.39.0(vite@8.1.0)(workerd@1.20260526.1)(wrangler@4.95.0) '@flue/runtime': 0.8.1(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(typebox@1.1.38)(typescript@6.0.3)(zod-to-json-schema@3.25.2)(zod@4.3.6) '@vercel/detect-agent': 1.2.3 package-up: 5.0.0 @@ -17585,7 +17607,6 @@ snapshots: wrangler: 4.95.0(@cloudflare/workers-types@4.20260527.1) transitivePeerDependencies: - '@cfworker/json-schema' - - '@cloudflare/workers-types' - '@standard-schema/spec' - '@types/json-schema' - '@types/node' @@ -19191,7 +19212,7 @@ snapshots: svelte: 5.55.3 optionalDependencies: vite: 8.1.0(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) - vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) + vitest: 4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.1.0) '@textlint/ast-node-types@15.5.1': {} @@ -25845,7 +25866,7 @@ snapshots: optionalDependencies: vite: 8.1.0(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0) - vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(esbuild@0.28.0)(jiti@2.6.1)(sass@1.98.0)(tsx@4.22.3)(yaml@2.9.0): + vitest@4.1.0(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.1.0): dependencies: '@vitest/expect': 4.1.0 '@vitest/mocker': 4.1.0(vite@8.1.0) @@ -25871,18 +25892,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@types/node': 22.19.19 transitivePeerDependencies: - - '@vitejs/devtools' - - esbuild - - jiti - - less - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml vitest@5.0.0-beta.3(@opentelemetry/api@1.9.0)(@types/node@22.19.19)(vite@8.1.0): dependencies: