Search code examples
javascriptreactjswebpack

process is undefined when trying to provide environment variables to client code through webpack


I'm trying to provide access to certain environment variables in the front end code package by webpack, but process is always undefined. The application I'm working on is doing a bunch of extra stuff in the webpack configuration that I thought might be creating issues, so I wanted to see how it worked with a default create react app instance, and then compare the two. However I'm seeing the same behavior with the create react app instance (process is always undefined in the client side code).

These are the steps I performed:

  1. Created a react app using the template cra-template-pwa: npx create-react-app my-app --template cra-template-pwa
  2. Performed an npm run eject to look at the webpack config files and confirmed that in this code from conifg/webpack.config.js, that certain environment variables should be available through process.env in the client side code:
// We will provide `paths.publicUrlOrPath` to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
// Get environment variables to inject into our app.
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));

(then further down in webpack.config.js...)

// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV is set to production 
// during a production build. 
// Otherwise React will be compiled in the very slow development mode. 
new webpack.DefinePlugin(env.stringified), 

This section from config/env.js show that at the very least process.env.NODE_ENV=development, regardless of what it picks up from the underlying environment:

  const raw = Object.keys(process.env)
    .filter(key => REACT_APP.test(key))
    .reduce(
      (env, key) => {
        env[key] = process.env[key];
        return env;
      },
      {
        // Useful for determining whether we’re running in production mode.
        // Most importantly, it switches React into the correct mode.
        NODE_ENV: process.env.NODE_ENV || 'development',
        // Useful for resolving the correct path to static assets in `public`.
        // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
        // This should only be used as an escape hatch. Normally you would put
        // images into the `src` and `import` them in code to get their paths.
        PUBLIC_URL: publicUrl,
        // We support configuring the sockjs pathname during development.
        // These settings let a developer run multiple simultaneous projects.
        // They are used as the connection `hostname`, `pathname` and `port`
        // in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
        // and `sockPort` options in webpack-dev-server.
        WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
        WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
        WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
        // Whether or not react-refresh is enabled.
        // It is defined here so it is available in the webpackHotDevClient.
        FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
      }
    );
  // Stringify all values so we can feed into webpack DefinePlugin
  const stringified = {
    'process.env': Object.keys(raw).reduce((env, key) => {
      env[key] = JSON.stringify(raw[key]);
      return env;
    }, {}),
  };

  return { raw, stringified };
}
  1. Then in App.js I added console.log(process);
function App() {

  console.log(process);

  return (
    <div className="App">
      <header className="App-header">
  1. Ran npm start and then checked the console log in developer tools for http://localhost:3000 and I received the error:
ReferenceError: process is not defined
    at App (App.js:7:1)
    at renderWithHooks (react-dom.development.js:15486:1)
    at mountIndeterminateComponent (react-dom.development.js:20103:1)
    at beginWork (react-dom.development.js:21626:1)
    at beginWork$1 (react-dom.development.js:27465:1)
    at performUnitOfWork (react-dom.development.js:26596:1)
    at workLoopSync (react-dom.development.js:26505:1)
    at renderRootSync (react-dom.development.js:26473:1)
    at recoverFromConcurrentError (react-dom.development.js:25889:1)
    at performConcurrentWorkOnRoot (react-dom.development.js:25789:1)

UPDATE: This question has been marked as a duplicate of Uncaught ReferenceError: process is not defined however I believe that there is a key difference between the two. The other question focuses entirely on the details of how to set up the configuration, whereas this question deals with the mechanics of how the data is being injected into the client side app. The configuration I had was in fact correct (since I was relying on Create React App to do the configuration for me). It was when I tried to troubleshoot the behavior by adding console.log(process); it was then demonstrated that I had a misunderstanding of how the data was being made available (as an object vs straight text substitution). TL;DR; I feel there's knowledge that is highlighted in this question & answer that isn't nearly as apparent in the other thread.


Solution

  • DefinePlugin doesn't create a variable named process. It does direct text replacement, so just substitutes strings with other stings during build. For example, if you use process.env.NODE_ENV, this will be replaced with string "development" or "production" or whatever your actuall process.env.NODE_ENV was set to. Using process.env for such stuff is more of a convention that some libraries rely on. So, you write process.env.NODE_ENV === "development", and after DefinePlugin it will be "development" === "development" and then this will be replaced with true or removed altogether during minification and dead code elimination step.