Skip to content

Commit

Permalink
add option to set custom section delimiter
Browse files Browse the repository at this point in the history
fixes npm#60
  • Loading branch information
ext committed Apr 18, 2020
1 parent 738eca5 commit 7a033ad
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 11 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,16 @@ to the filesystem with the following content:

## API

### decode(inistring)
### decode(inistring, [options])

Decode the ini-style formatted `inistring` into a nested object.

### parse(inistring)
The `options` object may contain the following:

* `delimiter` Character used when splitting sections into nested objects.
Can be set to `false` to disable splitting. Defaults to `"."`.

### parse(inistring, [options])

Alias for `decode(inistring)`

Expand All @@ -78,6 +83,8 @@ 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.
* `delimiter` Character used when joining nested objects into sections.
Defaults to `"."`.

For backwards compatibility reasons, if a `string` options is passed
in, then it is assumed to be the `section` value.
Expand Down
31 changes: 22 additions & 9 deletions ini.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ exports.stringify = exports.encode = encode
exports.safe = safe
exports.unsafe = unsafe

var DEFAULT_DELIMITER = '.'

var eol = typeof process !== 'undefined' &&
process.platform === 'win32' ? '\r\n' : '\n'

Expand All @@ -15,11 +17,13 @@ function encode (obj, opt) {
if (typeof opt === 'string') {
opt = {
section: opt,
whitespace: false
whitespace: false,
delimiter: DEFAULT_DELIMITER
}
} else {
opt = opt || {}
opt.whitespace = opt.whitespace === true
opt.delimiter = opt.delimiter || DEFAULT_DELIMITER
}

var separator = opt.whitespace ? ' = ' : '='
Expand All @@ -42,11 +46,12 @@ function encode (obj, opt) {
}

children.forEach(function (k, _, __) {
var nk = dotSplit(k).join('\\.')
var section = (opt.section ? opt.section + '.' : '') + nk
var nk = dotSplit(k, opt.delimiter).join('\\' + opt.delimiter)
var section = (opt.section ? opt.section + opt.delimiter : '') + nk
var child = encode(obj[k], {
section: section,
whitespace: opt.whitespace
whitespace: opt.whitespace,
delimiter: opt.delimiter
})
if (out.length && child.length) {
out += eol
Expand All @@ -57,23 +62,31 @@ function encode (obj, opt) {
return out
}

function dotSplit (str) {
function dotSplit (str, delimiter) {
if (delimiter === false) {
return [str]
}
var escapeRegex = new RegExp('\\\\[' + delimiter[0] + ']', 'g')
var splitRegex = new RegExp('[' + delimiter[0] + ']')
return str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002')
.replace(/\\\./g, '\u0001')
.split(/\./).map(function (part) {
.replace(escapeRegex, '\u0001')
.split(splitRegex).map(function (part) {
return part.replace(/\1/g, '\\.')
.replace(/\2LITERAL\\1LITERAL\2/g, '\u0001')
})
}

function decode (str) {
function decode (str, opt) {
var out = {}
var p = out
var section = null
// section |key = value
var re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i
var lines = str.split(/[\r\n]+/g)

opt = opt || {}
opt.delimiter = typeof opt.delimiter === 'undefined' ? DEFAULT_DELIMITER : opt.delimiter

lines.forEach(function (line, _, __) {
if (!line || line.match(/^\s*[;#]/)) return
var match = line.match(re)
Expand Down Expand Up @@ -120,7 +133,7 @@ function decode (str) {
}
// see if the parent section is also an object.
// if so, add it to that, and mark this one for deletion
var parts = dotSplit(k)
var parts = dotSplit(k, opt.delimiter)
var p = out
var l = parts.pop()
var nl = l.replace(/\\\./g, '.')
Expand Down
134 changes: 134 additions & 0 deletions test/delimiter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
var i = require('../')
var tap = require('tap')
var test = tap.test

test('decode with default delimiter', function (t) {
var decoded = i.decode([
'[a.b]',
'foo = 1',
'[c\\.d]',
'foo = 2',
'[a_b]',
'foo = 3'
].join('\n'))
t.deepEqual(decoded, {
a: {
b: {
foo: '1'
}
},
'c.d': {
foo: '2'
},
'a_b': {
foo: '3'
}
})
t.end()
})

test('decode with custom delimiter', function (t) {
var decoded = i.decode([
'[a_b]',
'foo = 1',
'[c\\_d]',
'foo = 2',
'[a.b]',
'foo = 3'
].join('\n'), { delimiter: '_' })
t.deepEqual(decoded, {
a: {
b: {
foo: '1'
}
},
'c.d': {
foo: '2'
},
'a.b': {
foo: '3'
}
})
t.end()
})

test('decode with no delimiter', function (t) {
var decoded = i.decode([
'[a.b]',
'foo = 1',
'[c\\.d]',
'foo = 2'
].join('\n'), { delimiter: false })
t.deepEqual(decoded, {
'a.b': {
foo: '1'
},
'c.d': {
foo: '2'
}
})
t.end()
})

test('encode with default delimiter', function (t) {
var obj = {
a: {
b: {
foo: 'bar'
}
},
'a.b': {
foo: 'bar'
}
}
var encoded = i.encode(obj)
t.equal(encoded, [
'[a.b]',
'foo=bar',
'',
'[a\\.b]',
'foo=bar',
''
].join('\n'))
t.end()
})

test('encode with custom delimiter', function (t) {
var obj = {
a: {
b: {
foo: 'bar'
}
},
'a_b': {
foo: 'bar'
}
}
var encoded = i.encode(obj, { delimiter: '_' })
t.equal(encoded, [
'[a_b]',
'foo=bar',
'',
'[a\\_b]',
'foo=bar',
''
].join('\n'))
t.end()
})

test('encode with no delimiter set should default to dot', function (t) {
var obj = {
a: {
b: {
foo: 'bar'
}
}
}
var encoded = i.encode(obj, { delimiter: false })
t.equal(encoded, [
'[a.b]',
'foo=bar',
''
].join('\n'))
t.end()
})

0 comments on commit 7a033ad

Please sign in to comment.