Home

Awesome

properties

.properties parser/stringifier

NPM version Build Status

NPM installation

.properties specification

This module implements the Java .properties specification and adds additional features like ini sections, variables (key referencing), namespaces, importing files and much more.

Quick example

# file
a = 1
b: 2
c 3
var properties = require ("properties");

properties.parse ("file", { path: true }, function (error, obj){
  if (error) return console.error (error);
  
  console.log (obj);
  //{ a: 1, b: 2, b: 3 }
});

Documentation

Functions

Objects


<a name="sections"></a> Sections

INI sections can be enabled with the sections option. With them you can better organize your configuration data.

app_name App

[web]
hostname 10.10.10.10
port 1234

[db]
hostname 10.10.10.20
port 4321

Creates:

{
  app_name: "App",
  web: {
    hostname: "10.10.10.10",
    port: 1234
  },
  db: {
    hostname: "10.10.10.20",
    port: 4321
  }
}

<a name="variables"></a> Variables

When the variables option is enabled you can get the value of another key. The value is read before the type conversion. Imagine them like the C macros. They simply copy the characters, they don't care if the value is a number or a string.

a = 1
# b = 1
b = ${a}

Note: If you are using the include option, take into account that the variables are local to the file, they cannot be used to access the properties of other files.

If you need to get the value of a key that belongs to a section, prefix the key with the section followed by |.

a = 1
[section]
a = 2
# b = 2
b = ${section|a}

You can use the variables anywhere including the variable itself. Look at the variables example for further details.

a = 1
# s1
[s${a}]
a = b
b = c
# d = c
d = ${s${a}|${s${a}|a}}

<a name="environment"></a> Environment

You can also pass external variables with the vars option and use their value while the file is being parsed. This is an extremly useful feature because you don't need to change anything from your configuration files if you want to dynamically assign the value of the properties. It could be used to load different configurations depending on the environment. Look at the vars and environment-vars examples for further details.


<a name="namespaces"></a> Namespaces

When the namespaces option is enabled dot separated keys and sections are parsed as namespaces, that is, they are interpreted as JavaScript objects.

a.b = 1
a.c.d = 2

These properties create the following object:

{
  a: {
    b: 1,
    c: {
      d: 2
    }
  }
}

You can also use sections and variables:

[s1.x]
a.b = 1
# a.c.d = 1
a.c.d = ${s1.x|a.b}

{
  s1: {
    x: {
      a: {
        b: 1,
        c: {
          d: 1
        }
      }
    }
  }
}

The external variables can be also read using namespaces:

var options = {
  vars: {
    a: {
      b: 1
    }
  }
};
# a = 1
a = ${a.b}

Look at the namespaces example for further details.


<a name="ini"></a> INI

This module implements the .properties specification but there are some options that can be enabled, some of them are the sections, comments, separators and strict. With these four options this module can parse INI files. There isn't an official INI specification, each program implements its own features, but there is a de facto standard that says that INI files are just .properties files with sections and the = token as a separator.

If you want to parse INI files, then enable these options:

var options = {
  sections: true,
  comments: ";", //Some INI files also consider # as a comment, if so, add it, comments: [";", "#"]
  separators: "=",
  strict: true
};

The strict option says that only the tokens that are specified in the comments and separators options are used to parse the file. If strict is not enabled, the default .properties comment (#, !) and separator (=, :) tokens are also used to parse comments and separators. Look at the ini examples for further details.

Note: The whitespace (<space>, \t, \f) is still considered a separator even if strict is true.


<a name="include"></a> Importing files

When the include option is enabled, the include key allow you import files. If the path is a directory, it tries to load the file index.properties. The paths are relative to the current .properties file.

The imported files are merged with the current file, they can replace old data.

The include keyword cannot appear inside a section, it must be a global property.

include a/file

# Load a/dir/index.properties
include a/dir

Note: You can include files using a simple key-value string:

properties.parse ("include my/file", function (error, data){
  ...
})

In this case the files are always relative to .. You cannot use __dirname like this: "include " + __dirname + "/my/file".


<a name="useful"></a> Useful options that you should always use

There are too many options that you can enable but, which of them should you use? Well, this depends on what you need but I like to enable the following ones:

Wrapping this module it's also a good idea. This is a good starting point:

//config.js

var properties = require ("properties");

var options = {
  path: true,
  namespaces: true,
  sections: true,
  variables: true,
  include: true
};

var configDir = "./path/to/config/dir";

module.exports.load = function (path, cb){
  //NODE_ENV can be "production" or "development"
  //Load specific configuration depending on the environment
  properties.parse (configDir + "/" + process.env.NODE_ENV, options,
      function (error, env){
    if (error) return cb (error);
    
    //Pass the specific configuration as external variables to the common
    //configuration
    options.vars = env;
    
    //If the path is a directory it tries to load the "index.properties" file
    properties.parse (configDir, options, cb);
  });
};

Usage:

var config = require ("./config");

config.load (function (error, obj){
  if (error) return console.error (error);
  
  ...
});

Note: You can also use a configuration loader like seraphim.


<a name="parse"></a> module.parse(data[, options][, callback]) : undefined | Object

Parses a .properties string.

If a callback is given, the result is returned as the second parameter. Some options will require a callback.

try{
  //Certain options can throw errors, so if the callback is not used, try-catch
  //the function
  obj = properties.parse ({ ... });
}catch (error){
  ...
}

properties.parse ({ ... }, function (error, obj){
  //The callback must be used if the "path" option is used
});

Options:


<a name="createStringifier"></a> module.createStringifier() : Stringifier

Returns a new Stringifier instance.


<a name="stringify"></a> module.stringify(obj[, options][, callback]) : undefined | String

Stringifies an object or a Stringifier.

If you don't need to add sections or comments simply pass an object, otherwise use a Stringifier.

The callback is only necessary when the path option is used.

Nested objects and arrays cannot be stringified like in JSON.stringify:

properties.stringify ({
  a: [1, "a"],
  b: {}
});

/*
a = 1,a
b = [object Object]
*/

This also applies to the Stringifier keys and values.

Options:


<a name="Stringifier"></a> Stringifier

This class is used when you want to add sections or comments to the final string.

To create a Stringifier use the createStringifier() function.

Methods

<a name="Stringifier_header"></a> Stringifier#header(comment) : Stringifier

Writes a header comment. It will be written to the top of the final string. Returns the Stringifier being used.

<a name="Stringifier_property"></a> Stringifier#property(obj) : Stringifier

Writes a property line. It takes an object with three options: key, value and comment. Both the key and the value are converted into a string automatically. Returns the Stringifier being used.

stringifier
  //No value
  .property ({ key: "a" })
  .property ({ key: "b", value: [1, 2, 3] })
  //No key and no value
  .property ({ comment: "empty" })

/*
a = 
b = 1,2,3
# empty
 = 
*/

<a name="Stringifier_section"></a> Stringifier#section(obj) : Stringifier

Writes a section line. It gets an object with two options: name and comment. The name is converted into a string. If you don't need to write a comment, you can pass the name instead of an object. Returns the stringifier being used.

stringifier.section ("my section");

/*
[my section]
*/

stringifier.section ({ name: "my section", comment: "My Section" });

/*
# My Section
[my section]
*/

Look at the stringify-ini example for further details.