Skip to content

Commit

Permalink
fix: load env earlier (#12977)
Browse files Browse the repository at this point in the history
  • Loading branch information
florian-lefebvre authored Jan 13, 2025
1 parent 3aff68a commit 80067c0
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/cool-rings-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a case where accessing `astro:env` APIs or `import.meta.env` inside the content config file would not work
8 changes: 4 additions & 4 deletions packages/astro/src/core/create-vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '../content/index.js';
import { createEnvLoader } from '../env/env-loader.js';
import { astroEnv } from '../env/vite-plugin-env.js';
import { importMetaEnv } from '../env/vite-plugin-import-meta-env.js';
import astroInternationalization from '../i18n/vite-plugin-i18n.js';
import astroPrefetch from '../prefetch/vite-plugin-prefetch.js';
import astroDevToolbar from '../toolbar/vite-plugin-dev-toolbar.js';
Expand All @@ -23,7 +24,6 @@ import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.j
import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js';
import astroVitePlugin from '../vite-plugin-astro/index.js';
import configAliasVitePlugin from '../vite-plugin-config-alias/index.js';
import envVitePlugin from '../vite-plugin-env/index.js';
import vitePluginFileURL from '../vite-plugin-fileurl/index.js';
import astroHeadPlugin from '../vite-plugin-head/index.js';
import astroHmrReloadPlugin from '../vite-plugin-hmr-reload/index.js';
Expand Down Expand Up @@ -124,7 +124,7 @@ export async function createVite(
});

const srcDirPattern = glob.convertPathToPattern(fileURLToPath(settings.config.srcDir));
const envLoader = createEnvLoader();
const envLoader = createEnvLoader(mode, settings.config);

