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.envI 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).
The "fix" is to avoid destructuring read from the property directly:
const FRAME_SOURCE = import.meta.env.FRAME_SOURCE ?? "foo"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.envDestructing 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.