Skip to content

Commit ffe6b2f

Browse files
eliperelmangaearon
authored andcommitted
Adding namespaced environment variables to DefinePlugin under REACT_APP_ (#342)
1 parent 6e18b2a commit ffe6b2f

File tree

5 files changed

+165
-14
lines changed

5 files changed

+165
-14
lines changed

config/env.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
11+
// injected into the application via DefinePlugin in Webpack configuration.
12+
13+
var REACT_APP = /^REACT_APP_/i;
14+
var NODE_ENV = JSON.stringify(process.env.NODE_ENV || 'development');
15+
16+
module.exports = Object
17+
.keys(process.env)
18+
.filter(key => REACT_APP.test(key))
19+
.reduce((env, key) => {
20+
env['process.env.' + key] = JSON.stringify(process.env[key]);
21+
return env;
22+
}, {
23+
'process.env.NODE_ENV': NODE_ENV
24+
});

config/webpack.config.dev.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
1414
var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
1515
var WatchMissingNodeModulesPlugin = require('../scripts/utils/WatchMissingNodeModulesPlugin');
1616
var paths = require('./paths');
17+
var env = require('./env');
1718

1819
// This is the development configuration.
1920
// It is focused on developer experience and fast rebuilds.
@@ -170,9 +171,9 @@ module.exports = {
170171
template: paths.appHtml,
171172
favicon: paths.appFavicon,
172173
}),
173-
// Makes the environment available to the JS code, for example:
174+
// Makes some environment variables available to the JS code, for example:
174175
// if (process.env.NODE_ENV === 'development') { ... }. See `env.js`.
175-
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"' }),
176+
new webpack.DefinePlugin(env),
176177
// Note: only CSS is currently hot reloaded
177178
// This is necessary to emit hot updates (currently CSS only):
178179
new webpack.HotModuleReplacementPlugin(),

config/webpack.config.prod.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
1414
var ExtractTextPlugin = require('extract-text-webpack-plugin');
1515
var url = require('url');
1616
var paths = require('./paths');
17+
var env = require('./env');
18+
19+
// Assert this just to be safe.
20+
// Development builds of React are slow and not intended for production.
21+
if (env['process.env.NODE_ENV'] !== '"production"') {
22+
throw new Error('Production builds must have NODE_ENV=production.');
23+
}
1724

1825
// We use "homepage" field to infer "public path" at which the app is served.
1926
// Webpack needs to know it to put the right <script> hrefs into HTML even in
@@ -188,11 +195,11 @@ module.exports = {
188195
minifyURLs: true
189196
}
190197
}),
191-
// Makes the environment available to the JS code, for example:
198+
// Makes some environment variables available to the JS code, for example:
192199
// if (process.env.NODE_ENV === 'production') { ... }. See `env.js`.
193200
// It is absolutely essential that NODE_ENV was set to production here.
194201
// Otherwise React will be compiled in the very slow development mode.
195-
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' }),
202+
new webpack.DefinePlugin(env),
196203
// This helps ensure the builds are consistent if source hasn't changed:
197204
new webpack.optimize.OccurrenceOrderPlugin(),
198205
// Try to dedupe duplicated modules, if any:

