decomment = function (str) { var i, curChar, nextChar, inString = false, inComment = false, newStr = ''; for (i = 0; i < str.length; ++i) { curChar = str.charAt(i); nextChar = str.charAt(i + 1); // it's either closing or opening inString and it is not escaped if (!inComment && curChar === '"' && str.charAt(i - 1) !== '\\') { inString = !inString; } // we are not inside of a string if (!inString) { // singleline comment start if (!inComment && curChar + nextChar === '//') { ++i; inComment = 1; // singleline comment end } else if (inComment === 1 && curChar === '\n') { inComment = false; // multiline comment start } else if (!inComment && curChar + nextChar === '/*') { ++i; inComment = 2; curChar = ''; // multiline comment end } else if (inComment === 2 && curChar + nextChar === '*/') { ++i; inComment = false; curChar = ''; } if (inComment) { curChar = ''; } } newStr += curChar; } return newStr; }
...
*
* @param {String} json.
* @param {Function} [reviver] will be called for every key and value at every
* level of the final result.
* @return {Object} parsed json object.
*/
exports.parse = function(str, reviver) {
return exports.options.parse(exports.decomment(str), reviver);
};
/**
* Replace templates with data. {{toReplace}}
*
* @param {String} json.
* @param {Object} data data hash.
...
function extend(deep, obj1, obj2) { // take first argument, if its not a boolean var args = arguments, i = deep === true ? 1 : 0, key, target = args[i]; for (++i; i < args.length; ++i) { for (key in args[i]) { if (deep === true && target[key] && // if not doing this check you may end in // endless loop if using deep option toString.call(args[i][key]) === obj && toString.call(target[key]) === obj) { // create a copy of target object to avoid subobjects changes target[key] = extend(deep, {}, target[key]); extend(deep, target[key], args[i][key]); } else { target[key] = args[i][key]; } } } return target; }
...
exports.load = function load(path, options) {
var data, paths, conf;
if (options === true) {
options = {merge: true};
}
options = exports.extend({}, exports.options, options);
if (Array.isArray(path)) {
conf = {};
path.forEach(function(path) {
if (fs.existsSync(path)) {
var data = load(path, options),
filename;
...
function freeze(obj) { var key; if (obj instanceof Object) { for (key in obj) { freeze(obj[key]); } Object.freeze(obj); } }
...
var key;
if (obj instanceof Object) {
for (key in obj) {
freeze(obj[key]);
}
Object.freeze(obj);
}
};
/**
* Load and parse a config file/files.
*
* @param {String|Array} path absolute path/paths to the file/files or dir.
...
function load(path, options) { var data, paths, conf; if (options === true) { options = {merge: true}; } options = exports.extend({}, exports.options, options); if (Array.isArray(path)) { conf = {}; path.forEach(function(path) { if (fs.existsSync(path)) { var data = load(path, options), filename; if (options.merge) { exports.extend(true, conf, data); } else { filename = Path.basename(path, options.ext); conf[filename] = data; } } }); return conf; } if (fs.statSync(path).isDirectory()) { paths = []; fs.readdirSync(path).forEach(function(filename) { var file = Path.join(path, filename); if (Path.extname(file) == options.ext && fs.statSync(file).isFile()) { paths.push(file); } }); return load(paths, options); } data = fs.readFileSync(path, 'utf-8'); // replace BOM Character data = data.replace(/\ufeff/g, ''); if (options.replace) { data = exports.replace(data, options.replace); } try { data = exports.parse(data); } catch(err) { err.message += '\nFile: "' + path + '"'; throw err; } if (options.freeze) { exports.freeze(data); } return data; }
...
```
## API
### load the module
var cjson = require('cjson');
### cjson.load(path, [options]);
Load config file from given path, array of paths or directory. Second parameter is optional and can be a boolean or object.
- `path` {String|Array} absolute path to the file, array of paths or directory
- `options` {Boolean|Object} optional options. If you pass `true` as second param, its the same like ` {merge: true}` and will
merge all configs together.
...
parse = function (str, reviver) { return exports.options.parse(exports.decomment(str), reviver); }
...
*
* @param {String} json.
* @param {Function} [reviver] will be called for every key and value at every
* level of the final result.
* @return {Object} parsed json object.
*/
exports.parse = function(str, reviver) {
return exports.options.parse(exports.decomment(str), reviver);
};
/**
* Replace templates with data. {{toReplace}}
*
* @param {String} json.
* @param {Object} data data hash.
...
replace = function (str, data) { return str.replace(/\{\{([^}]+)\}\}/g, function(match, search) { if (data.hasOwnProperty(search)) { // If the variable is an object, stringify it before replacement. // The false positive of "null" is fine in this case. if (typeof data[search] === 'object') { return JSON.stringify(data[search]); } return data[search]; } return match; }); }
...
* Replace templates with data. {{toReplace}}
*
* @param {String} json.
* @param {Object} data data hash.
* @return {String} json string with replaced data.
*/
exports.replace = function(str, data) {
return str.replace(/\{\{([^}]+)\}\}/g, function(match, search) {
if (data.hasOwnProperty(search)) {
// If the variable is an object, stringify it before replacement.
// The false positive of "null" is fine in this case.
if (typeof data[search] === 'object') {
return JSON.stringify(data[search]);
}
return data[search];
...
function parse(text, reviver) { try { return JSON.parse(text, reviver); } catch (err) { // we expect this to throw with a more informative message jju.parse(text, { mode: 'json', reviver: reviver }); // backup if jju is not as strict as JSON.parse; re-throw error // data-dependent code path, I do not know how to cover it throw err; } }
...
*
* @param {String} json.
* @param {Function} [reviver] will be called for every key and value at every
* level of the final result.
* @return {Object} parsed json object.
*/
exports.parse = function(str, reviver) {
return exports.options.parse(exports.decomment(str), reviver);
};
/**
* Replace templates with data. {{toReplace}}
*
* @param {String} json.
* @param {Object} data data hash.
...