diff --git a/.gitignore b/.gitignore index e920c16..3b60b19 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,7 @@ node_modules # Optional REPL history .node_repl_history + +# npm manifestation of the manifest +# https://docs.npmjs.com/files/package-lock.json +package-lock.json diff --git a/config.js b/config.js index 417a8d5..1697340 100644 --- a/config.js +++ b/config.js @@ -54,8 +54,12 @@ Config.prototype.getDir = function (name, opts, done) { }; function merge_config (defaults, overrides, type) { - if (type === 'ini' || type === 'json' || type === 'yaml') { - return merge_struct(JSON.parse(JSON.stringify(defaults)), overrides); + switch (type) { + case 'ini': + case 'hjson': + case 'json': + case 'yaml': + return merge_struct(JSON.parse(JSON.stringify(defaults)), overrides); } if (Array.isArray(overrides) && Array.isArray(defaults) && @@ -113,7 +117,7 @@ Config.prototype.arrange_args = function (args) { options = args[i]; break; case 'string': - if (/^(ini|value|list|data|json|yaml|binary)$/.test(args[i])) { + if (/^(ini|value|list|data|h?json|yaml|binary)$/.test(args[i])) { fs_type = args[i]; break; } @@ -125,7 +129,8 @@ Config.prototype.arrange_args = function (args) { } if (!fs_type) { - if (/\.json$/.test(fs_name)) fs_type = 'json'; + if (/\.hjson$/.test(fs_name)) fs_type = 'hjson'; + else if (/\.json$/.test(fs_name)) fs_type = 'json'; else if (/\.yaml$/.test(fs_name)) fs_type = 'yaml'; else if (/\.ini$/.test(fs_name)) fs_type = 'ini'; else fs_type = 'value'; diff --git a/configfile.js b/configfile.js index 05836a7..0f17275 100644 --- a/configfile.js +++ b/configfile.js @@ -337,18 +337,18 @@ cfreader.load_config = function (name, type, options) { let cfrType = cfreader.get_filetype_reader(type); if (!fs.existsSync(name)) { - - if (!/\.json$/.test(name)) { + if (!/\.h?json$/.test(name)) { return cfrType.empty(options, type); } - const yaml_name = name.replace(/\.json$/, '.yaml'); + const yaml_name = name.replace(/\.h?json$/, '.yaml'); if (!fs.existsSync(yaml_name)) { return cfrType.empty(options, type); } name = yaml_name; type = 'yaml'; + cfrType = cfreader.get_filetype_reader(type); } @@ -358,6 +358,7 @@ cfreader.load_config = function (name, type, options) { case 'ini': result = cfrType.load(name, options, regex); break; + case 'hjson': case 'json': case 'yaml': result = cfrType.load(name); diff --git a/package.json b/package.json index 5c4b5f9..434075b 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "dependencies": { "js-yaml": "^3.5.3" }, - "optionalDependencies": {}, + "optionalDependencies": { + "hjson": "^3.1.0" + }, "devDependencies": { "eslint": "*", "eslint-plugin-haraka": "*", diff --git a/readers/hjson.js b/readers/hjson.js new file mode 100644 index 0000000..f638781 --- /dev/null +++ b/readers/hjson.js @@ -0,0 +1,12 @@ +'use strict'; + +const fs = require('fs'); +const hjson = require('hjson'); + +exports.load = function (name) { + return hjson.parse(fs.readFileSync(name, "utf8")); +}; + +exports.empty = function () { + return {}; +}; diff --git a/test/config.js b/test/config.js index e69ce3b..dc470ad 100644 --- a/test/config.js +++ b/test/config.js @@ -157,6 +157,14 @@ exports.arrange_args = { test.done(); }, // config.get('name', type, cb, options); + 'name, hjson type, callback, options' : function (test) { + test.expect(1); + test.deepEqual( + this.config.arrange_args(['test.ini','hjson',cb, opts]), + ['test.ini', 'hjson', cb, opts]); + test.done(); + }, + // config.get('name', type, cb, options); 'name, json type, callback, options' : function (test) { test.expect(1); test.deepEqual( @@ -174,6 +182,15 @@ exports.arrange_args = { }, }; +const hjsonRes = { + matt: 'waz here and also made comments', + differentArray: [ 'has element #1', 'has element #2' ], + object: { + 'has a property one': 'with a value A', + 'has a property two': 'with a value B' + } +}; + const jsonRes = { matt: 'waz here', array: [ 'has an element' ], @@ -296,6 +313,15 @@ exports.get = { ['line1', 'line2','line3', '', 'line5'] ); }, + // config.get('test.hjson'); + 'test.hjson, type=' : function (test) { + _test_get(test, 'test.hjson', null, null, null, hjsonRes); + }, + // config.get('test.hjson', 'hjson'); + 'test.hjson, type=hjson' : function (test) { + _test_get(test, 'test.hjson', 'hjson', null, null, hjsonRes); + }, + // config.get('test.json'); 'test.json, type=' : function (test) { _test_get(test, 'test.json', null, null, null, jsonRes); @@ -313,6 +339,10 @@ exports.get = { 'test.yaml, type=yaml' : function (test) { _test_get(test, 'test.yaml', 'yaml', null, null, yamlRes); }, + // config.get('missing2.hjson'); + 'missing2.yaml, asked for hjson' : function (test) { + _test_get(test, 'missing2.hjson', 'hjson', null, null, {"matt": "waz here - hjson type"}); + }, // config.get('missing.json'); 'missing.yaml, asked for json' : function (test) { _test_get(test, 'missing.json', 'json', null, null, {"matt": "waz here"}); @@ -438,6 +468,30 @@ exports.getDir = { } } +exports.hjsonOverrides = { + 'setUp' : setUp, + 'no override for smtpgreeting': function (test) { + test.expect(1); + // console.log(this.config); + test.deepEqual( + this.config.get('smtpgreeting', 'list'), + [] + ); + test.done(); + }, + 'with smtpgreeting override': function (test) { + test.expect(1); + process.env.WITHOUT_CONFIG_CACHE=''; + const main = this.config.get('main.hjson'); + console.log(main); + test.deepEqual( + this.config.get('smtpgreeting', 'list'), + [ 'this is line one for hjson', 'this is line two for hjson' ] + ); + test.done(); + } +} + exports.jsonOverrides = { 'setUp' : setUp, 'no override for smtpgreeting': function (test) { @@ -460,4 +514,4 @@ exports.jsonOverrides = { ); test.done(); } -} \ No newline at end of file +} diff --git a/test/config/main.hjson b/test/config/main.hjson new file mode 100644 index 0000000..02a9221 --- /dev/null +++ b/test/config/main.hjson @@ -0,0 +1,11 @@ +{ + # specify smtp greeting (because comments are helpful!) + "!smtpgreeting": + [ + # yes, commas are optional! + "this is line one for hjson" + "this is line two for hjson" + ] + // prefer c-style comments? + /* feeling old fashioned? */ +} diff --git a/test/config/missing2.yaml b/test/config/missing2.yaml new file mode 100644 index 0000000..a088f48 --- /dev/null +++ b/test/config/missing2.yaml @@ -0,0 +1,3 @@ +--- +matt: "waz here - hjson type" +... diff --git a/test/config/override2.yaml b/test/config/override2.yaml new file mode 100644 index 0000000..b8be68b --- /dev/null +++ b/test/config/override2.yaml @@ -0,0 +1,2 @@ +hasDifferent: + value: false diff --git a/test/config/test.hjson b/test/config/test.hjson new file mode 100644 index 0000000..b123867 --- /dev/null +++ b/test/config/test.hjson @@ -0,0 +1,19 @@ +{ + // sign matt (because comments are helpful!) + "matt": "waz here and also made comments", + + // fill in this array + "differentArray": [ + "has element #1", + 'has element #2' + ] + # best of all + # yes, commas are optional! + + /* feeling old fashioned? */ + "object": { + // prefer c-style comments? + "has a property one": 'with a value A', + 'has a property two': with a value B + } +} diff --git a/test/configfile.js b/test/configfile.js index 4ddda4b..cb9afbd 100644 --- a/test/configfile.js +++ b/test/configfile.js @@ -174,6 +174,13 @@ exports.get_filetype_reader = { test.equal(typeof reader.empty, 'function'); test.done(); }, + 'hjson': function (test) { + test.expect(2); + const reader = this.cfreader.get_filetype_reader('hjson'); + test.equal(typeof reader.load, 'function'); + test.equal(typeof reader.empty, 'function'); + test.done(); + }, 'json': function (test) { test.expect(2); const reader = this.cfreader.get_filetype_reader('json'); @@ -221,6 +228,14 @@ exports.get_filetype_reader = { exports.non_existing = { setUp: _setUp, + 'empty object for HJSON files': function (test) { + test.expect(1); + const result = this.cfreader.load_config( + 'test/config/non-existent.hjson' + ); + test.deepEqual(result, {}); + test.done(); + }, 'empty object for JSON files': function (test) { test.expect(1); const result = this.cfreader.load_config( @@ -388,6 +403,13 @@ exports.bad_config = { exports.overrides = { setUp: _setUp, + 'missing hjson loads yaml instead' : function (test) { + test.expect(1); + test.deepEqual( + this.cfreader.load_config('test/config/override2.hjson'), + { hasDifferent: { value: false } }); + test.done(); + }, 'missing json loads yaml instead' : function (test) { test.expect(1); test.deepEqual( @@ -423,4 +445,4 @@ exports.get_path_to_config_dir = { test.ok(/haraka-config$/.test(this.cfreader.config_path), this.cfreader.config_path); test.done(); }, -} \ No newline at end of file +} diff --git a/test/readers/hjson.js b/test/readers/hjson.js new file mode 100644 index 0000000..66ba1cb --- /dev/null +++ b/test/readers/hjson.js @@ -0,0 +1,30 @@ +'use strict'; + +const _set_up = function (done) { + this.hjson = require('../../readers/hjson'); + done(); +}; + +exports.load = { + setUp : _set_up, + 'module is required' : function (test) { + test.expect(1); + test.ok(this.hjson); + test.done(); + }, + 'has a load function': function (test) { + test.expect(1); + test.ok(typeof this.hjson.load === 'function'); + test.done(); + }, + 'loads the test HJSON file': function (test) { + test.expect(4); + const result = this.hjson.load('test/config/test.hjson'); + // console.log(result); + test.equal(result.matt, 'waz here and also made comments'); + test.ok(result.differentArray.length); + test.ok(result.object['has a property one']); + test.ok(result.object['has a property two']); + test.done(); + }, +};