npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@julr/vite-plugin-validate-env

v1.2.0

Published

✅ Vite plugin for validating your environment variables

Downloads

51,243

Readme

This Vite plugin allows you to validate your environment variables at build or dev time. This allows your build/dev-server to fail-fast if your setup is misconfigured.

No more CI to restart because you are missing an environment variable, or to realize after 10 minutes of debugging that you forgot a variable 🥲

Features

  • Validate your environment variables at build time only. No runtime overhead
  • Totally type-safe
  • Support multiple validation librairies ( Zod, and @poppinss/validator-lite )
  • Parsing, validation and transformation of your variables
  • Custom rules and error messages

Installation

pnpm add -D @julr/vite-plugin-validate-env

Usage

vite-plugin-validate-env plugin allows you to validate your env, either with a very simplified builtin validation lib, or with Zod in the most complex cases when you want a very strict validation.

Plugin options

The easiest way to define the options is to directly define the scheme as follows:

// vite.config.ts
import { defineConfig } from "vite";
import { Schema, ValidateEnv } from "@julr/vite-plugin-validate-env";

export default defineConfig({
  plugins: [
    ValidateEnv({
      VITE_MY_VAR: Schema.string()
    }),
  ],
})

In case you want to change some plugin options, in particular change the validator (for Zod), you have to set your options as follows:

import { defineConfig } from "vite";
import { z } from 'zod'
import { ValidateEnv } from "@julr/vite-plugin-validate-env";

export default defineConfig({
  plugins: [
    ValidateEnv({
      validator: 'zod',
      schema: {
        VITE_MY_VAR: z.string()
      }
    }),
  ],
})

If you want to see what values are being evaluated for the build, for example when running in CI. You can pass the debug option as follows:

import { defineConfig } from "vite";
import { Schema, ValidateEnv } from "@julr/vite-plugin-validate-env";

export default defineConfig({
  plugins: [
    ValidateEnv({
      debug: true,
      schema: {
        VITE_MY_VAR: Schema.string()
      }
    }),
  ],
})

Built-in validator

import { Schema, ValidateEnv } from "@julr/vite-plugin-validate-env"
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    ValidateEnv({
      // Data types
      VITE_STRING_VARIABLE: Schema.string(),
      VITE_BOOLEAN_VARIABLE: Schema.boolean(),
      VITE_NUMBER_VARIABLE: Schema.number(),
      VITE_ENUM_VARIABLE: Schema.enum(['foo', 'bar'] as const),
      
      // Optional variable
      VITE_OPTIONAL_VARIABLE: Schema.boolean.optional(),

      // Specify string format
      VITE_AUTH_API_URL: Schema.string({ format: 'url', protocol: true }),

      // Specify error message
      VITE_APP_PORT: Schema.number({ message: 'You must set a port !' }),

      // Custom validator
      VITE_CUSTOM_VARIABLE: (key, value) => {
        if (!value) {
          throw new Error(`Missing ${key} env variable`)
        }

        if (value.endsWith('foo')) {
          throw new Error('Value cannot end with "foo"')
        }

        return value
      },
    }),
  ],
})

Zod Validator

To use the Zod validator, you must first install it if you have not already done so

pnpm install zod

Then, you can use it as follows:

// env.ts
import { defineConfig } from '@julr/vite-plugin-validate-env'
import { z } from 'zod'

export default defineConfig({
  validator: 'zod',
  schema: {
    VITE_MY_STRING: z.string().min(5, 'This is too short !'),
    VITE_ENUM: z.enum(['a', 'b', 'c']),
    VITE_BOOLEAN_VARIABLE: z.boolean(),
  }
})

Beware, there are some limitations if you use Zod. For example, you can't use a boolean or number type directly. Because everything that comes from your .env file is a string by default.

So to validate other types than string you must use preprocess, and transform, like this:

// env.ts
import { defineConfig } from '@julr/vite-plugin-validate-env'
import { z } from 'zod'

