INI Config Parser With Sections and Types

Parse INI configuration into null-prototype objects with sections, comments, quoted values, type coercion and duplicate detection.

JavascriptConfig Management

Code

snippet
export function parseIni(text, options = {}) {
  if (typeof text !== "string") throw new TypeError("text must be a string");
  const { typed = true } = options;
  const result = Object.create(null);
  let section = result;
  const lines = text.split(/\r?\n/);
  for (let index = 0; index < lines.length; index += 1) {
    const line = lines[index].trim();
    if (line === "" || line.startsWith(";") || line.startsWith("#")) continue;
    if (line.startsWith("[") && line.endsWith("]")) {
      const name = line.slice(1, -1).trim();
      if (name === "") throw new Error(`empty section name on line ${index + 1}`);
      if (Object.prototype.hasOwnProperty.call(result, name)) {
        throw new Error(`duplicate section "${name}" on line ${index + 1}`);
      }
      section = Object.create(null);
      result[name] = section;
      continue;
    }
    const equals = line.indexOf("=");
    if (equals <= 0) throw new Error(`invalid entry on line ${index + 1}`);
    const key = line.slice(0, equals).trim();
    let value = line.slice(equals + 1).trim();
    if (key === "" || /[\[\]]/.test(key)) throw new Error(`invalid key on line ${index + 1}`);
    if (Object.prototype.hasOwnProperty.call(section, key)) {
      throw new Error(`duplicate key "${key}" on line ${index + 1}`);
    }
    value = unquote(value);
    section[key] = typed ? coerce(value) : value;
  }
  return result;
}

function unquote(value) {
  if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
    return value.slice(1, -1);
  }
  if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
    return value.slice(1, -1);
  }
  return value;
}

function coerce(value) {
  if (value === "true") return true;
  if (value === "false") return false;
  if (value === "null") return null;
  if (/^-?\d+$/.test(value)) return Number(value);
  if (/^-?\d+\.\d+$/.test(value)) return Number(value);
  return value;
}

const config = parseIni(`
; application settings
name = "Demo App"
debug = false
retries = 3

[server]
host = 127.0.0.1
port = 8080
`);
console.log(config);