/* jslint node: true */ 'use strict'; // deps const paths = require('path'); const fs = require('graceful-fs'); const hjson = require('hjson'); const sane = require('sane'); const _ = require('lodash'); module.exports = new (class ConfigCache { constructor() { this.cache = new Map(); // path->parsed config } getConfigWithOptions(options, cb) { options.hotReload = _.get(options, 'hotReload', true); const cached = this.cache.has(options.filePath); if (options.forceReCache || !cached) { this.recacheConfigFromFile(options.filePath, (err, config) => { if (!err && !cached) { if (options.hotReload) { const watcher = sane(paths.dirname(options.filePath), { glob: `**/${paths.basename(options.filePath)}`, }); watcher.on('change', (fileName, fileRoot) => { require('./logger.js').log.info( { fileName, fileRoot }, 'Configuration file changed; re-caching' ); this.recacheConfigFromFile( paths.join(fileRoot, fileName), err => { if (!err) { if (options.callback) { options.callback({ fileName, fileRoot, configCache: this, }); } } } ); }); } } return cb(err, config, true); }); } else { return cb(null, this.cache.get(options.filePath), false); } } getConfig(filePath, cb) { return this.getConfigWithOptions({ filePath }, cb); } recacheConfigFromFile(path, cb) { fs.readFile(path, { encoding: 'utf-8' }, (err, data) => { if (err) { return cb(err); } let parsed; try { parsed = hjson.parse(data); this.cache.set(path, parsed); } catch (e) { try { require('./logger.js').log.error( { filePath: path, error: e.message }, 'Failed to re-cache' ); } catch (ignored) { // nothing - we may be failing to parse the config in which we can't log here! } return cb(e); } return cb(null, parsed); }); } })();