class PropertyList { constructor(...properties) { for (const property of properties) { if (!(property instanceof Property)) throw TypeError("properties has to be instances of Property"); } this.properties = []; for (const property of properties) { this.add(property); } } add(property) { Object.defineProperty(this, property.key, { get() { return property.value }, set(value) { property.parse(value) } }); this.properties.push(property); } } export class Property { constructor(key, defaultValue = null, parser = (v) => v) { this.key = key; this.defaultValue = defaultValue; this.parser = parser; this.parse(this.defaultValue); } parse(v) { this.value = this.parser(v); return this.value; } clone() { return new Property(this.key, this.defaultValue, this.parser); } } /** Regex attempts (first to final) * ^\s*({0})\s*(=|:)\s*(.*)$ * ^\s*({0})\s*(=|:)\s*([^#\n]*).*$ * ^\s*({0})\s*(=|:)\s*(.*?)((#|\/\/).*)?$ */ export class PropertyReader { constructor(...properties) { this.propertyList = new PropertyList(...properties); this.regex = new RegExp(`^\\s*(${this.propertyList.properties.map(p => p.key).join("|")})\\s*(=|:)\\s*(.*?)((#|\\/\\/).*)?$`, "mg"); } read(str) { let match = null; while ((match = this.regex.exec(str)) !== null) { if (match.index === this.regex.lastIndex) // Infinite loop fix this.regex.lastIndex++; const key = match[1]; const value = match[3].trim(); this.propertyList[key] = value; } return this.propertyList; } }