Skip to content

Instantly share code, notes, and snippets.

@nicolashery
Created September 25, 2014 16:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicolashery/e479593bcb7b0ad739c8 to your computer and use it in GitHub Desktop.
Save nicolashery/e479593bcb7b0ad739c8 to your computer and use it in GitHub Desktop.
Using env variables with Webpack and Divshot

Using env variables with Webpack and Divshot

This shows a way to have "environment variables" working for both the webpack-dev-server (local development) and the Divshot server (to test a build locally).

Problem:

  • Divshot server expects a .env.json file in the project directory, and uses it to serve a __/env.js file
  • Webpack dev server can serve static files from the project directory, but we need to create the __/env.js file ourselves

A solution:

Your index.html in the project root directory looks something like:

<!DOCTYPE html>
<html>
  <!-- ... -->

  <body>
    <p>Loading app...</p>
    <script type="text/javascript" src="/__/env.js"></script>
    <script type="text/javascript" src="bundle.js"></script>
  </body>

</html>

Create a .env.js file that looks something like:

module.exports = {
  API_URL: process.env.API_URL || 'http://localhost:8081'
};

Create a scripts/ directory with two files.

The first, scripts/buildenv.js, will create __/env.js for the Webpack dev server and .env.json for the Divshot static server:

require('shelljs/global');
var util = require('util');

var output;
var env = require('../.env.js');

// Used for local development with webpack-dev-server
output = util.inspect(env, {depth: null});
output = 'window.__env = ' + output + ';\n';
output.to('__/env.js');

// Used to test build with divshot server
output = JSON.stringify(env, null, 2);
output = output + '\n';
output.to('.env.json');

The second, scripts/build.js, will build the Webpack app ready for deployment in the dist/ directory:

require('shelljs/global');

console.log('Cleaning output directory "dist/"...');
rm('-rf', 'dist');
mkdir('-p', 'dist');

console.log('Bundling all the things...');
exec('webpack --colors --progress');

console.log('Copying "index.html"...');
cp('index.html', 'dist/index.html');

console.log('Build successfull');

Make sure to update your .gitignore with the necessary things:

.divshot-cache
dist
__
.env.json

We can create npm scripts in our package.json to easily call what we just created:

{
  "scripts": {
    "start": "npm run build-env && webpack-dev-server --devtool eval-source-map --cache --colors --progress",
    "build-env": "node scripts/buildenv",
    "build": "node scripts/build",
    "server": "npm run build-env && divshot server -p 8080",
    "deploy": "divshot push"
  }
}

Now, if we want to work on the app, hitting a particular API, we can do:

$ export API_URL=http://localhost:3001
$ npm start

If we want to deploy, we'll first build the app:

$ npm run build

Then we can test our build, for example hitting a remote development API:

$ export API_URL=http://development-api.example.com
$ npm run server

If all seems to work, we can deploy!

$ npm run deploy
@mbleigh
Copy link

mbleigh commented Sep 25, 2014

Thanks for the great writeup! I wish that it were easier to integrate Divshot's serving capabilities with other tools that provide their own static server. After a bit of thought this has actually inspired divshot/superstatic#116 but that's not the question you asked.

I'm not strongly opposed to the idea of supporting .env.js (and it'd be maybe a couple line diff, so that's not a big deal), but I worry that it's a bit overkill. I feel like a better solution might be to support direct environment variable passing in the command itself, like Docker:

divshot server -e "API_URL=$API_URL" -p 3000

Would that be an acceptable solution for your needs?

@nicolashery
Copy link
Author

Yes you're right, it might be overkill. Plus, having support to .env.js really ties it to Node. Also, we could ask why not support .env.sh, or .env.yml? Probably best to pick a language-agnostic format, in your case .json, and stick with it.

Another thought, would be supporting some kind of templating language directly in the JSON? For example, using Lo-Dash templates, an .env.json could look like:

{
  "API_URL": "<%= process.env.API_URL || 'http://localhost:8080' %>"
}

Still might be a bit overkill, but would eliminate the need to support an extra file (.env.js). The value is not having to check in sensitive values to the repo, but still having that .env.json present (not having to generate it like I am).

The support for env variables as command line itself is definitely interesting! Only issue I find (in my case) is that if I need to add or remove an env variable, I now need to do it in two places (package.json which runs the divshot server command, and .env.js which is used by my Webpack dev server).

And the "piggyback" thing is cool too! I can see the benefit of the Divshot server being a layer for Single Page App things like "clean URLs" and "env variables", that you put on top of any other workflow...

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