Repository: create-react-app
Issue: Allow building once for multiple environments
Labels: issue: proposal,needs triage

### Is your proposal related to a problem?

Building once for multiple environments seems to be incredibly hacky. Currently it seems like the least painful solution is to inspect the hostname to determine which environment we are in, and then select the correct variables from there (ALL of which must be included in the build, preventing optimal minification, etc.) E.g.:

```typescript
const correctValue = window.location.hostname.startsWith("dev.") ? process.env.REACT_APP_DEV_VALUE : 
    window.location.hostname.startsWith("staging.") ? process.env.REACT_APP_STAGING_VALUE :
        process.env.REACT_APP_PROD_VALUE;
```


### Describe the solution you'd like

I would like a new react-scripts command (let's call it `initenv`) to add previously absent `REACT_APP_*` variables *after* the build has completed using the current server's environment variables as input. This would allow me to save the original build as a single docker image, and then *later* insert those environment-specific variables right before starting up the server.

This would effectively mean the ability to re-run the webpack `DefinePlugin` followed by another terser minification pass as a single command over the js bundle files. Which could then be run in a docker image like this:

```dockerfile
FROM node:16-alpine AS builder
... blah blah blah ...
RUN npm run build

FROM node:16-alpine
COPY --from=builder /app/build build

# 1. insert REACT_APP_* variables from server environment into build folder js files
# 2. serve application
CMD react-scripts initenv build && serve -s build
```

Implementing this functionality might be tricky, since the first Define/minification pass obliterates `process.env` by replacing it with an object literal everywhere, which is then further minified. *However*, Terser has a [bug/feature](https://github.com/terser/terser/issues/584) that would actually make this functionality possible with some clever surgery: variables accessed from `process.env` which are not present in that object *are not replaced with undefined*. Instead, the whole object literal is kept intact (e.g. `{NODE_ENV: "production"}.REACT_APP_ABSENT_VARIABLE`). They need to avoid minifying further here in case `REACT_APP_ABSENT_VARIABLE` is defined on the Object prototype! Which means, that if you were to add a random GUID to the `process.env` object, you could walk the AST looking for this guid in the `initenv` function. Any object containing this guid must have been originally process.env, so you could insert the server environment variables into that object literal at that point (above example now becomes `{NODE_ENV: "production", REACT_APP_ABSENT_VARIABLE: "now present"}.REACT_APP_ABSENT_VARIABLE`), and then re-run the minification step.

Alternatively, if that's too complicated, a second implementation option would be to simply add a flag to the build step that disables the DefinePlugin on `REACT_APP_*` variables for the first pass (and then simply run the DefinePlugin as normal later, within the specific server environment).

### Describe alternatives you've considered

- I've considered re-building the entire app for each environment, however I reaaaally don't like that option, philosophically speaking. `npm ci` helps with build reproducibility, but it can only guarantee so much... ultimately it is still trusting the package server. Also it is really annoying to have to commit package-lock.json when the slightest change (or no change--someone simply running a different npm version) adds and removes thousands of lines of code in their commits for no reason. This is also a much _SLOWER_ solution. I simply don't want to have to wait for the app to build 4 times.

- I've considered writing my own script that implements the first solution I described above, however, I would be duplicating/copy-pasting/making assumptions about a lot of existing CRA functionality, and would thus have to keep it up to date with the CRA implementation... so it seems like it would be preferable if this were baked into CRA.

- I've considered ditching the `process.env.REACT_APP_*` pattern altogether in favor of a custom environment variable file that is swapped out right before server startup on a per-environment basis. But I would prefer a built-in solution that integrates well with the existing react development patterns. This seems like a fairly common use-case. There are a [ton of articles](https://dev.to/eslynn/how-to-dynamically-change-your-react-environment-variables-without-re-building-55el) describing various super-hacky solutions to hack around this limitation. But it would be nice if this just came out of the box.