13

I would like to set an environment variable dynamically in an npm script.

I'm using cross-env as I'm developing on Windows and the server is Unix-based. I want to initialize an environment variable with the current date (new Date()) so I can access and render it in my create-react-app:

This works (hard-coded string):

"scripts": {
  "start": "cross-env-shell REACT_APP_BUILD_DATE=\"currentDate\" react-scripts-ts start",
}

Obviously, currentDate shouldn't be a string but the result of following expression: new Date().

How can I achieve that? In other words: How can evaluate some regular JavaScript and use its result an npm script? Or is this not possible?

2
  • Why not simply use new Date() inside your CRA app? What's the reason to pass it as an environment variable? Commented May 6, 2019 at 6:05
  • @NikolaMihajlović, the goal is to save the build date, not just get a date at the time that the app was opened. Commented Jun 22, 2019 at 20:30

5 Answers 5

8

I am using simple node script for passing environment variables into called script. It uses child_process.execSync.

// File name: ./build.js
/* eslint-env node */
const execSync = require('child_process').execSync;
const env = Object.create(process.env);

env.REACT_APP_BUILD_DATE= Date.now();

console.log('Used env variables: ' + JSON.stringify(env));
console.log('Run command: react-scripts start');
execSync('react-scripts-ts start', { env: env, stdio: 'inherit' });

Update start command in package.json scripts. like this:

"scripts": {"start": "node ./build.js"}
1
  • This is the only way to make it work on poor windows systems I added env['REACT_APP_GIT_SHA'] = execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim() and modified the last line to make it work execSync('npx react-scripts start', { env: env, stdio: 'inherit' }); Commented Mar 13, 2020 at 22:16
1

Just for the record, I'm now using following approach: Write current date to a custom property in package.json and read that value in the app by importing package.json

package.json

"scripts": {
  "start": "react-scripts-ts start",
  "build": "node ./update-packagejson.js && react-scripts-ts build"
}

update-packagejson.js

const fs = require("fs");
const filePath = "./package.json";

const packageJson = JSON.parse(fs.readFileSync(filePath).toString());
packageJson.ngrvd.buildDate = new Date().toUTCString();

fs.writeFileSync(filePath, JSON.stringify(packageJson, null, 2));

Component

import { ngrvd, version } from "../../package.json";

// ... 

private static getAppInfo(): string {
  const buildDate = process.env.NODE_ENV === "development" ? new Date() : ngrvd.buildDate;
  return "Version " + version + "  - Built " + moment(buildDate).fromNow();
}

This works on any environment, is simple and understandable and could be extended to also contain other information. When in dev mode I don't write to package.json to prevent having local changes everytime.

2
  • Went there, did that... if you resort on writing into a file beforehand, try writing into an .env file and use the library github.com/motdotla/dotenv to populate environment variables with it.
    – remix23
    Commented Sep 5, 2018 at 21:07
  • I've edited my answer to mention the dotenv solution
    – remix23
    Commented Sep 5, 2018 at 21:14
1

For example you want to put build time to your reactjs app. Edit package.json like that:

"scripts": {
    "start": "REACT_APP_BUILD_TIME=$(date +%s) react-app-rewired start",
    "build": "REACT_APP_BUILD_TIME=$(date +%s) react-app-rewired build"
}

You can use REACT_APP_BUILD_TIME  variable in public/index.html file. For example:

<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico?%REACT_APP_BUILD_TIME%" />

You must wrap env variable with % characters. And there is another rule: you must add REACT_APP_ to your env variable. You can not add other env variables to react app.

How can you add all .env variables to reactjs app?

You can use env-cmd package for this.

yarn add env-cmd

"scripts": {
    "start": "REACT_APP_BUILD_TIME=$(date +%s) ./node_modules/.bin/env-cmd react-app-rewired start",
    "build": "REACT_APP_BUILD_TIME=$(date +%s) ./node_modules/.bin/env-cmd react-app-rewired build"
}

Example .env content:

REACT_APP_NAME="My React App"
REACT_APP_API_ENDPOINT=https://127.0.0.1:8080/api
REACT_APP_SOCKETIO_ENDPOINT=http://127.0.0.1:3333

After that you can add these variables to your public/index.html file like that:

<script>
  window.env.REACT_APP_NAME = "%REACT_APP_NAME%";
  window.env.REACT_APP_API_ENDPOINT = "%REACT_APP_API_ENDPOINT%";
  window.env.REACT_APP_SOCKETIO_ENDPOINT = "%REACT_APP_SOCKETIO_ENDPOINT%";
</script>

In reactjs side you can use these variables like that:

alert(window.env.REACT_APP_SOCKETIO_ENDPOINT);

That's all.

Edit: Normally there is no this property: window.env but we set this now for easy to use. You can assign your env variables to anywhere in index.html file.

0

In this particular case you'd be better off using shell command instead of JavaScript, so it should be something like the following:

"scripts": {
  "start": "cross-env-shell REACT_APP_BUILD_DATE=$(date '+%F %H:%M:%S') react-scripts-ts start",
}
2
  • Using this approach I get following error: react-scripts-ts was unexpected at this time.. If I add the quotes again, then the value of the variable is $(date +%F %H:%M:%S). Can this be related to Windows?
    – PzYon
    Commented Sep 5, 2018 at 4:54
  • 2
    Oh man, Windows. Sorry, my bad, missed this fact. Then yeah, you should probably go with the script approach @remix23 is suggesting.
    – bredikhin
    Commented Sep 5, 2018 at 9:24
0

I'd create a custom javascript script doing it for you:

execute.js

var spawn = require('child_process').spawn;

// because first arg will actually be something like "./execute.js"
// this is the "regular javascript" you want to evaluate
var arg1 = process.argv[1];
// so lets eval it
var res = eval(arg1);
// this is the remaining args, that is the command you want to run (and its args)
var command = process.argv[2];
var commandArgs = process.argv.slice(3);
// if arg1 evaluation resulted in a value, append this value to the list of args
if (res) {
    commandArgs.push(res);
}
// execute the command
var prc = spawn(command, commandArgs);

and your script definition will become:

"scripts": {
    "start": "cross-env-shell ./execute.js \"process.env.REACT_APP_BUILD_DATE = new Date();\" react-scripts-ts start",
}

Or something similar.

This is untested but should get you started on a solution for "evaluate some regular JavaScript and use its result an npm script"

But if you only want to set a date in a env variable, the solution from @bredikhin is better.

ALTERNATIVE SOLUTION TO DEAL WITH ENVIRONMENT VARIABLES

If you can afford to write into an .envfile at the root of your project (by hand or programatically), you can then use dotenv to populate environment variables with it (from dotenv documentation):

// Usage
// As early as possible in your application, require and configure dotenv.

require('dotenv').config()
/* Create a .env file in the root directory of your project. Add environment-specific variables on new lines in the form of NAME=VALUE. For example:

DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3
That's it.

process.env now has the keys and values you defined in your .env file.
*/
const db = require('db');
db.connect({
    host: process.env.DB_HOST,
    username: process.env.DB_USER,
    password: process.env.DB_PASS
});
1
  • I tried this approach but it didn't quite work out. I'm now writing the current date to package.json using a js-script and then reading it from there. See my answer below for more details.
    – PzYon
    Commented Sep 5, 2018 at 20:31

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.