From f108a141973055e6bf1f12dc6c61a5aa3541f5eb Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 14:51:24 +0200 Subject: [PATCH 01/21] chore: add todos --- packages/astro/src/env/vite-plugin-env.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/astro/src/env/vite-plugin-env.ts b/packages/astro/src/env/vite-plugin-env.ts index fdcd6ce4015d..9a05d24e8af9 100644 --- a/packages/astro/src/env/vite-plugin-env.ts +++ b/packages/astro/src/env/vite-plugin-env.ts @@ -17,6 +17,9 @@ import { getEnvFieldType, validateEnvVariable } from './validators.js'; // and server will only contain getSecret for unknown variables. Then, specifying a schema should only add // variables as needed. For secret variables, it will only require specifying SecretValues and it should get // merged with the static types/content.d.ts +// TODO: rename experimentalWhatever in ssr manifest +// TODO: update integrations compat +// TODO: update adapters interface AstroEnvVirtualModPluginParams { settings: AstroSettings; From 31a3b3a92c112c810b2e326f27ceb3ad4728e30f Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 15:15:28 +0200 Subject: [PATCH 02/21] feat: work on types and codegen --- packages/astro/client.d.ts | 1 + packages/astro/src/@types/astro.ts | 284 +++++++++--------- packages/astro/src/core/config/schema.ts | 25 +- packages/astro/src/env/constants.ts | 3 +- packages/astro/src/env/sync.ts | 39 ++- packages/astro/src/env/vite-plugin-env.ts | 20 +- .../templates/{env/module.mjs => env.mjs} | 0 .../env/types.d.ts => types/env.d.ts} | 6 +- 8 files changed, 185 insertions(+), 193 deletions(-) rename packages/astro/templates/{env/module.mjs => env.mjs} (100%) rename packages/astro/{templates/env/types.d.ts => types/env.d.ts} (59%) diff --git a/packages/astro/client.d.ts b/packages/astro/client.d.ts index ed5c1b894d28..9775afa431d7 100644 --- a/packages/astro/client.d.ts +++ b/packages/astro/client.d.ts @@ -1,6 +1,7 @@ /// /// /// +/// // eslint-disable-next-line @typescript-eslint/no-namespace declare namespace App { diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 64923d91c98b..b67ca9ba7050 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1755,6 +1755,148 @@ export interface AstroUserConfig { */ legacy?: object; + /** + * @docs + * @name experimental.env + * @type {object} + * @default `undefined` + * @version 4.10.0 + * @description + * + * Enables experimental `astro:env` features. + * + * The `astro:env` API lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client. Import and use your defined variables from the appropriate `/client` or `/server` module: + * + * ```astro + * --- + * import { API_URL } from "astro:env/client" + * import { API_SECRET_TOKEN } from "astro:env/server" + * + * const data = await fetch(`${API_URL}/users`, { + * method: "GET", + * headers: { + * "Content-Type": "application/json", + * "Authorization": `Bearer ${API_SECRET_TOKEN}` + * }, + * }) + * --- + * + * + * ``` + * + * To define the data type and properties of your environment variables, declare a schema in your Astro config in `experimental.env.schema`. The `envField` helper allows you define your variable as a string, number, or boolean and pass properties in an object: + * + * ```js + * // astro.config.mjs + * import { defineConfig, envField } from "astro/config" + * + * export default defineConfig({ + * experimental: { + * env: { + * schema: { + * API_URL: envField.string({ context: "client", access: "public", optional: true }), + * PORT: envField.number({ context: "server", access: "public", default: 4321 }), + * API_SECRET: envField.string({ context: "server", access: "secret" }), + * } + * } + * } + * }) + * ``` + * + * There are currently four data types supported: strings, numbers, booleans and enums. + * + * There are three kinds of environment variables, determined by the combination of `context` (client or server) and `access` (secret or public) settings defined in your [`env.schema`](#experimentalenvschema): + * + * - **Public client variables**: These variables end up in both your final client and server bundles, and can be accessed from both client and server through the `astro:env/client` module: + * + * ```js + * import { API_URL } from "astro:env/client" + * ``` + * + * - **Public server variables**: These variables end up in your final server bundle and can be accessed on the server through the `astro:env/server` module: + * + * ```js + * import { PORT } from "astro:env/server" + * ``` + * + * - **Secret server variables**: These variables are not part of your final bundle and can be accessed on the server through the `astro:env/server` module. The `getSecret()` helper function can be used to retrieve secrets not specified in the schema. Its implementation is provided by your adapter and defaults to `process.env`: + * + * ```js + * import { API_SECRET, getSecret } from "astro:env/server" + * + * const SECRET_NOT_IN_SCHEMA = getSecret("SECRET_NOT_IN_SCHEMA") // string | undefined + * ``` + * + * **Note:** Secret client variables are not supported because there is no safe way to send this data to the client. Therefore, it is not possible to configure both `context: "client"` and `access: "secret"` in your schema. + * + * For a complete overview, and to give feedback on this experimental API, see the [Astro Env RFC](https://github.com/withastro/roadmap/blob/feat/astro-env-rfc/proposals/0046-astro-env.md). + */ + env?: { + /** + * @docs + * @name experimental.env.schema + * @kind h4 + * @type {EnvSchema} + * @default `undefined` + * @version 4.10.0 + * @description + * + * An object that uses `envField` to define the data type (`string`, `number`, or `boolean`) and properties of your environment variables: `context` (client or server), `access` (public or secret), a `default` value to use, and whether or not this environment variable is `optional` (defaults to `false`). + * ```js + * // astro.config.mjs + * import { defineConfig, envField } from "astro/config" + * + * export default defineConfig({ + * experimental: { + * env: { + * schema: { + * API_URL: envField.string({ context: "client", access: "public", optional: true }), + * PORT: envField.number({ context: "server", access: "public", default: 4321 }), + * API_SECRET: envField.string({ context: "server", access: "secret" }), + * } + * } + * } + * }) + * ``` + */ + schema?: EnvSchema; + + /** + * @docs + * @name experimental.env.validateSecrets + * @kind h4 + * @type {boolean} + * @default `false` + * @version 4.11.6 + * @description + * + * Whether or not to validate secrets on the server when starting the dev server or running a build. + * + * By default, only public variables are validated on the server when starting the dev server or a build, and private variables are validated at runtime only. If enabled, private variables will also be checked on start. This is useful in some continuous integration (CI) pipelines to make sure all your secrets are correctly set before deploying. + * + * ```js + * // astro.config.mjs + * import { defineConfig, envField } from "astro/config" + * + * export default defineConfig({ + * experimental: { + * env: { + * schema: { + * // ... + * }, + * validateSecrets: true + * } + * } + * }) + * ``` + */ + validateSecrets?: boolean; + }; + /** * @docs * @kind heading @@ -1977,148 +2119,6 @@ export interface AstroUserConfig { */ globalRoutePriority?: boolean; - /** - * @docs - * @name experimental.env - * @type {object} - * @default `undefined` - * @version 4.10.0 - * @description - * - * Enables experimental `astro:env` features. - * - * The `astro:env` API lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client. Import and use your defined variables from the appropriate `/client` or `/server` module: - * - * ```astro - * --- - * import { API_URL } from "astro:env/client" - * import { API_SECRET_TOKEN } from "astro:env/server" - * - * const data = await fetch(`${API_URL}/users`, { - * method: "GET", - * headers: { - * "Content-Type": "application/json", - * "Authorization": `Bearer ${API_SECRET_TOKEN}` - * }, - * }) - * --- - * - * - * ``` - * - * To define the data type and properties of your environment variables, declare a schema in your Astro config in `experimental.env.schema`. The `envField` helper allows you define your variable as a string, number, or boolean and pass properties in an object: - * - * ```js - * // astro.config.mjs - * import { defineConfig, envField } from "astro/config" - * - * export default defineConfig({ - * experimental: { - * env: { - * schema: { - * API_URL: envField.string({ context: "client", access: "public", optional: true }), - * PORT: envField.number({ context: "server", access: "public", default: 4321 }), - * API_SECRET: envField.string({ context: "server", access: "secret" }), - * } - * } - * } - * }) - * ``` - * - * There are currently four data types supported: strings, numbers, booleans and enums. - * - * There are three kinds of environment variables, determined by the combination of `context` (client or server) and `access` (secret or public) settings defined in your [`env.schema`](#experimentalenvschema): - * - * - **Public client variables**: These variables end up in both your final client and server bundles, and can be accessed from both client and server through the `astro:env/client` module: - * - * ```js - * import { API_URL } from "astro:env/client" - * ``` - * - * - **Public server variables**: These variables end up in your final server bundle and can be accessed on the server through the `astro:env/server` module: - * - * ```js - * import { PORT } from "astro:env/server" - * ``` - * - * - **Secret server variables**: These variables are not part of your final bundle and can be accessed on the server through the `astro:env/server` module. The `getSecret()` helper function can be used to retrieve secrets not specified in the schema. Its implementation is provided by your adapter and defaults to `process.env`: - * - * ```js - * import { API_SECRET, getSecret } from "astro:env/server" - * - * const SECRET_NOT_IN_SCHEMA = getSecret("SECRET_NOT_IN_SCHEMA") // string | undefined - * ``` - * - * **Note:** Secret client variables are not supported because there is no safe way to send this data to the client. Therefore, it is not possible to configure both `context: "client"` and `access: "secret"` in your schema. - * - * For a complete overview, and to give feedback on this experimental API, see the [Astro Env RFC](https://github.com/withastro/roadmap/blob/feat/astro-env-rfc/proposals/0046-astro-env.md). - */ - env?: { - /** - * @docs - * @name experimental.env.schema - * @kind h4 - * @type {EnvSchema} - * @default `undefined` - * @version 4.10.0 - * @description - * - * An object that uses `envField` to define the data type (`string`, `number`, or `boolean`) and properties of your environment variables: `context` (client or server), `access` (public or secret), a `default` value to use, and whether or not this environment variable is `optional` (defaults to `false`). - * ```js - * // astro.config.mjs - * import { defineConfig, envField } from "astro/config" - * - * export default defineConfig({ - * experimental: { - * env: { - * schema: { - * API_URL: envField.string({ context: "client", access: "public", optional: true }), - * PORT: envField.number({ context: "server", access: "public", default: 4321 }), - * API_SECRET: envField.string({ context: "server", access: "secret" }), - * } - * } - * } - * }) - * ``` - */ - schema?: EnvSchema; - - /** - * @docs - * @name experimental.env.validateSecrets - * @kind h4 - * @type {boolean} - * @default `false` - * @version 4.11.6 - * @description - * - * Whether or not to validate secrets on the server when starting the dev server or running a build. - * - * By default, only public variables are validated on the server when starting the dev server or a build, and private variables are validated at runtime only. If enabled, private variables will also be checked on start. This is useful in some continuous integration (CI) pipelines to make sure all your secrets are correctly set before deploying. - * - * ```js - * // astro.config.mjs - * import { defineConfig, envField } from "astro/config" - * - * export default defineConfig({ - * experimental: { - * env: { - * schema: { - * // ... - * }, - * validateSecrets: true - * } - * } - * }) - * ``` - */ - validateSecrets?: boolean; - }; - /** * @docs * @name experimental.serverIslands diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index 9ffb58934ba0..313089182a55 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -82,6 +82,10 @@ export const ASTRO_CONFIG_DEFAULTS = { legacy: {}, redirects: {}, security: {}, + env: { + schema: {}, + validateSecrets: false, + }, experimental: { actions: false, directRenderScript: false, @@ -89,9 +93,6 @@ export const ASTRO_CONFIG_DEFAULTS = { clientPrerender: false, globalRoutePriority: false, serverIslands: false, - env: { - validateSecrets: false, - }, }, } satisfies AstroUserConfig & { server: { open: boolean } }; @@ -505,6 +506,14 @@ export const AstroConfigSchema = z.object({ }) .optional() .default(ASTRO_CONFIG_DEFAULTS.security), + env: z + .object({ + schema: EnvSchema.optional().default(ASTRO_CONFIG_DEFAULTS.env.schema), + validateSecrets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.env.validateSecrets), + }) + .strict() + .optional() + .default(ASTRO_CONFIG_DEFAULTS.env), experimental: z .object({ actions: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.actions), @@ -524,16 +533,6 @@ export const AstroConfigSchema = z.object({ .boolean() .optional() .default(ASTRO_CONFIG_DEFAULTS.experimental.globalRoutePriority), - env: z - .object({ - schema: EnvSchema.optional(), - validateSecrets: z - .boolean() - .optional() - .default(ASTRO_CONFIG_DEFAULTS.experimental.env.validateSecrets), - }) - .strict() - .optional(), serverIslands: z .boolean() .optional() diff --git a/packages/astro/src/env/constants.ts b/packages/astro/src/env/constants.ts index de5f06233d3a..220f63373c16 100644 --- a/packages/astro/src/env/constants.ts +++ b/packages/astro/src/env/constants.ts @@ -8,5 +8,4 @@ export const VIRTUAL_MODULES_IDS_VALUES = new Set(Object.values(VIRTUAL_MODULES_ export const ENV_TYPES_FILE = 'env.d.ts'; const PKG_BASE = new URL('../../', import.meta.url); -export const MODULE_TEMPLATE_URL = new URL('templates/env/module.mjs', PKG_BASE); -export const TYPES_TEMPLATE_URL = new URL('templates/env/types.d.ts', PKG_BASE); +export const MODULE_TEMPLATE_URL = new URL('templates/env.mjs', PKG_BASE); diff --git a/packages/astro/src/env/sync.ts b/packages/astro/src/env/sync.ts index 9ba11469ad7a..597d222b6232 100644 --- a/packages/astro/src/env/sync.ts +++ b/packages/astro/src/env/sync.ts @@ -1,30 +1,37 @@ import fsMod from 'node:fs'; import type { AstroSettings } from '../@types/astro.js'; -import { ENV_TYPES_FILE, TYPES_TEMPLATE_URL } from './constants.js'; +import { ENV_TYPES_FILE } from './constants.js'; import { getEnvFieldType } from './validators.js'; export function syncAstroEnv(settings: AstroSettings, fs = fsMod) { - if (!settings.config.experimental.env) { - return; - } - - const schema = settings.config.experimental.env.schema ?? {}; + let client: string | null = null; + let server: string | null = null; - let client = ''; - let server = ''; - - for (const [key, options] of Object.entries(schema)) { - const str = `export const ${key}: ${getEnvFieldType(options)}; \n`; + for (const [key, options] of Object.entries(settings.config.env.schema)) { + const str = ` export const ${key}: ${getEnvFieldType(options)}; \n`; if (options.context === 'client') { + client ??= ''; client += str; } else { + server ??= ''; server += str; } } - const template = fs.readFileSync(TYPES_TEMPLATE_URL, 'utf-8'); - const dts = template.replace('// @@CLIENT@@', client).replace('// @@SERVER@@', server); - - fs.mkdirSync(settings.dotAstroDir, { recursive: true }); - fs.writeFileSync(new URL(ENV_TYPES_FILE, settings.dotAstroDir), dts, 'utf-8'); + let content: string | null = null; + if (client !== null) { + content = `declare module 'astro:env/client' { +${client} +}`; + } + if (server !== null) { + content ??= ''; + content += `declare module 'astro:env/server' { +${server} +}`; + } + if (content) { + fs.mkdirSync(settings.dotAstroDir, { recursive: true }); + fs.writeFileSync(new URL(ENV_TYPES_FILE, settings.dotAstroDir), content, 'utf-8'); + } } diff --git a/packages/astro/src/env/vite-plugin-env.ts b/packages/astro/src/env/vite-plugin-env.ts index 9a05d24e8af9..06f34ae97de1 100644 --- a/packages/astro/src/env/vite-plugin-env.ts +++ b/packages/astro/src/env/vite-plugin-env.ts @@ -12,32 +12,22 @@ import { type InvalidVariable, invalidVariablesToError } from './errors.js'; import type { EnvSchema } from './schema.js'; import { getEnvFieldType, validateEnvVariable } from './validators.js'; -// TODO: reminders for when astro:env comes out of experimental -// Types should always be generated (like in types/content.d.ts). That means the client module will be empty -// and server will only contain getSecret for unknown variables. Then, specifying a schema should only add -// variables as needed. For secret variables, it will only require specifying SecretValues and it should get -// merged with the static types/content.d.ts // TODO: rename experimentalWhatever in ssr manifest // TODO: update integrations compat // TODO: update adapters -interface AstroEnvVirtualModPluginParams { +interface AstroEnvPluginParams { settings: AstroSettings; mode: 'dev' | 'build' | string; fs: typeof fsMod; sync: boolean; } -export function astroEnv({ - settings, - mode, - fs, - sync, -}: AstroEnvVirtualModPluginParams): Plugin | undefined { - if (!settings.config.experimental.env || sync) { +export function astroEnv({ settings, mode, fs, sync }: AstroEnvPluginParams): Plugin | undefined { + if (sync) { return; } - const schema = settings.config.experimental.env.schema ?? {}; + const { schema, validateSecrets } = settings.config.env; let templates: { client: string; server: string; internal: string } | null = null; @@ -59,7 +49,7 @@ export function astroEnv({ const validatedVariables = validatePublicVariables({ schema, loadedEnv, - validateSecrets: settings.config.experimental.env?.validateSecrets ?? false, + validateSecrets, }); templates = { diff --git a/packages/astro/templates/env/module.mjs b/packages/astro/templates/env.mjs similarity index 100% rename from packages/astro/templates/env/module.mjs rename to packages/astro/templates/env.mjs diff --git a/packages/astro/templates/env/types.d.ts b/packages/astro/types/env.d.ts similarity index 59% rename from packages/astro/templates/env/types.d.ts rename to packages/astro/types/env.d.ts index 5af1ac6a10a9..6dc51a221389 100644 --- a/packages/astro/templates/env/types.d.ts +++ b/packages/astro/types/env.d.ts @@ -1,9 +1,5 @@ -declare module 'astro:env/client' { - // @@CLIENT@@ -} +declare module 'astro:env/client' {} declare module 'astro:env/server' { - // @@SERVER@@ - export const getSecret: (key: string) => string | undefined; } From da6b6137197d1bbd7589807596d27ce93c594ab2 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 15:26:40 +0200 Subject: [PATCH 03/21] feat: work on todos --- packages/astro/src/container/index.ts | 2 +- packages/astro/src/core/app/types.ts | 3 +-- packages/astro/src/core/base-pipeline.ts | 2 +- packages/astro/src/core/build/generate.ts | 2 +- packages/astro/src/core/build/plugins/plugin-manifest.ts | 3 +-- packages/astro/src/core/sync/setup-env-ts.ts | 8 +++----- packages/astro/src/env/vite-plugin-env.ts | 4 ---- packages/astro/src/integrations/features-validation.ts | 2 +- packages/astro/src/vite-plugin-astro-server/plugin.ts | 2 +- 9 files changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/astro/src/container/index.ts b/packages/astro/src/container/index.ts index 292b49ece101..645b8bb134f7 100644 --- a/packages/astro/src/container/index.ts +++ b/packages/astro/src/container/index.ts @@ -129,7 +129,7 @@ function createManifest( i18n: manifest?.i18n, checkOrigin: false, middleware: manifest?.middleware ?? middleware ?? defaultMiddleware, - experimentalEnvGetSecretEnabled: false, + envGetSecretEnabled: false, }; } diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts index 2e4e8d8057e9..e028d79aa589 100644 --- a/packages/astro/src/core/app/types.ts +++ b/packages/astro/src/core/app/types.ts @@ -69,8 +69,7 @@ export type SSRManifest = { i18n: SSRManifestI18n | undefined; middleware: MiddlewareHandler; checkOrigin: boolean; - // TODO: remove experimental prefix - experimentalEnvGetSecretEnabled: boolean; + envGetSecretEnabled: boolean; }; export type SSRManifestI18n = { diff --git a/packages/astro/src/core/base-pipeline.ts b/packages/astro/src/core/base-pipeline.ts index 01e18bfa0356..f85daa8ae3eb 100644 --- a/packages/astro/src/core/base-pipeline.ts +++ b/packages/astro/src/core/base-pipeline.ts @@ -69,7 +69,7 @@ export abstract class Pipeline { } // In SSR, getSecret should fail by default. Setting it here will run before the // adapter override. - if (callSetGetEnv && manifest.experimentalEnvGetSecretEnabled) { + if (callSetGetEnv && manifest.envGetSecretEnabled) { setGetEnv(() => { throw new AstroError(AstroErrorData.EnvUnsupportedGetSecret); }, true); diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 5897ba7e4dfc..334b975971d0 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -551,6 +551,6 @@ function createBuildManifest( buildFormat: settings.config.build.format, middleware, checkOrigin: settings.config.security?.checkOrigin ?? false, - experimentalEnvGetSecretEnabled: false, + envGetSecretEnabled: false, }; } diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts index bb1add5b4553..e04589c9590d 100644 --- a/packages/astro/src/core/build/plugins/plugin-manifest.ts +++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts @@ -277,8 +277,7 @@ function buildManifest( buildFormat: settings.config.build.format, checkOrigin: settings.config.security?.checkOrigin ?? false, serverIslandNameMap: Array.from(settings.serverIslandNameMap), - experimentalEnvGetSecretEnabled: - settings.config.experimental.env !== undefined && + envGetSecretEnabled: (settings.adapter?.supportedAstroFeatures.envGetSecret ?? 'unsupported') !== 'unsupported', }; } diff --git a/packages/astro/src/core/sync/setup-env-ts.ts b/packages/astro/src/core/sync/setup-env-ts.ts index 39eb062e5bcd..c04faa7487bd 100644 --- a/packages/astro/src/core/sync/setup-env-ts.ts +++ b/packages/astro/src/core/sync/setup-env-ts.ts @@ -48,12 +48,10 @@ export async function setUpEnvTs({ filename: ACTIONS_TYPES_FILE, meetsCondition: () => fs.existsSync(new URL(ACTIONS_TYPES_FILE, settings.dotAstroDir)), }, - ]; - if (settings.config.experimental.env) { - injectedTypes.push({ + { filename: ENV_TYPES_FILE, - }); - } + }, + ]; if (fs.existsSync(envTsPath)) { const initialEnvContents = await fs.promises.readFile(envTsPath, 'utf-8'); diff --git a/packages/astro/src/env/vite-plugin-env.ts b/packages/astro/src/env/vite-plugin-env.ts index 06f34ae97de1..242f2caea396 100644 --- a/packages/astro/src/env/vite-plugin-env.ts +++ b/packages/astro/src/env/vite-plugin-env.ts @@ -12,10 +12,6 @@ import { type InvalidVariable, invalidVariablesToError } from './errors.js'; import type { EnvSchema } from './schema.js'; import { getEnvFieldType, validateEnvVariable } from './validators.js'; -// TODO: rename experimentalWhatever in ssr manifest -// TODO: update integrations compat -// TODO: update adapters - interface AstroEnvPluginParams { settings: AstroSettings; mode: 'dev' | 'build' | string; diff --git a/packages/astro/src/integrations/features-validation.ts b/packages/astro/src/integrations/features-validation.ts index 87de3592b726..17302cfe94f6 100644 --- a/packages/astro/src/integrations/features-validation.ts +++ b/packages/astro/src/integrations/features-validation.ts @@ -94,7 +94,7 @@ export function validateSupportedFeatures( adapterName, logger, 'astro:env getSecret', - () => config?.experimental?.env !== undefined, + () => Object.keys(config?.env.schema ?? {}).length !== 0, ); return validationResult; diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index 7d1e2fb6f84a..0d8e2dc74a97 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -147,7 +147,7 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest inlinedScripts: new Map(), i18n: i18nManifest, checkOrigin: settings.config.security?.checkOrigin ?? false, - experimentalEnvGetSecretEnabled: false, + envGetSecretEnabled: false, middleware(_, next) { return next(); }, From 18978c70bdd0609d705b6e8046cda3b4f5d4245a Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 15:39:37 +0200 Subject: [PATCH 04/21] feat: update adapters --- packages/integrations/node/src/index.ts | 2 +- packages/integrations/vercel/src/serverless/adapter.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index 36d9ee30f488..437bc8280707 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -19,7 +19,7 @@ export function getAdapter(options: Options): AstroAdapter { isSquooshCompatible: true, }, i18nDomains: 'experimental', - envGetSecret: 'experimental', + envGetSecret: 'stable', }, }; } diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts index 8cddc0be1019..9a4ec50786bf 100644 --- a/packages/integrations/vercel/src/serverless/adapter.ts +++ b/packages/integrations/vercel/src/serverless/adapter.ts @@ -98,7 +98,7 @@ function getAdapter({ isSquooshCompatible: true, }, i18nDomains: 'experimental', - envGetSecret: 'experimental', + envGetSecret: 'stable', }, }; } From d835645adfce5ee53cc17a4007e9f715457e260f Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 15:45:09 +0200 Subject: [PATCH 05/21] chore: update tests --- packages/astro/test/env-secret.test.js | 6 ++--- .../astro.config.mjs | 10 ++++----- .../astro-env-server-fail/astro.config.mjs | 8 +++---- .../astro-env-server-secret/astro.config.mjs | 8 +++---- .../test/fixtures/astro-env/astro.config.mjs | 12 +++++----- .../test/units/config/config-validate.test.js | 22 +++++++------------ 6 files changed, 25 insertions(+), 41 deletions(-) diff --git a/packages/astro/test/env-secret.test.js b/packages/astro/test/env-secret.test.js index 7a569e35a524..cad86dcdaded 100644 --- a/packages/astro/test/env-secret.test.js +++ b/packages/astro/test/env-secret.test.js @@ -71,10 +71,8 @@ describe('astro:env secret variables', () => { it('fails if validateSecrets is enabled and secret is not set', async () => { fixture = await loadFixture({ root: './fixtures/astro-env-server-secret/', - experimental: { - env: { - validateSecrets: true, - }, + env: { + validateSecrets: true, }, }); diff --git a/packages/astro/test/fixtures/astro-env-required-public/astro.config.mjs b/packages/astro/test/fixtures/astro-env-required-public/astro.config.mjs index c668e1e21f20..57cca3cecf35 100644 --- a/packages/astro/test/fixtures/astro-env-required-public/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-env-required-public/astro.config.mjs @@ -2,12 +2,10 @@ import { defineConfig, envField } from 'astro/config'; // https://astro.build/config export default defineConfig({ - experimental: { - env: { - schema: { - FOO: envField.string({ context: "client", access: "public" }), - BAR: envField.number({ context: "server", access: "public" }), - } + env: { + schema: { + FOO: envField.string({ context: "client", access: "public" }), + BAR: envField.number({ context: "server", access: "public" }), } } }); diff --git a/packages/astro/test/fixtures/astro-env-server-fail/astro.config.mjs b/packages/astro/test/fixtures/astro-env-server-fail/astro.config.mjs index a5b69ee5fc80..b4ca20243f5f 100644 --- a/packages/astro/test/fixtures/astro-env-server-fail/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-env-server-fail/astro.config.mjs @@ -2,11 +2,9 @@ import { defineConfig, envField } from 'astro/config'; // https://astro.build/config export default defineConfig({ - experimental: { - env: { - schema: { - FOO: envField.string({ context: "server", access: "public", optional: true, default: "ABC" }), - } + env: { + schema: { + FOO: envField.string({ context: "server", access: "public", optional: true, default: "ABC" }), } } }); diff --git a/packages/astro/test/fixtures/astro-env-server-secret/astro.config.mjs b/packages/astro/test/fixtures/astro-env-server-secret/astro.config.mjs index 1737fa64acdd..3f5fea3c725d 100644 --- a/packages/astro/test/fixtures/astro-env-server-secret/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-env-server-secret/astro.config.mjs @@ -2,11 +2,9 @@ import { defineConfig, envField } from 'astro/config'; // https://astro.build/config export default defineConfig({ - experimental: { - env: { - schema: { - KNOWN_SECRET: envField.number({ context: "server", access: "secret" }) - } + env: { + schema: { + KNOWN_SECRET: envField.number({ context: "server", access: "secret" }) } } }); diff --git a/packages/astro/test/fixtures/astro-env/astro.config.mjs b/packages/astro/test/fixtures/astro-env/astro.config.mjs index 6b6276e89ba2..fbd76c522aa3 100644 --- a/packages/astro/test/fixtures/astro-env/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-env/astro.config.mjs @@ -2,13 +2,11 @@ import { defineConfig, envField } from 'astro/config'; // https://astro.build/config export default defineConfig({ - experimental: { - env: { - schema: { - FOO: envField.string({ context: "client", access: "public", optional: true, default: "ABC" }), - BAR: envField.string({ context: "client", access: "public", optional: true, default: "DEF" }), - BAZ: envField.string({ context: "server", access: "public", optional: true, default: "GHI" }), - } + env: { + schema: { + FOO: envField.string({ context: "client", access: "public", optional: true, default: "ABC" }), + BAR: envField.string({ context: "client", access: "public", optional: true, default: "DEF" }), + BAZ: envField.string({ context: "server", access: "public", optional: true, default: "GHI" }), } } }); diff --git a/packages/astro/test/units/config/config-validate.test.js b/packages/astro/test/units/config/config-validate.test.js index fc69b595fa4f..87e437813676 100644 --- a/packages/astro/test/units/config/config-validate.test.js +++ b/packages/astro/test/units/config/config-validate.test.js @@ -358,10 +358,8 @@ describe('Config Validation', () => { assert.doesNotThrow(() => validateConfig( { - experimental: { - env: { - schema: undefined, - }, + env: { + schema: undefined, }, }, process.cwd(), @@ -373,11 +371,9 @@ describe('Config Validation', () => { assert.doesNotThrow(() => validateConfig( { - experimental: { - env: { - schema: { - ABC123: envField.string({ access: 'public', context: 'server' }), - }, + env: { + schema: { + ABC123: envField.string({ access: 'public', context: 'server' }), }, }, }, @@ -389,11 +385,9 @@ describe('Config Validation', () => { it('Should not allow schema variables starting with a number', async () => { const configError = await validateConfig( { - experimental: { - env: { - schema: { - '123ABC': envField.string({ access: 'public', context: 'server' }), - }, + env: { + schema: { + '123ABC': envField.string({ access: 'public', context: 'server' }), }, }, }, From 5bee7ff39b16b5a0b032bb46351f4f010cb27c5d Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 17:16:15 +0200 Subject: [PATCH 06/21] fix: do not use fs mock --- packages/astro/src/core/create-vite.ts | 2 +- packages/astro/src/env/vite-plugin-env.ts | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index 0570d9d5d584..dcf99c687cfb 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -133,7 +133,7 @@ export async function createVite( // the build to run very slow as the filewatcher is triggered often. mode !== 'build' && vitePluginAstroServer({ settings, logger, fs }), envVitePlugin({ settings }), - astroEnv({ settings, mode, fs, sync }), + astroEnv({ settings, mode, sync }), markdownVitePlugin({ settings, logger }), htmlVitePlugin(), mdxVitePlugin(), diff --git a/packages/astro/src/env/vite-plugin-env.ts b/packages/astro/src/env/vite-plugin-env.ts index 242f2caea396..ebff5c765d56 100644 --- a/packages/astro/src/env/vite-plugin-env.ts +++ b/packages/astro/src/env/vite-plugin-env.ts @@ -1,4 +1,4 @@ -import type fsMod from 'node:fs'; +import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { type Plugin, loadEnv } from 'vite'; import type { AstroSettings } from '../@types/astro.js'; @@ -15,11 +15,10 @@ import { getEnvFieldType, validateEnvVariable } from './validators.js'; interface AstroEnvPluginParams { settings: AstroSettings; mode: 'dev' | 'build' | string; - fs: typeof fsMod; sync: boolean; } -export function astroEnv({ settings, mode, fs, sync }: AstroEnvPluginParams): Plugin | undefined { +export function astroEnv({ settings, mode, sync }: AstroEnvPluginParams): Plugin | undefined { if (sync) { return; } @@ -49,7 +48,7 @@ export function astroEnv({ settings, mode, fs, sync }: AstroEnvPluginParams): Pl }); templates = { - ...getTemplates(schema, fs, validatedVariables), + ...getTemplates(schema, validatedVariables), internal: `export const schema = ${JSON.stringify(schema)};`, }; }, @@ -126,11 +125,10 @@ function validatePublicVariables({ function getTemplates( schema: EnvSchema, - fs: typeof fsMod, validatedVariables: ReturnType, ) { let client = ''; - let server = fs.readFileSync(MODULE_TEMPLATE_URL, 'utf-8'); + let server = readFileSync(MODULE_TEMPLATE_URL, 'utf-8'); let onSetGetEnv = ''; for (const { key, value, context } of validatedVariables) { From d878774766a64057c75cec3da17d100a583342d6 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Mon, 12 Aug 2024 17:28:03 +0200 Subject: [PATCH 07/21] feat: work on jsdoc --- packages/astro/src/@types/astro.ts | 90 +++--------------------------- 1 file changed, 9 insertions(+), 81 deletions(-) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index b67ca9ba7050..545149c77841 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1757,92 +1757,21 @@ export interface AstroUserConfig { /** * @docs - * @name experimental.env + * @name env * @type {object} - * @default `undefined` - * @version 4.10.0 + * @default `{}` + * @version 5.0.0 * @description * - * Enables experimental `astro:env` features. - * - * The `astro:env` API lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client. Import and use your defined variables from the appropriate `/client` or `/server` module: - * - * ```astro - * --- - * import { API_URL } from "astro:env/client" - * import { API_SECRET_TOKEN } from "astro:env/server" - * - * const data = await fetch(`${API_URL}/users`, { - * method: "GET", - * headers: { - * "Content-Type": "application/json", - * "Authorization": `Bearer ${API_SECRET_TOKEN}` - * }, - * }) - * --- - * - * - * ``` - * - * To define the data type and properties of your environment variables, declare a schema in your Astro config in `experimental.env.schema`. The `envField` helper allows you define your variable as a string, number, or boolean and pass properties in an object: - * - * ```js - * // astro.config.mjs - * import { defineConfig, envField } from "astro/config" - * - * export default defineConfig({ - * experimental: { - * env: { - * schema: { - * API_URL: envField.string({ context: "client", access: "public", optional: true }), - * PORT: envField.number({ context: "server", access: "public", default: 4321 }), - * API_SECRET: envField.string({ context: "server", access: "secret" }), - * } - * } - * } - * }) - * ``` - * - * There are currently four data types supported: strings, numbers, booleans and enums. - * - * There are three kinds of environment variables, determined by the combination of `context` (client or server) and `access` (secret or public) settings defined in your [`env.schema`](#experimentalenvschema): - * - * - **Public client variables**: These variables end up in both your final client and server bundles, and can be accessed from both client and server through the `astro:env/client` module: - * - * ```js - * import { API_URL } from "astro:env/client" - * ``` - * - * - **Public server variables**: These variables end up in your final server bundle and can be accessed on the server through the `astro:env/server` module: - * - * ```js - * import { PORT } from "astro:env/server" - * ``` - * - * - **Secret server variables**: These variables are not part of your final bundle and can be accessed on the server through the `astro:env/server` module. The `getSecret()` helper function can be used to retrieve secrets not specified in the schema. Its implementation is provided by your adapter and defaults to `process.env`: - * - * ```js - * import { API_SECRET, getSecret } from "astro:env/server" - * - * const SECRET_NOT_IN_SCHEMA = getSecret("SECRET_NOT_IN_SCHEMA") // string | undefined - * ``` - * - * **Note:** Secret client variables are not supported because there is no safe way to send this data to the client. Therefore, it is not possible to configure both `context: "client"` and `access: "secret"` in your schema. - * - * For a complete overview, and to give feedback on this experimental API, see the [Astro Env RFC](https://github.com/withastro/roadmap/blob/feat/astro-env-rfc/proposals/0046-astro-env.md). + * Holds `astro:env` options. */ env?: { /** * @docs - * @name experimental.env.schema - * @kind h4 + * @name env.schema * @type {EnvSchema} - * @default `undefined` - * @version 4.10.0 + * @default `{}` + * @version 5.0.0 * @description * * An object that uses `envField` to define the data type (`string`, `number`, or `boolean`) and properties of your environment variables: `context` (client or server), `access` (public or secret), a `default` value to use, and whether or not this environment variable is `optional` (defaults to `false`). @@ -1867,11 +1796,10 @@ export interface AstroUserConfig { /** * @docs - * @name experimental.env.validateSecrets - * @kind h4 + * @name env.validateSecrets * @type {boolean} * @default `false` - * @version 4.11.6 + * @version 5.0.0 * @description * * Whether or not to validate secrets on the server when starting the dev server or running a build. From b3bc57bebf4eb3ee133f8394620611d014106de9 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 14:10:34 +0200 Subject: [PATCH 08/21] fix: feature validation test --- packages/astro/src/integrations/features-validation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/integrations/features-validation.ts b/packages/astro/src/integrations/features-validation.ts index 17302cfe94f6..84936ffb347b 100644 --- a/packages/astro/src/integrations/features-validation.ts +++ b/packages/astro/src/integrations/features-validation.ts @@ -94,7 +94,7 @@ export function validateSupportedFeatures( adapterName, logger, 'astro:env getSecret', - () => Object.keys(config?.env.schema ?? {}).length !== 0, + () => Object.keys(config?.env?.schema ?? {}).length !== 0, ); return validationResult; From 2ed705ccf2fd1ab547c799a085c145c754f1c77b Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 14:21:44 +0200 Subject: [PATCH 09/21] chore: changeset --- .changeset/eighty-boxes-applaud.md | 21 +++++++++++++++++++++ .changeset/selfish-impalas-grin.md | 6 ++++++ 2 files changed, 27 insertions(+) create mode 100644 .changeset/eighty-boxes-applaud.md create mode 100644 .changeset/selfish-impalas-grin.md diff --git a/.changeset/eighty-boxes-applaud.md b/.changeset/eighty-boxes-applaud.md new file mode 100644 index 000000000000..a90660e0c1cf --- /dev/null +++ b/.changeset/eighty-boxes-applaud.md @@ -0,0 +1,21 @@ +--- +'astro': major +--- + +Makes `astro:env` stable + +To upgrade, update your Astro config: + +```diff +import { defineConfig, envField } from 'astro/config' + +export default defineConfig({ +- experimental: { + env: { + schema: { + FOO: envField.string({ /* ... */ }) + } + } +- } +}) +``` \ No newline at end of file diff --git a/.changeset/selfish-impalas-grin.md b/.changeset/selfish-impalas-grin.md new file mode 100644 index 000000000000..2efd92b9884c --- /dev/null +++ b/.changeset/selfish-impalas-grin.md @@ -0,0 +1,6 @@ +--- +'@astrojs/vercel': minor +'@astrojs/node': minor +--- + +Adds stable support for `astro:env` From 9d6dd211a873e472cdd3c16a55062dd4b1d4a8c5 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 14:22:31 +0200 Subject: [PATCH 10/21] chore: remove setup chunk --- packages/astro/src/core/build/plugins/plugin-chunks.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/astro/src/core/build/plugins/plugin-chunks.ts b/packages/astro/src/core/build/plugins/plugin-chunks.ts index bcd1d15bf5ca..3348e126c01c 100644 --- a/packages/astro/src/core/build/plugins/plugin-chunks.ts +++ b/packages/astro/src/core/build/plugins/plugin-chunks.ts @@ -16,11 +16,6 @@ export function vitePluginChunks(): VitePlugin { if (id.includes('astro/dist/runtime')) { return 'astro'; } - // Place `astro/env/setup` import in its own chunk to prevent Rollup's TLA bug - // https://github.com/rollup/rollup/issues/4708 - if (id.includes('astro/dist/env/setup')) { - return 'astro/env-setup'; - } }, }); }, From 0962b38dc5820f7ff5e2e1ce1fb1826c9e2d7849 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 14:24:55 +0200 Subject: [PATCH 11/21] feat: do not use TLA --- .changeset/selfish-impalas-grin.md | 4 ++-- packages/integrations/node/src/server.ts | 9 +++------ .../integrations/vercel/src/serverless/entrypoint.ts | 8 ++------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.changeset/selfish-impalas-grin.md b/.changeset/selfish-impalas-grin.md index 2efd92b9884c..d2319c3d8de7 100644 --- a/.changeset/selfish-impalas-grin.md +++ b/.changeset/selfish-impalas-grin.md @@ -1,6 +1,6 @@ --- -'@astrojs/vercel': minor -'@astrojs/node': minor +'@astrojs/vercel': major +'@astrojs/node': major --- Adds stable support for `astro:env` diff --git a/packages/integrations/node/src/server.ts b/packages/integrations/node/src/server.ts index e5b503292d40..6bf2e2241499 100644 --- a/packages/integrations/node/src/server.ts +++ b/packages/integrations/node/src/server.ts @@ -1,17 +1,14 @@ import type { SSRManifest } from 'astro'; import { NodeApp, applyPolyfills } from 'astro/app/node'; +import { setGetEnv } from 'astro/env/setup'; import createMiddleware from './middleware.js'; import { createStandaloneHandler } from './standalone.js'; import startServer from './standalone.js'; import type { Options } from './types.js'; -// Won't throw if the virtual module is not available because it's not supported in -// the users's astro version or if astro:env is not enabled in the project -await import('astro/env/setup') - .then((mod) => mod.setGetEnv((key) => process.env[key])) - .catch(() => {}); - +setGetEnv((key) => process.env[key]); applyPolyfills(); + export function createExports(manifest: SSRManifest, options: Options) { const app = new NodeApp(manifest); options.trailingSlash = manifest.trailingSlash; diff --git a/packages/integrations/vercel/src/serverless/entrypoint.ts b/packages/integrations/vercel/src/serverless/entrypoint.ts index 876ab6b07fe3..b655816e9ee5 100644 --- a/packages/integrations/vercel/src/serverless/entrypoint.ts +++ b/packages/integrations/vercel/src/serverless/entrypoint.ts @@ -1,6 +1,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http'; import type { SSRManifest } from 'astro'; import { NodeApp, applyPolyfills } from 'astro/app/node'; +import { setGetEnv } from 'astro/env/setup'; import { ASTRO_LOCALS_HEADER, ASTRO_MIDDLEWARE_SECRET_HEADER, @@ -8,12 +9,7 @@ import { ASTRO_PATH_PARAM, } from './adapter.js'; -// Won't throw if the virtual module is not available because it's not supported in -// the users's astro version or if astro:env is not enabled in the project -await import('astro/env/setup') - .then((mod) => mod.setGetEnv((key) => process.env[key])) - .catch(() => {}); - +setGetEnv((key) => process.env[key]); applyPolyfills(); export const createExports = ( From 74be1bd7a57ed13c6dc503025516677740b2a173 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 13 Aug 2024 14:46:07 +0200 Subject: [PATCH 12/21] fix: only create env reference if needed --- packages/astro/src/core/sync/setup-env-ts.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/astro/src/core/sync/setup-env-ts.ts b/packages/astro/src/core/sync/setup-env-ts.ts index c04faa7487bd..d92e27ef834b 100644 --- a/packages/astro/src/core/sync/setup-env-ts.ts +++ b/packages/astro/src/core/sync/setup-env-ts.ts @@ -50,6 +50,7 @@ export async function setUpEnvTs({ }, { filename: ENV_TYPES_FILE, + meetsCondition: () => fs.existsSync(new URL(ENV_TYPES_FILE, settings.dotAstroDir)), }, ]; From a497e4475dd4dbbd599d6ae503f96e139834d1b8 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 14 Aug 2024 14:53:32 +0200 Subject: [PATCH 13/21] feat: misc --- packages/astro/src/core/sync/index.ts | 2 +- packages/astro/src/env/constants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index c9b2ec235b98..ee08bcd0df16 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -125,7 +125,7 @@ export async function syncInternal({ await contentLayer.sync(); settings.timer.end('Sync content layer'); } - syncAstroEnv(settings, fs); + syncAstroEnv(settings); await writeFiles(settings, fs, logger); logger.info('types', `Generated ${dim(getTimeStat(timerStart, performance.now()))}`); diff --git a/packages/astro/src/env/constants.ts b/packages/astro/src/env/constants.ts index 220f63373c16..ac2c2c297ff6 100644 --- a/packages/astro/src/env/constants.ts +++ b/packages/astro/src/env/constants.ts @@ -5,7 +5,7 @@ export const VIRTUAL_MODULES_IDS = { }; export const VIRTUAL_MODULES_IDS_VALUES = new Set(Object.values(VIRTUAL_MODULES_IDS)); -export const ENV_TYPES_FILE = 'env.d.ts'; +export const ENV_TYPES_FILE = 'astro/env.d.ts'; const PKG_BASE = new URL('../../', import.meta.url); export const MODULE_TEMPLATE_URL = new URL('templates/env.mjs', PKG_BASE); From 24e915007813a6ee05e485379743ef8abd80f741 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Wed, 14 Aug 2024 15:11:34 +0200 Subject: [PATCH 14/21] feat: bump peer deps --- packages/integrations/node/package.json | 2 +- packages/integrations/vercel/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index a87f261331bb..20529eac9267 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -37,7 +37,7 @@ "server-destroy": "^1.0.1" }, "peerDependencies": { - "astro": "^4.2.0" + "astro": "^5.0.0-alpha.0" }, "devDependencies": { "@types/node": "^18.17.8", diff --git a/packages/integrations/vercel/package.json b/packages/integrations/vercel/package.json index 70fe485b287f..f87210a97435 100644 --- a/packages/integrations/vercel/package.json +++ b/packages/integrations/vercel/package.json @@ -60,7 +60,7 @@ "web-vitals": "^3.5.2" }, "peerDependencies": { - "astro": "^4.2.0" + "astro": "^5.0.0-alpha.0" }, "devDependencies": { "astro": "workspace:*", From e181f3fa6ac101d84b3303ec033b7adb015d6f72 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 15 Aug 2024 14:17:53 +0200 Subject: [PATCH 15/21] feat: address reviews --- .changeset/eighty-boxes-applaud.md | 14 +++++++------- packages/astro/src/env/sync.ts | 15 ++++++--------- .../vercel/src/serverless/entrypoint.ts | 2 +- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.changeset/eighty-boxes-applaud.md b/.changeset/eighty-boxes-applaud.md index a90660e0c1cf..93a8ca7f51bb 100644 --- a/.changeset/eighty-boxes-applaud.md +++ b/.changeset/eighty-boxes-applaud.md @@ -10,12 +10,12 @@ To upgrade, update your Astro config: import { defineConfig, envField } from 'astro/config' export default defineConfig({ -- experimental: { - env: { - schema: { - FOO: envField.string({ /* ... */ }) - } - } -- } +- experimental: { + env: { + schema: { + FOO: envField.string({ /* ... */ }) + } + } +- } }) ``` \ No newline at end of file diff --git a/packages/astro/src/env/sync.ts b/packages/astro/src/env/sync.ts index f0d9933ffa39..f51ce0888068 100644 --- a/packages/astro/src/env/sync.ts +++ b/packages/astro/src/env/sync.ts @@ -3,32 +3,29 @@ import { ENV_TYPES_FILE } from './constants.js'; import { getEnvFieldType } from './validators.js'; export function syncAstroEnv(settings: AstroSettings): void { - let client: string | null = null; - let server: string | null = null; + let client = ''; + let server = ''; for (const [key, options] of Object.entries(settings.config.env.schema)) { const str = ` export const ${key}: ${getEnvFieldType(options)}; \n`; if (options.context === 'client') { - client ??= ''; client += str; } else { - server ??= ''; server += str; } } - let content: string | null = null; - if (client !== null) { + let content = ''; + if (client !== '') { content = `declare module 'astro:env/client' { ${client}}`; } - if (server !== null) { - content ??= ''; + if (server !== '') { content += `declare module 'astro:env/server' { ${server}}`; } - if (content) { + if (content !== '') { settings.injectedTypes.push({ filename: ENV_TYPES_FILE, content, diff --git a/packages/integrations/vercel/src/serverless/entrypoint.ts b/packages/integrations/vercel/src/serverless/entrypoint.ts index b655816e9ee5..2eca1f4e9545 100644 --- a/packages/integrations/vercel/src/serverless/entrypoint.ts +++ b/packages/integrations/vercel/src/serverless/entrypoint.ts @@ -9,8 +9,8 @@ import { ASTRO_PATH_PARAM, } from './adapter.js'; -setGetEnv((key) => process.env[key]); applyPolyfills(); +setGetEnv((key) => process.env[key]); export const createExports = ( manifest: SSRManifest, From 5c97a921ea8761a219106dafae8e2a68c86dba8e Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Thu, 15 Aug 2024 16:59:57 +0200 Subject: [PATCH 16/21] feat: update changeset --- .changeset/eighty-boxes-applaud.md | 48 +++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/.changeset/eighty-boxes-applaud.md b/.changeset/eighty-boxes-applaud.md index 93a8ca7f51bb..80d1bc07d665 100644 --- a/.changeset/eighty-boxes-applaud.md +++ b/.changeset/eighty-boxes-applaud.md @@ -2,20 +2,52 @@ 'astro': major --- -Makes `astro:env` stable +The `astro:env` feature introduced behind a flag in [v4.10.0](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md#x4100) is no longer experimental and is available for general use. -To upgrade, update your Astro config: +This feature lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client. Import and use your defined variables from the appropriate `/client` or `/server` module: + +```astro +--- +import { API_URL } from "astro:env/client" +import { API_SECRET_TOKEN } from "astro:env/server" + +const data = await fetch(`${API_URL}/users`, { + method: "GET", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${API_SECRET_TOKEN}` + }, +}) +--- + + +``` + +If you were previously using this feature, please remove the experimental flag from your Astro config: ```diff import { defineConfig, envField } from 'astro/config' export default defineConfig({ - experimental: { - env: { - schema: { - FOO: envField.string({ /* ... */ }) - } - } +- env: { +- schema: { +- FOO: envField.string({ /* ... */ }) +- } +- } - } ++ env: { ++ schema: { ++ FOO: envField.string({ /* ... */ }) ++ } ++ } }) -``` \ No newline at end of file +``` + +If you have been waiting for stabilization before using `astro:env`, you can now do so. + +Please see [Using environment variables](https://docs.astro.build/en/guides/environment-variables/#astroenv) for more about this feature. \ No newline at end of file From 03abff116d5370c240c84bc89dc14919c38d630b Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 20 Aug 2024 14:34:25 +0200 Subject: [PATCH 17/21] feat: address reviews --- packages/astro/src/types/public/config.ts | 47 +++++++++++------------ 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index 445dcd44b07c..1cbf18c6c136 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -1379,17 +1379,6 @@ export interface AstroUserConfig { /** ! WARNING: SUBJECT TO CHANGE */ db?: Config.Database; - /** - * @docs - * @kind heading - * @name Legacy Flags - * @description - * To help some users migrate between versions of Astro, we occasionally introduce `legacy` flags. - * These flags allow you to opt in to some deprecated or otherwise outdated behavior of Astro - * in the latest version, so that you can continue to upgrade and take advantage of new Astro releases. - */ - legacy?: object; - /** * @docs * @name env @@ -1415,13 +1404,11 @@ export interface AstroUserConfig { * import { defineConfig, envField } from "astro/config" * * export default defineConfig({ - * experimental: { - * env: { - * schema: { - * API_URL: envField.string({ context: "client", access: "public", optional: true }), - * PORT: envField.number({ context: "server", access: "public", default: 4321 }), - * API_SECRET: envField.string({ context: "server", access: "secret" }), - * } + * env: { + * schema: { + * API_URL: envField.string({ context: "client", access: "public", optional: true }), + * PORT: envField.number({ context: "server", access: "public", default: 4321 }), + * API_SECRET: envField.string({ context: "server", access: "secret" }), * } * } * }) @@ -1446,13 +1433,11 @@ export interface AstroUserConfig { * import { defineConfig, envField } from "astro/config" * * export default defineConfig({ - * experimental: { - * env: { - * schema: { - * // ... - * }, - * validateSecrets: true - * } + * env: { + * schema: { + * // ... + * }, + * validateSecrets: true * } * }) * ``` @@ -1460,6 +1445,18 @@ export interface AstroUserConfig { validateSecrets?: boolean; }; + + /** + * @docs + * @kind heading + * @name Legacy Flags + * @description + * To help some users migrate between versions of Astro, we occasionally introduce `legacy` flags. + * These flags allow you to opt in to some deprecated or otherwise outdated behavior of Astro + * in the latest version, so that you can continue to upgrade and take advantage of new Astro releases. + */ + legacy?: object; + /** * @docs * @kind heading From 5bde3883f0291ce064ec5d164fbc0892e9f94dc5 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 20 Aug 2024 14:35:12 +0200 Subject: [PATCH 18/21] feat: update errors --- packages/astro/src/core/errors/errors-data.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index e083ba2f54ae..475acdbbf7de 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -1235,15 +1235,15 @@ export const RouteNotFound = { /** * @docs * @description - * Some environment variables do not match the data type and/or properties defined in `experimental.env.schema`. + * Some environment variables do not match the data type and/or properties defined in `env.schema`. * @message - * The following environment variables defined in `experimental.env.schema` are invalid. + * The following environment variables defined in `env.schema` are invalid. */ export const EnvInvalidVariables = { name: 'EnvInvalidVariables', title: 'Invalid Environment Variables', message: (errors: Array) => - `The following environment variables defined in \`experimental.env.schema\` are invalid:\n\n${errors.map((err) => `- ${err}`).join('\n')}\n`, + `The following environment variables defined in \`env.schema\` are invalid:\n\n${errors.map((err) => `- ${err}`).join('\n')}\n`, } satisfies ErrorData; /** From f7a156d1825f0c35ce4a1c2624a9959ad6718444 Mon Sep 17 00:00:00 2001 From: Florian Lefebvre Date: Tue, 20 Aug 2024 15:09:36 +0200 Subject: [PATCH 19/21] fix: fixture --- .../astro-env-content-collections/astro.config.mjs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/astro/test/fixtures/astro-env-content-collections/astro.config.mjs b/packages/astro/test/fixtures/astro-env-content-collections/astro.config.mjs index 310a5ebab804..ad36aa9c7180 100644 --- a/packages/astro/test/fixtures/astro-env-content-collections/astro.config.mjs +++ b/packages/astro/test/fixtures/astro-env-content-collections/astro.config.mjs @@ -2,11 +2,9 @@ import { defineConfig, envField } from 'astro/config'; // https://astro.build/config export default defineConfig({ - experimental: { - env: { - schema: { - FOO: envField.string({ context: "client", access: "public", optional: true, default: "ABC" }), - } + env: { + schema: { + FOO: envField.string({ context: "client", access: "public", optional: true, default: "ABC" }), } } }); From 24c5720c8ea68f6e19929b29f13471b744fe0b35 Mon Sep 17 00:00:00 2001 From: Sarah Rainsberger Date: Tue, 20 Aug 2024 12:43:47 -0300 Subject: [PATCH 20/21] Sarah edit of changeset --- .changeset/eighty-boxes-applaud.md | 47 +++++++++++++----------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/.changeset/eighty-boxes-applaud.md b/.changeset/eighty-boxes-applaud.md index 80d1bc07d665..71440b42f073 100644 --- a/.changeset/eighty-boxes-applaud.md +++ b/.changeset/eighty-boxes-applaud.md @@ -2,9 +2,27 @@ 'astro': major --- -The `astro:env` feature introduced behind a flag in [v4.10.0](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md#x4100) is no longer experimental and is available for general use. +The `astro:env` feature introduced behind a flag in [v4.10.0](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md#x4100) is no longer experimental and is available for general use. If you have been waiting for stabilization before using `astro:env`, you can now do so. -This feature lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client. Import and use your defined variables from the appropriate `/client` or `/server` module: +This feature lets you configure a type-safe schema for your environment variables, and indicate whether they should be available on the server or the client. + +To configure a schema, add the `env` option to your Astro config and define your client and server variables. If you were previously using this feature, please remove the experimental flag from your Astro config and move your entire `env` configuration unchanged to a top-level option. + +```js +import { defineConfig, envField } from 'astro/config' + +export default defineConfig({ + env: { + schema: { + API_URL: envField.string({ context: "client", access: "public", optional: true }), + PORT: envField.number({ context: "server", access: "public", default: 4321 }), + API_SECRET: envField.string({ context: "server", access: "secret" }), + } + } +}) +``` + +Then, you can import and use your defined variables from the appropriate `/client` or `/server` module: ```astro --- @@ -27,27 +45,4 @@ fetch(`${API_URL}/ping`) ``` -If you were previously using this feature, please remove the experimental flag from your Astro config: - -```diff -import { defineConfig, envField } from 'astro/config' - -export default defineConfig({ -- experimental: { -- env: { -- schema: { -- FOO: envField.string({ /* ... */ }) -- } -- } -- } -+ env: { -+ schema: { -+ FOO: envField.string({ /* ... */ }) -+ } -+ } -}) -``` - -If you have been waiting for stabilization before using `astro:env`, you can now do so. - -Please see [Using environment variables](https://docs.astro.build/en/guides/environment-variables/#astroenv) for more about this feature. \ No newline at end of file +Please see our [guide to using environment variables](https://docs.astro.build/en/guides/environment-variables/#astroenv) for more about this feature. From fbb460e40f0c83fee49decb377f0153d83a300c4 Mon Sep 17 00:00:00 2001 From: Sarah Rainsberger Date: Tue, 20 Aug 2024 16:03:33 -0300 Subject: [PATCH 21/21] tiny changeset edit --- .changeset/eighty-boxes-applaud.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/eighty-boxes-applaud.md b/.changeset/eighty-boxes-applaud.md index 71440b42f073..a732758cb93c 100644 --- a/.changeset/eighty-boxes-applaud.md +++ b/.changeset/eighty-boxes-applaud.md @@ -22,7 +22,7 @@ export default defineConfig({ }) ``` -Then, you can import and use your defined variables from the appropriate `/client` or `/server` module: +You can import and use your defined variables from the appropriate `/client` or `/server` module: ```astro ---