Comparing version 4.0.0 to 4.1.0
115
lib/ini.js
const { hasOwnProperty } = Object.prototype | ||
/* istanbul ignore next */ | ||
const eol = typeof process !== 'undefined' && | ||
process.platform === 'win32' ? '\r\n' : '\n' | ||
const encode = (obj, opt = {}) => { | ||
if (typeof opt === 'string') { | ||
opt = { section: opt } | ||
} | ||
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 | ||
opt.bracketedArray = opt.bracketedArray !== false | ||
const encode = (obj, opt) => { | ||
/* istanbul ignore next */ | ||
const eol = opt.platform === 'win32' ? '\r\n' : '\n' | ||
const separator = opt.whitespace ? ' = ' : '=' | ||
const children = [] | ||
let out = '' | ||
if (typeof opt === 'string') { | ||
opt = { | ||
section: opt, | ||
whitespace: false, | ||
} | ||
} else { | ||
opt = opt || Object.create(null) | ||
opt.whitespace = opt.whitespace === true | ||
const keys = opt.sort ? Object.keys(obj).sort() : Object.keys(obj) | ||
let padToChars = 0 | ||
// If aligning on the separator, then 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 `safe` key | ||
// 6. Get the `safe` length | ||
if (opt.align) { | ||
padToChars = safe( | ||
( | ||
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) => safe(a).length >= safe(b).length ? a : b) | ||
).length | ||
} | ||
const separator = opt.whitespace ? ' = ' : '=' | ||
let out = '' | ||
const arraySuffix = opt.bracketedArray ? '[]' : '' | ||
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}${arraySuffix}`).padEnd(padToChars, ' ') + separator + safe(item) + eol | ||
} | ||
@@ -32,3 +54,3 @@ } else if (val && typeof val === 'object') { | ||
} else { | ||
out += safe(k) + separator + safe(val) + eol | ||
out += safe(k).padEnd(padToChars, ' ') + separator + safe(val) + eol | ||
} | ||
@@ -38,12 +60,11 @@ } | ||
if (opt.section && out.length) { | ||
out = '[' + safe(opt.section) + ']' + eol + out | ||
out = '[' + safe(opt.section) + ']' + (opt.newline ? eol + eol : eol) + out | ||
} | ||
for (const k of children) { | ||
const nk = dotSplit(k).join('\\.') | ||
const nk = splitSections(k, '.').join('\\.') | ||
const section = (opt.section ? opt.section + '.' : '') + nk | ||
const { whitespace } = opt | ||
const child = encode(obj[k], { | ||
...opt, | ||
section, | ||
whitespace, | ||
}) | ||
@@ -60,20 +81,40 @@ if (out.length && child.length) { | ||
const dotSplit = str => | ||
str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002') | ||
.replace(/\\\./g, '\u0001') | ||
.split(/\./) | ||
.map(part => | ||
part.replace(/\1/g, '\\.') | ||
.replace(/\2LITERAL\\1LITERAL\2/g, '\u0001')) | ||
function splitSections (str, separator) { | ||
var lastMatchIndex = 0 | ||
var lastSeparatorIndex = 0 | ||
var nextIndex = 0 | ||
var sections = [] | ||
const decode = str => { | ||
do { | ||
nextIndex = str.indexOf(separator, lastMatchIndex) | ||
if (nextIndex !== -1) { | ||
lastMatchIndex = nextIndex + separator.length | ||
if (nextIndex > 0 && str[nextIndex - 1] === '\\') { | ||
continue | ||
} | ||
sections.push(str.slice(lastSeparatorIndex, nextIndex)) | ||
lastSeparatorIndex = nextIndex + separator.length | ||
} | ||
} while (nextIndex !== -1) | ||
sections.push(str.slice(lastSeparatorIndex)) | ||
return sections | ||
} | ||
const decode = (str, opt = {}) => { | ||
opt.bracketedArray = opt.bracketedArray !== false | ||
const out = Object.create(null) | ||
let p = out | ||
let section = null | ||
// section |key = value | ||
const re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i | ||
// section |key = value | ||
const re = /^\[([^\]]*)\]\s*$|^([^=]+)(=(.*))?$/i | ||
const lines = str.split(/[\r\n]+/g) | ||
const duplicates = {} | ||
for (const line of lines) { | ||
if (!line || line.match(/^\s*[;#]/)) { | ||
if (!line || line.match(/^\s*[;#]/) || line.match(/^\s*$/)) { | ||
continue | ||
@@ -97,3 +138,9 @@ } | ||
const keyRaw = unsafe(match[2]) | ||
const isArray = keyRaw.length > 2 && keyRaw.slice(-2) === '[]' | ||
let isArray | ||
if (opt.bracketedArray) { | ||
isArray = keyRaw.length > 2 && keyRaw.slice(-2) === '[]' | ||
} else { | ||
duplicates[keyRaw] = (duplicates?.[keyRaw] || 0) + 1 | ||
isArray = duplicates[keyRaw] > 1 | ||
} | ||
const key = isArray ? keyRaw.slice(0, -2) : keyRaw | ||
@@ -139,3 +186,3 @@ if (key === '__proto__') { | ||
// if so, add it to that, and mark this one for deletion | ||
const parts = dotSplit(k) | ||
const parts = splitSections(k, '.') | ||
p = out | ||
@@ -142,0 +189,0 @@ const l = parts.pop() |
@@ -5,3 +5,3 @@ { | ||
"description": "An ini encoder/decoder for node", | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"repository": { | ||
@@ -24,3 +24,3 @@ "type": "git", | ||
"@npmcli/eslint-config": "^4.0.0", | ||
"@npmcli/template-oss": "4.12.0", | ||
"@npmcli/template-oss": "4.13.0", | ||
"tap": "^16.0.1" | ||
@@ -38,3 +38,4 @@ }, | ||
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.", | ||
"version": "4.12.0" | ||
"version": "4.13.0", | ||
"publish": "true" | ||
}, | ||
@@ -41,0 +42,0 @@ "tap": { |
@@ -79,4 +79,9 @@ An ini format parser and serializer for node. | ||
* `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 | ||
@@ -86,2 +91,14 @@ `=` character. By default, whitespace is omitted, to be friendly to | ||
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 | ||
current platform name is used. | ||
* `bracketedArrays` Boolean to specify whether array values are appended | ||
with `[]`. By default this is true but there are some ini parsers | ||
that instead treat duplicate names as arrays. | ||
@@ -88,0 +105,0 @@ For backwards compatibility reasons, if a `string` options is passed |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
12533
246
126
1