export default defineConfig({
  validator: 'zod',
  schema: {
    // This will transform the string 'true' or '1' to a boolean
    VITE_BOOLEAN_VARIABLE: z
      .preprocess((value) => value === 'true' || value === '1', z.boolean()),

    // Will convert the string to a number
    VITE_NUMBER: z.preprocess((value) => Number(value), z.number()),

    // Will parse the string to an object
    VITE_OBJECT: z.preprocess(
      (value) => JSON.parse(value as string),
      z.object({
        a: z.string(),
        b: z.number(),
      }),
    ),
  }
})

In this case, true and 1 will be transformed to true and your variable will be valid and considered as a boolean.

Dedicated config file

You can also add a env.ts file at the root of your project to define your environment variables.

// vite.config.ts
import { defineConfig } from 'vite'
import { ValidateEnv } from "@julr/vite-plugin-validate-env";

export default defineConfig({
  plugins: [ValidateEnv()],
})
// env.ts
import { defineConfig, Schema } from '@julr/vite-plugin-validate-env'

export default defineConfig({
 VITE_MY_VAR: Schema.enum(['foo', 'bar'] as const),
})

Custom config file path

By default, the plugin is looking for a file named env.ts at the root of your project. If you want to use a different file, you can specify the path to your file in the plugin options.

// vite.config.ts
import { defineConfig } from 'vite'
import { ValidateEnv } from "@julr/vite-plugin-validate-env";

export default defineConfig({
  plugins: [ValidateEnv({ configFile: 'config/env' })],
})

This will look for a file named env.ts in the config folder at the root of your project. Make sure to not include the file extension in the path as the plugin will automatically search for .js, .ts and other valid file extensions.

Transforming variables

In addition to the validation of your variables, there is also a parsing that is done. This means that you can modify the value of an environment variable before it is injected.

Let's imagine the following case: you want to expose a variable VITE_AUTH_API_URL in order to use it to call an API. However, you absolutely need a trailing slash at the end of this environment variable. Here's how it can be done :

// Built-in validation
import { defineConfig, Schema } from '@julr/vite-plugin-validate-env'

export default defineConfig({
  VITE_AUTH_API_URL: (key, value) => {
    if (!value) {
      throw new Error(`Missing ${key} env variable`)
    }

    if (!value.endsWith('/')) {
      return `${value}/`
    }

    return value
  },
})
// Zod validation
import { defineConfig } from '@julr/vite-plugin-validate-env'
import { z } from 'zod'

export default defineConfig({
  validator: 'zod',
  schema: {
    VITE_AUTH_API_URL: z
      .string()
      .transform((value) => value.endsWith('/') ? value : `${value}/`),
  },
})

Now, in your client front-end code, when you call import.meta.env.VITE_AUTH_API_URL, you can be sure that it will always end with a slash.

Typing import.meta.env

In order to have a type-safe import.meta.env, the ideal is to use the dedicated configuration file env.ts. Once this is done, you would only need to add an env.d.ts in src/ folder to augment ImportMetaEnv (as suggested here ) with the following content:

/// <reference types="vite/client" />

type ImportMetaEnvAugmented = import('@julr/vite-plugin-validate-env').ImportMetaEnvAugmented<
  typeof import('../env').default
>

interface ImportMetaEnv extends ImportMetaEnvAugmented {
  // Now import.meta.env is totally type-safe and based on your `env.ts` schema definition
  // You can also add custom variables that are not defined in your schema
}

Forbid unknown variables

Since we rely on module augmentation to type import.meta.env, using unknown variables won’t trigger errors because the ImportMetaEnv interface from Vite includes a [key: string]: string signature.

To enforce stricter typing and prevent the use of unknown variables, you can set up the following:

// lib/env.ts or wherever you want
import { ImportMetaEnvAugmented } from '@julr/vite-plugin-validate-env';

export const env: ImportMetaEnvAugmented = import.meta.env;

By using env instead of import.meta.env in your code, TypeScript will now throw an error if you try to access an unknown variable.

Sponsors

If you like this project, please consider supporting it by sponsoring it. It will help a lot to maintain and improve it. Thanks a lot !

License

MIT License © 2022 Julien Ripouteau