configBase.js

// load any environment variables from .env (if it exists)
import DotEnv from 'dotenv';
DotEnv.config();

import EventEmitter from 'events';

/**
 * Combine given options with environment variables
 * @param {object} options Incoming options
 * @return {object} Processed configuration options
 */
export function parseConfig(options = {}) {
  const configKeys = Object.keys(options);

  const config = {};

  options.configPrefixes = ['THEMEPARKS'].concat(
      options.configPrefixes || [],
  );

  // build this.config object with our settings
  configKeys.forEach((key) => {
    // default prefixes are either "classname_" or "THEMEPARKS_"
    //  classes can add more with configPrefixes
    options.configPrefixes.forEach((prefix) => {
      const configEnvName = `${prefix}_${key}`.toUpperCase();

      if (process.env[configEnvName]) {
        // console.log(`Using env variable ${configEnvName}`);
        config[key] = process.env[configEnvName];
        // console.log(` ${key}(env.${configEnvName})=${config[key]}`);
      }
    });

    if (config[key] === undefined) {
      config[key] = options[key];
    } else {
      // convert env variable to number if the base default is a number
      if (typeof config[key] === 'number') {
        config[key] = Number(config[key]);
      } else if (typeof config[key] === 'boolean') {
        // convert any boolean configs too
        config[key] = (config[key] === 'true');
      } /* and arrays */ else if (Array.isArray(config[key])) {
        config[key] = config[key].split(',');
      }
    }
  });

  return config;
}

/**
 * Base Config Object
 * Supports classes with a single argument "options"
 * These will be sorted into a member called "this.config" containing all the same keys
 *
 * Crucially, these can also be overriden through environment variables
 * For example, for a config option "timeout" for class Database, this could be overriden through either:
 *   env.THEMEPARKS_TIMEOUT (using a "global module name")
 *   env.DATABASE_TIMEOUT (using the class name)
 *
 * Classes can also add additional prefixes to the supported environment variables through:
 *   new ClassInstance({configPrefixes: ['myCustomPrefix']});
 * Which would also allow env.MYCUSTOMPREFIX_TIMEOUT to be used
 *
 * Note that a default value must be supplied for the environment variable to be processed
 * If the default value is a number, the environment variable will be cast to a number as well
 * @class
 */
export class ConfigBase extends EventEmitter {
  /**
   * A base class that can be configured through environment variables
   * @param {object} options Config for instantiating this object
   */
  constructor(options = {}) {
    super();

    options.configPrefixes = [this.constructor.name].concat(
        options.configPrefixes || [],
    );

    this.config = parseConfig(options || {});
  }
}

export default ConfigBase;