Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/wrangler-vars-astro-env.md
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 2 additions & 1 deletion packages/integrations/cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions packages/integrations/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
69 changes: 69 additions & 0 deletions packages/integrations/cloudflare/src/utils/wrangler-config.ts
Original file line number Diff line number Diff line change
@@ -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));
}
}
2 changes: 0 additions & 2 deletions packages/integrations/cloudflare/test/astro-env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/',
});
Expand Down
60 changes: 35 additions & 25 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading