guidesmiths/confabulous

View on GitHub
lib/loaders/require.js

Summary

Maintainability
A
0 mins
Test Coverage
A
95%
const debug = require('debug')('confabulous:loaders:require');
const EventEmitter = require('events').EventEmitter;
const fs = require('fs');
const path = require('path');
const async = require('async');
const merge = require('../merge');

module.exports = function req(_options, postProcessors) {
  const options = merge({ mandatory: true, encoding: 'utf8' }, _options);
  const emitter = new EventEmitter();

  return function (confabulous, cb) {
    debug('running');
    setImmediate(() => {
      async.waterfall([validate, resolve, exists, watch, clear, load], (err, result) => {
        if (err) return cb(err);
        async.seq.apply(async, postProcessors)(result, cb);
      });
    });
    return emitter;

    function validate(cb) {
      debug('validate: %s', JSON.stringify(options));
      if (options.mandatory && !options.path) return cb(new Error('path is required'));
      if (options.path) return cb();
      cb(true);
    }

    function resolve(cb) {
      debug('resolve: %s', options.path);
      cb(null, path.resolve(options.path));
    }

    function exists(target, cb) {
      debug('exists: %s', target);
      fs.stat(target, (err) => {
        if (err && err.code === 'ENOENT' && !options.mandatory) return cb(true);
        cb(err, target);
      });
    }

    function watch(target, cb) {
      debug('watch: %s', target);
      if (!options.watch) return cb(null, target);
      const watcher = fs.watch(target, { persistent: false, encoding: options.encoding }, () => {
        emitter.emit('change');
      });
      confabulous.once('reloading', () => {
        watcher.close();
      });
      confabulous.once('closing', () => {
        watcher.close();
      });
      cb(null, target);
    }

    function clear(target, cb) {
      debug('clear cache: %s', target);
      delete require.cache[target];
      return cb(null, target);
    }

    function load(target, cb) {
      debug('load: %s', target);
      let required;
      try {
        required = require(target);
      } catch (err) {
        return cb(err);
      }
      cb(null, required);
    }
  };
};