npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

hocon-config

v1.0.94

Published

A HOCON parser for Node.js with environment expansions, partial array merges, programmatic overrides, and CLI runtime integration.

Readme

hocon-config

CI Tests npm version License npm downloads

A powerful HOCON (Human-Optimized Config Object Notation) parser and loader for Node.js. We fully handle:

  • Environment variable substitutions (?ENV_VAR / ${?ENV_VAR})
  • Multiple-file includes (include "overrides.conf")
  • Nested objects & arrays (dotted keys → nested objects)
  • Key merging (last definition wins, partial array overrides, etc.)
  • Programmatic overrides for advanced usage
  • Built-in CLI + ENV merging in the parse function
  • Zero dependencies – only Node’s built-ins
  • Jest-based tests ensuring quality

No need to be humble: hocon-config is robust yet straightforward, making your Node.js configuration a breeze.


Installation

npm install hocon-config

Once installed, you can simply import or require it in your Node.js code.


Quick Start

  1. Create a HOCON file (e.g., config/base.conf):

    app.name = "ExampleApp"
    database {
      host = "localhost"
      port = 5432
    }
  2. Use the parse function to load it, automatically merging environment variables & CLI arguments:

    const path = require('path');
    const { parse } = require('hocon-config');
    
    // Suppose we want to read config/base.conf
    // We'll pass no options => parseEnv=true, parseArgs=true, envPrefix=''
    const filePath = path.join(__dirname, 'config', 'base.conf');
    const config = parse(filePath);
    
    console.log(config);
    // Might print:
    // {
    //   app: { name: 'ExampleApp' },
    //   database: { host: 'localhost', port: 5432 }
    // }
  3. Done! You have a Node.js object ready to use, and if you define environment variables or pass CLI arguments (prefixed keys, etc.), they override the config automatically.


The parse Function

/**
 * parse(filePath, [runtimeOptions]):
 * 1) Gather overrides from process.env + process.argv
 * 2) parseFile(...) with those overrides
 *
 * runtimeOptions:
 *   envPrefix?: string  (default "")
 *   parseEnv?: boolean  (default true)
 *   parseArgs?: boolean (default true)
 *   debug?: boolean
 */
function parse(filePath, runtimeOptions = {}) {
  const {
    envPrefix = "",
    parseEnv = true,
    parseArgs = true,
    debug = false,
  } = runtimeOptions;

  // Collect env-based overrides (keys with prefix => dottedKey)
  let envMap = {};
  if (parseEnv) envMap = buildEnvMap(process.env, envPrefix);

  // Collect CLI-based overrides (--app.name=Override)
  let argMap = {};
  if (parseArgs) {
    const argv = process.argv.slice(2);
    argMap = buildArgMap(argv);
  }

  // CLI overrides env if there's a conflict
  const finalOverrides = { ...envMap, ...argMap };

  // parseFile with final overrides
  return parseFile(filePath, {
    debug,
    overrides: finalOverrides,
  });
}

By default:

  • envPrefix="" ensures only environment variables like app_name'app.name' are considered.
  • parseEnv=true merges environment variables.
  • parseArgs=true merges CLI arguments of the form --some.dotted.key=value.
  • The final config merges these on top of your HOCON file’s contents.

Usage & Examples

Below are 11 scenarios from simple key-values to complex multi-file merges, environment usage, CLI arguments with nested keys, etc.

Scenario 1: Simple Key-Value

config/s1.conf:

hello = "world"

Code:

const conf = parse('config/s1.conf');
console.log(conf);
// => { hello: 'world' }

Nothing fancy.


Scenario 2: Nested Object

config/s2.conf:

app {
  name = "Scenario2"
  nested {
    level = "deep"
  }
}

Code:

const conf = parse('config/s2.conf');
console.log(conf);
// => {
//   app: {
//     name: 'Scenario2',
//     nested: { level: 'deep' }
//   }
// }

Objects like app.nested.level become nested JS objects.


Scenario 3: Arrays & Merging

config/s3.conf:

server.ports = [8080, 9090]
server.ports = [10000]

Code:

const conf = parse('config/s3.conf');
console.log(conf);
// => { server: { ports: [ '10000' ] } }

The second line overwrote the entire array—last definition wins.


Scenario 4: Environment Variables

config/s4.conf:

feature.flag = false
feature.flag = ${?FEATURE_FLAG}

Code:

