diff --git a/README.md b/README.md index 33df258..9dab00b 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ The `options` object may contain the following: `=` character. By default, whitespace is omitted, to be friendly to some persnickety old parsers that don't tolerate it well. But some find that it's more human-readable and pretty with the whitespace. +* `withoutArraySuffix` Boolean to specify whether to insert `[]` as array properties. default is `false`. For backwards compatibility reasons, if a `string` options is passed in, then it is assumed to be the `section` value. diff --git a/ini.js b/ini.js index 590195d..334dfc1 100644 --- a/ini.js +++ b/ini.js @@ -23,12 +23,13 @@ function encode (obj, opt) { } var separator = opt.whitespace ? ' = ' : '=' + var arraySuffix = opt.withoutArraySuffix ? '' : '[]' Object.keys(obj).forEach(function (k, _, __) { var val = obj[k] if (val && Array.isArray(val)) { val.forEach(function (item) { - out += safe(k + '[]') + separator + safe(item) + '\n' + out += safe(k + arraySuffix) + separator + safe(item) + '\n' }) } else if (val && typeof val === 'object') { children.push(k) @@ -73,6 +74,11 @@ function decode (str) { // section |key = value var re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i var lines = str.split(/[\r\n]+/g) + // for duplicate property statist + var bucket = { + 'root': {} + } + var curSection = 'root' lines.forEach(function (line, _, __) { if (!line || line.match(/^\s*[;#]/)) return @@ -81,10 +87,19 @@ function decode (str) { if (match[1] !== undefined) { section = unsafe(match[1]) p = out[section] = out[section] || {} + bucket[section] = bucket[section] || {} + curSection = section return } var key = unsafe(match[2]) var value = match[3] ? unsafe(match[4]) : true + + var _keyWithoutArraySuffix = key.split('[]')[0] + var curBucket = bucket[curSection] + curBucket[_keyWithoutArraySuffix] = curBucket[_keyWithoutArraySuffix] ? ++curBucket[_keyWithoutArraySuffix] : 1 + + if (curBucket[_keyWithoutArraySuffix] > 1) key = _keyWithoutArraySuffix + '[]' + switch (value) { case 'true': case 'false': diff --git a/test/duplicate-properties.js b/test/duplicate-properties.js new file mode 100644 index 0000000..e7a6c7e --- /dev/null +++ b/test/duplicate-properties.js @@ -0,0 +1,59 @@ +/* + * @Author: John Trump + * @Date: 2020-11-28 15:26:01 + * @LastEditors: John Trump + * @LastEditTime: 2020-11-28 15:52:26 + */ +var ini = require("../ini"); +var tap = require("tap"); +var test = tap.test; + +test("decode with duplicate properties", function(t) { + var d = ini.decode(` + zr[] = deedee; + zr=123; + ar[] = one + ar[] = three; + str = 3; + brr = 1; + brr = 2; + brr = 3; + brr = 3;`); + t.deepEqual(d, { + zr: ["deedee", "123"], + ar: ["one", "three"], + str: "3", + brr: ["1", "2", "3", "3"], + }); + t.end(); +}); + +test("encode with duplicate properties", function(t) { + var d = ini.encode({ + ar: ["1", "2", "3"], + br: ["1", "2"], + }); + var excepted = 'ar[]=1\n' + + 'ar[]=2\n' + + 'ar[]=3\n' + + 'br[]=1\n' + + 'br[]=2\n' + t.deepEqual(d, excepted) + t.end(); +}); + +test("encode with option with duplicate properties", function(t) { + var d = ini.encode({ + ar: ["1", "2", "3"], + br: ["1", "2"], + }, { + withoutArraySuffix: true + }); + var excepted = 'ar=1\n' + + 'ar=2\n' + + 'ar=3\n' + + 'br=1\n' + + 'br=2\n' + t.deepEqual(d, excepted) + t.end(); +}); \ No newline at end of file diff --git a/test/fixtures/foo.ini b/test/fixtures/foo.ini index fc2080f..2745e05 100644 --- a/test/fixtures/foo.ini +++ b/test/fixtures/foo.ini @@ -25,7 +25,7 @@ ar[] = three ; This should be included in the array ar = this is included -; Test resetting of a value (and not turn it into an array) +; Test resetting of a value (turn it into an array) br = cold br = warm @@ -63,3 +63,10 @@ nocomment = this\; this is not a comment # this next one is not a comment! it's escaped! noHashComment = this\# this is not a comment + +[duplicate] +ar = 1 +ar = 2 +ar[] = 3 +br = 1; +br = 2; \ No newline at end of file diff --git a/test/foo.js b/test/foo.js index 58102d1..742ec57 100644 --- a/test/foo.js +++ b/test/foo.js @@ -17,7 +17,8 @@ var i = require("../") + 'ar[]=one\n' + 'ar[]=three\n' + 'ar[]=this is included\n' - + 'br=warm\n' + + 'br[]=cold\n' + + 'br[]=warm\n' + 'eq=\"eq=eq\"\n' + '\n' + '[a]\n' @@ -33,6 +34,13 @@ var i = require("../") + '[x\\.y\\.z.a\\.b\\.c]\na.b.c=abc\n' + 'nocomment=this\\; this is not a comment\n' + 'noHashComment=this\\# this is not a comment\n' + + '\n' + + '[duplicate]\n' + + 'ar[]=1\n' + + 'ar[]=2\n' + + 'ar[]=3\n' + + 'br[]=1\n' + + 'br[]=2\n' , expectD = { o: 'p', 'a with spaces': 'b c', @@ -43,7 +51,7 @@ var i = require("../") 's2': 'something else', 'zr': ['deedee'], 'ar': ['one', 'three', 'this is included'], - 'br': 'warm', + 'br': ['cold', 'warm'], 'eq': 'eq=eq', a: { av: 'a val', @@ -59,6 +67,10 @@ var i = require("../") 'nocomment': 'this\; this is not a comment', noHashComment: 'this\# this is not a comment' } + }, + duplicate: { + ar: ["1", "2", "3"], + br: ["1", "2"] } } , expectF = '[prefix.log]\n'