facebook / create-react-app

Set up a modern web app by running one command.
https://create-react-app.dev
MIT License
102.7k stars 26.84k forks source link

Static config file at project root #1469

Closed joshhornby closed 7 years ago

joshhornby commented 7 years ago

After searching through the issues I can't seem to find anyone asking this, but if I have missed it please link me to it.

I know CRA has the concept of env variables but I am looking for a little more than this (I want to include objects and arrays). I want to have a config directory in the root of my project, and inside an index.js something like:

import local from './local';
import global from './global';
const env = require(`./${process.env.NODE_ENV}`);

export default { ...global, ...env.default, ...local };

This will give me a config file for each environment but also a local.js where I can override anything I need, for example during development I want to change an API_URL to a local branch.

My question is using CRA can I get webpack to recognize this file outside of the src directory? I don't want to have to eject just to add some static config files.

gaearon commented 7 years ago

Have you tried it? I’m not entirely sure if it works because it’s not clear what you tried, and what problems you have encountered.

I would just note that dynamic expressions in require cause all files to be included in your bundle. This means you'll likely ship your development config to production. I would suggest to explicitly compare NODE_ENV and doing static require()s in conditional branches instead.

alex-pex commented 7 years ago

I have a similar need : static configuration folder with nested key => values.

I'm currently using nwb, with webpack + node-config-loader. It works great, and support a lot of file and formats.

I configured a "config" alias to the .configloaderrc file at the root of the project, which is handled by the loader. I can write something like :

import config from 'config';
console.log(config.api.baseUrl);

@gaearon Is there a way to solve this with CRA out of the box? Do you think of another solution?

joshhornby commented 7 years ago

For anyone finding this issue, a working solution has been to have a config folder at the project root, inside I have production.js and development.js, each file looks something like:

module.exports = { key: 'value' }

I then have a index.js:

const merge = require('lodash/merge');
const global = require('./global');

var env; // let doesn't seem to work here
if (process.env.NODE_ENV === 'development') {
    env = require('./development');
} else {
    env = require('./production');
}

module.exports = merge(global, env);

And then in code I have just import this file and get the values eg config.key

moodysalem commented 7 years ago

This is how I handle configuration in my own project-I'd like to do something similar with create-react-app

This is how my index.js looks

fetch('env.json')
.then(res => res.json())
.then(env => render(<app env={env}/>)

In the production file build I want the code to fetch an env.json in the root directory-this file isn't emitted by the build, it's just expected to exist and I update it by hand depending on the environment

I want the dev server to serve up the process.env object in response to app.get('/env.json') so that I can configure my local environment to run against different servers by setting environment variables

webpack-dev-server can be configured to do this with the devServer.setup option:

setup(app) {
  app.get('/env.json', (req, res) => {
    res.json(process.env);
  });
}

EDIT: I'm fine with following the strategy posted above, but it requires that I commit environment configuration files and build for particular environments

gaearon commented 7 years ago

@moodysalem This is not a very good way because the user has to wait a whole extra request before the app starts.

The approach suggested by @joshhornby sounds like the best solution to me.

moodysalem commented 7 years ago

@gaearon I agree it's not ideal to wait for the extra request, but I add headers to let the file be cached by the client for a day so it only happens on the first request of the day.

I don't know another way to get around creating N builds for N environments on the front end though-we have dev/staging/prd which point to different APIs and sometimes we create additional temporary environments which is easier if I don't have to commit the configuration

gaearon commented 7 years ago

@moodysalem You can use environment variables for this (and indeed create separate builds for each environment).

moodysalem commented 7 years ago

@gaearon Would the impact of an additional request right at the beginning of the entry JS be reduced by http2? If not when initiated via fetch, you could put a <script src="env.js" ...></script> tag in the body and store config in window.env

The practice of separating your build from your environment config has a couple of upsides:

With the downside that if you are caching the environment config file, it would take time for your changes to propagate to the users

gaearon commented 7 years ago

Yea, maybe there are ways to make it work. Just saying in most cases it’s suboptimal in my opinion.

joshhornby commented 7 years ago

@gaearon With the 1.0 updates the method listed above (https://github.com/facebookincubator/create-react-app/issues/1469#issuecomment-290081143) breaks due to the config file being outside src.

What do you suggest now? I don't think pulling the config folder into src makes sense, so would be interested to hear how you'd solve this issue.