UNPKG

conf-cfg-ini

Version:

encode and decode ini,conf,cfg files

216 lines (205 loc) 7.23 kB
/** * Encode and decode ini/conf/cfg files * @author Rolf Loges * @licence MIT * @param {{lineEnding: string, sectionOpenIdentifier: string, sectionCloseIdentifier: string, defaultValue: boolean, assignIdentifier: string, commentIdentifiers: string, trimLines: boolean, ignoreMultipleAssignIdentifier: boolean}} options */ function Config(options){ this.options = { lineEnding: "\r\n", sectionOpenIdentifier: '[', sectionCloseIdentifier: ']', defaultValue: true, assignIdentifier: "=", valueIdentifier: undefined, commentIdentifiers: [";"], trimLines: true, ignoreMultipleAssignIdentifier: false }; if(typeof options === 'object'){ this.setOptions(options); } } /** * Decode a config-string * * @param {string} data * @return {{}} */ Config.prototype.decode = function(data){ if(typeof data != 'string'){ if(typeof data.toString === 'function'){ data = data.toString(); } else { throw new Error('expecting string but got '+typeof data); } } var protectedKeys = ['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', '__proto__']; var result = {}; var currentSection = undefined; var lines = data.split(this.options.lineEnding); for(var i = 0; i < lines.length; i++){ var line = lines[i]; if(this.options.trimLines === true){ line = line.trim(); } if(line.length == 0 || stringBeginsWithOnOfTheseStrings(line,this.options.commentIdentifiers)){ continue; } var sectionRegExp = new RegExp("^\\"+this.options.sectionOpenIdentifier+"(.*?)\\"+this.options.sectionCloseIdentifier+"$"); var newSection = line.match(sectionRegExp); if(newSection !== null){ currentSection = newSection[1]; if(typeof result[currentSection] === 'undefined' && !protectedKeys.includes(currentSection)){ result[currentSection] = {}; } continue; } var assignPosition = line.indexOf(this.options.assignIdentifier); var key = undefined; var value = undefined; if(assignPosition === -1){ key = line; value = this.options.defaultValue; } else { var assignIdentifierLength = this.options.assignIdentifier.length if (this.options.ignoreMultipleAssignIdentifier) { var regExp = new RegExp(escapeRegExp(this.options.assignIdentifier) + '+') var matchResult = line.match(regExp) if (matchResult !== null) { assignIdentifierLength = matchResult[0].length } } key = line.substr(0,assignPosition); value = line.substr(assignPosition+assignIdentifierLength); } if (typeof this.options.valueIdentifier === 'string') { value = this.valueTrim(value, this.options.valueIdentifier); } if (protectedKeys.includes(currentSection) || protectedKeys.includes(key)) { continue; } if(typeof currentSection === 'undefined'){ result[key] = value; } else { result[currentSection][key] = value; } } return result; } /** * Encode a object * no nesting section supported! * * @param {{}} object * @return {string} */ Config.prototype.encode = function(object){ var resultSections = ""; var resultAttributesWithoutSection = ""; var sections = Object.keys(object); if (typeof this.options.valueIdentifier === 'string'){ var valueIdentifier = this.options.valueIdentifier; } else { var valueIdentifier = ""; } for(var i = 0; i < sections.length; i++){ if(typeof object[sections[i]] === 'object'){ if(resultSections != ""){ resultSections += this.options.lineEnding; } resultSections += this.options.sectionOpenIdentifier; resultSections += sections[i]; resultSections += this.options.sectionCloseIdentifier; resultSections += this.options.lineEnding; var attributes = Object.keys(object[sections[i]]); for(var j = 0; j < attributes.length; j++){ resultSections += attributes[j]; resultSections += this.options.assignIdentifier; resultSections += valueIdentifier; resultSections += object[sections[i]][attributes[j]]; resultSections += valueIdentifier; resultSections += this.options.lineEnding; } } else { resultAttributesWithoutSection += sections[i]; resultAttributesWithoutSection += this.options.assignIdentifier; resultAttributesWithoutSection += object[sections[i]]; resultAttributesWithoutSection += this.options.lineEnding; } } return resultAttributesWithoutSection+resultSections; } /** * Set Options * @param {{lineEnding: string, sectionOpenIdentifier: string, sectionCloseIdentifier: string, defaultValue: boolean, assignIdentifier: string, commentIdentifiers: string, trimLines: boolean}} options */ Config.prototype.setOptions = function(options){ if(typeof options !== 'object'){ throw new Error('expecting object but got '+typeof options); } var option = Object.keys(options); for(var i = 0; i < option.length; i++){ if(typeof options[option[i]] !== 'undefined'){ this.options[option[i]] = options[option[i]]; } } } /** * Try to detect the used line ending * (windows, unix, mac) * @param {string} data * @return {string} */ Config.prototype.detectLineEnding = function(data){ var hasCaridgeReturn = data.indexOf("\r") !== -1; var hasLineFeed = data.indexOf("\n") !== -1 if(hasCaridgeReturn && hasLineFeed){ if(data.indexOf("\r\n") !== -1){ return "\r\n"; } else if(data.indexOf("\n\r") !== -1){ return "\n\r"; } else { throw new Error('found multiple line endings'); } } else if(hasLineFeed){ return "\n"; } else if(hasCaridgeReturn){ return "\r"; } else { return "\n"; } } /** * @param string value * @param string chars */ Config.prototype.valueTrim = function(value, chars){ var charsEscaped = chars.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); var regEx = new RegExp("^["+charsEscaped+"]?"); value = value.replace(regEx, ''); regEx = new RegExp("["+charsEscaped+"]?$"); value = value.replace(regEx, ''); return value; } /** * @param {string} string * @param {string[]} stringList * @return {boolean} */ function stringBeginsWithOnOfTheseStrings(string, stringList){ for(var i = 0; i < stringList.length; i++){ if(string.indexOf(stringList[i]) === 0){ return true; } } return false; } /** * @param {string} string * @returns {string} */ function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } module.exports = Config;