README
AwesomeConfig
AwesomeConfig is a powerful configuration system for building enterprise node.js application. It provides a unified, transparent configuration object to your application based on configuration files or objects that you define. It includes support for conditional configuration based on external values like environment variables, hostname, or OS; variables to reference one part of your configuration from another; and lots more.
Features
AwesomeConfig provides...
- Add configuration from files, directories, as JSON, or as a plain javascript objects;
- Uses JSON notation or our custom notation that supports mixing JSON style config and key/value style config;
- Globally accessable config without the need to pass config objects around;
- Configuration is exposed as a plain JavaScript object for easy usage;
- Support for namespaced instances to isolate usage as needed;
- Configuration is immutable once started;
- Configuration Variables allow cross referencing other parts of your configuration;
- Configuration Conditions allow you to toggle on/off different parts of your configuration based on external values like hostname, OS, or environment variables.
- Configuration Plcaeholders to force users to provide key configuration values.
- No reserved words.
Contents
- Installation
- Setup
- Adding Configurations
- Configuration Notation
- Namespaces
- Variables
- Placeholders
- Conditions
- Documentation
- Examples
- Awesome Engineering
- Support and Help
- License
Installation
Couldn't be easier.
npm install --save @awesomeeng/awesome-config
Setup
To use AwesomeConifg you need to go through four steps to set it up and add configuration into it. These four steps should be done at the top module of your project or as close to the top as possible. Once AwesomeConfig is started all subsquent requires of config (aka const config = require("@awesomeeng/awesome-config")
) will return the fully started configuration. This allows you to setup your config at the top of your application and then just use it in subsequent parts of your application without needing to pass a configuration around manually.
Initial Setup
You initially Setup configuration with 4 easy steps...
1). Require AwesomeConfig:
const config = require("@awesomeeng/awesome-config");
2). Initialize AwesomeConfig. This setups configuration and prepares configuration for additions.
const config = require("@awesomeeng/awesome-config");
config().init();
Note that we call config()
when we are referencing our configuration management systems like init()
or 'start()'. We use just config
when we are referencing actual configuration properties. This separation allows AwesomeConfig to have zero reserved words and lets you express your configuration however you want.
3). Add zero or more configurations:
const config = require("@awesomeeng/awesome-config");
config().init();
config().add(someconfig);
There are a variety of ways to add configurations, so make sure to read the Adding Configurations section below.
4). Start AwesomeConfig:
const config = require("@awesomeeng/awesome-config");
config().init();
config().add(someconfig);
config().start();
Once config().start()
is called your configuration gets merged into a single configuration view based on any conditions you specified, variables are resolved, placeholders are checked, and the entire resulting structure is made immutable.
Usage
You access configuration as you would any JavaScript object. Say you have a configuration property called "one.two.three". Simply access it thus:
config.one.two.three
If the property doesnt exist or any of its ancestors ("one" for example) does not exist, the property throws an exception. This ensures that your configuration is always met or fails fast.
Regardless of where you require AwesomeConfig, it exposes the same configuration details. This lets you initialize and start config once in your application, but access it from anywhere without the need to pass the config object around.
const config = require("@awesomeeng/awesome-config");
console.log(config.this.is.cool);
There is a little more nuance to it, but that is basically all there is to using AwesomeConfig.
Adding Configurations
You can add configuration in five basic ways: A JavaScript Object, a JSON String, an AwesomeConfig Notation string, a resolved filename, or a resolved directory. Each approach has slight differences and fits different needs.
You may add as many configurations you want before calling config().start()
.
When config().start()
is called, all of the added configurations are merged together into a single configuration view. The order they are merged is the order in which they are added, thus things added later will overwrite things added earlier if they have the same property names. This intentionally lets you provide some values, but then override them later as needed.
Adding A JavaScrpt Object Configuration
You may add a JavaScript object as a configuration. The object must be a plain JavaScript object and cannot be an Array or other primative.
config().add({
one: {
two: {
three: 123
}
},
four: {
five: 45
}
});
This would result in the following properties:
config.one.two.three === 123
config.four.five === 45
Adding A JSON String Configuration
You can add a JSON String. AwesomeConfig will parse the JSON using JSON.parse()
and the resulting object will be added as if config().add(object)
was called. The parsed JSON must return a JavaScript object and cannot return an Array or other Primative.
config().add("{\"one\":1}");
This would result in the following properties:
config.one === 1
Adding an AwesomeConfig Notation String Configuration
You can add a AwesomeConfig Notation String. AwesomeConfig will parse the string using the AwesomeConfig Notation parser and the resulting configuration will be added.
config().add(`
one.two.three: 123
{
four: {
five: 45
}
}
`);
This would result in the following properties:
config.one.two.three === 123
config.four.five === 45
Adding A Filename Configuration
You may pass a filename into config().add()
and the file contents will be loaded, parsed using the AwesomeConfig Notation parser (see Configuration Notation below), and the resulting objects and conditions are added.
The passed filename is resolved relative to the current working directory, or the calling module if not found in the current working directory. If the file is not found or otherwise unreadable, an exception will be thrown.
config().add("./MyConfig.cfg");
Adding A Directory Configuration
Passing a directory into config().add()
will result in all files within the directory that match *.cfg
being loaded (via config().add(filename)
from above). The order the files are loaded is based on their case sensitive alphabetical order in the directory.
Adding with Conditions
The second argument in config().add()
may specify a condition string that is applied as the default conditions to the configuration being added. This allows you to control configurations more programatically.
config().add("./MyConfig.cfg","hostname==localhost and os=windows");
See Conditions below for more information on condition strings.
Configuration Notation
AwesomeConfig configuration files use a custom configuration notation that is a hybrid of both JSON and key/value pairs. If you want to just use pure JSON that is fine. However, AwesomeConfig notation gives you a little bit more such as comments, conditions, key/values, variables, placeholders, and mixing key/values with json.
Example
Here's an example configuration file:
//
// this is an example configuration.
//
one.two.three: 123
four.five: {
six: {
seven: some string value # and a comment
}
}
eight: [{
nine: 89
}]
This configuration would result in the following properties:
one.two.three = 123
four.five.six.seven = "some string value"
eight.0.nine = 89
In the example you will notice a few things going on:
First we have a standard key/value pair in "one.two.three". The dot notation is used to separate the path into levels. The key/value pair notation form saves a lot of space and creates cleaner configurations. If we wanted to write the same using JSON it would look like this:
{
"one": {
"two": {
"three": 123
}
}
}
Second, we have a key/value pair again, but the value side is a JSON object. This mixing of key/value and JSON can result in very clear, easy to read configurations. Also, you might notice that the quotes around the key names are optional.
Finally, we have a key in "eight" which takes an array as its argument, which in turn has some JSON inside of it.
Structure
An AwesomeConfig file has the following structure:
config = [<comment>|<json_block>|<key_value_pair>|<condition>]*
That is the top level have a comment, a json block, a key/value pair, or a condition. We look at each of these below...
Comments
comment = [<double_slash_comment>|<hash_comment>|<multi_line_comment>]
AwesomeConfig files support three different types of comments:
- Double Slash Comments: starts with two forward slash characters ("//") and terminate at a newline character.
// this is a double slash comment blah=123 // and so is this
- Hash Comments start with the hash character "#" (sometimes called the pound sign) and terminate at a newline character.
# this is a hash comment blah=123 # and so is this
- Multi-Line Comments start with a "/" and run until a corresponding "/" string is detected. Multi-Line comments can span multiple lines and are great for large blocks of comment text.
/* This is a multiline comment. */
JSON Block
json_block = "{" [<json>] "}"
A JSON block is a valid JSON string that begins with the open brace character ("{") and ends with the close brace character ("}"). Array JSON blocks are not supported at the top level.
Key/Value Pairs
key_value_pair = <key> <assignment_operator> <value>
A key/value pair matches some key or key path with a value. The following are all examples of key/value pairs...
one: 1
two = two
three.four = {
five: 345
}
Keys
A key consist of one or more letter ("Abc"), digit ("123"), underscore ("_"), dash ("-"), or dollar sign ("