Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
add option to set custom section delimiter
fixes #60
  • Loading branch information
ext committed Apr 18, 2020
commit 7a033ad8aaa1a3f353b0fa26ec83314f9927c077
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': {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong.

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()
})