Skip to content

Commit

Permalink
Introduce options to align the values based upon the longest key, and…
Browse files Browse the repository at this point in the history
… to sort the entries.
  • Loading branch information
rquadling committed Apr 13, 2023
1 parent ad4b5d8 commit a9c99c6
Show file tree
Hide file tree
Showing 5 changed files with 431 additions and 6 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,25 @@ prepended to all sub-sections, see the usage example above.

The `options` object may contain the following:

* `section` A string which will be the first `section` in the encoded
* `align` Boolean to specify whether to align the `=` characters for
each section. This option will automatically enable `whitespace`.
Defaults to `false`.
* `section` String which will be the first `section` in the encoded
ini data. Defaults to none.
* `sort` Boolean to specify if all keys in each section, as well as
all sections, will be alphabetically sorted. Defaults to `false`.
* `whitespace` Boolean to specify whether to put whitespace around the
`=` 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.
Defaults to `false`.
* `newline` Boolean to specify whether to put an additional newline
after a section header. Some INI file parsers (for example the TOSHIBA
FlashAir one) need this to parse the file successfully. By default,
the additional newline is omitted.
* `platform` String to define which platform this INI file is expected
to be used with: when `platform` is `win32`, line terminations are
CR+LF, for other platforms line termination is LF. By default the
CR+LF, for other platforms line termination is LF. By default, the
current platform name is used.

