Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] Use environment variables inside the HTML #1209

Closed
GaelS opened this issue Apr 17, 2018 · 18 comments
Closed

[feature] Use environment variables inside the HTML #1209

GaelS opened this issue Apr 17, 2018 · 18 comments

Comments

@GaelS
Copy link

GaelS commented Apr 17, 2018

🙋 feature request!

First thing first: Thanks for the nice work ! 👍

I have a use case where an anchor tag redirects to an external URL based on the environment (localhost:3333 if in DEV for instance and https://whatever.com in PROD).

Therefore, it would be really convenient to be able to use the environment variables directly inside the index.html without going through any javascript code.

Just like this : <a href=%MY_ENV_VARIABLE%>Ok</a>

The % are used randomly just to show some kind of formatting to handle ENV variables at build time.

Maybe I have missed something but I was not able to find any kind of documentation or issues (open or close) dealing with this kind of things. Maybe I'm totally missing something but with this type of use cases, this feature would be very convenient.

@mririgoyen
Copy link

+1

We would like to use different favicons to signify our dev environment versus our production environment so that when we have many tabs open, it's easy to pick out where you're looking. Having access to an env variable in the HTML would allow us to do this easily.

@adamelliotfields
Copy link

adamelliotfields commented May 13, 2018

Would also be useful for additional script tags (service worker, Google analytics, etc) that you don't need when developing/designing your app; but obviously need when deployed.

I think this could be resolved by allowing the entry point to be a template file like Handlebars, EJS, or Pug. Parcel automatically sets the NODE_ENV to production which could be passed to the template at build time.

Those of us coming from Webpack got used to html-webpack-plugin, so it would be great to see Parcel incorporate similar functionality, while still maintaining the exceptional developer experience it offers over Webpack.

UPDATE:

This is actually already supported. I created a simple example using Pug. You do have to npm add -D pug.

index.pug

doctype html
html(lang='en')
  head
    meta(charset='utf-8')
    meta(name='viewport', content='width=device-width, initial-scale=1, shrink-to-fit=no')
    title Parcel
  body
    if process.env.NODE_ENV === 'production'
      h1 PRODUCTION
    else
      h1 DEVELOPMENT

Running parcel build index.pug will generate the following index.html:

dist/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Parcel</title>
  </head>
  <body>
    <h1>PRODUCTION</h1>
  </body>
</html>

Therefor, to resolve @GaelS 's issue, you could do something like this:

body
  a(href=process.env.LINK_HREF) Ok
LINK_HREF='http://example.com' parcel build index.pug
<body>
  <a href="http://example.com">Ok</a>
<body>

Pretty cool and still zero config 👍

@breier
Copy link

breier commented Oct 19, 2018

You can use <%= MY_ENV_VAR %> syntax.

The example bellow uses VUE_APP_TITLE variable that I set in my .env files and BASE_URL that's a system environment variable.

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= VUE_APP_TITLE %></title>
</head>

@mrcoles
Copy link

mrcoles commented Oct 23, 2018

If you’re using something that runs JS (like react) as mentioned you can just reference process.env in your code. However, I was able to get environment variables within my regular HTML files using PostHTML with the following steps:

  1. Set some environment variables, e.g., I created .env.development in the root of my project as such:

    HOME_URL=/index.html
    
  2. Add the posthtml-expressions library:

    yarn add -D posthtml-expressions
  3. Add a posthtml config file—important that it’s a JS file and not just JSON—posthtml.config.js (the reason for using these as JS files is not really documented currently, but the ability to access process.env here is why I’m using it):

    module.exports = {
      plugins: {
        "posthtml-expressions": {
          locals: {
            HOME_URL: process.env.HOME_URL
          }
        }
      }
    };
  4. Update my HTML to use the posthtml-expressions syntax:

    <a href="{{ HOME_URL || '/' }}">home</a>
  5. In order to try it out in dev yarn start and to build it for production yarn build taking into account different environments, I have the following in my package.json:

    {
      "scripts": {
        "start": "yarn parcel src/*.html",
        "build": "NODE_ENV=prod parcel build --public-url ./ src/index.html",
      }
    }

And voila, you have environment variables reflected in your HTML.

Also, one gotcha: I’m not seeing changes to .env triggering a re-build. You’ll have to edit something to trigger a re-build or stop parcel, rm -r .cache, and then start parcel.


@breier in your example, are you using some extra asset-type handler to handle that ruby syntax <%= … %>?

@breier
Copy link

breier commented Nov 1, 2018

@mrcoles I've used vue-cli to start a project using webpack template. No additions.

@devongovett
Copy link
Member

This isn't something we'll support out of the box since it isn't standard HTML syntax. But you could easily use a PostHTML plugin for this, or another template language like Pug.

@vicpon
Copy link

vicpon commented Apr 5, 2019

@mrcoles I've used vue-cli to start a project using webpack template. No additions.

@breier Are you using the vue cli to have process.env available? If so, how? Thanks

@mrcoles
Copy link

mrcoles commented Oct 8, 2019

is it HOME_URL or HOME_PATH?

@dandv, sorry that was a typo, I updated my comment so all HOME_PATH references are HOME_URL. Of course, feel free to use any variable names you want in your code :shipit:

@breier
Copy link

breier commented Mar 11, 2020

@mrcoles I've used vue-cli to start a project using webpack template. No additions.

@breier Are you using the vue cli to have process.env available? If so, how? Thanks

Sorry for the really late reply, but for the record, here it is...
https://cli.vuejs.org/guide/mode-and-env.html#environment-variables

Note that only NODE_ENV, BASE_URL, and variables that start with VUE_APP_ will be statically embedded into the client bundle with webpack.DefinePlugin. It is to avoid accidentally exposing a private key on the machine that could have the same name.

Basically, any custom env var just have to start with VUE_APP_ in order to be auto loaded and assimilated.

I hope it helps someone ;)