process.env.FEATURE_FLAG = 'true';
const conf = parse('config/s4.conf');
console.log(conf);
// => { feature: { flag: 'true' } }

Scenario 5: Partial Array Overwrites

config/s5-base.conf:

server.ports = [8080, 9090, 10000]

config/s5-override.conf:

include "s5-base.conf"
server.ports = [${?APP_PORT}]

Usage:

delete process.env.APP_PORT;
const conf1 = parse('config/s5-override.conf');
console.log(conf1.server.ports);
// => [ '8080', '9090', '10000' ] (unchanged)

process.env.APP_PORT = '9999';
const conf2 = parse('config/s5-override.conf');
console.log(conf2.server.ports);
// => [ '9999', '9090', '10000' ]

If $APP_PORT isn’t defined, we skip overwriting the array.


Scenario 6: Simple CLI Override

config/s6.conf:

app.name = "BaseCLI"
server.port = 3000

CLI:

node index.js --app.name=MyCLIoverride --server.port=9999

Code (index.js):

const conf = parse('config/s6.conf');
// => merges env vars w/ prefix '' plus CLI
console.log(conf);
// => { app: { name: 'MyCLIoverride' }, server: { port: '9999' } }

parse sees --app.name=MyCLIoverride{'app.name': 'MyCLIoverride'}, overshadowing file definitions.


Scenario 7: Multi-file Includes

config/s7-base.conf:

app {
  name = "Scenario7"
}
include "s7-mid.conf"

config/s7-mid.conf:

app.midKey = true
include "s7-leaf.conf"

config/s7-leaf.conf:

app.final = "leaf"

Code:

const conf = parse('config/s7-base.conf');
console.log(conf);
// => {
//   app: {
//     name: 'Scenario7',
//     midKey: 'true',
//     final: 'leaf'
//   }
// }

All merges happen in correct order.


Scenario 8: Programmatic Overrides

process.env.FEATURE_FLAG = 'false';
const conf = parse('config/s8.conf', {
  // override everything, if we want
  overrides: {
    'database.host': 'prod-db.internal',
    'app.enableBeta': true
  }
});
console.log(conf);
// merges file + env + CLI + final overrides

Any overrides object merges last, overshadowing everything else.


Scenario 9: Combining Env + CLI + Hard Overrides

config/s9.conf:

app.name = "BaseEnvCLI"
app.debug = false

CLI:

node index.js --app.debug=true

Code:

process.env.app_name = 'EnvOverride'; 
const conf = parse('config/s9.conf', {
  overrides: { 'app.logLevel': 'VERBOSE' }
});
// Priority order (lowest -> highest):
// 1) file  => app.name=BaseEnvCLI, app.debug=false
// 2) env   => app.name=EnvOverride
// 3) CLI   => app.debug=true
// 4) overrides => app.logLevel=VERBOSE
console.log(conf);
// => {
//   app: {
//     name: 'EnvOverride',
//     debug: 'true',
//     logLevel: 'VERBOSE'
//   }
// }

Scenario 10: Parsing a String Instead of a File

const { parseString } = require('hocon-config');

const hoconData = `
  server { port = 3000 }
  feature.enabled = ${'?FEATURE_FLAG'}
`;

process.env.FEATURE_FLAG = 'true';
const inlineConfig = parseString(hoconData, __dirname, { debug: true });
console.log(inlineConfig);
// => { server: { port: '3000' }, feature: { enabled: 'true' } }

No file needed, just inline usage.


Scenario 11: CLI Nested Dotted Keys

config/s11.conf:

app {
  nestedKey = "original"
}

CLI:

node index.js --app.nestedKey=CLIOverride

Code:

// parse() sees envPrefix '' for env, and parseArgs for CLI
const conf = parse('config/s11.conf');
console.log(conf);
// => { app: { nestedKey: 'CLIOverride' } }

parse automatically reads process.argv--app.nestedKey=CLIOverride => {'app.nestedKey': 'CLIOverride'} overshadowing the file definition.


parseFile and parseString

  • parseFile(filePath, options?):
    Loads from a HOCON file, merges environment expansions, partial array merges, includes, plus optional overrides. Typically used behind the scenes by the simpler parse(filePath, runtimeOptions).
  • parseString(hocon, baseDir, options?):
    Same logic, just from inline text. Great for dynamic or test configs.

The star of the show is parse(filePath, runtimeOptions?), which merges environment & CLI arguments automatically so your config can be manipulated by external factors with zero extra code.


License

MIT