6

I have a project to migrate from legacy React app to standard create-react-app one (not ejected).

In legacy project, it manually loads .env files with dotenv and dotenv-expand and injects through webpack DefinePlugin.

create-react-app intuitively supports dotenv but can only recognize variables with REACT_APP_ prefix.

There are so many places in legacy code also along with imported packages in other repositories that are using process.env.xxx variables, so it is impossible for now to rename them with prefix before migration.

In this case, how can I make create-react-app recognize dotenv variables without REACT_APP_ prefix?

BTW, I tried to rewire build script before with some simple customizations over webpack, like bundling js and css:

const path = require('path');
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');

let webpackConfig = defaults.__get__('config');

webpackConfig.output.filename = 'static/js/[name].js';
webpackConfig.optimization.splitChunks = { cacheGroups: { default: false } };
webpackConfig.optimization.runtimeChunk = false;
webpackConfig.plugins.find(plugin => plugin.__proto__.constructor.name === 'MiniCssExtractPlugin').options.filename = 'static/css/[name].css';
webpackConfig.plugins.find(plugin => plugin.__proto__.constructor.name === 'MiniCssExtractPlugin').options.moduleFilename = () => 'static/css/[name].css';

But it seems dotenv and DefinePlugin are more complicated. Not sure if it can be achieved in the same way.

Mike
  • 313
  • 4
  • 15
  • Not sure if this is a good option in your situation, but what if you defined all your variables using both naming styles? (e.g. `FOO=bar` and `REACT_APP_FOO=bar`) – sloppypasta Apr 23 '20 at 19:15
  • 1
    I've reviewed the source-code of cra, it is not possible to change the prefix of `REACT_APP_`. – felixmosh Apr 24 '20 at 19:19

3 Answers3

1

Also use rewire

// "start": "node start.js"
const rewire = require('rewire');

process.env.NODE_ENV = 'development';

let getClientEnvironment = rewire('react-scripts/config/env.js');
getClientEnvironment.__set__(
  'REACT_APP',
  /(^REACT_APP_|API|DEPLOY|SECRET|TOKEN|URL)/,
);

let configFactory = rewire('react-scripts/config/webpack.config.js');
configFactory.__set__('getClientEnvironment', getClientEnvironment);

let start = rewire('react-scripts/scripts/start.js');
start.__set__('configFactory', configFactory);

build is a little bit different

// "build": "node build.js"
const rewire = require('rewire');

process.env.NODE_ENV = 'production';

let getClientEnvironment = rewire('react-scripts/config/env.js');
getClientEnvironment.__set__(
  'REACT_APP',
  /(^REACT_APP_|API|DEPLOY|SECRET|TOKEN|URL)/,
);

let configFactory = rewire('react-scripts/config/webpack.config.js');
configFactory.__set__('getClientEnvironment', getClientEnvironment);

let webpackConfig = configFactory('production');

let build = rewire('react-scripts/scripts/build.js');
build.__set__('config', webpackConfig);
Mike
  • 313
  • 4
  • 15
1

Years ago Dan Abramov (co-creator of CRA) suggested to redefine the variables to fit REACT_APP_ in an entrypoint script that then calls react-scripts in package.json.

If you have simpler needs to support these variables, such as CI platform variables like Netlify's COMMIT_REF or Gitlab's CI_COMMIT_SHA, you could set those variables inline without adding another script.

  "scripts": {
    "start": "react-scripts start",
    "build": "REACT_APP_COMMIT_SHA=$COMMIT_REF$CI_COMMIT_SHA react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

This will set REACT_APP_COMMIT_SHA to the value of the hash of whichever service it's built in (since the other variable will be empty).

Jeff Puckett
  • 37,464
  • 17
  • 118
  • 167
1

My solution is create config-overrides.js and modify process.env when build time. You still defined REACT_APP_[VARIABLE NAME] in your env file, and remove prefix when build time.

// config-overrides.js
const {
    override,
} = require('customize-cra');
const ENV_PREFIX = /^REACT_APP_/i;

const findWebpackPlugin = (plugins, pluginName) =>
    plugins.find((plugin) => plugin.constructor.name === pluginName);

const overrideProcessEnv = () => (config) => {
    const plugin = findWebpackPlugin(config.plugins, 'DefinePlugin');
    const processEnv = plugin.definitions['process.env'] || {};

    const transformedEnv = Object.keys(processEnv)
        .filter((key) => ENV_PREFIX.test(key))
        .reduce((env, key) => {
            const craKey = key.replace('REACT_APP_', '');
            env[craKey] = processEnv[key];
            return env;
        }, {});

    plugin.definitions['process.env'] = {
        ...processEnv,
        ...transformedEnv,
    };

    return config;
};
module.exports = override(
  overrideProcessEnv()
)
Dharman
  • 30,962
  • 25
  • 85
  • 135
tqdungit97
  • 11
  • 1
  • Can you please add more detail to this. I want to use it but it doesn't work off the bat. What are the additional config steps to get it to work? – jacktim Feb 15 '23 at 01:23