@zanona
Copy link

zanona commented Nov 25, 2020

@mrcoles, were you able to use NODE_ENV though?
It seems to be set to production regardless of what's used.

posthtml.config.js

console.log(process.env.NODE_ENV)
module.expors = {};
$ npx parcel --version
2.0.0-nightly.462
$ NODE_ENV=test npx parcel build --no-cache index.html
console: production
⠸ Building index.html...

If you’re using something that runs JS (like react) as mentioned you can just reference process.env in your code. However, I was able to get environment variables within my regular HTML files using PostHTML with the following steps:
3. Add a posthtml config file—important that it’s a JS file and not just JSON—posthtml.config.js (the reason for using these as JS files is not really documented currently, but the ability to access process.env here is why I’m using it):
js module.exports = { plugins: { "posthtml-expressions": { locals: { HOME_URL: process.env.HOME_URL } } } };

@talor-hammond
Copy link

If you’re using something that runs JS (like react) as mentioned you can just reference process.env in your code. However, I was able to get environment variables within my regular HTML files using PostHTML with the following steps:

  1. Set some environment variables, e.g., I created .env.development in the root of my project as such:
    HOME_URL=/index.html
    
  2. Add the posthtml-expressions library:
    yarn add -D posthtml-expressions
  3. Add a posthtml config file—important that it’s a JS file and not just JSON—posthtml.config.js (the reason for using these as JS files is not really documented currently, but the ability to access process.env here is why I’m using it):
    module.exports = {
      plugins: {
        "posthtml-expressions": {
          locals: {
            HOME_URL: process.env.HOME_URL
          }
        }
      }
    };
  4. Update my HTML to use the posthtml-expressions syntax:
    <a href="{{ HOME_URL || '/' }}">home</a>
    
  5. In order to try it out in dev yarn start and to build it for production yarn build taking into account different environments, I have the following in my package.json:
    {
      "scripts": {
        "start": "yarn parcel src/*.html",
        "build": "NODE_ENV=prod parcel build --public-url ./ src/index.html",
      }
    }

And voila, you have environment variables reflected in your HTML.

For Parcel v2, you will have to call dotenv.config() at the top of your posthml config:

const dotenv = require("dotenv");

dotenv.config();

