0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

fix: incorrect astro:env runtime error (#11479)

* fix: incorrect astro:env runtime error

* fix: import

* feat: type check template
This commit is contained in:
Florian Lefebvre 2024-07-18 08:21:38 +02:00 committed by Emanuele Stoppa
parent 4779b8ef15
commit 6059b4234b
8 changed files with 63 additions and 43 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Fixes a case where invalid `astro:env` variables at runtime would not throw correctly

5
packages/astro/dev-only.d.ts vendored Normal file
View file

@ -0,0 +1,5 @@
// IMPORTANT: do not publish this file! It's only intended for development within the monorepo
declare module 'virtual:astro:env/internal' {
export const schema: import('./src/env/schema.js').EnvSchema;
}

View file

@ -1235,6 +1235,8 @@ export const RouteNotFound = {
* @docs
* @description
* Some environment variables do not match the data type and/or properties defined in `experimental.env.schema`.
* @message
* The following environment variables defined in `experimental.env.schema` are invalid.
*/
export const EnvInvalidVariables = {
name: 'EnvInvalidVariables',
@ -1243,18 +1245,6 @@ export const EnvInvalidVariables = {
`The following environment variables defined in \`experimental.env.schema\` are invalid:\n\n${errors.map((err) => `- ${err}`).join('\n')}\n`,
} satisfies ErrorData;
/**
* @docs
* @description
* An environment variable does not match the data type and/or properties defined in `experimental.env.schema`.
*/
export const EnvInvalidVariable = {
name: 'EnvInvalidVariable',
title: 'Invalid Environment Variable',
message: (key: string, type: string) =>
`The following environment variable does not match the data type and/or properties defined in \`experimental.env.schema\`: ${key} is not of type ${type}`,
} satisfies ErrorData;
/**
* @docs
* @description

22
packages/astro/src/env/errors.ts vendored Normal file
View file

@ -0,0 +1,22 @@
import type { ValidationResultErrors } from './validators.js';
export interface InvalidVariable {
key: string;
type: string;
errors: ValidationResultErrors;
}
export function invalidVariablesToError(invalid: Array<InvalidVariable>) {
const _errors: Array<string> = [];
for (const { key, type, errors } of invalid) {
if (errors[0] === 'missing') {
_errors.push(`${key} is missing`);
} else if (errors[0] === 'type') {
_errors.push(`${key}'s type is invalid, expected: ${type}`);
} else {
// constraints
_errors.push(`The following constraints for ${key} are not met: ${errors.join(', ')}`);
}
}
return _errors;
}

View file

@ -1,5 +1,7 @@
import { AstroError, AstroErrorData } from '../core/errors/index.js';
export { validateEnvVariable } from './validators.js';
import { invalidVariablesToError } from './errors.js';
import type { ValidationResultInvalid } from './validators.js';
export { validateEnvVariable, getEnvFieldType } from './validators.js';
export type GetEnv = (key: string) => string | undefined;
@ -21,11 +23,15 @@ export function getEnv(...args: Parameters<GetEnv>) {
return _getEnv(...args);
}
export function createInvalidVariableError(
...args: Parameters<typeof AstroErrorData.EnvInvalidVariable.message>
export function createInvalidVariablesError(
key: string,
type: string,
result: ValidationResultInvalid
) {
return new AstroError({
...AstroErrorData.EnvInvalidVariable,
message: AstroErrorData.EnvInvalidVariable.message(...args),
...AstroErrorData.EnvInvalidVariables,
message: AstroErrorData.EnvInvalidVariables.message(
invalidVariablesToError([{ key, type, errors: result.errors }])
),
});
}

View file

@ -2,16 +2,15 @@ import type { EnumSchema, EnvFieldType, NumberSchema, StringSchema } from './sch
export type ValidationResultValue = EnvFieldType['default'];
export type ValidationResultErrors = ['missing'] | ['type'] | Array<string>;
type ValidationResult =
| {
ok: true;
value: ValidationResultValue;
}
| {
ok: false;
errors: ValidationResultErrors;
};
interface ValidationResultValid {
ok: true;
value: ValidationResultValue;
}
export interface ValidationResultInvalid {
ok: false;
errors: ValidationResultErrors;
}
type ValidationResult = ValidationResultValid | ValidationResultInvalid;
export function getEnvFieldType(options: EnvFieldType) {
const optional = options.optional ? (options.default !== undefined ? false : true) : false;

View file

@ -9,7 +9,8 @@ import {
VIRTUAL_MODULES_IDS_VALUES,
} from './constants.js';
import type { EnvSchema } from './schema.js';
import { type ValidationResultErrors, getEnvFieldType, validateEnvVariable } from './validators.js';
import { getEnvFieldType, validateEnvVariable } from './validators.js';
import { invalidVariablesToError, type InvalidVariable } from './errors.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
@ -105,7 +106,7 @@ function validatePublicVariables({
validateSecrets: boolean;
}) {
const valid: Array<{ key: string; value: any; type: string; context: 'server' | 'client' }> = [];
const invalid: Array<{ key: string; type: string; errors: ValidationResultErrors }> = [];
const invalid: Array<InvalidVariable> = [];
for (const [key, options] of Object.entries(schema)) {
const variable = loadedEnv[key] === '' ? undefined : loadedEnv[key];
@ -125,20 +126,9 @@ function validatePublicVariables({
}
if (invalid.length > 0) {
const _errors: Array<string> = [];
for (const { key, type, errors } of invalid) {
if (errors[0] === 'missing') {
_errors.push(`${key} is missing`);
} else if (errors[0] === 'type') {
_errors.push(`${key}'s type is invalid, expected: ${type}`);
} else {
// constraints
_errors.push(`The following constraints for ${key} are not met: ${errors.join(', ')}`);
}
}
throw new AstroError({
...AstroErrorData.EnvInvalidVariables,
message: AstroErrorData.EnvInvalidVariables.message(_errors),
message: AstroErrorData.EnvInvalidVariables.message(invalidVariablesToError(invalid)),
});
}

View file

@ -1,9 +1,11 @@
// @ts-check
import { schema } from 'virtual:astro:env/internal';
import {
createInvalidVariableError,
createInvalidVariablesError,
getEnv,
setOnSetGetEnv,
validateEnvVariable,
getEnvFieldType,
} from 'astro/env/runtime';
export const getSecret = (key) => {
@ -19,7 +21,8 @@ const _internalGetSecret = (key) => {
if (result.ok) {
return result.value;
}
throw createInvalidVariableError(key, result.type);
const type = getEnvFieldType(options);
throw createInvalidVariablesError(key, type, result);
};
setOnSetGetEnv((reset) => {