-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
closes #10 add a way to get detailed error messages
- Loading branch information
Showing
7 changed files
with
259 additions
and
235 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
node_modules | ||
components | ||
.DS_Store | ||
npm-debug.log | ||
tmp | ||
*~ | ||
*.swp | ||
*.log | ||
*.pid | ||
*.swo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,4 @@ | ||
test: | ||
node ./test/test.js | ||
|
||
lint: | ||
linter -f ./lib/cjson.js | ||
|
||
|
||
.PHONY: test lint | ||
.PHONY: test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,228 @@ | ||
module.exports = require('./lib/cjson.js'); | ||
var fs = require('fs'), | ||
Path = require('path'); | ||
|
||
/** | ||
* Default options. | ||
* | ||
* @type {Object} | ||
*/ | ||
exports.options = { | ||
// merge all passed/found config files, see `cjson.extend` | ||
merge: false, | ||
// allows you to do some string replacements, see `cjson.replace`. | ||
replace: null, | ||
// freeze config recursively, see `cjson.freeze` | ||
freeze: false, | ||
// you can use any other extension for your config files, f.e. *.cjson | ||
ext: '.json', | ||
// you can use any parser, f.e. if you need a more detailed error description, | ||
// you could use "jsonlint" module | ||
parse: JSON.parse | ||
} | ||
|
||
/** | ||
* Remove single and multilie comments. Make sure to | ||
* leave them inside of strings. | ||
* | ||
* @param {String} json file. | ||
* @return {String} json without comments. | ||
*/ | ||
exports.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; | ||
}; | ||
|
||
/** | ||
* Decomment the string and parse json. | ||
* | ||
* @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. | ||
* @return {String} json string with replaced data. | ||
*/ | ||
exports.replace = function(str, data) { | ||
return str.replace(/\{\{([^}]+)\}\}/g, function(match, search) { | ||
return data[search] ? data[search] : match; | ||
}); | ||
}; | ||
|
||
/** | ||
* Merge objects to the first one | ||
* | ||
* @param {Boolean|Object} deep if set true, deep merge will be done. | ||
* @param {Object} obj1 any object. | ||
* @param {Object} obj2 any object. | ||
* @return {Object} target merged object. | ||
*/ | ||
exports.extend = (function() { | ||
var toString = Object.prototype.toString, | ||
obj = '[object Object]'; | ||
|
||
return function extend(deep, obj1, obj2 /*, obj1, obj2, obj3 */) { | ||
// 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; | ||
}; | ||
}()); | ||
|
||
/** | ||
* Freeze the object recursively. | ||
* | ||
* @param {Object} obj. | ||
* @return {Object} | ||
*/ | ||
exports.freeze = function 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. | ||
* @param {Object|Boolean} [options] if true, extend all jsons to the first one, | ||
* it can be also object {merge: true, replace: {key: 'value'}} | ||
* @return {Object} conf parsed json object. | ||
*/ | ||
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) { | ||
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'); | ||
|
||
if (options.replace) { | ||
data = exports.replace(data, options.replace); | ||
} | ||
|
||
try { | ||
data = exports.parse(data); | ||
} catch(e) { | ||
throw new Error(e.message + '"\nFile: "' + path + '"\n'); | ||
} | ||
|
||
if (options.freeze) { | ||
exports.freeze(data); | ||
} | ||
|
||
return data; | ||
}; |
Oops, something went wrong.