<commit_before>var N = require('libnested')

var isModule = require('./is')
var apply = require('./apply')

module.exports = function combine () {
  var unflattenedModules = Array.prototype.slice.call(arguments)
  var modules = flattenNested(unflattenedModules)

  assertDependencies(modules)

  var combinedModules = {}

  for (var key in modules) {
    var module = modules[key]
    var dependencies = getNeededDependencies(module.needs, combinedModules)
    var given = module.create(dependencies)

    if (!given) { throw new Error('export declared but not returned for ' + key) }
    addGivenToCombined(given, combinedModules, module)
  }

  if (isEmpty(combinedModules)) {
    throw new Error('could not resolve any modules')
  }

  return combinedModules
}

function isString (s) {
  return typeof s === 'string'
}

function isEmpty (e) {
  for (var k in e) return false
  return true
}

function isObject (o) {
  return o && typeof o === 'object'
}

function append (obj, path, value) {
  var a = N.get(obj, path)
  if (!a) N.set(obj, path, a = [])
  a.push(value)
}

function flattenNested (modules) {
  return modules.reduce(function (a, b) {
    eachModule(b, function (value, path) {
      var k = path.join('/')
      a[k] = value
    })
    return a
  }, {})
}

function assertDependencies (modules) {
  var allNeeds = {}
  var allGives = {}

  for (var k in modules) {
    var module = modules[k]
    N.each(module.needs, function (v, path) {
      N.set(allNeeds, path, true)
    })
    if (isString(module.gives)) {
      N.set(allGives, [module.gives], true)
    } else {
      N.each(module.gives, function (v, path) {
        N.set(allGives, path, true)
      })
    }
  }

  N.each(allNeeds, function (_, path) {
    if (!N.get(allGives, path)) { throw new Error('export needed but not given' + path.join('.') + ' in: ' + path) }
  })
}

function addGivenToCombined (given, combined, module) {
  if (isString(module.gives)) {
    append(combined, [module.gives], given)
  } else {
    N.each(module.gives, function (_, path) {
      var fun = N.get(given, path)
      append(combined, path, fun)
    })
  }
}

function getNeededDependencies (needs, combined) {
  return N.map(needs, function (type, path) {
    var dependency = N.get(combined, path)
    if (!dependency) {
      dependency = N.set(combined, path, [])
    }
    return apply[type](dependency)
  })
}

function eachModule (obj, iter, path) {
  path = path || []
  for (var k in obj) {
    if (isObject(obj[k])) {
      if (isModule(obj[k])) iter(obj[k], path.concat(k))
      else eachModule(obj[k], iter, path.concat(k))
    }
  }
}
<commit_msg>Rename variable.<commit_after>var N = require('libnested')

var isModule = require('./is')
var apply = require('./apply')

module.exports = function combine () {
  var nestedModules = Array.prototype.slice.call(arguments)
  var modules = flattenNested(nestedModules)

  assertDependencies(modules)

  var combinedModules = {}

  for (var key in modules) {
    var module = modules[key]
    var dependencies = getNeededDependencies(module.needs, combinedModules)
    var given = module.create(dependencies)

    if (!given) { throw new Error('export declared but not returned for ' + key) }
    addGivenToCombined(given, combinedModules, module)
  }

  if (isEmpty(combinedModules)) {
    throw new Error('could not resolve any modules')
  }

  return combinedModules
}

function isString (s) {
  return typeof s === 'string'
}

function isEmpty (e) {
  for (var k in e) return false
  return true
}

function isObject (o) {
  return o && typeof o === 'object'
}

function append (obj, path, value) {
  var a = N.get(obj, path)
  if (!a) N.set(obj, path, a = [])
  a.push(value)
}

function flattenNested (modules) {
  return modules.reduce(function (a, b) {
    eachModule(b, function (value, path) {
      var k = path.join('/')
      a[k] = value
    })
    return a
  }, {})
}

function assertDependencies (modules) {
  var allNeeds = {}
  var allGives = {}

  for (var k in modules) {
    var module = modules[k]
    N.each(module.needs, function (v, path) {
      N.set(allNeeds, path, true)
    })
    if (isString(module.gives)) {
      N.set(allGives, [module.gives], true)
    } else {
      N.each(module.gives, function (v, path) {
        N.set(allGives, path, true)
      })
    }
  }

  N.each(allNeeds, function (_, path) {
    if (!N.get(allGives, path)) { throw new Error('export needed but not given' + path.join('.') + ' in: ' + path) }
  })
}

function addGivenToCombined (given, combined, module) {
  if (isString(module.gives)) {
    append(combined, [module.gives], given)
  } else {
    N.each(module.gives, function (_, path) {
      var fun = N.get(given, path)
      append(combined, path, fun)
    })
  }
}

function getNeededDependencies (needs, combined) {
  return N.map(needs, function (type, path) {
    var dependency = N.get(combined, path)
    if (!dependency) {
      dependency = N.set(combined, path, [])
    }
    return apply[type](dependency)
  })
}

function eachModule (obj, iter, path) {
  path = path || []
  for (var k in obj) {
    if (isObject(obj[k])) {
      if (isModule(obj[k])) iter(obj[k], path.concat(k))
      else eachModule(obj[k], iter, path.concat(k))
    }
  }
}

{'fraction_non_alphanumeric': 0.14742248430663876, 'fraction_numerical': 0.0, 'mean_word_length': 3.1565217391304348, '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': '6103271', 'n_tokens_mistral': 1831, 'n_tokens_neox': 1798, 'n_words': 471}