pax_global_header00006660000000000000000000000064126740527610014524gustar00rootroot0000000000000052 comment=573078a4872435a08162fb5a531d52c3b26e75c7 node-cjson-master/000077500000000000000000000000001267405276100144205ustar00rootroot00000000000000node-cjson-master/.gitignore000066400000000000000000000000231267405276100164030ustar00rootroot00000000000000node_modules *.log node-cjson-master/.travis.yml000066400000000000000000000000521267405276100165260ustar00rootroot00000000000000language: node_js node_js: - 0.6 - 0.8node-cjson-master/LICENSE000066400000000000000000000020671267405276100154320ustar00rootroot00000000000000Copyright (c) 2015 Oleg Slobodskoi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. node-cjson-master/Makefile000066400000000000000000000000511267405276100160540ustar00rootroot00000000000000test: node ./test/test.js .PHONY: test node-cjson-master/index.js000066400000000000000000000146441267405276100160760ustar00rootroot00000000000000var fs = require('fs'), Path = require('path'), jph = require('json-parse-helpfulerror'); /** * 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 you want. the default uses JSON.parse for maximum // speed, if it throws it uses uses an alternative parser to give more // helpful errors parse: jph.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) { 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; }); }; /** * 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'); // 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; }; node-cjson-master/package.json000066400000000000000000000011301267405276100167010ustar00rootroot00000000000000{ "name": "cjson", "description": "cjson - Commented JavaScript Object Notation. It is a json loader, which parses only valid json files, but with comments enabled. Useful for loading configs.", "version": "0.4.0", "repository": "git://github.com/kof/node-cjson.git", "keywords": [ "json", "parser", "comments", "config", "loader" ], "author": "Oleg Slobodskoi ", "engines": { "node": ">= 0.3.0" }, "dependencies": { "json-parse-helpfulerror": "^1.0.3" }, "scripts": { "test": "node ./test/test.js" }, "license": "MIT" } node-cjson-master/readme.md000066400000000000000000000076551267405276100162140ustar00rootroot00000000000000[![build status](https://secure.travis-ci.org/kof/node-cjson.png)](http://travis-ci.org/kof/node-cjson) ## CJSON (Commented JavaScript Object Notation) is a comments enabled json config loader. JSON has a good spec, is implemented in every language, has easy to read syntax and is much more powerful than ini files. JSON is perfect for writing config files, except of one problem - there is no comments, but sometimes config files get large and need to be commented. Well, you could just evaluate json file as a JavaScript using one-liner, right? The purpose of this module is to avoid dirty JavaScript configs and to enable clean, consistent, secure, portable and JSON valid notation. CJSON supports JavaScript style comments: singleline "//" and multiline "/**/". It takes care about comments inside of strings. Example of such shiny config file: ```javascript /* * This is my app configuration file. * */ { "host": "localhost", // app is listening on this port "port": 8888 } ``` ## 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. `options` defaults: ```javascript { // 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 you want. the default uses JSON.parse for maximum // speed, if it throws it uses uses an alternative parser to give more // helpful errors parse: jph.parse } ``` Examples: ```javascript // just one config var conf = cjson.load('/path/to/your/config.json'); // array of configs var conf = cjson.load(['/path/to/your/config1.json', '/path/to/your/config2.json']); //output { config1: {key1: 'value1'} config2: {key2: 'value2'} } // use optional merge parameter // array of configs var conf = cjson.load(['/path/to/your/config1.json', '/path/to/your/config2.json'], true); // output { key1: 'value1', key2: 'value2' } // load all config files from a directory var conf = cjson.load('/path/to/your/configs'); // overwriting dev config with production var paths = ['/path/to/conf.json']; if (process.env.NODE_ENV ==='production') { paths.push('/path/to/conf-prod.json'); } var conf = cjson.load(paths, true); ``` ### cjson.extend([deep], target, object1, [objectN]) Merge the contents of two or more objects together into the first object. - `deep` If true, the merge becomes recursive. - `target` The object to extend. It will receive the new properties. - `object1` An object containing additional properties to merge in. - `objectN` Additional objects containing properties to merge in. Example: ```javascript var object = cjson.extend({}, object1, object2); ``` ### cjson.decomment(str) Remove JavaScript style comments, singleline - '//' and multiline - '/**/'. It takes care about comments inside of strings and escaping. ### cjson.parse(str, [reviver]) Like `JSON.parse`, but it takes care about comments. Optional `reviver` argument is for `JSON.parse` method and will be called for every key and value at every level of the final result ### cjson.replace(str, obj) Replace all strings `{{key}}` contained in `{key: 'value'}`, where `key` can be any property of passed `obj`. Example: ```javascript var str = '{"path": "{{root}}/src"}'; // json file contents cjson.replace(str, {root: '/usr'}); // '{"path": "/usr/src"}' ``` ### cjson.freeze(obj) Recursively freeze an object. ## Installation npm install cjson node-cjson-master/test/000077500000000000000000000000001267405276100153775ustar00rootroot00000000000000node-cjson-master/test/fixtures/000077500000000000000000000000001267405276100172505ustar00rootroot00000000000000node-cjson-master/test/fixtures/conf1.json000066400000000000000000000000271267405276100211500ustar00rootroot00000000000000{ "key": "value" } node-cjson-master/test/fixtures/conf10.json000066400000000000000000000001631267405276100212310ustar00rootroot00000000000000{ // First character of this file is BOM "test": "valid JSON, except for the the hidden BOM character" }node-cjson-master/test/fixtures/conf11.json000066400000000000000000000001571267405276100212350ustar00rootroot00000000000000{ "subobject": { "test": 5 }, "subarray": [ { "foo": 7 } ] }node-cjson-master/test/fixtures/conf12.json000066400000000000000000000001451267405276100212330ustar00rootroot00000000000000{ "num": 1, "str": "moo", "bool": false, "arr": [], "obj": {}, "null": null }node-cjson-master/test/fixtures/conf2.json000066400000000000000000000000621267405276100211500ustar00rootroot00000000000000{ // single line comment "key": "value" } node-cjson-master/test/fixtures/conf3.json000066400000000000000000000001311267405276100211460ustar00rootroot00000000000000{ /* multiline comment */ "key": "value" /* multiline comment */ } node-cjson-master/test/fixtures/conf4.json000066400000000000000000000001361267405276100211540ustar00rootroot00000000000000{ "//key" : "value", "key": "//value", "/*key": "value", "key": "/*value*/" } node-cjson-master/test/fixtures/conf5.json000066400000000000000000000000501267405276100211500ustar00rootroot00000000000000{ "'key/*test*/'": "'value//test'" }node-cjson-master/test/fixtures/conf6.json000066400000000000000000000000501267405276100211510ustar00rootroot00000000000000{ "key\"/*test*/": "value\"//test" }node-cjson-master/test/fixtures/conf7.json000066400000000000000000000000361267405276100211560ustar00rootroot00000000000000{ "key": "{{root}}/src" } node-cjson-master/test/fixtures/conf8.json000066400000000000000000000000221267405276100211520ustar00rootroot00000000000000{ // "foo": 1 } node-cjson-master/test/fixtures/conf9.cjson000066400000000000000000000000171267405276100213220ustar00rootroot00000000000000{ "a": 1 } node-cjson-master/test/fixtures/errors/000077500000000000000000000000001267405276100205645ustar00rootroot00000000000000node-cjson-master/test/fixtures/errors/invalid.cjson000066400000000000000000000000241267405276100232440ustar00rootroot00000000000000{ "foo": "bar } node-cjson-master/test/fixtures/templates/000077500000000000000000000000001267405276100212465ustar00rootroot00000000000000node-cjson-master/test/fixtures/templates/conf11tmpl.json000066400000000000000000000001001267405276100241140ustar00rootroot00000000000000{ "subobject": {{subobject}}, "subarray": {{subarray}} }node-cjson-master/test/fixtures/templates/conf12tmpl.json000066400000000000000000000002001267405276100241160ustar00rootroot00000000000000{ "num": {{num}}, "str": "{{str}}", "bool": {{bool}}, "arr": {{arr}}, "obj": {{obj}}, "null": {{null}} }node-cjson-master/test/test.js000066400000000000000000000107771267405276100167300ustar00rootroot00000000000000var a = require('assert'); var cjson = require('../'); var fixtures = __dirname + '/fixtures'; var data = { conf1: {key: 'value'}, conf2: {key: 'value'}, conf3: {key: 'value'}, conf4: { "//key" : "value", "key": "//value", "/*key": "value", "key": "/*value*/" }, conf5: {"'key/*test*/'": "'value//test'"}, conf6: {"key\"/*test*/": "value\"//test"}, conf7: {"key": "{{root}}/src"}, conf8: {}, conf10: {"test":"valid JSON, except for the the hidden BOM character"}, conf11: { "subobject": { "test": 5 }, "subarray": [ { "foo": 7 } ] }, conf12: { "num": 1, "str": "moo", "bool": false, "arr": [], "obj": {}, "null": null } }; a.doesNotThrow(function() { cjson.load(fixtures + '/conf1.json'); }, 'valid config loaded'); a.deepEqual(cjson.load(fixtures + '/conf1.json'), data.conf1, 'data is correct'); a.deepEqual(cjson.load(fixtures + '/conf2.json'), data.conf2, 'singleline comment'); a.deepEqual(cjson.load(fixtures + '/conf3.json'), data.conf3, 'multiline comment'); a.deepEqual(cjson.load(fixtures + '/conf4.json'), data.conf4, 'comments inside of a string'); a.deepEqual(cjson.load(fixtures + '/conf5.json'), data.conf5, 'single and double quotes mix'); a.deepEqual(cjson.load(fixtures + '/conf6.json'), data.conf6, 'escaped double quote inside of a string'); a.deepEqual(cjson.load(fixtures + '/conf7.json', {replace: {root: '/usr'}}), {"key": "/usr/src"}, 'tmpl replacement'); a.deepEqual(cjson.load(fixtures + '/conf8.json'), data.conf8, 'string-like comment'); a.deepEqual(cjson.load(fixtures + '/conf10.json'), data.conf10, 'BOM character'); var conf11 = cjson.load(fixtures + '/conf11.json'); a.deepEqual(cjson.load(fixtures + '/templates/conf11tmpl.json', {replace: conf11}), data.conf11, 'object and array as template variables'); var conf12 = cjson.load(fixtures + '/conf12.json'); a.deepEqual(cjson.load(fixtures + '/templates/conf12tmpl.json', {replace: conf12}), data.conf12, 'JSON types as template variables'); var data1 = { conf1: {key: 'value'}, conf6: data.conf6 }; a.deepEqual(cjson.load([fixtures + '/conf1.json', fixtures + '/conf6.json']), data1, 'load array of jsons'); var data2 = { key: 'value', "key\"/*test*/": "value\"//test" }; a.deepEqual(cjson.load([fixtures + '/conf1.json', fixtures + '/conf6.json'], true), data2, 'load array of jsons and merge them'); a.deepEqual(cjson.load(fixtures), data, 'load all and merge them'); a.deepEqual(cjson.load(fixtures, {ext: '.cjson'}), {conf9: {a: 1}}, 'use custom ext'); var str = require('fs').readFileSync(fixtures + '/conf2.json').toString(); a.deepEqual(cjson.parse(str), data.conf2, '.parse method with comments'); (function errors() { try { cjson.load(fixtures + '/errors/invalid.cjson'); } catch (e) { // Taking the first line from the error message var message = e.message.split('\n')[0]; // The error we expect is that the value ended with \n instead of quotes a.equal( message.trim().toLowerCase(), "unexpected token '}' at 2:17", 'Assert that the error message is properly formatted.' ); } }()); (function extend() { a.deepEqual(cjson.extend({test1: 1}, {test2: 2}), {test1: 1, test2: 2}, 'extend 2 simple objects'); a.deepEqual(cjson.extend({test1: 1}, {test2: 2}, {test3: 3}), {test1: 1, test2: 2, test3: 3}, 'extend 3 simple objects'); a.deepEqual(cjson.extend({test1: 1}, true), {test1: 1}, '2 arg is not an object'); a.deepEqual(cjson.extend( true, {test1: {test1: 1}}, {test1: {test2: 2} } ), { test1: {test1: 1, test2: 2} }, 'deep extend' ); a.deepEqual(cjson.extend( true, {test: {test: 'test'}}, {test: {test: 'test'} } ), {test: {test: 'test'} }, 'deep extend, check endless lop' ); var data1 = {a: {b: 1}}, data2 = {a: {b: 2}}; cjson.extend(true, {}, data1, data2); a.notDeepEqual(data1, data2, 'original deep object is not mangled'); }()); (function freeze() { var data1 = {a: {b: 1}}, data2 = {a: {b: 1}}; cjson.freeze(data1); data1.abc = 123; data1.a = 123; a.deepEqual(data1, data2, 'data1 wasn\'t changed'); data1 = cjson.load(fixtures + '/conf1.json', {freeze: true}), data2 = cjson.load(fixtures + '/conf1.json', {freeze: true}); data1.abc = 123; data1.a = 123; a.deepEqual(data1, data2, 'data1 wasn\'t changed'); }()); console.log('All tests passed.');