// Start with the Vite configuration that Astro core needs
const commonConfig: vite.InlineConfig = {
Expand All @@ -148,8 +148,8 @@ export async function createVite(
// The server plugin is for dev only and having it run during the build causes
// the build to run very slow as the filewatcher is triggered often.
command === 'dev' && vitePluginAstroServer({ settings, logger, fs, manifest, ssrManifest }), // ssrManifest is only required in dev mode, where it gets created before a Vite instance is created, and get passed to this function
envVitePlugin({ envLoader }),
astroEnv({ settings, mode, sync, envLoader }),
importMetaEnv({ envLoader }),
astroEnv({ settings, sync, envLoader }),
markdownVitePlugin({ settings, logger }),
htmlVitePlugin(),
astroPostprocessVitePlugin(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# vite-plugin-env
# env

The content of this directory is for `astro:env` features, except for `vite-plugin-import-meta-env.ts`.

# vite-plugin-import-meta-env

Improves Vite's [Env Variables](https://vite.dev/guide/env-and-mode.html#env-files) support to include **private** env variables during Server-Side Rendering (SSR) but never in client-side rendering (CSR).

Expand Down
15 changes: 5 additions & 10 deletions packages/astro/src/env/env-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,12 @@ function getPrivateEnv(
return privateEnv;
}

export const createEnvLoader = () => {
let privateEnv: Record<string, string> = {};
export const createEnvLoader = (mode: string, config: AstroConfig) => {
const loaded = loadEnv(mode, config.vite.envDir ?? fileURLToPath(config.root), '');
const privateEnv = getPrivateEnv(loaded, config);
return {
load: (mode: string, config: AstroConfig) => {
const loaded = loadEnv(mode, config.vite.envDir ?? fileURLToPath(config.root), '');
privateEnv = getPrivateEnv(loaded, config);
return loaded;
},
getPrivateEnv: () => {
return privateEnv;
},
get: () => loaded,
getPrivateEnv: () => privateEnv,
};
};

Expand Down
56 changes: 33 additions & 23 deletions packages/astro/src/env/vite-plugin-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,52 @@ import { getEnvFieldType, validateEnvVariable } from './validators.js';

interface AstroEnvPluginParams {
settings: AstroSettings;
mode: string;
sync: boolean;
envLoader: EnvLoader;
}

export function astroEnv({ settings, mode, sync, envLoader }: AstroEnvPluginParams): Plugin {
export function astroEnv({ settings, sync, envLoader }: AstroEnvPluginParams): Plugin {
const { schema, validateSecrets } = settings.config.env;
let isDev: boolean;

let templates: { client: string; server: string; internal: string } | null = null;

function ensureTemplateAreLoaded() {
if (templates !== null) {
return;
}

const loadedEnv = envLoader.get();

if (!isDev) {
for (const [key, value] of Object.entries(loadedEnv)) {
if (value !== undefined) {
process.env[key] = value;
}
}
}

const validatedVariables = validatePublicVariables({
schema,
loadedEnv,
validateSecrets,
sync,
});

templates = {
...getTemplates(schema, validatedVariables, isDev ? loadedEnv : null),
internal: `export const schema = ${JSON.stringify(schema)};`,
};
}

return {
name: 'astro-env-plugin',
enforce: 'pre',
config(_, { command }) {
isDev = command !== 'build';
},
buildStart() {
const loadedEnv = envLoader.load(mode, settings.config);

if (!isDev) {
for (const [key, value] of Object.entries(loadedEnv)) {
if (value !== undefined) {
process.env[key] = value;
}
}
}

const validatedVariables = validatePublicVariables({
schema,
loadedEnv,
validateSecrets,
sync,
});

templates = {
...getTemplates(schema, validatedVariables, isDev ? loadedEnv : null),
internal: `export const schema = ${JSON.stringify(schema)};`,
};
ensureTemplateAreLoaded();
},
buildEnd() {
templates = null;
Expand All @@ -64,10 +71,12 @@ export function astroEnv({ settings, mode, sync, envLoader }: AstroEnvPluginPara
},
load(id, options) {
if (id === resolveVirtualModuleId(VIRTUAL_MODULES_IDS.client)) {
ensureTemplateAreLoaded();
return templates!.client;
}
if (id === resolveVirtualModuleId(VIRTUAL_MODULES_IDS.server)) {
if (options?.ssr) {
ensureTemplateAreLoaded();
return templates!.server;
}
throw new AstroError({
Expand All @@ -76,6 +85,7 @@ export function astroEnv({ settings, mode, sync, envLoader }: AstroEnvPluginPara
});
}
if (id === resolveVirtualModuleId(VIRTUAL_MODULES_IDS.internal)) {
ensureTemplateAreLoaded();
return templates!.internal;
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { transform } from 'esbuild';
import MagicString from 'magic-string';
import type * as vite from 'vite';
import type { EnvLoader } from '../env/env-loader.js';
import type { EnvLoader } from './env-loader.js';

interface EnvPluginOptions {
envLoader: EnvLoader;
Expand Down Expand Up @@ -65,7 +65,7 @@ async function replaceDefine(
};
}

export default function envVitePlugin({ envLoader }: EnvPluginOptions): vite.Plugin {
export function importMetaEnv({ envLoader }: EnvPluginOptions): vite.Plugin {
let privateEnv: Record<string, string>;
let defaultDefines: Record<string, string>;
let isDev: boolean;
Expand Down
7 changes: 6 additions & 1 deletion packages/astro/test/astro-sync.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,15 @@ describe('astro sync', () => {
assert.fail();
}
});
it('Does not throw if a virtual module is imported in content.config.ts', async () => {
it('Does not throw if a virtual module is imported in content.config.ts or import.meta.env is not loaded', async () => {
try {
await fixture.load('./fixtures/astro-env-content-collections/');
fixture.clean();
fs.writeFileSync(
new URL('./fixtures/astro-env-content-collections/.env', import.meta.url),
'BAR=abc',
'utf-8',
);
await fixture.whenSyncing();
assert.ok(true);
} catch {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { defineCollection, z } from "astro:content";
import { FOO } from "astro:env/client"

console.log({ FOO })
console.log({ FOO, BAR: import.meta.env.BAR })

export const collections = {
foo: defineCollection({
type: "data",
loader: () => [{
id: 'x',
title: import.meta.env.BAR
}],
schema: z.object({
title: z.string()
})
Expand Down

0 comments on commit 80067c0

Please sign in to comment.