├── .codeclimate.yml ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── README.md ├── examples ├── browserify │ ├── .gitignore │ ├── app │ │ ├── client.js │ │ ├── components │ │ │ └── VariableList.jsx │ │ ├── dist │ │ │ └── main.js │ │ ├── index.html │ │ └── server.js │ ├── config │ │ ├── client.js │ │ └── server.js │ └── package.json ├── shared │ ├── assets │ │ └── css │ │ │ └── styles.css │ ├── config │ │ ├── client.js │ │ └── server.js │ └── utils │ │ └── index.js └── webpack │ ├── .gitignore │ ├── app │ ├── client.js │ ├── components │ │ └── VariableList.jsx │ ├── dist │ │ └── main.js │ ├── index.html │ └── server.js │ ├── config │ ├── client.js │ └── server.js │ ├── package.json │ └── webpack.config.js ├── package.json ├── scripts └── copy-lib.sh ├── src └── index.js ├── test ├── browserify.js ├── server.js ├── shared │ ├── client.js │ ├── has.js │ └── set.js ├── stubs │ └── config │ │ ├── client.js │ │ ├── dev.js │ │ ├── prod.js │ │ └── server.js ├── support │ └── browserify-transform-stub.js └── webpack.js ├── testem.json └── webpack.config.test.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | exclude_paths: 2 | - "examples/*" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | *.log 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | esnext: true, 3 | node: true 4 | } 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "iojs" 4 | script: 5 | - npm run test:ci 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Universal Config 2 | ================== 3 | Configuration for your [Universal JavaScript](https://medium.com/@mjackson/universal-javascript-4761051b7ae9) apps. 4 | 5 | [![NPM Version](https://img.shields.io/npm/v/universal-config.svg?style=flat-square)](https://www.npmjs.org/package/universal-config)[![npm downloads](https://img.shields.io/npm/dm/universal-config.svg?style=flat-square)](https://www.npmjs.com/package/universal-config)[![Travis](https://img.shields.io/travis/naoufal/universal-config.svg?style=flat-square)](https://travis-ci.org/naoufal/universal-config)[![Climate](https://img.shields.io/codeclimate/github/naoufal/universal-config.svg?style=flat-square)](https://codeclimate.com/github/naoufal/universal-config) 6 | 7 | [![Sauce Test Status](https://saucelabs.com/browser-matrix/universal-config.svg)](https://saucelabs.com/u/universal-config) 8 | 9 | ## Install 10 | 11 | ```shell 12 | npm i --save universal-config 13 | ``` 14 | 15 | ## Usage 16 | ### Setup 17 | Create a config folder at the root of your project. 18 | ``` 19 | mkdir config 20 | ``` 21 | 22 | Within the `config` folder, create a `server.js` file that exports an Object containing your private config. 23 | 24 | __`config/server.js`__ 25 | 26 | ```js 27 | export default { 28 | AWS: { 29 | accessKey: process.env.AWS_ACCESS_KEY, 30 | secretKey: process.env.AWS_SECRET_KEY 31 | }, 32 | MAILCHIMP: { 33 | username: process.env.MAILCHIMP_USERNAME, 34 | password: process.env.MAILCHIMP_PASSWORD 35 | } 36 | } 37 | ``` 38 | Once that's done, create a `client.js` file that does the same but for your publicly accessible config. 39 | 40 | __`config/client.js`__ 41 | 42 | ```js 43 | export default { 44 | API_URL: process.env.API_URL || "//api.herokuapp.com", 45 | FACEBOOK_APP_ID: 123456789012345, 46 | NODE_ENV: process.env.NODE_ENV || "development", 47 | SENTRY_CLIENT_KEY: process.env.SENTRY_CLIENT_KEY || "a1b2c3d4e5f6g7h8i9j0" 48 | } 49 | ``` 50 | 51 | ### Server side 52 | Using Universal Config on the server is as simple as importing the module. 53 | 54 | ```js 55 | // Server JavaScript 56 | 57 | import config from "universal-config"; 58 | 59 | config.get("AWS:accessKey"); // Outputs your AWS_SECRET_KEY 60 | ``` 61 | 62 | ### Client side 63 | Whether you're on team Browserify or Webpack, Universal Config has you covered! 64 | 65 | #### Browserify 66 | See the [Browserify example](https://github.com/naoufal/universal-config/tree/master/examples/browserify) for more info. 67 | 68 | #### Webpack 69 | See the [Webpack example](https://github.com/naoufal/universal-config/tree/master/examples/webpack) for more info. 70 | 71 | --- 72 | 73 | Once you're setup, you'll be able to retrive your client config. It's worth noting that your server config won't be accessible on the client. 74 | 75 | ```js 76 | // Client JavaScript 77 | 78 | import config from "universal-config"; 79 | 80 | config.get("AWS:accessKey"); // undefined 81 | config.get("FACEBOOK_APP_ID"); // Outputs your FACEBOOK_APP_ID 82 | ``` 83 | 84 | ### Local Development 85 | In development, you may want to environment variables in a file instead of your `.bash_profile`. 86 | 87 | If you want to use this approach, you can override any server or client variable by creating a `dev.js` file in your `config` directory. You'll want to add this file to your `.gitignore`. 88 | 89 | For testing production locally, you may also specify a `prod.js` in the config directory. It will be imported when NODE_ENV is set to 'production'. 90 | 91 | __NOTE:__ _The variables in these files will be exposed both on the client and the server, but this shouldn't be a problem since you should only be using this locally._ 92 | 93 | ## Methods 94 | 95 | ### get(key) 96 | Retrieves a key from your config. 97 | 98 | __Arguments__ 99 | - `key` - The variable you want to retrieve from your configuration. 100 | 101 | __Examples__ 102 | ```js 103 | import config from "universal-config"; 104 | 105 | const FACEBOOK_APP_ID = config.get("FACEBOOK_APP_ID"); 106 | ``` 107 | --- 108 | 109 | ### set(key, value) 110 | Overwrites a variable in your configuration or sets a new one if the variable doesn't exist. 111 | 112 | __Arguments__ 113 | - `key` - The name of the variable you want to overwrite or sets it on your config instance. 114 | - `value` - The value you want to store. 115 | 116 | __Examples__ 117 | ```js 118 | import config from "universal-config"; 119 | 120 | config.set("FOO", "bar"); 121 | console.log(config.get("FOO")); // bar 122 | ``` 123 | 124 | ## License 125 | Copyright (c) 2015, [Naoufal Kadhom](http://naoufal.com) 126 | 127 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 128 | 129 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 130 | -------------------------------------------------------------------------------- /examples/browserify/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /examples/browserify/app/client.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import config from 'universal-config'; 3 | import VariableList from './components/VariableList.jsx'; 4 | 5 | window.onload = () => { 6 | let clientVars = React.createElement(VariableList); 7 | React.render(clientVars, document.getElementById('client-variables')); 8 | 9 | console.log(`Looking for this? ${window.location.href}js/main.js`); 10 | } 11 | -------------------------------------------------------------------------------- /examples/browserify/app/components/VariableList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import config from 'universal-config'; 3 | 4 | export default class VariableList extends React.Component { 5 | renderChildren() { 6 | var listItems = Object.keys(config._store).map(key => { 7 | var value = config._store[key]; 8 | 9 | if (typeof value === 'object') { 10 | value = JSON.stringify(value); 11 | } 12 | 13 | return ( 14 |
  • {key}: {value}
  • 15 | ); 16 | }); 17 | 18 | return listItems; 19 | } 20 | 21 | render() { 22 | return ( 23 |
    24 |

    25 | {config._env} Variables 26 |

    27 | 30 |
    31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/browserify/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Universal Config Browserify Demo 4 | 5 | 6 |

    Universal Config Browserify Demo

    7 |
    8 | %{serverVariables} 9 |
    10 |
    11 | 12 | -------------------------------------------------------------------------------- /examples/browserify/app/server.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import React from 'react'; 3 | import config from 'universal-config'; 4 | import VariableList from './components/VariableList.jsx'; 5 | import { setupServer, writeFile } from '../../shared/utils'; 6 | 7 | const PORT = config.get('PORT') || 3001; 8 | 9 | const serverVars = React.createElement(VariableList); 10 | const serverVariables = React.renderToStaticMarkup(serverVars); 11 | const JS = fs.readFileSync(`${__dirname}/dist/main.js`).toString(); 12 | const CSS = fs.readFileSync(`${__dirname}/../../shared/assets/css/styles.css`) 13 | .toString(); 14 | const HTML = fs.readFileSync(`${__dirname}/index.html`) 15 | .toString() 16 | .replace('%{serverVariables}', serverVariables); 17 | 18 | var app = setupServer(HTML, JS, CSS); 19 | 20 | app.listen(PORT, () => console.log('Server started on port', PORT)); 21 | -------------------------------------------------------------------------------- /examples/browserify/config/client.js: -------------------------------------------------------------------------------- 1 | export * from '../../shared/config/client'; 2 | -------------------------------------------------------------------------------- /examples/browserify/config/server.js: -------------------------------------------------------------------------------- 1 | export * from '../../shared/config/server'; 2 | -------------------------------------------------------------------------------- /examples/browserify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "universal-config-browserify-example", 3 | "version": "0.0.0", 4 | "description": "Universal config browserify example", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "mkdir -p ./app/dist && ./node_modules/.bin/browserify ./app/client.js -t babelify -o ./app/dist/main.js", 8 | "server": "./node_modules/.bin/babel-node app/server.js", 9 | "start": "npm run build && npm run server" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/naoufal/universal-config" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/naoufal/universal-config/issues" 19 | }, 20 | "homepage": "https://github.com/naoufal/universal-config", 21 | "dependencies": { 22 | "babel": "5.8.21", 23 | "babelify": "6.2.0", 24 | "browserify": "11.0.1", 25 | "react": "0.13.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/shared/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | h3 { 6 | margin: 0; 7 | } 8 | 9 | div { 10 | margin: 0; 11 | } 12 | 13 | #server-variables { 14 | float: left; 15 | width: 50%; 16 | height: 100%; 17 | /* background: #2e2e2c; */ 18 | /* color: #f7db4d; */ 19 | overflow: auto; 20 | } 21 | 22 | #client-variables { 23 | float: left; 24 | width: 50%; 25 | height: 100%; 26 | /* background: #f7db4d; */ 27 | /* color: #2e2e2c; */ 28 | } 29 | 30 | .variable-list { 31 | 32 | } 33 | 34 | .variable-list__heading { 35 | text-transform: capitalize; 36 | } 37 | -------------------------------------------------------------------------------- /examples/shared/config/client.js: -------------------------------------------------------------------------------- 1 | export default { 2 | API_URL: process.env.API_URL || '//api.herokuapp.com', 3 | FACEBOOK_APP_ID: 123456789012345, 4 | NODE_ENV: process.env.NODE_ENV || 'development', 5 | SENTRY_CLIENT_KEY: process.env.SENTRY_CLIENT_KEY || 'a1b2c3d4e5f6g7h8i9j0', 6 | STRIPE_PUBLISHABLE_KEY: process.env.STRIPE_PUBLISHABLE_KEY || 'pk_test_aBcdDfGHijKlMNOPqrsTUvWxYz' 7 | } 8 | 9 | -------------------------------------------------------------------------------- /examples/shared/config/server.js: -------------------------------------------------------------------------------- 1 | export default { 2 | AWS: { 3 | accessKey: process.env.AWS_ACCESS_KEY || 'foo', 4 | secretKey: process.env.AWS_SECRET_KEY 5 | }, 6 | MAILCHIMP: { 7 | username: process.env.MAILCHIMP_USERNAME, 8 | password: process.env.MAILCHIMP_PASSWORD 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /examples/shared/utils/index.js: -------------------------------------------------------------------------------- 1 | import http from 'http'; 2 | 3 | export function writeFile(content = '', type = 'text/plain', res) { 4 | res.writeHead(200, { 5 | 'Content-Length': content.length, 6 | 'Content-Type': type 7 | }); 8 | res.write(content); 9 | res.end(); 10 | } 11 | 12 | export function setupServer(html, js, css) { 13 | var app = http.createServer((req, res) => { 14 | switch (req.url) { 15 | case '/js/main.js': 16 | return writeFile(js, 'text/javascript', res); 17 | case '/css/styles.css': 18 | return writeFile(css, 'text/css', res); 19 | default: 20 | return writeFile(html, 'text/html', res); 21 | } 22 | }); 23 | 24 | return app; 25 | } 26 | -------------------------------------------------------------------------------- /examples/webpack/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /examples/webpack/app/client.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import config from 'universal-config'; 3 | import VariableList from './components/VariableList.jsx'; 4 | 5 | window.onload = () => { 6 | let clientVars = React.createElement(VariableList); 7 | React.render(clientVars, document.getElementById('client-variables')); 8 | 9 | console.log(`Looking for this? ${window.location.href}js/main.js`); 10 | } 11 | -------------------------------------------------------------------------------- /examples/webpack/app/components/VariableList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import config from 'universal-config'; 3 | 4 | export default class VariableList extends React.Component { 5 | renderChildren() { 6 | var listItems = Object.keys(config._store).map(key => { 7 | var value = config._store[key]; 8 | 9 | if (typeof value === 'object') { 10 | value = JSON.stringify(value); 11 | } 12 | 13 | return ( 14 |
  • {key}: {value}
  • 15 | ); 16 | }); 17 | 18 | return listItems; 19 | } 20 | 21 | render() { 22 | return ( 23 |
    24 |

    25 | {config._env} Variables 26 |

    27 | 30 |
    31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/webpack/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Universal Config Webpack Demo 4 | 5 | 6 |

    Universal Config Webpack Demo

    7 |
    8 | %{serverVariables} 9 |
    10 |
    11 | 12 | -------------------------------------------------------------------------------- /examples/webpack/app/server.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import React from 'react'; 3 | import config from 'universal-config'; 4 | import VariableList from './components/VariableList.jsx'; 5 | import { setupServer, writeFile } from '../../shared/utils'; 6 | 7 | const PORT = config.get('PORT') || 3000; 8 | 9 | const serverVars = React.createElement(VariableList); 10 | const serverVariables = React.renderToStaticMarkup(serverVars); 11 | const JS = fs.readFileSync(`${__dirname}/dist/main.js`).toString(); 12 | const CSS = fs.readFileSync(`${__dirname}/../../shared/assets/css/styles.css`) 13 | .toString(); 14 | const HTML = fs.readFileSync(`${__dirname}/index.html`) 15 | .toString() 16 | .replace('%{serverVariables}', serverVariables); 17 | 18 | var app = setupServer(HTML, JS, CSS); 19 | 20 | app.listen(PORT, () => console.log('Server started on port', PORT)); 21 | -------------------------------------------------------------------------------- /examples/webpack/config/client.js: -------------------------------------------------------------------------------- 1 | export * from '../../shared/config/client'; 2 | -------------------------------------------------------------------------------- /examples/webpack/config/server.js: -------------------------------------------------------------------------------- 1 | export * from '../../shared/config/server'; 2 | -------------------------------------------------------------------------------- /examples/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "universal-config-webpack-example", 3 | "version": "0.0.0", 4 | "description": "Universal config webpack example", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "./node_modules/.bin/webpack", 8 | "server": "./node_modules/.bin/babel-node app/server.js", 9 | "start": "npm run build && npm run server" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/naoufal/universal-config" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/naoufal/universal-config/issues" 19 | }, 20 | "homepage": "https://github.com/naoufal/universal-config", 21 | "dependencies": { 22 | "babel": "5.8.21", 23 | "babel-core": "5.8.22", 24 | "babel-loader": "5.3.2", 25 | "react": "0.13.3", 26 | "webpack": "1.11.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | entry: "./app/client", 5 | 6 | output: { 7 | filename: 'main.js', 8 | path: ('./app/dist'), 9 | publicPath: '/js/' 10 | }, 11 | 12 | plugins: [ 13 | // Add process.env support in clients 14 | new webpack.DefinePlugin({ 15 | 'process.env': Object.keys(process.env).reduce(function(o, k) { 16 | o[k] = JSON.stringify(process.env[k]); 17 | return o; 18 | }, {}) 19 | }), 20 | 21 | // Exclude server config requires from clients 22 | new webpack.IgnorePlugin(/config\/server/) 23 | ], 24 | 25 | module: { 26 | loaders: [ 27 | { 28 | test: /\.jsx?$/, 29 | exclude: /(node_modules|bower_components)/, 30 | loader: 'babel-loader?optional[]=runtime&stage=0' 31 | } 32 | ] 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "universal-config", 3 | "version": "0.3.0", 4 | "description": "Configuration for your universal Javascript application", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "build": "./node_modules/.bin/babel --optional runtime src --out-dir lib", 8 | "build:test": "npm run build:test:browserify && npm run build:test:webpack", 9 | "build:test:browserify": "./node_modules/.bin/browserify -e test/browserify.js -o test/browserify-test.js -t ./test/support/browserify-transform-stub.js", 10 | "build:test:webpack": "./node_modules/.bin/webpack --config webpack.config.test.js", 11 | "copy:lib": "sh ./scripts/copy-lib.sh", 12 | "dev": "./node_modules/.bin/babel --optional runtime src --watch --out-dir lib && npm run copy:lib", 13 | "test": "npm run build && NODE_ENV=test ./node_modules/.bin/testem --port 8080", 14 | "test:ci": "npm run build && NODE_ENV=test ./node_modules/.bin/testem ci --port 8080" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/naoufal/universal-config" 19 | }, 20 | "keywords": [ 21 | "configuration", 22 | "config", 23 | "conf", 24 | "env", 25 | "environment variables", 26 | "files", 27 | "universal", 28 | "node", 29 | "browser", 30 | "key", 31 | "value", 32 | "store", 33 | "broswerify", 34 | "webpack" 35 | ], 36 | "author": "Naoufal Kadhom (https://github.com/naoufal)", 37 | "license": "ISC", 38 | "bugs": { 39 | "url": "https://github.com/naoufal/universal-config/issues" 40 | }, 41 | "homepage": "https://github.com/naoufal/universal-config", 42 | "dependencies": { 43 | "envify": "3.4.0", 44 | "through": "2.3.8" 45 | }, 46 | "devDependencies": { 47 | "babel": "5.8.21", 48 | "babel-core": "5.8.22", 49 | "babel-loader": "5.3.2", 50 | "babel-runtime": "5.8.20", 51 | "browserify": "11.0.1", 52 | "chai": "2.0.0", 53 | "mocha": "2.2.5", 54 | "mockery": "1.4.0", 55 | "saucie": "0.1.6", 56 | "testem": "0.9.4", 57 | "webpack": "1.11.0" 58 | }, 59 | "browser": { 60 | "../../../config/server": false, 61 | "../../../config/local/server": false 62 | }, 63 | "browserify": { 64 | "transform": [ 65 | "envify" 66 | ] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /scripts/copy-lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | examples=("browserify" "webpack") 4 | 5 | for example in ${examples[@]} 6 | do 7 | path=./examples/$example/node_modules/universal-config 8 | 9 | # Setup folders 10 | mkdir -p ./examples/$example/node_modules 11 | mkdir -p $path 12 | mkdir -p $path/lib 13 | mkdir -p $path/node_modules 14 | mkdir -p $path/node_modules/envify 15 | 16 | # Copy lib 17 | cp lib/* $path/lib/ 18 | cp package.json $path 19 | cp -r node_modules/envify $path/node_modules/envify 20 | done 21 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | class Config { 2 | constructor() { 3 | this.setEnvironment(); 4 | 5 | this._server = this.getServerVars(); 6 | this._client = this.getClientVars(); 7 | this._localOverrides = this.getLocalOverrides(); 8 | 9 | this._store = Object.assign({}, 10 | this._client, 11 | this._server, 12 | this._localOverrides 13 | ); 14 | } 15 | 16 | set(key, value) { 17 | if (key.match(/:/)) { 18 | const keys = key.split(':'); 19 | let store_key = this._store; 20 | 21 | keys.forEach(function(k, i) { 22 | if (keys.length === (i + 1)) { 23 | store_key[k] = value; 24 | } 25 | 26 | if (store_key[k] === undefined) { 27 | store_key[k] = {}; 28 | } 29 | 30 | store_key = store_key[k]; 31 | }); 32 | 33 | } else { 34 | this._store[key] = value; 35 | } 36 | } 37 | 38 | get(key) { 39 | // Is the key a nested object 40 | if (key.match(/:/)) { 41 | // Transform getter string into object 42 | const store_key = this.buildNestedKey(key); 43 | 44 | return store_key; 45 | } 46 | 47 | // Return regular key 48 | return this._store[key]; 49 | } 50 | 51 | has(key) { 52 | return this.get(key) ? true : false; 53 | } 54 | 55 | setEnvironment() { 56 | if (process.browser) { 57 | this._env = 'client'; 58 | } else { 59 | this._env = 'server'; 60 | } 61 | } 62 | 63 | getServerVars() { 64 | let serverVars = {}; 65 | 66 | if (this._env === 'server') { 67 | try { 68 | serverVars = require('../../../config/server'); 69 | } catch(e) { 70 | if (process.env.NODE_ENV === 'development') { 71 | console.warn(`Didn't find a server config in \`./config\`.`); 72 | } 73 | } 74 | } 75 | 76 | return serverVars; 77 | } 78 | 79 | getClientVars() { 80 | let clientVars; 81 | 82 | try { 83 | clientVars = require('../../../config/client'); 84 | } catch(e) { 85 | clientVars = {}; 86 | 87 | if (process.env.NODE_ENV === 'development') { 88 | console.warn(`Didn't find a client config in \`./config\`.`); 89 | } 90 | } 91 | 92 | return clientVars; 93 | } 94 | 95 | getLocalOverrides() { 96 | let overrides; 97 | const filename = process.env.NODE_ENV === 'production' ? 'prod' : 'dev'; 98 | 99 | try { 100 | overrides = process.env.NODE_ENV === 'production' 101 | ? require('../../../config/prod') 102 | : require('../../../config/dev'); 103 | 104 | console.warn(`Using local overrides in \`./config/${filename}.js\`.`); 105 | } catch(e) { 106 | overrides = {}; 107 | } 108 | 109 | return overrides; 110 | } 111 | 112 | // Builds out a nested key to get nested values 113 | buildNestedKey(nested_key) { 114 | // Transform getter string into object 115 | const keys = nested_key.split(':'); 116 | let store_key = this._store; 117 | 118 | keys.forEach(function(k) { 119 | try { 120 | store_key = store_key[k]; 121 | } catch(e) { 122 | return undefined; 123 | } 124 | }); 125 | 126 | return store_key; 127 | } 128 | } 129 | 130 | const config = new Config(); 131 | 132 | export default config; 133 | -------------------------------------------------------------------------------- /test/browserify.js: -------------------------------------------------------------------------------- 1 | describe('client built with browserify', function () { 2 | require('./shared/client'); 3 | }); 4 | -------------------------------------------------------------------------------- /test/server.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var mockery = require('mockery'); 3 | var config; 4 | var CLIENT_VAR = require('./stubs/config/client'); 5 | var SERVER_VAR = require('./stubs/config/server'); 6 | 7 | describe('Server', function() { 8 | before(function() { 9 | mockery.enable({ 10 | warnOnReplace: false, 11 | warnOnUnregistered: false, 12 | useCleanCache: true 13 | }); 14 | 15 | // Stub config files 16 | mockery.registerSubstitute( 17 | '../../../config/server', 18 | '../test/stubs/config/server' 19 | ); 20 | mockery.registerSubstitute( 21 | '../../../config/client', 22 | '../test/stubs/config/client' 23 | ); 24 | mockery.registerSubstitute( 25 | '../../../config/dev', 26 | '../test/stubs/config/dev' 27 | ); 28 | 29 | config = require('../lib/index'); 30 | }); 31 | 32 | after(function() { 33 | mockery.disable(); 34 | }); 35 | 36 | describe('config.get', function() { 37 | // Client values 38 | it('should get client value', function() { 39 | assert.strictEqual(config.get('CLIENT'), CLIENT_VAR.CLIENT); 40 | }); 41 | 42 | it('should get an environment variable client value', function() { 43 | assert.strictEqual(config.get('NODE_ENV'), CLIENT_VAR.NODE_ENV); 44 | }); 45 | 46 | it('should get a nested client value', function() { 47 | assert.strictEqual( 48 | config.get('NESTED_CLIENT:VALUE'), 49 | CLIENT_VAR.NESTED_CLIENT.VALUE 50 | ); 51 | }); 52 | 53 | // Server values 54 | it('should get a server value', function() { 55 | assert.strictEqual(config.get('SERVER'), SERVER_VAR.SERVER); 56 | }); 57 | 58 | it('should get an environment variable server value', function() { 59 | assert.strictEqual(config.get('LANG'), SERVER_VAR.LANG); 60 | }); 61 | 62 | it('should get a nested server value', function() { 63 | assert.strictEqual( 64 | config.get('NESTED_SERVER:VALUE'), 65 | SERVER_VAR.NESTED_SERVER.VALUE 66 | ); 67 | }); 68 | 69 | // Dev values 70 | it('should get a dev value', function() { 71 | assert.strictEqual( 72 | config.get('DEV'), 73 | true 74 | ); 75 | }); 76 | 77 | // Prod values 78 | it('should not get the dev value', function() { 79 | assert.isUndefined(config.get('PROD')); 80 | }); 81 | 82 | // Undefined values 83 | it('should return `undefined` for variables that are not defined', function() { 84 | assert.isUndefined(config.get('FOO')); 85 | }); 86 | 87 | it('should return `undefined` when getting an undefined key on a nested variable', function() { 88 | assert.isUndefined(config.get('NESTED:FOO')); 89 | }); 90 | 91 | it('should return `undefined` when getting a nested key on an undefined variable', function() { 92 | assert.isUndefined(config.get('FOO:BAR')); 93 | }); 94 | }); 95 | 96 | require('./shared/set'); 97 | require('./shared/has'); 98 | }); 99 | -------------------------------------------------------------------------------- /test/shared/client.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var config = require('../../lib/index'); 3 | var CLIENT_VAR = require('../stubs/config/client'); 4 | 5 | describe('config.get', function() { 6 | it('should get client value', function() { 7 | assert.strictEqual(config.get('CLIENT'), CLIENT_VAR.CLIENT); 8 | }); 9 | 10 | it('should get an environment variables client value', function() { 11 | assert.strictEqual(config.get('NODE_ENV'), CLIENT_VAR.NODE_ENV); 12 | }); 13 | 14 | it('should get a nested client value', function() { 15 | assert.strictEqual(config.get('NESTED_CLIENT:VALUE'), CLIENT_VAR.NESTED_CLIENT.VALUE); 16 | }); 17 | 18 | it('should return `undefined` for variables that are not defined', function() { 19 | assert.isUndefined(config.get('FOO')); 20 | }); 21 | 22 | it('should return `undefined` when getting an undefined key on a nested variable', function() { 23 | assert.isUndefined(config.get('NESTED:FOO')); 24 | }); 25 | 26 | it('should return `undefined` when getting a nested key on an undefined variable', function() { 27 | assert.isUndefined(config.get('FOO:BAR')); 28 | }); 29 | 30 | it('should return `undefined` when getting server variables', function() { 31 | assert.isUndefined(config.get('SERVER')); 32 | }); 33 | }); 34 | 35 | require('./set'); 36 | require('./has'); 37 | -------------------------------------------------------------------------------- /test/shared/has.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var config = require('../../lib/index'); 3 | var CLIENT_VAR = require('../stubs/config/client'); 4 | 5 | describe('config.has', function() { 6 | it('should return `true` if value is in config', function() { 7 | assert.isTrue(config.has('CLIENT')); 8 | }); 9 | 10 | it('should return `false` if value is not in config', function() { 11 | assert.isFalse(config.has('FOO')); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/shared/set.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var config = require('../../lib/index'); 3 | var CLIENT_VAR = require('../stubs/config/client'); 4 | 5 | describe('config.set', function() { 6 | it('should set a new value successfully', function() { 7 | var value = 'new value'; 8 | config.set('NEW_VALUE', value); 9 | 10 | assert.strictEqual(config.get('NEW_VALUE'), value); 11 | }); 12 | 13 | it('should overwrite an existing value succesfully', function() { 14 | var value = 'new value'; 15 | config.set('CLIENT', value); 16 | 17 | assert.strictEqual(config.get('CLIENT'), value); 18 | }); 19 | 20 | it('should set a new nested value successfully', function() { 21 | var value = 'new value'; 22 | config.set('NEW:NESTED_VALUE', value); 23 | 24 | assert.isObject(config.get('NEW')); 25 | assert.strictEqual(config.get('NEW:NESTED_VALUE'), value); 26 | }); 27 | 28 | it('should overwrite an existing nested value successfully', function() { 29 | var value = 'new value'; 30 | config.set('NESTED_CLIENT:VALUE', value); 31 | 32 | assert.strictEqual(config.get('NESTED_CLIENT:VALUE'), value); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/stubs/config/client.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | CLIENT: 'client var', 3 | NODE_ENV: process.env.NODE_ENV, 4 | NESTED_CLIENT: { 5 | VALUE: 'nested value' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/stubs/config/dev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | DEV: true 3 | }; 4 | -------------------------------------------------------------------------------- /test/stubs/config/prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | PROD: true 3 | }; 4 | -------------------------------------------------------------------------------- /test/stubs/config/server.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | SERVER: 'server var', 3 | LANG: process.env.LANG, 4 | NESTED_SERVER: { 5 | VALUE: 'nest value' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/support/browserify-transform-stub.js: -------------------------------------------------------------------------------- 1 | var through = require('through'); 2 | 3 | module.exports = function(file) { 4 | return through(function (buf, enc, next) { 5 | var string = buf.toString('utf8').replace(/..\/..\/..\/config/g, '../test/stubs/config'); 6 | 7 | this.push(string); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /test/webpack.js: -------------------------------------------------------------------------------- 1 | describe('client built with webpack', function () { 2 | require('./shared/client'); 3 | }); 4 | -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "framework": "mocha", 3 | "src_files": [ 4 | "./test/browserify.js", 5 | "./test/webpack.js" 6 | ], 7 | "serve_files": [ 8 | "./test/browserify-test.js", 9 | "./test/webpack-test.js" 10 | ], 11 | "launchers": { 12 | "Node": { 13 | "command": "mocha ./test/server.js -R tap", 14 | "protocol": "tap" 15 | }, 16 | "SL_Chrome": { 17 | "command": "saucie --browserNameSL='chrome'", 18 | "protocol": "tap" 19 | }, 20 | "SL_Safari": { 21 | "command": "saucie --browserNameSL='safari'", 22 | "protocol": "tap" 23 | }, 24 | "SL_Firefox": { 25 | "command": "saucie --browserNameSL='firefox'", 26 | "protocol": "tap" 27 | }, 28 | "SL_IE9": { 29 | "command": "saucie --browserNameSL='internet explorer' --versionSL='9' --platformSL='Windows 7'", 30 | "protocol": "tap" 31 | }, 32 | "SL_IE10": { 33 | "command": "saucie --browserNameSL='internet explorer' --versionSL='10' --platformSL='Windows 7'", 34 | "protocol": "tap" 35 | }, 36 | "SL_IE11": { 37 | "command": "saucie --browserNameSL='internet explorer' --versionSL='11' --platformSL='Windows 7'", 38 | "protocol": "tap" 39 | } 40 | }, 41 | "reporter": "dot", 42 | "before_tests": "NODE_ENV=test npm run build:test", 43 | "on_exit": "rm ./test/browserify-test.js && rm ./test/webpack-test.js", 44 | "launch_in_dev": [ 45 | "node", 46 | "phantomjs", 47 | "chrome" 48 | ], 49 | "launch_in_ci": [ 50 | "node", 51 | "SL_Chrome", 52 | "SL_Safari", 53 | "SL_Firefox", 54 | "SL_IE9", 55 | "SL_IE10", 56 | "SL_IE11" 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /webpack.config.test.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | module.exports = { 4 | entry: "./test/webpack.js", 5 | 6 | output: { 7 | filename: 'webpack-test.js', 8 | path: ('./test/'), 9 | }, 10 | 11 | plugins: [ 12 | // Add process.env support in clients 13 | new webpack.DefinePlugin({ 14 | 'process.env': Object.keys(process.env).reduce(function(o, k) { 15 | o[k] = JSON.stringify(process.env[k]); 16 | return o; 17 | }, {}) 18 | }), 19 | 20 | // Exclude server config requires from clients 21 | new webpack.IgnorePlugin(/config\/server/), 22 | 23 | new webpack.NormalModuleReplacementPlugin(/..\/..\/..\/config\/client/, '../test/stubs/config/client') 24 | ] 25 | }; 26 | --------------------------------------------------------------------------------