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] read substitution variables from .env file #69

Closed
darioielardi opened this issue Apr 19, 2020 · 13 comments
Closed

[Feature] read substitution variables from .env file #69

darioielardi opened this issue Apr 19, 2020 · 13 comments

Comments

@darioielardi
Copy link

It would be nice to have a flag --env to specify an .env file to read substitution variables from.

This way I wouldn't need to have multiple 'define:process.env.K=V' for multiple environment variables.

It could simply leverage the '--define' flag capabilities, prepending process.env. to each variable read from the specified file.

@evanw
Copy link
Owner

evanw commented May 5, 2020

I recently added a JavaScript API that should make these kinds of things a lot nicer. This is pretty similar to "config files" in other bundlers.

For example, you can now write a build script like this:

const { build } = require('esbuild')

const options = {
  stdio: 'inherit',
  entryPoints: ['./src/main.ts'],
  outfile: './dist/main.js',
  bundle: true,
  define: {
    'process.env.NODE_ENV': '"production"',
    'process.env.DEBUG': 'false',
  },
}

build(options).catch(() => process.exit(1))

You can even draw from the real environment at build time if you need to:

const { build } = require('esbuild')
const define = {}

for (const k in process.env) {
  define[`process.env.${k}`] = JSON.stringify(process.env[k])
}

const options = {
  stdio: 'inherit',
  entryPoints: ['./src/main.ts'],
  outfile: './dist/main.js',
  bundle: true,
  define,
}

build(options).catch(() => process.exit(1))

I'm going to close this issue since I believe it's addressed by this new functionality.

@evanw evanw closed this as completed May 5, 2020
@darioielardi
Copy link
Author

That's awesome and it definitely addresses my issue, thanks!

@Speedy1991
Copy link

If you develop a react app and want the same behaviour as react-scripts you should only pass variables starting with REACT_APP_

for (const k in process.env) {
  if (k.startsWith('REACT_APP_')) {
    define[`process.env.${k}`] = JSON.stringify(process.env[k]);
  }
}

@iambumblehead
Copy link

Other toolchains support standard out-of-box env vars without requiring configuration scripts, eg VUE_APP_VAR, REACT_APP_VAR or S3_DIRECTORY_SYNC_VAR. Esbuild requires an extra config script to do this and, for people accustomed to using env vars with other tools, this situation makes esbuild incomplete and slightly annoying.

That said, esbuild is a terrific tool and I'm glad to be using it. I respect and appreciate this project. Esbuild would be even better if it supported env vars without requiring an extra config script.

@iambumblehead
Copy link

@evanw is there any chance you might re-open this for everyone who thumbs-upped my last comment?

@NatoBoram
Copy link

How do we use it, though?

I have some environment variables in my build.js:

const define = {
  'process.env.INLINE_BOLD': JSON.stringify(process.env['INLINE_BOLD']),
  'process.env.INLINE_COLOR': JSON.stringify(process.env['INLINE_COLOR']),
  'process.env.INLINE_ITALIC': JSON.stringify(process.env['INLINE_ITALIC']),
  'process.env.INLINE_LINK': JSON.stringify(process.env['INLINE_LINK']),
  'process.env.INLINE_MARKER': JSON.stringify(process.env['INLINE_MARKER']),
  'process.env.INLINE_STRIKETHROUGH': JSON.stringify(process.env['INLINE_STRIKETHROUGH']),
  'process.env.INLINE_UNDERLINE': JSON.stringify(process.env['INLINE_UNDERLINE']),
};

/** @type import('esbuild').BuildOptions */
let opts = {
  entryPoints: ['js/app.js'],
  bundle: true,
  target: 'es2016',
  outdir: '../priv/static/assets',
  logLevel: 'info',
  loader,
  plugins,
  external: ['/images/*'],
  define,
};

And some code that I want to be replaced:

console.log('process.env.INLINE_BOLD');
console.log(process.env.INLINE_BOLD);

The compiled output:

console.log("process.env.INLINE_BOLD");
console.log(process.env.INLINE_BOLD);

And the result in the console:

process.env.INLINE_BOLD
Uncaught ReferenceError: process is not defined

Something isn't happening on my end...

@marianneciara
Copy link

@NatoBoram I'm also having this issue – did you manage to resolve it in the end?

@NatoBoram
Copy link

Nope. Since we're doing back-end, we just injected these variables as a JSON string inside the HTML.

@marianneciara
Copy link

Thanks for your reply. I actually pieced together a solution that's working for me, posting my solution just in case it helps anyone.

Basically, I wasn't specifying the config file in my scripts – I wrongly assumed that the config file would be automatically picked up and would replace my inline esbuild command in package.json.

Currently I've got this setup that's working:

.env

VARIABLE="Value"

package.json

"dev:js": "node esbuild.config.js --watch"
"build:js": "node esbuild.config.js"

esbuild.config.js

const { build } = require('esbuild')
require('dotenv').config()
const args = process.argv.slice(2)

const options = {
  entryPoints: ['src/assets/js/_main.js'],
  outfile: 'dist/assets/js/main.bundle.js',
  bundle: true,
  minify: true,
  watch: args.includes('--watch'),
  define: {
    'VARIABLE': JSON.stringify(process.env.VARIABLE)
  },
}

build(options).catch(() => process.exit(1))

Then in the front-end JS

console.log(VARIABLE)

Result

Value

Feedback on this configuration more than welcome!

@ccentrella
Copy link

ccentrella commented Nov 23, 2022

Works great! Thank you.

Also, one tidbit I have is that you could easily create a loop to get all the variables from process.env.VARIABLE and then defining those. Like this:

const definitions = {};

Object.keys(process.env).forEach((key) => {
    if (isValidId(key)) {
      definitions[`process.env.${key}`] = JSON.stringify(process.env[key]);
    }
  })

(Copied from https://dev.to/rxliuli/using-the-esbuild-plug-in-mechanism-to-achieve-the-desired-functionality-4fh9).

@mdtanrikulu
Copy link

one drawback is that any secret key will be visible in the debugger because the env variables given at build time are embedded in the bundled js.

volodev121 pushed a commit to volodev121/bl-chat-fe that referenced this issue Jan 23, 2024
Added production/remote and local urls
meant to be evaluated on build time, but is not configured as such yet
evanw/esbuild#69
@soeasyjx
Copy link

soeasyjx commented Jul 2, 2024

esbuild.config.js

const { build } = require('esbuild')

const options = {
  stdio: 'inherit',
  entryPoints: ['./src/main.ts'],
  outfile: './dist/main.js',
  bundle: true,
  define: {
    'process.env.NODE_ENV': '"production"',
    'process.env.DEBUG': 'false',
  },
}

Use import process,{pid} from "process";
process.env.DEBUG is not available

@SanariSan
Copy link

SanariSan commented Sep 27, 2024

For those who are just looking for how to use env variables in a codebase, here is a link to the official docs. It shows a way to handle this using plugin resolution, no substitutions needed, which I think is a cleaner approach: https://esbuild.github.io/plugins/#using-plugins

Code
  let envPlugin = {
    name: 'env',
    setup(build) {
      build.onResolve({ filter: /^env$/ }, args => ({
        path: args.path,
        namespace: 'env-ns',
      }))
      build.onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({
        contents: JSON.stringify(process.env),
        loader: 'json',
      }))
    }
  }
import { PATH } from 'env'
console.log(`PATH is ${PATH}`)

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

No branches or pull requests

11 participants