react-universally
Version:
A starter kit for universal react applications.
115 lines (103 loc) • 4 kB
JavaScript
/**
* Unified Configuration Reader
*
* This helper function allows you to use the same API in accessing configuration
* values no matter where the code is being executed (i.e. browser/node).
*
* e.g.
* import config from '../config';
* config('welcomeMessage'); // => "Hello World!"
*/
/* eslint-disable no-console */
/* eslint-disable import/global-require */
/* eslint-disable no-underscore-dangle */
// PRIVATES
let configCache;
/**
* This resolves the correct configuration source based on the execution
* environment. For node we use the standard config file, however, for browsers
* we need to access the configuration object that would have been bound to
* the "window" by our "reactApplication" middleware.
*
* @return {Object} The executing environment configuration object.
*/
function resolveConfigForBrowserOrServer() {
if (configCache) {
return configCache;
}
// NOTE: By using the "process.env.BUILD_FLAG_IS_NODE" flag here this block of code
// will be removed when "process.env.BUILD_FLAG_IS_NODE === true".
// If no "BUILD_FLAG_IS_NODE" env var is undefined we can assume that we are running outside
// of a webpack run, and will therefore return the config file.
if (
typeof process.env.BUILD_FLAG_IS_NODE === 'undefined' ||
process.env.BUILD_FLAG_IS_NODE === 'true'
) {
// i.e. running in our server/node process.
configCache = require('./values').default;
return configCache;
}
// To get here we are likely running in the browser.
if (typeof window !== 'undefined' && typeof window.__CLIENT_CONFIG__ === 'object') {
configCache = window.__CLIENT_CONFIG__;
} else {
// To get here we must be running in the browser.
console.warn('No client configuration object was bound to the window.');
configCache = {};
}
return configCache;
}
// EXPORT
/**
* This function wraps up the boilerplate needed to access the correct
* configuration depending on whether your code will get executed in the
* browser/node.
*
* i.e.
* - For the browser the config values are available at window.__CLIENT_CONFIG__
* - For a node process they are within the "<root>/config".
*
* To request a configuration value you must provide the repective path. For
* example, f you had the following configuration structure:
* {
* foo: {
* bar: [1, 2, 3]
* },
* bob: 'bob'
* }
*
* You could use this function to access "bar" like so:
* import config from '../config';
* const value = config('foo.bar');
*
* And you could access "bob" like so:
* import config from '../config';
* const value = config('bob');
*
* If any part of the path isn't available as a configuration key/value then
* an error will be thrown indicating that a respective configuration value
* could not be found at the given path.
*/
export default function configGet(path) {
const parts = typeof path === 'string' ? path.split('.') : path;
if (parts.length === 0) {
throw new Error(
'You must provide the path to the configuration value you would like to consume.',
);
}
let result = resolveConfigForBrowserOrServer();
for (let i = 0; i < parts.length; i += 1) {
if (result === undefined) {
const errorMessage = `Failed to resolve configuration value at "${parts.join('.')}".`;
// This "if" block gets stripped away by webpack for production builds.
if (process.env.BUILD_FLAG_IS_DEV === 'true' && process.env.BUILD_FLAG_IS_CLIENT === 'true') {
throw new Error(
`${errorMessage} We have noticed that you are trying to access this configuration value from the client bundle (i.e. code that will be executed in a browser). For configuration values to be exposed to the client bundle you must ensure that the path is added to the client configuration filter in the project configuration values file.`,
);
}
throw new Error(errorMessage);
}
result = result[parts[i]];
}
return result;
}