For backwards compatibility reasons, if a `string` options is passed
Expand Down
27 changes: 23 additions & 4 deletions lib/ini.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,46 @@ const encode = (obj, opt = {}) => {
if (typeof opt === 'string') {
opt = { section: opt }
}
opt.whitespace = opt.whitespace === true
opt.align = opt.align === true
opt.newline = opt.newline === true
opt.sort = opt.sort === true
opt.whitespace = opt.whitespace === true || opt.align === true
/* istanbul ignore next */
opt.platform = opt.platform || process?.platform

/* istanbul ignore next */
const eol = opt.platform === 'win32' ? '\r\n' : '\n'
const separator = opt.whitespace ? ' = ' : '='
const children = []

const sort = opt.sort
const keys = sort ? Object.keys(obj).sort() : Object.keys(obj)

// padToChars is determined as follows:
// 1. Get the keys
// 2. Exclude keys pointing to objects unless the value is null or an array
// 3. Add `[]` to array keys
// 4. Ensure non empty set of keys
// 5. Reduce the set to the longest key
// 6. Get the length
const padToChars = opt.align
? (keys.filter(k => obj[k] === null || Array.isArray(obj[k]) || typeof obj[k] !== 'object')
.map(k => Array.isArray(obj[k]) ? `${k}[]` : k)
).concat(['']).reduce((a, b) => a.length >= b.length ? a : b).length
: 0

let out = ''

for (const k of Object.keys(obj)) {
for (const k of keys) {
const val = obj[k]
if (val && Array.isArray(val)) {
for (const item of val) {
out += safe(k + '[]') + separator + safe(item) + eol
out += safe(k + '[]').padEnd(padToChars, ' ') + separator + safe(item) + eol
}
} else if (val && typeof val === 'object') {
children.push(k)
} else {
out += safe(k) + separator + safe(val) + eol
out += safe(k).padEnd(padToChars, ' ') + separator + safe(val) + eol
}
}

Expand Down
246 changes: 246 additions & 0 deletions tap-snapshots/test/foo.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,88 @@ noHashComment=this\\# this is not a comment
`

exports[`test/foo.js TAP encode with align > must match snapshot 1`] = `
[number]
count = 10
[nested number.number]
count = 10
[string]
drink = white russian
[nested string.string]
drink = white russian
[boolean]
isTrue = true
[nested boolean.boolean]
isTrue = true
[traditionalFamily]
parents[] = Mother
parents[] = Father
siblings[] = Brother
siblings[] = Sister
greats[] = Aunts
greats[] = Uncles
grands[] = Mother
grands[] = Father
longest_key = here
[nested array.traditionalFamily]
parents[] = Mother
parents[] = Father
siblings[] = Brother
siblings[] = Sister
greats[] = Aunts
greats[] = Uncles
grands[] = Mother
grands[] = Father
longest_key = here
[nulls]
null1 = null
null2 = null
[nested nulls.nulls]
null1 = null
null2 = null
[mixture]
count = 10
drink = white russian
ordinals[] = first
ordinals[] = second
[mixture.boolean]
isTrue = true
[mixture.theDude]
abides = true
rugCount = 1
[mixture.traditionalFamily]
parents[] = Mother
parents[] = Father
siblings[] = Brother
siblings[] = Sister
greats[] = Aunts
greats[] = Uncles
grands[] = Mother
grands[] = Father
longest_key = here
[mixture.nulls]
null1 = null
null2 = null
[mixture.heavily.nested.object.that.should.be.all.dot]
joined = true
`

exports[`test/foo.js TAP encode with newline > must match snapshot 1`] = `
[log]
Expand Down Expand Up @@ -145,6 +227,170 @@ Array [
]
`

exports[`test/foo.js TAP encode with sort > must match snapshot 1`] = `
[boolean]
isTrue=true
[mixture]
count=10
drink=white russian
ordinals[]=first
ordinals[]=second
[mixture.boolean]
isTrue=true
[mixture.heavily.nested.object.that.should.be.all.dot]
joined=true
[mixture.nulls]
null1=null
null2=null
[mixture.theDude]
abides=true
rugCount=1
[mixture.traditionalFamily]
grands[]=Mother
grands[]=Father
greats[]=Aunts
greats[]=Uncles
longest_key=here
parents[]=Mother
parents[]=Father
siblings[]=Brother
siblings[]=Sister
[nested array.traditionalFamily]
grands[]=Mother
grands[]=Father
greats[]=Aunts
greats[]=Uncles
longest_key=here
parents[]=Mother
parents[]=Father
siblings[]=Brother
siblings[]=Sister
[nested boolean.boolean]
isTrue=true
[nested nulls.nulls]
null1=null
null2=null
[nested number.number]
count=10
[nested string.string]
drink=white russian
[nulls]
null1=null
null2=null
[number]
count=10
[string]
drink=white russian
[traditionalFamily]
grands[]=Mother
grands[]=Father
greats[]=Aunts
greats[]=Uncles
longest_key=here
parents[]=Mother
parents[]=Father
siblings[]=Brother
siblings[]=Sister
`

exports[`test/foo.js TAP encode with sort and align > must match snapshot 1`] = `
[boolean]
isTrue = true
[mixture]
count = 10
drink = white russian
ordinals[] = first
ordinals[] = second
[mixture.boolean]
isTrue = true
[mixture.heavily.nested.object.that.should.be.all.dot]
joined = true
[mixture.nulls]
null1 = null
null2 = null
[mixture.theDude]
abides = true
rugCount = 1
[mixture.traditionalFamily]
grands[] = Mother
grands[] = Father
greats[] = Aunts
greats[] = Uncles
longest_key = here
parents[] = Mother
parents[] = Father
siblings[] = Brother
siblings[] = Sister
[nested array.traditionalFamily]
grands[] = Mother
grands[] = Father
greats[] = Aunts
greats[] = Uncles
longest_key = here
parents[] = Mother
parents[] = Father
siblings[] = Brother
siblings[] = Sister
[nested boolean.boolean]
isTrue = true
[nested nulls.nulls]
null1 = null
null2 = null
[nested number.number]
count = 10
[nested string.string]
drink = white russian
[nulls]
null1 = null
null2 = null
[number]
count = 10
[string]
drink = white russian
[traditionalFamily]
grands[] = Mother
grands[] = Father
greats[] = Aunts
greats[] = Uncles
longest_key = here
parents[] = Mother
parents[] = Father
siblings[] = Brother
siblings[] = Sister
`

exports[`test/foo.js TAP encode with whitespace > must match snapshot 1`] = `
[log]
type = file
Expand Down

0 comments on commit a9c99c6

Please sign in to comment.