Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build time validation for client env #2392

Open
wants to merge 61 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
ddd10d1
WIP: Enables strict null checks in SDK
infomiho Oct 28, 2024
90baa94
Update handleApiError type
infomiho Oct 28, 2024
b5c3c63
Throw invalid credentials error explictly
infomiho Oct 28, 2024
7afc4f0
Formatting
infomiho Oct 28, 2024
b4e2362
Update comment and type
infomiho Oct 28, 2024
465856c
Update email env vars
infomiho Oct 28, 2024
5c329a4
Update isHttpErrorWithExtraMessage
infomiho Oct 28, 2024
e04aac3
Update comment
infomiho Oct 28, 2024
1928b6f
Remove TODO
infomiho Oct 28, 2024
e231dda
Fixes jobs types
infomiho Oct 28, 2024
f431153
Update todoApp tests. Comment update.
infomiho Oct 28, 2024
c8b2120
Update e2e tests
infomiho Oct 28, 2024
08304fe
Fixes headless tests
infomiho Oct 28, 2024
a509762
Fixes CORS error
infomiho Oct 28, 2024
9d618ea
Zod env validation WIP
infomiho Oct 25, 2024
7333b25
Define env vars validation. Use validate env vars.
infomiho Oct 29, 2024
80baf9c
Update e2e tests
infomiho Oct 29, 2024
fb899a0
Clean up
infomiho Oct 29, 2024
7593ac2
Update SKIP_EMAIL_VERIFICATION_IN_DEV validation
infomiho Oct 29, 2024
42d121c
Update headless tests
infomiho Oct 29, 2024
f877543
Update headless tests
infomiho Oct 30, 2024
025d185
Simplify the server config
infomiho Oct 30, 2024
1da4542
Fixes keycloak env usage
infomiho Oct 30, 2024
67de093
Merge branch 'main' into miho-zod-env
infomiho Nov 26, 2024
ee7c6de
Cleanup
infomiho Nov 26, 2024
7b0fedd
Cleanup
infomiho Nov 26, 2024
598eeab
Cleanup
infomiho Nov 26, 2024
4732a27
Cleanup
infomiho Nov 26, 2024
fcdc0d6
Cleanup
infomiho Nov 26, 2024
68e99ba
Fixes headless tests
infomiho Nov 26, 2024
c740e4e
Fixes e2e tests
infomiho Nov 26, 2024
88d7ea9
Cleanup
infomiho Nov 29, 2024
107fa51
Update e2e tests
infomiho Nov 29, 2024
42e9c78
Update API comment
infomiho Nov 29, 2024
3aa07d2
Build time validation for client env
infomiho Nov 29, 2024
35bb045
Organise code better
infomiho Nov 29, 2024
e068427
e2e tests
infomiho Nov 29, 2024
9dc30c1
Update waspc/data/Generator/templates/sdk/wasp/server/env.ts
infomiho Dec 13, 2024
4766a94
Merge branch 'main' into miho-zod-env
infomiho Dec 13, 2024
ba46aa8
PR comments
infomiho Dec 16, 2024
cdd5e03
Merge branch 'miho-zod-env' into miho-zod-env-client-build-time
infomiho Dec 19, 2024
e43a01e
Update build time validation
infomiho Dec 30, 2024
900ce7f
e2e tests
infomiho Dec 30, 2024
920cc90
Merge branch 'main' into miho-zod-env
infomiho Jan 2, 2025
835e17a
Cleanup
infomiho Jan 2, 2025
d275338
Use single prettier.config.js for all templates
infomiho Jan 2, 2025
73e5e11
Update required error messages. jwtTokenSchema conditionally added.
infomiho Jan 2, 2025
41e24be
e2e tests
infomiho Jan 2, 2025
ae51b4c
Enable users to define their env var validations (#2363)
infomiho Jan 10, 2025
a7caff0
Merge branch 'main' into miho-zod-env
infomiho Jan 10, 2025
a007937
Merge branch 'miho-zod-env' into miho-zod-env-client-build-time
infomiho Jan 14, 2025
c6c3a11
Update plugins dir
infomiho Jan 14, 2025
218d552
Merge branch 'main' into miho-zod-env
infomiho Jan 14, 2025
2395640
Merge branch 'miho-zod-env' into miho-zod-env-client-build-time
infomiho Jan 14, 2025
109d55a
Fixes headless tests (npm bug)
infomiho Jan 14, 2025
6d1e22f
Merge branch 'miho-zod-env' into miho-zod-env-client-build-time
infomiho Jan 14, 2025
afd6980
Merge branch 'main' into miho-zod-env-client-build-time
infomiho Jan 15, 2025
9c59b0d
Update naming. Make sure we validate user defined env vars.
infomiho Jan 17, 2025
c2e304b
e2e tests
infomiho Jan 17, 2025
3db8cb0
Merge branch 'main' into miho-zod-env-client-build-time
infomiho Feb 4, 2025
3d7e4bd
Merge branch 'main' into miho-zod-env-client-build-time
infomiho Feb 11, 2025
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
2 changes: 2 additions & 0 deletions waspc/data/Generator/templates/react-app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { mergeConfig } from "vite";
import react from "@vitejs/plugin-react";
import { defaultExclude } from "vitest/config"
import { detectServerImports } from "./vite/detectServerImports"
import { validateEnv } from "./vite/validateEnv.js";

{=# customViteConfig.isDefined =}
// Ignoring the TS error because we are importing a file outside of TS root dir.
Expand All @@ -17,6 +18,7 @@ const _waspUserProvidedConfig = {};
const defaultViteConfig = {
base: "{= baseDir =}",
plugins: [
validateEnv(),
react(),
detectServerImports(),
],
Expand Down
50 changes: 50 additions & 0 deletions waspc/data/Generator/templates/react-app/vite/validateEnv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { type Plugin, loadEnv } from 'vite'

import {
getValidatedEnvOrError,
type EnvValidationResult,
} from 'wasp/env/validation'
import { clientEnvSchema } from 'wasp/client/env/schema'

const redColor = '\x1b[31m'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redColor is defined in two places.
We also have yellowColor defined in providers/dummy.ts.

Maybe it's time we created an ansiColors file that exposes a function (or more) for coloring text into ANSI colors? I know there's a dependency for this but I don't think it's worth it. We can create a small module supporting only the colors we currently need, and build from there.

Also, when dealing with ASNI escape sequencees, the protocol is to reset the color after outputting the colored text. This is done by appending an additional escape sequence after the text, see this post for details).
Our code never does this. That might be OK since our colors are always used with console.log (which I'm guessing resets all colors when flushing the output). But, if we end up building the coloring module, it shouldn't assume the caller is using console.log (perhaps they want to output different text in different colors in a single line).


export function validateEnv(): Plugin {
let validationResult: EnvValidationResult<unknown> | null = null
return {
name: 'wasp-validate-env',
configResolved: (config) => {
const env = loadEnv(config.mode, process.cwd(), config.envPrefix)

validationResult = getValidatedEnvOrError(env, clientEnvSchema)

if (validationResult.type !== 'error') {
return
}

if (config.command !== 'build') {
return
}

console.error(`${redColor}${validationResult.message}`)
// Exit early if we are in build mode, because we can't show the error in the browser.
process.exit(1)
},
configureServer: (server) => {
if (validationResult === null || validationResult.type !== 'error') {
return
}

// Send the error to the browser.
const message = validationResult.message
server.ws.on('connection', () => {
server.ws.send({
type: 'error',
err: {
message,
stack: '',
},
})
})
},
}
}
22 changes: 1 addition & 21 deletions waspc/data/Generator/templates/sdk/wasp/client/env.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
{{={= =}=}}
import * as z from 'zod'

import { clientEnvSchema } from './env/schema.js'
import { ensureEnvSchema } from '../env/validation.js'

{=# envValidationSchema.isDefined =}
{=& envValidationSchema.importStatement =}
const userClientEnvSchema = {= envValidationSchema.importIdentifier =}
{=/ envValidationSchema.isDefined =}
{=^ envValidationSchema.isDefined =}
const userClientEnvSchema = z.object({})
{=/ envValidationSchema.isDefined =}

const waspClientEnvSchema = z.object({
REACT_APP_API_URL: z
.string({
required_error: 'REACT_APP_API_URL is required',
})
.default('{= defaultServerUrl =}')
})

const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema)

// PUBLIC API
export const env = ensureEnvSchema(import.meta.env, clientEnvSchema)
22 changes: 22 additions & 0 deletions waspc/data/Generator/templates/sdk/wasp/client/env/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{{={= =}=}}
import * as z from 'zod'

{=# envValidationSchema.isDefined =}
{=& envValidationSchema.importStatement =}
const userClientEnvSchema = {= envValidationSchema.importIdentifier =}
{=/ envValidationSchema.isDefined =}
{=^ envValidationSchema.isDefined =}
const userClientEnvSchema = z.object({})
{=/ envValidationSchema.isDefined =}

const waspClientEnvSchema = z.object({
REACT_APP_API_URL: z
.string()
.url({
message: 'REACT_APP_API_URL must be a valid URL',
})
.default('{= defaultServerUrl =}'),
})

// PRIVATE API (sdk, Vite config)
export const clientEnvSchema = userClientEnvSchema.merge(waspClientEnvSchema)
2 changes: 0 additions & 2 deletions waspc/data/Generator/templates/sdk/wasp/env/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ export function defineEnvValidationSchema<Schema extends z.ZodObject<any>>(
return schema
}

// PRIVATE API (SDK, Vite config)
export { ensureEnvSchema } from './validation.js'
44 changes: 40 additions & 4 deletions waspc/data/Generator/templates/sdk/wasp/env/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,43 @@ import * as z from 'zod'

const redColor = '\x1b[31m'

// PRIVATE API (SDK, Vite config)
export function ensureEnvSchema<Schema extends z.ZodTypeAny>(
data: unknown,
schema: Schema
): z.infer<Schema> {
const result = getValidatedEnvOrError(data, schema)
switch (result.type) {
case 'error':
console.error(`${redColor}${result.message}`)
throw new Error('Error parsing environment variables')
case 'success':
return result.env
default:
result satisfies never;
}
}

// PRIVATE API (Vite config)
export type EnvValidationResult<Env> = {
type: 'error',
message: string,
} | {
type: 'success',
env: Env,
}
Comment on lines +23 to +29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice candidate for universal/types (it's already generic enough, all that's necessary is to change its name to Result).

You can optionally make the message type generic as well (like in Either), but I don't think it's necessary. I already have it in the Wasp TS config package, but unfortunately there isn't a way to reuse that here.


// PRIVATE API (SDK, Vite config)
export function getValidatedEnvOrError<Schema extends z.ZodTypeAny>(
env: unknown,
schema: Schema
): EnvValidationResult<z.infer<Schema>> {
try {
return schema.parse(data)
const validatedEnv = schema.parse(env)
return {
type: 'success',
env: validatedEnv,
}
} catch (e) {
if (e instanceof z.ZodError) {
const errorOutput = ['', '══ Env vars validation failed ══', '']
Expand All @@ -16,10 +47,15 @@ export function ensureEnvSchema<Schema extends z.ZodTypeAny>(
}
errorOutput.push('')
errorOutput.push('════════════════════════════════')
console.error(redColor, errorOutput.join('\n'))
throw new Error('Error parsing environment variables')
return {
type: 'error',
message: errorOutput.join('\n'),
}
} else {
throw e
return {
type: 'error',
message: e.message,
}
}
}
}
4 changes: 4 additions & 0 deletions waspc/data/Generator/templates/sdk/wasp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@
"./client": "./dist/client/index.js",
"./dev": "./dist/dev/index.js",
"./env": "./dist/env/index.js",
{=! Private: [client] =}
"./env/validation": "./dist/env/validation.js",
{=! Private: [client, sdk] =}
"./client/env/schema": "./dist/client/env/schema.js",
Comment on lines +113 to +116
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the google sheet with these guys if you haven't :)


{=! todo(filip): Fixes below are for type errors in 0.13.1, remove ASAP =}
{=! Used by our code (SDK for full-stack type safety), uncodumented (but accessible) for users. =}
Expand Down
9 changes: 9 additions & 0 deletions waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest

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

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

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

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

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

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

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

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

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

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

Loading
Loading