Skip to content
This repository was archived by the owner on Nov 21, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
language: node_js
node_js:
- "9"
- "8"
- "7"
- "6"
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2016-2017 Matt Venables
Copyright (c) 2016-2018 Matt Venables

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
112 changes: 83 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ Simple environment-specific configuration for your node apps

[![Build Status](https://img.shields.io/travis/hello-js/hello-config/master.svg)](https://travis-ci.org/hello-js/hello-config)
[![Coverage Status](https://img.shields.io/coveralls/hello-js/hello-config.svg)](https://coveralls.io/github/hello-js/hello-config)
[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)

## Installation

```
```sh
yarn add hello-config
```

Expand All @@ -18,50 +17,64 @@ hello-config loads environment-specific config files from a directory.

### Setup

The easiest way to get set up is running the following from your command line

```sh
hello-config
```

This will create a `config` directory that is ready to be required anywhere in your app (`const config = require('../config');`)

### Manual Setup

You can also set up `hello-config` manually.

```js
const path = require('path')
const Config = require('hello-config')
/**
* config/index.js
*/

let config = Config.load(path.join(__dirname, 'environments'))
const Config = require('hello-config');

module.exports = Config.load();
```

The above code will load `./environments/default.js` and merge in `./environments/development.js` overrides.
The above code will load `config/config.js` and merge in contents from `config/development.js` as overrides.

If there is a `config/development.local.js` file, this will be merged in as well. You can have a `.local.js` file for any environment.

If there is a `./environments/development.local.js` file, this will be merged in as well.
You can also set local environment variables using a [`.env`](https://github.com/motdotla/dotenv) file the root of your project if you'd like.

*NOTE:* `*.local.js` should be added to `.gitignore` -- it should only be used for developer-specific settings
*NOTE:* `*.local.js` and `.env` should be added to `.gitignore` -- it should only be used for developer-specific settings

### Recommended directory structure

The recommended directory structure is

```
./config/
config.js
development.js
index.js
environments/
default.js
development.js
production.js
staging.js
test.js
production.js
test.js
```


Sample `config/index.js` file:

```js
'use strict'
'use strict';

const path = require('path')
const Config = require('hello-config')
const Config = require('hello-config');

module.exports = Config.load(path.join(__dirname, 'environments'))
module.exports = Config.load();
```

Sample `default.js` file:
Sample `config.js` file:

```js
'use strict'
'use strict';

module.exports = {
port: process.env.PORT || 80,
Expand All @@ -71,7 +84,7 @@ module.exports = {
username: 'matt'
// ...
}
}
};
```

Sample `development.js` file:
Expand All @@ -85,29 +98,70 @@ module.exports = {
db: {
host: '127.0.0.1'
}
}
};
```

At this point, you can run the following code:

```js
let config = require('./config')
config config = require('./config');

config.port
config.port;
// => 3000

config.get('port')
config.get('port');
// => 3000

config.db.host
config.db.host;
// => '127.0.0.1'

config.get'db.host')
config.get'db.host');
// => '127.0.0.1'

config.does.not.exist
config.does.not.exist;
// => TypeError: Cannot read property 'not' of undefined

config.get('does.not.exist')
config.get('does.not.exist');
// => undefined
```

### Custom directory structure

You can use any directory structure you prefer. For example, to have a
structure like the following:

```
config/
index.js
environments/
all.js
development.js
production.js
test.js
```

You can use the following options for `Config.load()`:

```
'use strict';

const path = require('path');
const Config = require('hello-config');

module.exports = Config.load({
root: path.join(__dirname, 'environments'),
baseFilename: 'all'
});
```

By default, hello-config uses `process.env.NODE_ENV` as the environment, however,
if you'd like, you can directly load an environment's configuration:

```
'use strict';

// Loads the test environment:
Config.load({
env: 'test'
});
```
27 changes: 27 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env node

'use strict';

const fs = require('fs');
const path = require('path');

const template = `'use strict';

module.exports = {

};
`;

fs.mkdirSync(path.join('.', 'config'));
fs.writeFileSync('./config/index.js', `'use strict';

const Config = require('hello-config');

module.exports = Config.load();
`);

['config', 'development', 'production', 'test'].forEach(env => {
fs.writeFileSync(path.join('.', 'config', `${env}.js`), template);
});

process.exit(0);
85 changes: 58 additions & 27 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
'use strict'
'use strict';

try {
require('dotenv').config({
silent: true
})
} catch (e) { }
const _merge = require('lodash.merge')
const _get = require('lodash.get')
const _set = require('lodash.set')
const path = require('path')
});
} catch (err) { }