scripts/eject.js

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ prompt(
3434
path.join('config', 'flow', 'file.js.flow'),
3535
path.join('config', 'eslint.js'),
3636
path.join('config', 'paths.js'),
37+
path.join('config', 'env.js'),
3738
path.join('config', 'polyfills.js'),
3839
path.join('config', 'webpack.config.dev.js'),
3940
path.join('config', 'webpack.config.prod.js'),

template/README.md

+128-10
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ You can find the most recent version of this guide [here](https://github.com/fac
1818
- [Adding Images and Fonts](#adding-images-and-fonts)
1919
- [Adding Bootstrap](#adding-bootstrap)
2020
- [Adding Flow](#adding-flow)
21+
- [Adding Custom Environment Variables](#adding-custom-environment-variables)
2122
- [Integrating with a Node Backend](#integrating-with-a-node-backend)
23+
- [Proxying API Requests in Development](#proxying-api-requests-in-development)
2224
- [Deployment](#deployment)
2325
- [Now](#now)
2426
- [Heroku](#heroku)
@@ -179,6 +181,7 @@ export default Button; // Don’t forget to use export default!
179181

180182
### `DangerButton.js`
181183

184+
182185
```js
183186
import React, { Component } from 'react';
184187
import Button from './Button'; // Import a component from another file
@@ -370,7 +373,7 @@ esproposal.class_static_fields=enable
370373
esproposal.class_instance_fields=enable
371374

372375
module.name_mapper='^\(.*\)\.css$' -> 'react-scripts/config/flow/css'
373-
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|otf\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> 'react-scripts/config/flow/file'
376+
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|otf\|webp\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> 'react-scripts/config/flow/file'
374377

375378
suppress_type=$FlowIssue
376379
suppress_type=$FlowFixMe
@@ -382,24 +385,141 @@ If you later `eject`, you’ll need to replace `react-scripts` references with t
382385
383386
```ini
384387
module.name_mapper='^\(.*\)\.css$' -> '<PROJECT_ROOT>/config/flow/css'
385-
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|otf\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> '<PROJECT_ROOT>/config/flow/file'
388+
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|otf\|webp\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> '<PROJECT_ROOT>/config/flow/file'
386389
```
387390
388391
We will consider integrating more tightly with Flow in the future so that you don’t have to do this.
389392
393+
## Adding Custom Environment Variables
394+
395+
>Note: this feature is available with `react-scripts@0.2.3` and higher.
396+
397+
Your project can consume variables declared in your environment as if they were declared locally in your JS files. By
398+
default you will have `NODE_ENV` defined for you, and any other environment variables starting with
399+
`REACT_APP_`. These environment variables will be defined for you on `process.env`. For example, having an environment
400+
variable named `REACT_APP_SECRET_CODE` will be exposed in your JS as `process.env.REACT_APP_SECRET_CODE`, in addition
401+
to `process.env.NODE_ENV`.
402+
403+
These environment variables can be useful for displaying information conditionally based on where the project is
404+
deployed or consuming sensitive data that lives outside of version control.
405+
406+
First, you need to have environment variables defined, which can vary between OSes. For example, let's say you wanted to
407+
consume a secret defined in the environment inside a `<form>`:
408+
409+
```jsx
410+
render() {
411+
return (
412+
<div>
413+
<small>You are running this application in <b>{process.env.NODE_ENV}</b> mode.</small>
414+
<form>
415+
<input type="hidden" defaultValue={process.env.REACT_APP_SECRET_CODE} />
416+
</form>
417+
</div>
418+
);
419+
}
420+
```
421+
422+
The above form is looking for a variable called `REACT_APP_SECRET_CODE` from the environment. In order to consume this
423+
value, we need to have it defined in the environment:
424+
425+
### Windows (cmd.exe)
426+
427+
```cmd
428+
set REACT_APP_SECRET_CODE=abcdef&&npm start
429+
```
430+
431+
(Note: the lack of whitespace is intentional.)
432+
433+
### Linux, OS X (Bash)
434+
435+
```bash
436+
REACT_APP_SECRET_CODE=abcdef npm start
437+
```
438+
439+
> Note: Defining environment variables in this manner is temporary for the life of the shell session. Setting
440+
permanent environment variables is outside the scope of these docs.
441+
442+
With our environment variable defined, we start the app and consume the values. Remember that the `NODE_ENV`
443+
variable will be set for you automatically. When you load the app in the browser and inspect the `<input>`, you will see
444+
its value set to `abcdef`, and the bold text will show the environment provided when using `npm start`:
445+
446+
```html
447+
<div>
448+
<small>You are running this application in <b>development</b> mode.</small>
449+
<form>
450+
<input type="hidden" value="abcdef" />
451+
</form>
452+
</div>
453+
```
454+
455+
Having access to the `NODE_ENV` is also useful for performing actions conditionally:
456+
457+
```js
458+
if (process.env.NODE_ENV !== 'production') {
459+
analytics.disable();
460+
}
461+
```
462+
390463
## Integrating with a Node Backend
391464
392465
Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/) for instructions on integrating an app with a Node backend running on another port, and using `fetch()` to access it. You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo).
393466
467+
## Proxying API Requests in Development
468+
469+
>Note: this feature is available with `react-scripts@0.2.3` and higher.
470+
471+
People often serve the front-end React app from the same host and port as their backend implementation.
472+
For example, a production setup might look like this after the app is deployed:
473+
474+
```
475+
/ - static server returns index.html with React app
476+
/todos - static server returns index.html with React app
477+
/api/todos - server handles any /api/* requests using the backend implementation
478+
```
479+
480+
Such setup is **not** required. However, if you **do** have a setup like this, it is convenient to write requests like `fetch('/api/todos')` without worrying about redirecting them to another host or port during development.
481+
482+
To tell the development server to proxy any unknown requests to your API server in development, add a `proxy` field to your `package.json`, for example:
483+
484+
```js
485+
"proxy": "http://localhost:4000",
486+
```
487+
488+
This way, when you `fetch('/api/todos')` in development, the development server will recognize that it’s not a static asset, and will proxy your request to `http://localhost:4000/api/todos` as a fallback.
489+
490+
Conveniently, this avoids [CORS issues](http://stackoverflow.com/questions/21854516/understanding-ajax-cors-and-security-considerations) and error messages like this in development:
491+
492+
```
493+
Fetch API cannot load http://localhost:4000/api/todos. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
494+
```
495+
496+
Keep in mind that `proxy` only has effect in development (with `npm start`), and it is up to you to ensure that URLs like `/api/todos` point to the right thing in production. You don’t have to use the `/api` prefix. Any unrecognized request will be redirected to the specified `proxy`.
497+
498+
Currently the `proxy` option only handles HTTP requests, and it won’t proxy WebSocket connections.
499+
If the `proxy` option is **not** flexible enough for you, alternatively you can:
500+
501+
* Enable CORS on your server ([here’s how to do it for Express](http://enable-cors.org/server_expressjs.html)).
502+
* Use [environment variables](#adding-custom-environment-variables) to inject the right server host and port into your app.
503+
394504
## Deployment
395505
506+
By default, Create React App produces a build assuming your app is hosted at the server root.
507+
To override this, specify the `homepage` in your `package.json`, for example:
508+
509+
```js
510+
"homepage": "http://mywebsite.com/relativepath",
511+
```
512+
513+
This will let Create React App correctly infer the root path to use in the generated HTML file.
514+
396515
### Now
397516
398517
See [this example](https://github.com/xkawi/create-react-app-now) for a zero-configuration single-command deployment with [now](https://zeit.co/now).
399518
400519
### Heroku
401520
402-
Use the [Heroku Buildpack for create-react-app](https://github.com/mars/create-react-app-buildpack).
521+
Use the [Heroku Buildpack for Create React App](https://github.com/mars/create-react-app-buildpack).
522+
You can find instructions in [Deploying React with Zero Configuration](https://blog.heroku.com/deploying-react-with-zero-configuration).
403523
404524
### Surge
405525
@@ -426,17 +546,15 @@ Note that in order to support routers that use html5 `pushState` API, you may wa
426546
427547
>Note: this feature is available with `[email protected]` and higher.
428548
429-
First, open your `package.json` and add a `homepage` field.
430-
It could look like this:
549+
Open your `package.json` and add a `homepage` field:
431550
432551
```js
433-
{
434-
"name": "my-app",
435552
"homepage": "http://myusername.github.io/my-app",
436-
// ...
437-
}
438553
```
439554
555+
**The above step is important!**
556+
Create React App uses the `homepage` field to determine the root URL in the built HTML file.
557+
440558
Now, whenever you run `npm run build`, you will see a cheat sheet with a sequence of commands to deploy to GitHub pages:
441559
442560
```sh
@@ -451,7 +569,7 @@ git checkout -
451569
452570
You may copy and paste them, or put them into a custom shell script. You may also customize them for another hosting provider.
453571
454-
Note that GitHub Pages doesn't support routers that use the HTML5 `pushState` history API under the hood (for example, React Router using `browserHistory`). This is becasue when there is a fresh page load for a url like `http://user.github.io/todomvc/todos/42`, where `/todos/42` is a frontend route, the GitHub Pages server returns 404 because it knows nothing of `/todos/42`. If you want to add a router to a project hosted on GitHub Pages, here are a couple of solutions:
572+
Note that GitHub Pages doesn't support routers that use the HTML5 `pushState` history API under the hood (for example, React Router using `browserHistory`). This is because when there is a fresh page load for a url like `http://user.github.io/todomvc/todos/42`, where `/todos/42` is a frontend route, the GitHub Pages server returns 404 because it knows nothing of `/todos/42`. If you want to add a router to a project hosted on GitHub Pages, here are a couple of solutions:
455573
* You could switch from using HTML5 history API to routing with hashes. If you use React Router, you can switch to `hashHistory` for this effect, but the URL will be longer and more verbose (for example, `http://user.github.io/todomvc/#/todos/42?_k=yknaj`). [Read more](https://github.com/reactjs/react-router/blob/master/docs/guides/Histories.md#histories) about different history implementations in React Router.
456574
* Alternatively, you can use a trick to teach GitHub Pages to handle 404 by redirecting to your `index.html` page with a special redirect parameter. You would need to add a `404.html` file with the redirection code to the `build` folder before deploying your project, and you’ll need to add code handling the redirect parameter to `index.html`. You can find a detailed explanation of this technique [in this guide](https://github.com/rafrex/spa-github-pages).
457575

0 commit comments

Comments
 (0)