Skip to content

garronej/vite-envs-starter

Repository files navigation

Starter setup for vite-envs

This is a starter setup to demonstrate how to set up vite-envs in a Vite/TypeScript/Docker WebApp.

Try it

Declare the variables that your app will accept.

.env (Should be added to Git, if your .env is gitignored you can use another file)

TITLE=Default title
DESCRIPTION=

Set the values for your dev environment.

.env.local (Should be gitignored, it can also be the .env file if you decided to use another file for declaring your variables)

TITLE=Custom title
DESCRIPTION="Custom description"

src/main.tsx

console.log(`The title of the page is ${import.meta.env.TITLE}`);

index.html

<!doctype html>
<html lang="en">
  <head>
    <!-- ... -->

    <title>%TITLE%</title>
    <meta name="description" content="%DESCRIPTION%">

</head>

Optionally, define computed variables.

vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { viteEnvs } from 'vite-envs'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    viteEnvs({
      computedEnv: async ({ resolvedConfig, env, envLocal }) => {

        const path = await import('path')
        const fs = await import('fs/promises')

        const packageJson = JSON.parse(
          await fs.readFile(
            path.resolve(__dirname, 'package.json'),
            'utf-8'
          )
        )

        // Here you can define any arbitrary values they will be available 
        // in `import.meta.env` and it's type definitions.  
        // You can also compute defaults for variable declared in `.env` files.
        return {
          BUILD_TIME: Date.now(),
          VERSION: packageJson.version
        }

      }
    })
  ]
})

Run build and run your docker image with environment variables.:

docker build -t garronej/vite-envs-starter:main .

docker run -it -p 8083:8080 \
    --env TITLE='Title from container env' \
    --env DESCRIPTION='Description from container env' \
    garronej/vite-envs-starter:main

Reach http://localhost:8083 🚀.

image

Demo video

Config highlights

Here are listed the configurations that diverges from a vanilla Vite/Docker setup.

package.json

 "devDependencies": {
+    "vite-envs": "^3.5.4",
 }

vite.config.ts

 import { defineConfig } from 'vite'
+import { viteEnvs } from 'vite-envs'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
+   viteEnvs({
+     declarationFile: ".env"
+   })
  ]
})

package.json

 "scripts": {
+  "postinstall": "vite-envs update-types",
   "dev": "vite",
   "build": "tsc && vite build"
 }

npx vite-envs update-types updates src/vite-envs.d.ts to make TypeScript aware of the environment variables you have declared in you .env file.
This script is not strictly required it's just for a better development experience.

Dockerfile

 # build environment
 FROM node:20-alpine as build
 WORKDIR /app
 COPY package.json yarn.lock .env ./
 RUN yarn install --frozen-lockfile
 COPY . .
 RUN yarn build
 
 # production environment
 FROM nginx:stable-alpine
 COPY --from=build /app/nginx.conf /etc/nginx/conf.d/default.conf    
 WORKDIR /usr/share/nginx/html
 COPY --from=build /app/dist .
-ENTRYPOINT sh -c "nginx -g 'daemon off;'"
+ENTRYPOINT sh -c "./vite-envs.sh && nginx -g 'daemon off;'"

.env file gitignored

If you don't want to add the .env file to Git you can use another file for declaring the variables names and default values.

vite.config.ts

 import { defineConfig } from 'vite'
 import { viteEnvs } from 'vite-envs'
 
 // https://vitejs.dev/config/
 export default defineConfig({
   plugins: [
     viteEnvs({
+      declarationFile: ".env.declaration"
     })
   ]
 })

If you use another file that .env as your declaration files feel free to use the .env file in place of the .env.local file.

EJS

Caveats: Enabling EJS requires to have Node available in you Docker container this will add an extra 20MB to your docker image size.
Be also aware that it won't work if you use other vite plugin that transform the index.html.

For some usecases placeholder substitution like %FOO% in the index.html is not enough.
vite-envs let you use EJS expressions in your index.html file.
This enables you to generate different HTML based on the environment variables values.
This is useful if you want to perform operation like the following:

``html

<% const obj = JSON5.parse(import.meta.env.CUSTOM_META); %> <% for (const [key, value] of Object.entries(obj)) { %> <% } %> ```

💡: YAML.parse() is also available in the EJS global scope if you need it.

To enable this feature:

vite.config.ts

 import { defineConfig } from 'vite'
 import { viteEnvs } from 'vite-envs'

 export default defineConfig({
   plugins: [
     viteEnvs({
+      indexAsEjs: true
     })
   ]
 })

Dockerfile

 # production environment
 FROM nginx:stable-alpine
+RUN apk add --update nodejs npm
 COPY --from=build /app/nginx.conf /etc/nginx/conf.d/default.conf    
 WORKDIR /usr/share/nginx/html
 COPY --from=build /app/dist .
+RUN npm i -g vite-envs@`node -e 'console.log(require("./.vite-envs.json").version)'`
-ENTRYPOINT sh -c "./vite-envs.sh && nginx -g 'daemon off;'"
+ENTRYPOINT sh -c "npx vite-envs && nginx -g 'daemon off;'"

Accessing your env from service workers.

Warning

vite-envs does not provide a great solution for accessing your env variables from your service worker files at the moment. I have to work on that.
The solution below is a workaround that you'll have to adapt to make it work in dev mode.
Please open an issue if it's a deal breaker for you, I'll prioritize working on it.

You can use the dist/swEnv.js file generated by vite-envs to access your env from your service worker.

Assuming you have my-worker.js files that get copied over to your dist/ directory you can do:

// Adjust the argument depending on where is the swEnv.js relative to your
// service worker file in the final distribution.  
importScripts("swEnv.js");

self.__VITE_ENVS.MY_VAR;

Encoding/escaping issues

It can be challenging to define some values in the .env files due to the lack of escaping capabilities.
Especially when you need to use a combination of ", ' and # characters.

To tackle this cases vite-envs enable you to passes your envs as b64 values like:

.env or .env.local

FOO="vite-envs:b64Decode(VmFsdWUgb2YgRk9P)" # In your code `import.meta.env.FOO will === "Value of FOO"`

Publishing and deploying the Docker image of your Vite App

There's nothing else you need to know to start using vite-envs, however if you're interested, here are some instruction that you can follow to publish your Docker image on DockerHub with GitHub Actions and deploying the image using Railway, all for Free.

image

Publishing and deploying your Vite app Docker Image

👉 Step by step Guide.

This starter comes with a fully generic CI workflow that publishes the Docker image of your Vite App of DockerHub automatically.
You can copy past the ci.yaml file into your repo, there is nothing to change.
It will publish the Docker image <your github username>/<your vite repo name>.

To enable it simply create two GitHub secrets:

  • DOCKERHUB_USERNAME
  • DOCKERHUB_TOKEN

To trigger the workflow just bump the version number in the package.json and push!