Original Discussion: https://www.pika.dev/npm/snowpack/discuss/253
/cc @jiay0u, @FredKSchott
Currently, Snowpack has no good way to read env variables into your frontend application. Things like process.env.NODE_ENV won't work, since process is a Node thing that doesn't exist in the browser.
We should support either import.meta.env.FOO (Node-style) or import.meta.getEnv('FOO') (Deno style). Since this is a build-time transformation that gets converted to a string (and not an actual function call) I think Node-style makes the most sense.
The easiest way to test this would be as a 3rd party plugin that implements this in a transform() function. If we can get that working, then we can decide to integrate it into Snowpack directly.
See https://create-react-app.dev/docs/adding-custom-environment-variables/ for how CRA does it.
https://github.com/vitejs/vite seems to have first-party support for .env files, which is also interesting. I'd love to see a plugin that supports this as well.
How would you go about this?
I can easily see how a transform plugin would add this in to a file's content, however how would you go about determining which file to add it to. You surely don't want to add it to every file that is transformed?
I think the easiest way would be a REGEX find and replace to replace all references to import.meta.env with the actual string env value. This is what Webpack and others do today.
/import\.meta\.env\.(.*)/gm (.* isn't right, but you get the gist :)process.env.$1 when dev/build is run.The plugin would implement something like:
{
transform({ contents, urlPath, isDev }) {
if (!urlPath.endsWith(".js")) {
return null;
}
// DO FIND AND REPLACE, AND RETURN
}
Also for anyone who's blocked by this today, you could support this via a Babel plugin and the old process.env concept: https://babeljs.io/docs/en/babel-plugin-transform-node-env-inline
An alternate interface for consideration:
// snowpack.config.js
module.exports = {
scripts: {
'mount:src': 'mount ./src --to /',
},
plugins: ['@snowpack/plugin-env'],
}
// your app code
import env from '@snowpack/plugin-env'
console.log(env.NODE_ENV)
When the plugin is asked to transform itself it will more or less return:
export default {
// process.env outputted here
NODE_ENV: 'development'
}
This seems more explicit and less "magical", however I guess the main downside is that it departs from webpack's convention, which I guess means some lock in. But it is lock in to a plugin not snowpack itself.
This is a really interesting approach, although there are two issues that we'd need to solve for:
Next.js just has an env key on their config file. https://nextjs.org/docs/api-reference/next.config.js/environment-variables
Next also uses the NEXT_PUBLIC_ prefix, which is making me rethink whether we should too. I'd feel terrible if someone used some private key in their app without realizing that it was going out to the world.
Maybe we just go full "dotenv" support and build that into Snowpack instead of pulling from process.env directly. That feels a bit safer, since anything in your system ENV probably isn't meant for a public website. Also https://github.com/motdotla/dotenv is actually really easy to integrate, in my past experience.
Yeah I'm not sure 1. is a problem in itself but I can see how it could cause confusion.
process.env. So you could have something like:module.exports = {
scripts: {
'mount:src': 'mount ./src --to /',
},
plugins: [
['config/index', {
NODE_ENV: process.env.NODE_ENV
}]
],
}
I'm also a fan of dotenv and would like to see that support OOB as well. However I wonder if this would cause problems for some code bases if they are already using a .env file out of the same code base to run a node server and it has a bunch of not for public secrets in it.
Ah you're right, that must be why they still use NEXT_PUBLIC_ and REACT_APP_ even in the .env files.
If we do the same, then it would be fine to use one .env for both server and client since we could filter out anything that doesn't start with SNOWPACK_PUBLIC/SP_PUBLIC_/PUBLIC before passing it to the build-time transformer.
I'm interested in this topic.
Okay, took a stab at this: https://github.com/pikapkg/snowpack/pull/394
There are some open questions in the TODO section, would love any feedback.
Merged via #394
Most helpful comment
Also for anyone who's blocked by this today, you could support this via a Babel plugin and the old process.env concept: https://babeljs.io/docs/en/babel-plugin-transform-node-env-inline