plain-text-data-to-json
Version:
Transform a simple plain-text database to JSON
132 lines (115 loc) • 3.3 kB
JavaScript
var own = {}.hasOwnProperty
/**
* @typedef {Object} ToJsonOptions
* @property {boolean} [log=true]
* @property {string} [delimiter=':']
* @property {string[]|string|false} [comment='%']
* @property {boolean|'fix'} [forgiving]
*/
/**
* Transform a string into an array or object of values.
*
* @param {string} value
* @param {ToJsonOptions} [options={}]
*/
export function toJson(value, options = {}) {
var log =
options.log === null || options.log === undefined ? true : options.log
var comment =
options.comment === null || options.comment === undefined
? '%'
: options.comment
var comments = comment ? (Array.isArray(comment) ? comment : [comment]) : []
var delimiter = options.delimiter || ':'
var forgiving = options.forgiving
var propertyOrValues = {}
/** @type {boolean} */
var isPropertyValuePair
/** @type {Array.<Array.<string>>} */
var pairs
var lines = value
.split('\n')
.map((line) => {
var commentIndex = -1
/** @type {number} */
var index
while (++commentIndex < comments.length) {
index = line.indexOf(comments[commentIndex])
if (index !== -1) line = line.slice(0, index)
}
return line.trim()
})
.filter(Boolean)
pairs = lines.map(
// Transform `value` to a property--value tuple.
function (value) {
var values = value.split(delimiter)
var result = [values.shift().trim()]
if (values.length > 0) {
result.push(values.join(delimiter).trim())
}
return result
}
)
pairs.forEach(function (line, index) {
var currentLineIsPropertyValuePair = line.length === 2
if (index === 0) {
isPropertyValuePair = currentLineIsPropertyValuePair
} else if (currentLineIsPropertyValuePair !== isPropertyValuePair) {
throw new Error(
'Error at `' +
line +
'`: ' +
'Both property-value pairs and array values found. ' +
'Make sure either exists.'
)
}
if (own.call(propertyOrValues, line[0])) {
if (
!forgiving ||
(forgiving === true &&
currentLineIsPropertyValuePair &&
line[1] !== propertyOrValues[line[0]])
) {
throw new Error(
'Error at `' +
line +
'`: ' +
'Duplicate data found. ' +
'Make sure, in objects, no duplicate properties exist;' +
'in arrays, no duplicate values.'
)
}
if (log) {
if (forgiving === 'fix' && propertyOrValues[line[0]] !== line[1]) {
console.log(
'Overwriting `' +
propertyOrValues[line[0]] +
'` ' +
'to `' +
line[1] +
'` for `' +
line[0] +
'`'
)
} else {
console.log('Ignoring duplicate key for `' + line[0] + '`')
}
}
}
propertyOrValues[line[0]] = line[1]
})
if (isPropertyValuePair) {
pairs.sort(sortOnFirstIndex)
return Object.fromEntries(pairs)
}
return lines.sort()
}
/**
* Sort on the first (`0`) index.
* @param {Array.<string>} a
* @param {Array.<string>} b
*/
function sortOnFirstIndex(a, b) {
return a[0].charCodeAt(0) - b[0].charCodeAt(0)
}