<commit_before>var glob = require('glob');
var path = require('path');
var _ = require('lodash');

module.exports = function(app, appConfig, customDebug) {

  var debug = customDebug || function(){};

  var seedNodules = [], routes = {};

  var config = {
    // NOTE: the three route config params below must be specified in the nodule init method, they cannot be mutated at request-time
    // REQUIRED, must be unique within express app, can be String or RegExp or an array of either to handle multiple routes
    route: null,

    // get/post/put/delete
    routeVerb: 'get',

    // use to load routes before or after main group (use negative #s for to load route first - like z-index)
    routeIndex: 0,
    
    debugToConsole: false
  };

  var defaultConfig = _.extend(_.cloneDeep(config), appConfig);
  
  return {
    loadNodules: loadNodules,
    registerRoutes: registerRoutes
  };
  
  function loadNodules(dir, exclude) {
    var root = dir || process.cwd(); // TOOD - should this be process.cwd() + '/app' ?
    glob.sync('./**/*.js', { cwd: root })
      .filter(doesntMatch.apply(this, exclude))
      .forEach(function(file) { initNodule(path.join(root, file)); });
  } 

  function initNodule(filepath) {
    var config = require(filepath)(app);
    var seedNodule = _.extend(_.cloneDeep(defaultConfig), config); // merge config properties onto default config
    seedNodule.path = path.dirname(filepath);
    seedNodule.name = path.basename(filepath, '.js');

    // nodules can have multiple routes
    var routeArray = (typeof seedNodule.route === 'string') || (seedNodule.route instanceof RegExp) ? [seedNodule.route] : seedNodule.route;
    _.each(routeArray, function(routePath) {
      seedNodules[routePath] = seedNodule; // routes must me unique
      
      if (!routes[seedNodule.routeIndex])
        routes[seedNodule.routeIndex] = [];
      routes[seedNodule.routeIndex].push({path:routePath, verb:seedNodule.routeVerb});
    });
  }

  // register routes in order based on nodule.routeIndex (default 0, can be negative)
  function registerRoutes(middlewares) {
    var sortedRouteKeys = _.sortBy(_.keys(routes), function(num){ return 1*num; });
    _.each(sortedRouteKeys, function(key) {
      _.each(routes[key], function(route) {

        debug('registering route: ' + route.verb + ' ' + route.path);
        if (defaultConfig.debugToConsole) console.log('registering route: ' + route.verb + ' ' + route.path);

        app[route.verb].apply(app, [route.path, initRequest].concat(middlewares));
      });
    });
  }

  // attach new nodule instance to each incoming request
  function initRequest(req, res, next) {
    req.module = _.cloneDeep(seedNodules[req.route.path]);
    next();
  }
};

function doesntMatch() {
  var matchers = Array.prototype.slice.call(arguments, 0);
  return function(str) {
    var matches = !matchers.some(function(matcher) {
      return str.indexOf(matcher) !== -1;<commit_msg>Major changes to config - moved middleware array to inside noduleDefaults (so it can be customized at the nodule-level)<commit_after>var glob = require('glob');
var path = require('path');
var _ = require('lodash');

module.exports = function(app, config) {

  var seedNodules = [], routes = {};

  var rootConfig = {
    // set this to true if you have not defined a customDebugger but want to temporality see debugging output
    debugToConsole: false,

    // directories to look for nodules in, minus exclude pattern
    dirs: [
      { path: process.cwd(), exclude: null }
    ], 

    noduleDefaults: {
      // the array of middleware functions which will be called in order for each nodule
      middlewares: [],

      // NOTE: the three route config params below must be specified in the nodule init method, they cannot be mutated at request-time
      // REQUIRED, must be unique within express app, can be String or RegExp or an array of either to handle multiple routes
      route: null,

      // get/post/put/delete
      routeVerb: 'get',

      // use to load routes before or after main group (use negative #s for to load route first - like z-index)
      routeIndex: 0,
    },
  };

  var defaultConfig = _.merge(_.cloneDeep(rootConfig), config);
 
  var debug = config.customDebug || function(msg) { if (defaultConfig.debugToConsole) console.log(msg); };
  
  // find all nodules and init all routes first so they can be sorted based on routeIndex
  defaultConfig.dirs.forEach(function(dir) { loadNodules(dir.path, dir.exclude); });
  
  registerRoutes();

  // finds nodules in supplied dir, minus exclude patterns, and invokes initNodule method on them
  function loadNodules(dir, exclude) {
    var root = dir || process.cwd(); // TOOD - should this be process.cwd() + '/app' ?
    glob.sync('./**/*.js', { cwd: root })
      .filter(doesntMatch.apply(this, exclude))
      .forEach(function(file) { initNodule(path.join(root, file)); });
  } 

  // creates seedNodules for each found nodule (seedNodules are cloned at the beginning of each request and added to the req object)
  function initNodule(filepath) {
    var config = require(filepath)(app);
    var seedNodule = _.merge(_.cloneDeep(defaultConfig.noduleDefaults), config); // merge config properties onto default config
    seedNodule.path = path.dirname(filepath);
    seedNodule.name = path.basename(filepath, '.js');

    // nodules can have multiple routes
    var routeArray = (typeof seedNodule.route === 'string') || (seedNodule.route instanceof RegExp) ? [seedNodule.route] : seedNodule.route;
    _.each(routeArray, function(routePath) {
      seedNodules[routePath] = seedNodule; // routes must me unique
      
      if (!routes[seedNodule.routeIndex])
        routes[seedNodule.routeIndex] = [];
      routes[seedNodule.routeIndex].push({path:routePath, verb:seedNodule.routeVerb, middlewares:seedNodule.middlewares});
    });
  }

  // register express routes (call app.get()/app.post() etc.) in order based on nodule.routeIndex (default = 0, can be negative)
  function registerRoutes() {
    var sortedRouteKeys = _.sortBy(_.keys(routes), function(num){ return 1*num; });
    _.each(sortedRouteKeys, function(key) {
      _.each(routes[key], function(route) {

        debug('registering route: ' + route.verb + ' ' + route.path);
        if (defaultConfig.debugToConsole) console.log('registering route: ' + route.verb + ' ' + route.path);

        app[route.verb].apply(app, [route.path, initRequest].concat(route.middlewares));
      });
    });
  }

  // first step in middleware chain - clone applicable seedNodule and attach cloned instance to each incoming request
  function initRequest(req, res, next) {
    req.module = _.cloneDeep(seedNodules[req.route.path]);
    next();
  }
};

function doesntMatch() {
  var matchers = Array.prototype.slice.call(arguments, 0);
  return function(str) {
    var matches = !matchers.some(function(matcher) {
      return str.indexOf(matcher) !== -1;
{'fraction_non_alphanumeric': 0.12561221549985596, 'fraction_numerical': 0.0014405070584845865, 'mean_word_length': 3.473582474226804, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1236513', 'n_tokens_mistral': 2124, 'n_tokens_neox': 2067, 'n_words': 601}