const _ = require('lodash');
const path = require('path');

/**
* The environment-specific config loader.
Expand Down Expand Up @@ -49,8 +48,8 @@ const path = require('path')
* // TypeError: Cannot read property 'exist' of undefined
*/
class Config {
constructor (config) {
_merge(this, config)
constructor(config) {
_.merge(this, config);
}

/**
Expand All @@ -60,18 +59,24 @@ class Config {
* @example
* Config.load(path.join('..', 'config', 'environments'))
*
* @param {String} environmentDir - The path to the environment-specific config directory
* @returns {Config} - The updated config object
* @param {Object} [opts] - The options to the app
* @param {String} [opts.root] - The environment-specific config directory (Default: '.')
* @param {String} [opts.baseFilename] - The base configuration filename (Default: 'config')
* @param {String} [opts.env] - The environment to use (default: process.env.NODE_ENV, or 'development')
* @returns {Config} - A populated Config object
*/
static load (environmentDir) {
let env = process.env.NODE_ENV || 'development'
let defaultConfig = _safeLoad(path.join(environmentDir, 'default'))
let envConfig = _safeLoad(path.join(environmentDir, env))
let localConfig = _safeLoad(path.join(environmentDir, env + '.local'))
static load(opts = {}) {
const baseFilename = opts.baseFilename || 'config';
const env = opts.env || process.env.NODE_ENV || 'development';
const root = opts.root || _parentPath();

const defaultConfig = _safeLoad(path.join(root, baseFilename));
const envConfig = _safeLoad(path.join(root, env));
const localConfig = _safeLoad(path.join(root, env + '.local'));

return new Config(_merge({}, defaultConfig, envConfig, localConfig, {
env: env
}))
return new Config(_.merge({}, defaultConfig, envConfig, localConfig, {
env
}));
}

/**
Expand All @@ -90,8 +95,8 @@ class Config {
* @param {String} keyPath - The key (or key path) to access
* @returns {Any} - The config value, if any, otherwise `undefined`
*/
get (keyPath) {
return _get(this, keyPath)
get(keyPath) {
return _.get(this, keyPath);
}

/**
Expand All @@ -110,8 +115,22 @@ class Config {
* @param {Any} value - The value to set
* @returns {Any} - The config value
*/
set (keyPath, value) {
return _set(this, keyPath, value)
set(keyPath, value) {
return _.set(this, keyPath, value);
}

/**
* Convert the Config object to a Plain Javascript Object
*
* This method is called as part of JSON.stringify and used
* in tests.
*
* You likely do not need to use this method
*
* @returns {Object}
*/
toJSON() {
return _.toPlainObject(this);
}
}

Expand All @@ -122,12 +141,24 @@ class Config {
*
* @returns {Object} the contents of the file, falling back to an empty object
*/
function _safeLoad (file) {
function _safeLoad(file) {
try {
return require(file)
return require(file);
} catch (err) {
return {}
return {};
}
}

module.exports = Config
/**
* @private
*
* Determine the path of the module's parent (the calling module) to
* assist in auto-generating the config path
*
* @returns {String} - The path of the config directory
*/
function _parentPath() {
return path.dirname(module.parent.filename);
}

module.exports = Config;
Loading