Skip to content

Instantly share code, notes, and snippets.

@NickTomlin
Created April 22, 2023 20:28
Show Gist options
  • Save NickTomlin/0c824179c26e81735986759562596f68 to your computer and use it in GitHub Desktop.
Save NickTomlin/0c824179c26e81735986759562596f68 to your computer and use it in GitHub Desktop.

tldr; always read off of full paths with Vite's define.

// GOOD
const MY_THING = import.meta.env.MY_THING 
// BAD
const { MY_THING} = import.meta.env

Context

I am working with multiple vite projects that use a define statement to set a FRAME_SOURCE variable. This variable is used to load a secondary HTML file into an iframe.

// vite.config.js
export default config({
    define: {
        "import.meta.env.FRAME_SOURCE": "'./src/frame.html'"
    }
})
// src/components/frame.js
const { FRAME_SOURCE = "/default/path" } = import.meta.env
// write an react component that uses FRAME_SOURCE to load an iframe
function Frame() {
    return <iframe src={FRAME_SOURCE} />
}

This worked fine in development, but failed in prod. FRAME_SOURCE had become "'./src/frame.html'" instead of "./src/frame.html" (extra quotes).

Resolution

The "fix" is to avoid destructuring read from the property directly:

const FRAME_SOURCE = import.meta.env.FRAME_SOURCE ?? "foo"

Why?

This is due to the way that vite handles define statements in dev versus prod` mode.

in dev mode, additional values on import.meta.env are added to the import.meta.env object. This results in the following:

import.meta.env = {"BASE_URL":"/","MODE":"development","DEV":true,"PROD":false,"SSR":false}
import.meta.env.FRAME_SOURCE = './src/frame.html'

const { FRAME_SOURCE = ""} = import.meta.env

Destructing off the object works fine, since the value is just a JSON object.

When building for prod however, we get the following:

const { FRAME_SOURCE = ""} = import.meta.env

// boils down to
const { FRAME_SOURCE = `/src/components/configurable/frame/${FRAME_FILE}` } = {
    "VITE_BUILD_NUMBER":"",
    // ...
    "FRAME_SOURCE":"'src/frame.html'"
};

Notice that the value of FRAME_SOURCE is a string wrapped in a string.

This is because the define plugin is kicking in and processing user defines

Instead of adding "raw" properties to import.meta.env (as in dev mode) it is purely looking up keys on

const replacements = {
    // ... many more
    'import.meta.env.MODE': '"production"',
    'import.meta.env.FRAME_SOURCE': "'./src/frame.html'",
    'import.meta.env': `{"BASE_URL":"/","MODE":"production","DEV":false,"PROD":true,"SSR":false,"FRAME_SOURCE":"'./src/frame.html'","bar":"'bar!'"}`,
}

but import.meta.env.FRAME_SOURCE becomes './src/frame.html'

const FRAME_SOURCE = import.meta.env.FRAME_SOURCE ?? "foo"
// becomes
const FRAME_SOURCE = "./src/frame.html"

You'd expect this to be handled by the define plugin, but it's not. That's because of the logic here

This is because the replacements are stored like so:

const replacements = {
    // ... many more
    'import.meta.env.MODE': '"production"',
    'import.meta.env.FRAME_SOURCE': "'./src/frame.html'",
    'import.meta.env': `{"BASE_URL":"/","MODE":"production","DEV":false,"PROD":true,"SSR":false,"FRAME_SOURCE":"'./src/frame.html'","bar":"'bar!'"}`,
}

When we use import.meta.env.FRAME_SOURCE directly, vite prints the string './src/frame.html' when we access import.meta.env we don't have the wrapping quotes removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment