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
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).
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.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.