module.exports = {
  plugins: {
    "posthtml-expressions": {
      locals: {
        HOME_URL: process.env.HOME_URL,
        ...

@anantakrishna
Copy link

For Parcel v2, you will have to call dotenv.config() at the top of your posthml config:

Unfortunately, it seems to read only .env file:

error: Error: ENOENT: no such file or directory, open 'C:\Users\User\source\repos\project\.env'

Whereas Parcel explicitly loads .env.${NODE_ENV}.local and .env.${NODE_ENV}:

const dotenvFiles = [
'.env',
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV === 'test' ? null : '.env.local',
`.env.${NODE_ENV}`,
`.env.${NODE_ENV}.local`,
].filter(Boolean);

All this sounds like a show-stopper as of now. Switching to PUG is a bit… overkill to just inject 2-3 environment variables into HTML. I'd better consider writing a simple parcel transformer for this… or a PostHTML plugin.

@supersetapp
Copy link

I’m trying out Parcel2 and using my prior posthtml-expressions example and also building off the two prior comments from @talor-hammond and @anantakrishna where it’s identified that we need to load the environment variables ourselves, I was able to get things working by doing the following:

const dotenv = require('dotenv');

const NODE_ENV = process.env.NODE_ENV;

const dotenvFiles = [
  '.env',
  // Don't include `.env.local` for `test` environment
  // since normally you expect tests to produce the same
  // results for everyone
  NODE_ENV === 'test' ? null : '.env.local',
  `.env.${NODE_ENV}`,
  `.env.${NODE_ENV}.local`,
].filter(Boolean);

const env = {};

for (let dotenvFile of dotenvFiles) {
  const config = dotenv.config({ path: dotenvFile });
  if (config.parsed) {
    Object.assign(env, config.parsed);
  }
}

module.exports = {
  plugins: {
    'posthtml-expressions': {
      locals: {
        HOME_URL: env.HOME_URL,
      },
    },
  },
};

Given this extra, brittle-feeling code and also the big disclaimers that keep popping up about how a posthtml.config.js is terrible for caching, is there a preferred alternate way to inject environment variables into HTML? Maybe using a different template language like pug—or just moving any of this code into a JS file? (cc @devongovett)

danielfdsilva added a commit to NASA-IMPACT/veda-ui that referenced this issue Dec 13, 2021
@KaKi87
Copy link

KaKi87 commented Feb 14, 2022

Hello, accessing process.env.NODE_ENV doesn't work for me as of Parcel 2.3.1, using either html or pug. Thanks

@alshdavid
Copy link
Contributor

Seconded ^

@michaellenaghan
Copy link

Putting process.env vars in HTML files has been, by far, the most painful part of switching from Webpack to ParcelJS.

(I have API keys for things like Google Maps, and I want to use different keys for development and production. That means simple text substitution won't work; I need dotenv support.)

Here's what I'm currently doing...

First, I installed @plasmohq/parcel-transformer-inject-env:

npm install -D @plasmohq/parcel-transformer-inject-env

Then I added it to .parcelrc:

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.html": [
      "@plasmohq/parcel-transformer-inject-env",
      "..."
    ]
  }
}

Then I created .env.development and .env.production files that contained the keys I needed:

GOOGLE_MAPS_API_KEY=...

Then I referenced those keys using $ notation in my HTML files:

...?key=$GOOGLE_MAPS_API_KEY...

The way the transformer works may not quite suit your needs — in particular, there's no escape mechanism — but the implementation is actually quite simple; see here and here. If you need to, you can probably create your own version pretty easily.

The advantage of this approach over posthtml-expressions is that you aren't using a .js config file, so you don't loose caching.

@henriquenovais
Copy link

Just to put the experience of someone who tried the most liked strategies of implementation in this topic:
I am using the latest parcel version as of right now, nothing else.

  • I tried the pug solution to build into an html file: could not make it work out of the box (tried for about two hours)
  • I used the posthtml solution but it reduced greatly the build perfomance (in my opinion) if you think of scalating your project

Best solution to me was the one that came from @michaellenaghan.

It is indeed a pain to push an environment variable through dotenv to index.html using parcel, tons of trial and error.

I applied @michaellenaghan solution here: https://github.com/henriquenovais/Typescript101.

@jimfilippou
Copy link

Thank you @michaellenaghan we used what you proposed internally and works perfectly!

#1209 (comment)

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

No branches or pull requests