Skip to content
Merged
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
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
Loading