<commit_before>var getCanonicalName = require('./utils').getCanonicalName;
var glob = require('glob');
var toFileURL = require('./utils').toFileURL;
var fromFileURL = require('./utils').fromFileURL;
var asp = require('rsvp').denodeify;
var fs = require('fs');
var Graph = require('algorithms/data_structures/graph');
var depthFirst = require('algorithms/graph').depthFirstSearch;

module.exports = Trace;

function Trace(loader, traceCache) {
  this.loader = loader;
  // stored traced load records
  this.loads = traceCache;
  // in progress traces
  this.tracing = {};
}

// runs the pipeline hooks, returning the load record for a module
Trace.prototype.getLoadRecord = function(canonical) {
  var loader = this.loader;
  var loads = this.loads;

  // modules starting with "@" are assumed system modules
  if (canonical.substr(0, 1) == '@')
    return Promise.resolve(null);

  if (loads[canonical])
    return Promise.resolve(loads[canonical]);

  if (this.tracing[canonical])
    return this.tracing[canonical];

  var self = this;

  return this.tracing[canonical] = Promise.resolve(loader.normalize(canonical))
  .then(function(normalized) {

    var load = {
      name: canonical,
      normalized: normalized,
      address: null,
      metadata: null,
      deps: null,
      depMap: null,
      source: null,
      originalSource: null,

      // represents a build: false plugin that will not be built out for production
      runtimePlugin: null,

      // represents a conditional load record (sourceless)
      conditional: null
    };

    // -- conditional load record creation: sourceless intermediate load record --

    // boolean conditional
    var booleanIndex = canonical.lastIndexOf('#?');
    if (booleanIndex != -1) {
      var condition = canonical.substr(booleanIndex + 2)
      if (condition.indexOf('|') == -1)
        condition += '|default';
      load.conditional = {
        condition: condition,
        branch: canonical.substr(0, booleanIndex)
      };
      return load;
    }

    // package environment conditional
    var pkgEnvIndex = canonical.indexOf('#:');
    if (pkgEnvIndex != -1) {
      // NB handle a package plugin load here too
      if (canonical.indexOf('!') != -1)
        throw new Error('Unable to trace ' + canonical + ' - building package environment mappings of plugins is not currently supported.');

      var pkgName = canonical.substr(0, pkgEnvIndex);
      var subPath = canonical.substr(pkgEnvIndex + 2);

      var normalizedPkgName = loader.normalizeSync(pkgName + '/');
      normalizedPkgName = normalizedPkgName.substr(0, normalizedPkgName.length - 1);

      var pkg = loader.packages[normalizedPkgName];
      var envMap = pkg.map['./' + subPath];

      // effective analog of the same function in SystemJS packages.js
      // to work out the path with defaultExtension added.
      // we cheat here and use normalizeSync to apply the right checks, while
      // skipping any map entry by temporarily removing it.
      function toPackagePath(subPath) {
        var pkgMap = pkg.map;
        pkg.map = {};
        var normalized = loader.normalizeSync(pkgName + '/' + subPath);
        pkg.map = pkgMap;
        return normalized;
      }

      var metadata = {};
      var fallback;
      return loader.locate({ name: toPackagePath(subPath), metadata: metadata })
      .then(function(address) {
        if (metadata.build === false)
          return;

        fallback = getCanonicalName(loader, address);

        // check if the fallback exists
        return new Promise(function(resolve) {
          fs.exists(fromFileURL(address), resolve);
        })
        .then(function(fallbackExists) {
          if (!fallbackExists)
            fallback = null;
        });
      })
      .then(function() {
        // allow build: false trace opt-out
        if (metadata.build === false)
          return null;

        // environment trace
        return loader.normalize(pkg.map['@env'] || '@system-env')
        .then(function(normalizedCondition) {
          var conditionModule = getCanonicalName(loader, normalizedCondition);

          return Promise.all(Object.keys(envMap).map(function(envCondition) {
            var mapping = envMap[envCondition];
            var negate = envCondition[0] == '~';

            return Promise.resolve()
            .then(function() {
              if (mapping == '.')
                return normalizedPkgName;
              else if (mapping.substr(0, 2) == './')
                return toPackagePath(mapping.substr(2))
              else
                return loader.normalize(mapping);
            })
            .then(function(normalizedMapping) {
              return {
                condition: (negate ? '~' : '') + conditionModule + '|' + (negate ? envCondition.substr(1) : envCondition),
                branch: getCanonicalName(loader, normalizedMapping)
              };
            });
          }));
        })
        .then(function(envs) {
          load.conditional = {
            envs: envs,
            fallback: fallback
          };
          return load;
        });
      });
    }

    // conditional interpolation
    var interpolationRegEx = /#\{[^\}]+\}/;
    var interpolationMatch = canonical.match(interpolationRegEx);
    if (interpolationMatch) {
      var condition = interpolationMatch[0].substr(2, interpolationMatch[0].length - 3);

      if (condition.indexOf('|') == -1)
        condition += '|default';

      var metadata = {};
      return Promise.resolve(loader.locate({ name: normalized.replace(interpolationRegEx, '*'), metadata: metadata }))
      .then(function(address) {
        // allow build: false trace opt-out
        if (metadata.build === false)
          return null;

        // glob the conditional interpolation variations from the filesystem
        if (address.substr(0, 8) != 'file:///')
          throw new Error('Error tracing ' + canonical + '. It is only possible to trace conditional interpolation for modules resolving to local file:/// URLs during the build.');

        var globIndex = address.indexOf('*');
        return asp(glob)(fromFileURL(address), { dot: true, nobrace: true, noglobstar: true, noext: true, nodir: true })
        .then(function(paths) {
          var branches = {};
          paths.forEach(function(path) {
            path = toFileURL(path);

            var pathCanonical = getCanonicalName(loader, path);
            var interpolate = pathCanonical.substr(interpolationMatch.index, path.length - address.length + 1);

            if (metadata.plugin) {
              if (loader.pluginFirst)
                pathCanonical = metadata.plugin + '!' + pathCanonical;
              else
                pathCanonical = pathCanonical + '!' + metadata.plugin;
            }
            branches[interpolate] = pathCanonical;
          });

          load.conditional = {
            condition: condition,
            branches: branches
          };
          return load;
        });
      });
    }

    // -- trace loader hooks --
    return Promise.resolve(loader.locate({ name: normalized, metadata: load.metadata = {}}))
    .then(function(address) {
      // build: false build config - null load record
      if (load.metadata.build === false)
        return null;

      // build: false plugins - runtime plugins
      if (load.metadata.loaderModule && load.metadata.loaderModule.build === false)
        return Promise.resolve(loader.normalize(load.metadata.loader, normalized))
        .then(function(pluginNormalized) {
          load.runtimePlugin = getCanonicalName(loader, pluginNormalized);
          return load;
        });

      load.address = address;
      return Promise.resolve(loader.fetch({ name: normalized, metadata: load.metadata, address: load.address }))
      .then(function(source) {
        load.originalSource = source;
        return loader.translate({ name: normalized, metadata: load.metadata, address: load.address, source: source });
      })
      .then(function(source) {
        load.source = source;
        return loader.instantiate({ name: normalized, metadata: load.metadata, address: load.address, source: source });
      })
      .then(function(result) {
        if (!result)
          throw new TypeError('Native ES Module builds not supported. Ensure transpilation is included in the loader pipeline.');

        load.deps = result.deps;

        // normalize dependencies to populate depMap
        load.depMap = {};
        return Promise.all(result.deps.map(function(dep) {
          return loader.normalize(dep, normalized, load.address)
          .then(function(normalized) {
            load.depMap[dep] = getCanonicalName(loader, normalized);
          });
        }));
      })
      .then(function() {
        // remove unnecessary metadata for trace
        delete load.metadata.execute;
        delete load.metadata.builderExecute;
        delete load.metadata.parseTree;

        return load;
      });
    });
  })
  .then(function(load) {
    return loads[canonical] = load;
  });
};

/*
 * Returns the full trace tree of a module
 *
 * - traceAllConditionals indicates if conditional boundaries should be traversed during the trace.
 * - conditionalEnv represents the conditional tracing environment module values to impose on the trace
 *   forcing traces for traceAllConditionals false, and skipping traces for traceAllConditionals true.
 *
 * conditionalEnv provides canonical condition tracing rules of the form:
 *
 *  {
 *    'some/interpolation|value': true, // include ALL variations
 *    'another/interpolation|value': false, // include NONE
 *    'custom/interpolation|value': ['specific', 'values']
 *
 *    // default BOOLEAN entry::
 *    '@system-env|browser': false,
 *    '~@system-env|browser': false
 *
 *    // custom boolean entry
 *    // boolean only checks weak truthiness to allow overlaps
 *    '~@system-env|node': true
 *  }
 *
 */
Trace.prototype.getAllLoadRecords = function(canonical, traceAllConditionals, conditionalEnv, curLoads) {
  var loader = this.loader;

  curLoads = curLoads || {};

  if (canonical in curLoads)
    return curLoads;

  var self = this;
  return this.getLoadRecord(canonical)
  .then(function(load) {
    // conditionals, build: false and system modules are null loads in the trace trees
    // (that is, part of depcache, but not built)
    // we skip system modules starting with @ though
    if (canonical[0] != '@')
      curLoads[canonical] = load && load.conditional ? null : load;

    if (load)
      return Promise.all(Trace.getLoadDependencies(load, traceAllConditionals, conditionalEnv).map(function(dep) {
        return self.getAllLoadRecords(dep, traceAllConditionals, conditionalEnv, curLoads);
      }));
  })
  .then(function() {
    return curLoads;
  });
};

// helper function -> returns the "condition" build of a tree
// that is the modules needed to determine the exact conditional solution of the tree
Trace.prototype.getConditionLoadRecords = function(canonical, inConditionTree, curLoads) {
  var loader = this.loader;

  curLoads = curLoads || {};

  if (canonical in curLoads)
    return curLoads;

  var self = this;
  return this.getLoadRecord(canonical)
  .then(function(load) {
    if (inConditionTree)
      curLoads[canonical] = load && load.conditional ? null : load;

    if (load)
      return Promise.all(Trace.getLoadDependencies(load, true, {}).map(function(dep) {
        return self.getConditionLoadRecords(dep, inConditionTree || !!load.conditional, curLoads);
      }));
  })
  .then(function() {
    return curLoads;
  });
}

// Returns the ordered immediate dependency array from the trace of a module
Trace.getLoadDependencies = function(load, traceAllConditionals, conditionalEnv) {
  traceAllConditionals = traceAllConditionals || true;
  conditionalEnv = conditionalEnv || {};

  var deps = [];

  function envTrace(condition) {
    // trace the condition modules as dependencies themselves
    var exportIndex = condition.lastIndexOf('|');
    if (condition[0] == '~')
      deps.push(condition.substr(1, exportIndex - 1));
    else
      deps.push(condition.substr(0, exportIndex));

    // return the environment trace info
    var envTrace = conditionalEnv[condition];
    return envTrace === undefined ? traceAllConditionals : envTrace;
  }

  // conditional load records have their branches all included in the trace
  var conditional = load.conditional;
  if (conditional) {
    // { condition, branch } boolean conditional
    if (conditional.branch) {
      if (envTrace(conditional.condition))
        deps.push(branch);
    }

    // { envs: [{condition, branch},...], fallback } package environment map
    else if (conditional.envs) {
      conditional.envs.forEach(function(env) {
        if (envTrace(env.condition))
          deps.push(env.branch);
      });
      if (conditional.fallback)
        deps.push(conditional.fallback);
    }

    // { condition, branches } conditional interpolation
    else if (conditional.branches) {
      var et = envTrace(conditional.condition);
      if (et) {
        Object.keys(conditional.branches).forEach(function(branch) {
          var dep = conditional.branches[branch];
          if (et == true)
            deps.push(dep);
          else if (et.indexOf(branch) != -1)
            deps.push(dep);
        });
      }
    }

    // run the conditional trace
    return deps;
  }

  // if this record uses a runtime plugin (build: false), trace the plugin
  if (load.runtimePlugin)
    return [load.runtimePlugin];

  // standard dep trace
  return load.deps.map(function(dep) {
    return load.depMap[dep];
  });
};

function getGraphEntryPoints(graph, entryPoints) {
  entryPoints = [].concat(entryPoints || []);

  var modules = Object.keys(graph.adjList);
  var discarded = {};

  modules.forEach(function (moduleName) {
    Object.keys(graph.adjList[moduleName]).forEach(function (depName) {
      discarded[depName] = true;
    });
  });

  modules.filter(function (moduleName) {
    return !discarded[moduleName];
  }).sort().forEach(function (moduleName) {
    if (entryPoints.indexOf(moduleName) === -1) {
      entryPoints.push(moduleName);
    }
  });

  return entryPoints;
}

Trace.getTreeModulesPostOrder = function getTreeModulesPostOrder(tree, entryPoints) {
  entryPoints = entryPoints || [];

  // Post order traversal sorted module list
  var postOrder = [];
  var graph = new Graph(true);

  // Seed graph with all relations
  Object.keys(tree).forEach(function (moduleName) {
    var load = tree[moduleName];

    if (!graph.adjList[moduleName]) {
      graph.addVertex(moduleName);
    }

    Trace.getLoadDependencies(load).forEach(function (depName) {
      graph.addEdge(moduleName, depName);
    });
  });

  // Post order traversal of graph, one per entryPoint
  getGraphEntryPoints(graph, entryPoints).forEach(function (entryPoint) {
    depthFirst(graph, entryPoint, {
      leaveVertex: function (moduleName) {
        // Avoid duplicates and modules that have been skipped by tracer
        if (postOrder.indexOf(moduleName) === -1 && moduleName in tree) {
          postOrder.push(moduleName);
        }
      }
    });
  });

  return postOrder;
};
<commit_msg>getTreeModulePostOrder to support null loads<commit_after>var getCanonicalName = require('./utils').getCanonicalName;
var glob = require('glob');
var toFileURL = require('./utils').toFileURL;
var fromFileURL = require('./utils').fromFileURL;
var asp = require('rsvp').denodeify;
var fs = require('fs');
var Graph = require('algorithms/data_structures/graph');
var depthFirst = require('algorithms/graph').depthFirstSearch;

module.exports = Trace;

function Trace(loader, traceCache) {
  this.loader = loader;
  // stored traced load records
  this.loads = traceCache;
  // in progress traces
  this.tracing = {};
}

// runs the pipeline hooks, returning the load record for a module
Trace.prototype.getLoadRecord = function(canonical) {
  var loader = this.loader;
  var loads = this.loads;

  // modules starting with "@" are assumed system modules
  if (canonical.substr(0, 1) == '@')
    return Promise.resolve(null);

  if (loads[canonical])
    return Promise.resolve(loads[canonical]);

  if (this.tracing[canonical])
    return this.tracing[canonical];

  var self = this;

  return this.tracing[canonical] = Promise.resolve(loader.normalize(canonical))
  .then(function(normalized) {

    var load = {
      name: canonical,
      normalized: normalized,
      address: null,
      metadata: null,
      deps: null,
      depMap: null,
      source: null,
      originalSource: null,

      // represents a build: false plugin that will not be built out for production
      runtimePlugin: null,

      // represents a conditional load record (sourceless)
      conditional: null
    };

    // -- conditional load record creation: sourceless intermediate load record --

    // boolean conditional
    var booleanIndex = canonical.lastIndexOf('#?');
    if (booleanIndex != -1) {
      var condition = canonical.substr(booleanIndex + 2)
      if (condition.indexOf('|') == -1)
        condition += '|default';
      load.conditional = {
        condition: condition,
        branch: canonical.substr(0, booleanIndex)
      };
      return load;
    }

    // package environment conditional
    var pkgEnvIndex = canonical.indexOf('#:');
    if (pkgEnvIndex != -1) {
      // NB handle a package plugin load here too
      if (canonical.indexOf('!') != -1)
        throw new Error('Unable to trace ' + canonical + ' - building package environment mappings of plugins is not currently supported.');

      var pkgName = canonical.substr(0, pkgEnvIndex);
      var subPath = canonical.substr(pkgEnvIndex + 2);

      var normalizedPkgName = loader.normalizeSync(pkgName + '/');
      normalizedPkgName = normalizedPkgName.substr(0, normalizedPkgName.length - 1);

      var pkg = loader.packages[normalizedPkgName];
      var envMap = pkg.map['./' + subPath];

      // effective analog of the same function in SystemJS packages.js
      // to work out the path with defaultExtension added.
      // we cheat here and use normalizeSync to apply the right checks, while
      // skipping any map entry by temporarily removing it.
      function toPackagePath(subPath) {
        var pkgMap = pkg.map;
        pkg.map = {};
        var normalized = loader.normalizeSync(pkgName + '/' + subPath);
        pkg.map = pkgMap;
        return normalized;
      }

      var metadata = {};
      var fallback;
      return loader.locate({ name: toPackagePath(subPath), metadata: metadata })
      .then(function(address) {
        if (metadata.build === false)
          return;

        fallback = getCanonicalName(loader, address);

        // check if the fallback exists
        return new Promise(function(resolve) {
          fs.exists(fromFileURL(address), resolve);
        })
        .then(function(fallbackExists) {
          if (!fallbackExists)
            fallback = null;
        });
      })
      .then(function() {
        // allow build: false trace opt-out
        if (metadata.build === false)
          return null;

        // environment trace
        return loader.normalize(pkg.map['@env'] || '@system-env')
        .then(function(normalizedCondition) {
          var conditionModule = getCanonicalName(loader, normalizedCondition);

          return Promise.all(Object.keys(envMap).map(function(envCondition) {
            var mapping = envMap[envCondition];
            var negate = envCondition[0] == '~';

            return Promise.resolve()
            .then(function() {
              if (mapping == '.')
                return normalizedPkgName;
              else if (mapping.substr(0, 2) == './')
                return toPackagePath(mapping.substr(2))
              else
                return loader.normalize(mapping);
            })
            .then(function(normalizedMapping) {
              return {
                condition: (negate ? '~' : '') + conditionModule + '|' + (negate ? envCondition.substr(1) : envCondition),
                branch: getCanonicalName(loader, normalizedMapping)
              };
            });
          }));
        })
        .then(function(envs) {
          load.conditional = {
            envs: envs,
            fallback: fallback
          };
          return load;
        });
      });
    }

    // conditional interpolation
    var interpolationRegEx = /#\{[^\}]+\}/;
    var interpolationMatch = canonical.match(interpolationRegEx);
    if (interpolationMatch) {
      var condition = interpolationMatch[0].substr(2, interpolationMatch[0].length - 3);

      if (condition.indexOf('|') == -1)
        condition += '|default';

      var metadata = {};
      return Promise.resolve(loader.locate({ name: normalized.replace(interpolationRegEx, '*'), metadata: metadata }))
      .then(function(address) {
        // allow build: false trace opt-out
        if (metadata.build === false)
          return null;

        // glob the conditional interpolation variations from the filesystem
        if (address.substr(0, 8) != 'file:///')
          throw new Error('Error tracing ' + canonical + '. It is only possible to trace conditional interpolation for modules resolving to local file:/// URLs during the build.');

        var globIndex = address.indexOf('*');
        return asp(glob)(fromFileURL(address), { dot: true, nobrace: true, noglobstar: true, noext: true, nodir: true })
        .then(function(paths) {
          var branches = {};
          paths.forEach(function(path) {
            path = toFileURL(path);

            var pathCanonical = getCanonicalName(loader, path);
            var interpolate = pathCanonical.substr(interpolationMatch.index, path.length - address.length + 1);

            if (metadata.plugin) {
              if (loader.pluginFirst)
                pathCanonical = metadata.plugin + '!' + pathCanonical;
              else
                pathCanonical = pathCanonical + '!' + metadata.plugin;
            }
            branches[interpolate] = pathCanonical;
          });

          load.conditional = {
            condition: condition,
            branches: branches
          };
          return load;
        });
      });
    }

    // -- trace loader hooks --
    return Promise.resolve(loader.locate({ name: normalized, metadata: load.metadata = {}}))
    .then(function(address) {
      // build: false build config - null load record
      if (load.metadata.build === false)
        return null;

      // build: false plugins - runtime plugins
      if (load.metadata.loaderModule && load.metadata.loaderModule.build === false)
        return Promise.resolve(loader.normalize(load.metadata.loader, normalized))
        .then(function(pluginNormalized) {
          load.runtimePlugin = getCanonicalName(loader, pluginNormalized);
          return load;
        });

      load.address = address;
      return Promise.resolve(loader.fetch({ name: normalized, metadata: load.metadata, address: load.address }))
      .then(function(source) {
        load.originalSource = source;
        return loader.translate({ name: normalized, metadata: load.metadata, address: load.address, source: source });
      })
      .then(function(source) {
        load.source = source;
        return loader.instantiate({ name: normalized, metadata: load.metadata, address: load.address, source: source });
      })
      .then(function(result) {
        if (!result)
          throw new TypeError('Native ES Module builds not supported. Ensure transpilation is included in the loader pipeline.');

        load.deps = result.deps;

        // normalize dependencies to populate depMap
        load.depMap = {};
        return Promise.all(result.deps.map(function(dep) {
          return loader.normalize(dep, normalized, load.address)
          .then(function(normalized) {
            load.depMap[dep] = getCanonicalName(loader, normalized);
          });
        }));
      })
      .then(function() {
        // remove unnecessary metadata for trace
        delete load.metadata.execute;
        delete load.metadata.builderExecute;
        delete load.metadata.parseTree;

        return load;
      });
    });
  })
  .then(function(load) {
    return loads[canonical] = load;
  });
};

/*
 * Returns the full trace tree of a module
 *
 * - traceAllConditionals indicates if conditional boundaries should be traversed during the trace.
 * - conditionalEnv represents the conditional tracing environment module values to impose on the trace
 *   forcing traces for traceAllConditionals false, and skipping traces for traceAllConditionals true.
 *
 * conditionalEnv provides canonical condition tracing rules of the form:
 *
 *  {
 *    'some/interpolation|value': true, // include ALL variations
 *    'another/interpolation|value': false, // include NONE
 *    'custom/interpolation|value': ['specific', 'values']
 *
 *    // default BOOLEAN entry::
 *    '@system-env|browser': false,
 *    '~@system-env|browser': false
 *
 *    // custom boolean entry
 *    // boolean only checks weak truthiness to allow overlaps
 *    '~@system-env|node': true
 *  }
 *
 */
Trace.prototype.getAllLoadRecords = function(canonical, traceAllConditionals, conditionalEnv, curLoads) {
  var loader = this.loader;

  curLoads = curLoads || {};

  if (canonical in curLoads)
    return curLoads;

  var self = this;
  return this.getLoadRecord(canonical)
  .then(function(load) {
    // conditionals, build: false and system modules are null loads in the trace trees
    // (that is, part of depcache, but not built)
    // we skip system modules starting with @ though
    if (canonical[0] != '@')
      curLoads[canonical] = load && load.conditional ? null : load;

    if (load)
      return Promise.all(Trace.getLoadDependencies(load, traceAllConditionals, conditionalEnv).map(function(dep) {
        return self.getAllLoadRecords(dep, traceAllConditionals, conditionalEnv, curLoads);
      }));
  })
  .then(function() {
    return curLoads;
  });
};

// helper function -> returns the "condition" build of a tree
// that is the modules needed to determine the exact conditional solution of the tree
Trace.prototype.getConditionLoadRecords = function(canonical, inConditionTree, curLoads) {
  var loader = this.loader;

  curLoads = curLoads || {};

  if (canonical in curLoads)
    return curLoads;

  var self = this;
  return this.getLoadRecord(canonical)
  .then(function(load) {
    if (inConditionTree)
      curLoads[canonical] = load && load.conditional ? null : load;

    if (load)
      return Promise.all(Trace.getLoadDependencies(load, true, {}).map(function(dep) {
        return self.getConditionLoadRecords(dep, inConditionTree || !!load.conditional, curLoads);
      }));
  })
  .then(function() {
    return curLoads;
  });
}

// Returns the ordered immediate dependency array from the trace of a module
Trace.getLoadDependencies = function(load, traceAllConditionals, conditionalEnv) {
  traceAllConditionals = traceAllConditionals || true;
  conditionalEnv = conditionalEnv || {};

  var deps = [];

  function envTrace(condition) {
    // trace the condition modules as dependencies themselves
    var exportIndex = condition.lastIndexOf('|');
    if (condition[0] == '~')
      deps.push(condition.substr(1, exportIndex - 1));
    else
      deps.push(condition.substr(0, exportIndex));

    // return the environment trace info
    var envTrace = conditionalEnv[condition];
    return envTrace === undefined ? traceAllConditionals : envTrace;
  }

  // conditional load records have their branches all included in the trace
  var conditional = load.conditional;
  if (conditional) {
    // { condition, branch } boolean conditional
    if (conditional.branch) {
      if (envTrace(conditional.condition))
        deps.push(branch);
    }

    // { envs: [{condition, branch},...], fallback } package environment map
    else if (conditional.envs) {
      conditional.envs.forEach(function(env) {
        if (envTrace(env.condition))
          deps.push(env.branch);
      });
      if (conditional.fallback)
        deps.push(conditional.fallback);
    }

    // { condition, branches } conditional interpolation
    else if (conditional.branches) {
      var et = envTrace(conditional.condition);
      if (et) {
        Object.keys(conditional.branches).forEach(function(branch) {
          var dep = conditional.branches[branch];
          if (et == true)
            deps.push(dep);
          else if (et.indexOf(branch) != -1)
            deps.push(dep);
        });
      }
    }

    // run the conditional trace
    return deps;
  }

  // if this record uses a runtime plugin (build: false), trace the plugin
  if (load.runtimePlugin)
    return [load.runtimePlugin];

  // standard dep trace
  return load.deps.map(function(dep) {
    return load.depMap[dep];
  });
};

function getGraphEntryPoints(graph, entryPoints) {
  entryPoints = [].concat(entryPoints || []);

  var modules = Object.keys(graph.adjList);
  var discarded = {};

  modules.forEach(function (moduleName) {
    Object.keys(graph.adjList[moduleName]).forEach(function (depName) {
      discarded[depName] = true;
    });
  });

  modules.filter(function (moduleName) {
    return !discarded[moduleName];
  }).sort().forEach(function (moduleName) {
    if (entryPoints.indexOf(moduleName) === -1) {
      entryPoints.push(moduleName);
    }
  });

  return entryPoints;
}

Trace.getTreeModulesPostOrder = function getTreeModulesPostOrder(tree, entryPoints) {
  entryPoints = entryPoints || [];

  // Post order traversal sorted module list
  var postOrder = [];
  var graph = new Graph(true);

  // Seed graph with all relations
  Object.keys(tree).forEach(function (moduleName) {
    var load = tree[moduleName];

    if (!load)
      return;

    if (!graph.adjList[moduleName]) {
      graph.addVertex(moduleName);
    }

    Trace.getLoadDependencies(load).forEach(function (depName) {
      graph.addEdge(moduleName, depName);
    });
  });

  // Post order traversal of graph, one per entryPoint
  getGraphEntryPoints(graph, entryPoints).forEach(function (entryPoint) {
    depthFirst(graph, entryPoint, {
      leaveVertex: function (moduleName) {
        // Avoid duplicates and modules that have been skipped by tracer
        if (postOrder.indexOf(moduleName) === -1 && moduleName in tree) {
          postOrder.push(moduleName);
        }
      }
    });
  });

  return postOrder;
};
<|endoftext|>


<commit_before>module.exports = {
    auth: false,
    custom: {},
    env: 'DEV',
    id: 'git',
    lang: 'en',
    test: {
        node: [function (cb) {
            cb(false, 'no node tests have been defined')
        }],
        features: [function (cb) {
            cb(false, 'no feature tests have been defined')
        }]
    },
    defaultContentType: 'text/plain',
    name: 'my_service',
    path: '/service-status',
    state: {
        good: 'GOOD',
        bad: 'BAD',
        warn: 'WARN'
    },
    usage: true,
    version: '0.0.0'
}
<commit_msg>Upgraded default tests to the new format (function that returns a promise)<commit_after>module.exports = {
    auth: false,
    custom: {},
    env: 'DEV',
    id: 'git',
    lang: 'en',
    test: {
        node: [
            function () {
                return Promise.resolve('no node tests have been defined')
            }
        ],
        features: [
            function () {
                return Promise.resolve('no feature tests have been defined')
            }
        ]
    },
    defaultContentType: 'text/plain',
    name: 'my_service',
    path: '/service-status',
    state: {
        good: 'GOOD',
        bad: 'BAD',
        warn: 'WARN'
    },
    usage: true,
    version: '0.0.0'
}
<|endoftext|>


<commit_before>var fs = require('fs'),
    watch = require('node-watch'),
    logger = require('./log.js');

function deleteFolder(path)
{
    if(fs.existsSync(path))
    {
        fs.readdirSync(path).forEach(function(file,index)
        {
            var curPath = path + "/" + file;
            if(fs.lstatSync(curPath).isDirectory())
                deleteFolder(curPath);
            else
                fs.unlinkSync(curPath);
        });
        fs.rmdirSync(path);
    }
};

function exec(cmd, callback) {
    var exec = require('child_process').exec;
    exec(cmd, function(error, stdout, stderr) {
        if(error)
            console.log(error);
        if(stderr)
            console.log(stderr);
        if(stdout)
            console.log(stdout);
        if(callback)
            callback();
    });
}

module.exports = {
    wrap: (config) => {
        config.rmdir = deleteFolder;
        config.exec = exec;
        config.watch = watch;
    },
    load: (configFilePath, cmdArgument, callback) => {
        fs.readFile(configFilePath, (e, content) => {
            var config = e ? null : JSON.parse(content);
            if(!config)
            {
                logger.error(' [!] Can\'t find a configuration file');
                return;
            }
            if(!config.devWatch)
            {
                logger.error(' [!] Can\'t find \'devWatch\' in the configuration file');
                return;
            }
            config = config.devWatch;
            if(!config.cmd)
            {
                if(!cmdArgument)
                {
                    logger.error(' [!] Can\'t find \'cmd\' in the configuration file or as process argument');
                    return;
                }
                config.cmd = cmdArgument;
            }

            module.exports.init(config);

            callback(config);
        })
    },
    init: (config, isVerbose = false) => {
        logger.config = config;

        if(config.isVerbose === undefined || config.isVerbose === null)
            config.isVerbose = true;

        if(!config.cmd)
        {
            if(isVerbose)
                logger.error(' [!] Can\'t find \'cmd\' in the configuration');
            return;
        }

        module.exports.wrap(config);

        function log(text)
        {
            if(isVerbose)
                logger.log(text);
        }

        switch(config.cmd)
        {
            case 'clean':
                log(' [i] Clean mode');
                config.cmd = (f, o, config, callback) => {
                    if(config.noClean)
                        return;
                    o.clean(config, callback);
                    log(' [o] cleaned ' + f);
                };
                break;

            case 'dev':
                log(' [i] Development mode');
                config.cmd = (f, o, config, callback) => {
                    if(o.devRun)
                    {
                        o.devRun(config, callback);
                        log(' [o] loaded ' + f);
                    }
                    else if(o.run)
                    {
                        o.run(config, callback);
                        log(' [o] loaded ' + f);
                    }
                }
                break;

            case 'prod':
                log(' [i] Production mode');
                config.cmd = (f, o, config, callback) => {
                    if(o.prodRun)
                    {
                        o.prodRun(config, callback);
                        log(' [o] loaded ' + f);
                    }
                    else if(o.run)
                    {
                        o.run(config, callback);
                        log(' [o] loaded ' + f);
                    }
                }
                break;

            default:
                log(' [!] Unkown command ' + config.cmd);
                return;
        }
    }
}<commit_msg>Added more arguments to exec callback<commit_after>var fs = require('fs'),
    watch = require('node-watch'),
    logger = require('./log.js');

function deleteFolder(path)
{
    if(fs.existsSync(path))
    {
        fs.readdirSync(path).forEach(function(file,index)
        {
            var curPath = path + "/" + file;
            if(fs.lstatSync(curPath).isDirectory())
                deleteFolder(curPath);
            else
                fs.unlinkSync(curPath);
        });
        fs.rmdirSync(path);
    }
};

function exec(cmd, callback) {
    var exec = require('child_process').exec;
    exec(cmd, function(error, stdout, stderr) {
        if(error)
            console.log(error);
        if(stderr)
            console.log(stderr);
        if(stdout)
            console.log(stdout);
        if(callback)
            callback(error, stdout, stderr);
    });
}

module.exports = {
    wrap: (config) => {
        config.rmdir = deleteFolder;
        config.exec = exec;
        config.watch = watch;
    },
    load: (configFilePath, cmdArgument, callback) => {
        fs.readFile(configFilePath, (e, content) => {
            var config = e ? null : JSON.parse(content);
            if(!config)
            {
                logger.error(' [!] Can\'t find a configuration file');
                return;
            }
            if(!config.devWatch)
            {
                logger.error(' [!] Can\'t find \'devWatch\' in the configuration file');
                return;
            }
            config = config.devWatch;
            if(!config.cmd)
            {
                if(!cmdArgument)
                {
                    logger.error(' [!] Can\'t find \'cmd\' in the configuration file or as process argument');
                    return;
                }
                config.cmd = cmdArgument;
            }

            module.exports.init(config);

            callback(config);
        })
    },
    init: (config, isVerbose = false) => {
        logger.config = config;

        if(config.isVerbose === undefined || config.isVerbose === null)
            config.isVerbose = true;

        if(!config.cmd)
        {
            if(isVerbose)
                logger.error(' [!] Can\'t find \'cmd\' in the configuration');
            return;
        }

        module.exports.wrap(config);

        function log(text)
        {
            if(isVerbose)
                logger.log(text);
        }

        switch(config.cmd)
        {
            case 'clean':
                log(' [i] Clean mode');
                config.cmd = (f, o, config, callback) => {
                    if(config.noClean)
                        return;
                    o.clean(config, callback);
                    log(' [o] cleaned ' + f);
                };
                break;

            case 'dev':
                log(' [i] Development mode');
                config.cmd = (f, o, config, callback) => {
                    if(o.devRun)
                    {
                        o.devRun(config, callback);
                        log(' [o] loaded ' + f);
                    }
                    else if(o.run)
                    {
                        o.run(config, callback);
                        log(' [o] loaded ' + f);
                    }
                }
                break;

            case 'prod':
                log(' [i] Production mode');
                config.cmd = (f, o, config, callback) => {
                    if(o.prodRun)
                    {
                        o.prodRun(config, callback);
                        log(' [o] loaded ' + f);
                    }
                    else if(o.run)
                    {
                        o.run(config, callback);
                        log(' [o] loaded ' + f);
                    }
                }
                break;

            default:
                log(' [!] Unkown command ' + config.cmd);
                return;
        }
    }
}<|endoftext|>


<commit_before>/* AIDA Source Code */
/* Contributors located at: github.com/2nd47/CSC309-A4 */

// router middleware

var express = require('express');
var router = express.Router();

// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
  console.log('Time: ', Date.now());
  next();
});

// change the request methods as required
// refer to express documentation for more details

// if logged in: feed; else: landing page
router.get('/', function (req, res, next) {
  res.send('AIDA Home Page!');
});

router.post('/login', function (req, res, next) {
  res.send('AIDA Home Page!');
});

router.post('/signup', function (req, res, next) {
  res.send('AIDA Home Page!');
});

// list of contracts
router.get('/contracts', function (req, res) {
});

// create a new contract
router.get('/contracts/new', function (req, res) {
});

// details of contract with contract_id
router.get('/contracts/:contract_id', function (req, res) {
	/*
	Contract page:

	{
		id: contract id,
		title: contract title,
		employer_id: employer id,
		employer_name: employer name,
		project_id: project id,
		project_name: project name,
		status: contract status,
		latest_update: date of the latest update,
		tags: [tag names],
		budget: budget level estimation between 1 to 5,
		deadline: contract deadline,
		intro: introduction to the contract details
	}*/
	var json = new Object();
	var contrat_id = req.params.contract_id;
	var contract = db.Contract.findOne({"_id": ObjectId(contract_id)});
	json.id = contract_id;
	json.title = contract.name;
	json.employer_id = contract.owner;
	json.employer_name = db.User.findOne({"_id": ObjectId(contract.owner)},{name: 1}).name;
	json.project_id = contract.project;
	json.project_name = db.Project.findOne({"_id": ObjectId(contract.project)},{name: 1}).name;

});

// list of profiles
router.get('/people', function (req, res) {
});

// details of people with user_id
router.get('/people/:user_id', function (req, res) {
});

// list of projects
router.get('/projects', function (req, res, next) {
  res.send('AIDA Home Page!');
});

// create a new project
router.get('/projects/new', function (req, res, next) {
  res.send('AIDA Home Page!');
});

// details of project with project_id
router.get('/projects/:project_id', function (req, res, next) {
	/*
	{
		id: project id,
		title: project title,
		publisher:
		{
			publisher_id: id of owner,
			publisher_name: name of project owner
		},
		members:
		[
			{
				member_id: id of owner,
				member_name: name of project owner
			}
		],
		short_intro: short description, 300 characters max,
		long_intro:
		[
			{
				paragraph_title: title,
				paragraph_content: content
			}
		],
		showcase:
		[
			{
				path: path to the file,
				type: type of the file
			}
		]
		latest_update: date of the latest update, in seconds,
		status: project status,
		tags: [list of tags],
		open_contracts:
		[
			{
				contract_id: contract id,
				contract_title: contract title,
				contract_tags: [list of skill names with ratings],
				contract_budget: budget level estimation between 1 to 5,
				contract_deadline: contract deadline
			}
		],
	}*/
	var json = new Object();
	var project_id = req.params.project_id;
	var project = db.Project.findOne({"_id" : ObjectId(project_id)});
	// Build the file
	json.id = project_id;
	json.title = project.name;
	json.publisher = new Object();
	json.publisher.publisher_id = project.ownerUsername;
	var publisher_info = db.User.findOne({"username": project.ownerUsername}, {name: 1});
	json.publisher.publisher_name = publisher_info.name;
	json.members = [];
	var i;
	var numMembers = project.members.length;
	for (i=0;i<numMembers;i++) {
		var newMember = new Object();
		newMember.member_id = project.members[i].user;
		var memberName = db.User.findOne({"_id": project.members[i].user}, {name: 1});
		newMember.member_name = memberName.name;
		json.members.push(newMember);
	}
	json.short_intro = project.basicInfo;
	json.long_intro = [];
	var numParagraph = project.detailedInfo.length;
	for (i=0;i<numParagraph;i++) {
		var newParagraph = new Object();
		newParagraph.paragraph_title = project.detailedInfo[i].title;
		newParagraph.paragraph_content = project.detailedInfo[i].content;
		json.long_intro.push(newParagraph);
	}
	json.showcase = [];
	var numShowcase = project.showcase.assetPaths.length;
	for (i=0;i<numShowcase;i++) {
		var current_path = project.showcase.assetPaths[i];
		var current_type = project.showcase.mediaTypes[i];
		var newShowcase = new Object();
		newShowcase.path = current_path;
		newShowcase.type = currentcurrent_type;
	}
	json.latest_update = project.updatedAt;
	json.status = project.status;
	json.tags = project.tags;
	var contracts = db.Contract.find({"project": ObjectId(project_id)});
	json.open_contracts = [];
	while (contracts.hasNext()) {
		var newContract = new Object();
		var current = contracts.next();
		newContract.contract_id = current._id;
		newContract.contract_title = current.name;
		newContract.contract_tags = current.skillTags;
		newContract.contract_budget = current.budget;
		newContract.contract_deadline = current.deadline;
	}
	res.send(JSON.stringify(json));
  //res.send('AIDA Home Page!');
});

// message inbox
router.get('/inbox', function (req, res) {
});

// search page
router.get('/search', function (req, res) {
});

// etc...

module.exports = router;
<commit_msg>Add contract page router<commit_after>/* AIDA Source Code */
/* Contributors located at: github.com/2nd47/CSC309-A4 */

// router middleware

var express = require('express');
var router = express.Router();

// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
  console.log('Time: ', Date.now());
  next();
});

// change the request methods as required
// refer to express documentation for more details

// if logged in: feed; else: landing page
router.get('/', function (req, res, next) {
  res.send('AIDA Home Page!');
});

router.post('/login', function (req, res, next) {
  res.send('AIDA Home Page!');
});

router.post('/signup', function (req, res, next) {
  res.send('AIDA Home Page!');
});

// list of contracts
router.get('/contracts', function (req, res) {
});

// create a new contract
router.get('/contracts/new', function (req, res) {
});

// details of contract with contract_id
router.get('/contracts/:contract_id', function (req, res) {
	/*
	Contract page:

	{
		id: contract id,
		title: contract title,
		employer_id: employer id,
		employer_name: employer name,
		project_id: project id,
		project_name: project name,
		status: contract status,
		latest_update: date of the latest update,
		tags: [tag names],
		budget: budget level estimation between 1 to 5,
		deadline: contract deadline,
		intro: introduction to the contract details
	}*/
	var json = new Object();
	var contrat_id = req.params.contract_id;
	var contract = db.Contract.findOne({"_id": ObjectId(contract_id)});
	json.id = contract_id;
	json.title = contract.name;
	json.employer_id = contract.owner;
	json.employer_name = db.User.findOne({"_id": ObjectId(contract.owner)},{name: 1}).name;
	json.project_id = contract.project;
	json.project_name = db.Project.findOne({"_id": ObjectId(contract.project)},{name: 1}).name;
	if (contract.taker) {
		json.status = "signed";
	}
	else {
		json.status = "open";
	}
	json.latest_update = contract.updatedAt;
	json.tags = contract.skillTags;
	json.budget = contract.budget;
	json.deadline = contract.deadline;
	json.intro = contract.details;
	res.send(JSON.stringify(json));
});

// list of profiles
router.get('/people', function (req, res) {
});

// details of people with user_id
router.get('/people/:user_id', function (req, res) {
});

// list of projects
router.get('/projects', function (req, res, next) {
  res.send('AIDA Home Page!');
});

// create a new project
router.get('/projects/new', function (req, res, next) {
  res.send('AIDA Home Page!');
});

// details of project with project_id
router.get('/projects/:project_id', function (req, res, next) {
	/*
	{
		id: project id,
		title: project title,
		publisher:
		{
			publisher_id: id of owner,
			publisher_name: name of project owner
		},
		members:
		[
			{
				member_id: id of owner,
				member_name: name of project owner
			}
		],
		short_intro: short description, 300 characters max,
		long_intro:
		[
			{
				paragraph_title: title,
				paragraph_content: content
			}
		],
		showcase:
		[
			{
				path: path to the file,
				type: type of the file
			}
		]
		latest_update: date of the latest update, in seconds,
		status: project status,
		tags: [list of tags],
		open_contracts:
		[
			{
				contract_id: contract id,
				contract_title: contract title,
				contract_tags: [list of skill names with ratings],
				contract_budget: budget level estimation between 1 to 5,
				contract_deadline: contract deadline
			}
		],
	}*/
	var json = new Object();
	var project_id = req.params.project_id;
	var project = db.Project.findOne({"_id" : ObjectId(project_id)});
	// Build the file
	json.id = project_id;
	json.title = project.name;
	json.publisher = new Object();
	json.publisher.publisher_id = project.ownerUsername;
	var publisher_info = db.User.findOne({"username": project.ownerUsername}, {name: 1});
	json.publisher.publisher_name = publisher_info.name;
	json.members = [];
	var i;
	var numMembers = project.members.length;
	for (i=0;i<numMembers;i++) {
		var newMember = new Object();
		newMember.member_id = project.members[i].user;
		var memberName = db.User.findOne({"_id": project.members[i].user}, {name: 1});
		newMember.member_name = memberName.name;
		json.members.push(newMember);
	}
	json.short_intro = project.basicInfo;
	json.long_intro = [];
	var numParagraph = project.detailedInfo.length;
	for (i=0;i<numParagraph;i++) {
		var newParagraph = new Object();
		newParagraph.paragraph_title = project.detailedInfo[i].title;
		newParagraph.paragraph_content = project.detailedInfo[i].content;
		json.long_intro.push(newParagraph);
	}
	json.showcase = [];
	var numShowcase = project.showcase.assetPaths.length;
	for (i=0;i<numShowcase;i++) {
		var current_path = project.showcase.assetPaths[i];
		var current_type = project.showcase.mediaTypes[i];
		var newShowcase = new Object();
		newShowcase.path = current_path;
		newShowcase.type = currentcurrent_type;
	}
	json.latest_update = project.updatedAt;
	json.status = project.status;
	json.tags = project.tags;
	var contracts = db.Contract.find({"project": ObjectId(project_id)});
	json.open_contracts = [];
	while (contracts.hasNext()) {
		var newContract = new Object();
		var current = contracts.next();
		newContract.contract_id = current._id;
		newContract.contract_title = current.name;
		newContract.contract_tags = current.skillTags;
		newContract.contract_budget = current.budget;
		newContract.contract_deadline = current.deadline;
	}
	res.send(JSON.stringify(json));
  //res.send('AIDA Home Page!');
});

// message inbox
router.get('/inbox', function (req, res) {
});

// search page
router.get('/search', function (req, res) {
});

// etc...

module.exports = router;
<|endoftext|>


<commit_before>/*global Uint8Array, Uint16Array*/

var utils = require('./utils');
var isRegExp = utils.isRegExp;
var leftPad = utils.leftPad;
var shim = require('./shim');
var json = shim.JSON;
var every = shim.every;
var some = shim.some;
var forEach = shim.forEach;
var map = shim.map;
var getKeys = shim.getKeys;
var reduce = shim.reduce;
var extend = utils.extend;

module.exports = function (expect) {
    expect.addType({
        name: 'fallback',
        identify: function (value) {
            return true;
        },
        equal: function (a, b) {
            return a === b;
        },
        inspect: function (output, value) {
            return output.text(value);
        },
        toJSON: function (value) {
            return value;
        }
    });

    expect.addType({
        name: 'object',
        identify: function (arr) {
            return typeof arr === 'object';
        },
        equal: function (a, b, equal) {
            if (a === b) {
                return true;
            }

            // an identical "prototype" property.
            if (a.prototype !== b.prototype) {
                return false;
            }
            //~~~I've managed to break Object.keys through screwy arguments passing.
            //   Converting to array solves the problem.
            if (utils.isArguments(a)) {
                if (!utils.isArguments(b)) {
                    return false;
                }
                return equal(Array.prototype.slice.call(a), Array.prototype.slice.call(b));
            }
            var actualKeys = utils.getKeysOfDefinedProperties(a),
                expectedKeys = utils.getKeysOfDefinedProperties(b),
                key,
                i;

            // having the same number of owned properties (keys incorporates hasOwnProperty)
            if (actualKeys.length !== expectedKeys.length) {
                return false;
            }
            //the same set of keys (although not necessarily the same order),
            actualKeys.sort();
            expectedKeys.sort();
            //~~~cheap key test
            for (i = actualKeys.length - 1; i >= 0; i -= 1) {
                if (actualKeys[i] !== expectedKeys[i]) {
                    return false;
                }
            }

            //equivalent values for every corresponding key, and
            //~~~possibly expensive deep test
            for (i = actualKeys.length - 1; i >= 0; i -= 1) {
                key = actualKeys[i];
                if (!equal(a[key], b[key])) {
                    return false;
                }
            }
            return true;
        },
        inspect: function (output, obj, inspect) {
            var keys = getKeys(obj);
            if (keys.length === 0) {
                return output.text('{}');
            }

            var inspectedItems = map(keys, function (key) {
                var propertyOutput = output.clone();
                if (key.match(/["' ]/)) {
                    propertyOutput.strings(expect.inspect(key));
                } else {
                    propertyOutput.key(key);
                }
                propertyOutput.text(':');

                var hasGetter = obj.__lookupGetter__ && obj.__lookupGetter__(key);
                var hasSetter = obj.__lookupGetter__ && obj.__lookupSetter__(key);

                if (hasGetter || !hasSetter) {
                    propertyOutput.sp().append(inspect(propertyOutput.clone(), obj[key]));
                }

                if (hasGetter && hasSetter) {
                    propertyOutput.sp().text('[Getter/Setter]');
                } else if (hasGetter) {
                    propertyOutput.sp().text('[Getter]');
                } else if (hasSetter) {
                    propertyOutput.sp().text('[Setter]');
                }

                return propertyOutput;
            });

            var width = 0;
            var multipleLines = some(inspectedItems, function (o) {
                var size = o.size();
                width += size.width;
                return width > 50 || size.height > 1;
            });

            forEach(inspectedItems, function (inspectedItem, index) {
                var lastIndex = index === inspectedItems.length - 1;

                if (!lastIndex) {
                    inspectedItem.text(',');
                }
            });

            if (multipleLines) {
                output.text('{').nl().indentLines();

                forEach(inspectedItems, function (inspectedItem, index) {
                    output.i().block(inspectedItem).nl();
                });

                output.outdentLines().text('}');
            } else {
                output.text('{ ');
                forEach(inspectedItems, function (inspectedItem, index) {
                    output.append(inspectedItem);
                    var lastIndex = index === inspectedItems.length - 1;
                    if (!lastIndex) {
                        output.sp();
                    }
                });
                output.text(' }');
            }
            return output;
        },
        toJSON: function (obj, toJSON) {
            return reduce(getKeys(obj), function (result, key) {
                result[key] = toJSON(obj[key]);
                return result;
            }, {});
        }
    });

    expect.addType({
        name: 'array',
        identify: function (arr) {
            return utils.isArray(arr) || utils.isArguments(arr);
        },
        equal: function (a, b, equal) {
            return a === b || (a.length === b.length && every(a, function (v, index) {
                return equal(v, b[index]);
            }));
        },
        inspect: function (output, arr, inspect, depth) {
            if (arr.length === 0) {
                return output.text('[]');
            }

            if (depth === 1) {
                return output.text('[...]');
            }

            var inspectedItems = map(arr, function (v) {
                return inspect(output.clone(), v);
            });

            var width = 0;
            var multipleLines = some(inspectedItems, function (o) {
                var size = o.size();
                width += size.width;
                return width > 50 || o.height > 1;
            });

            forEach(inspectedItems, function (inspectedItem, index) {
                var lastIndex = index === inspectedItems.length - 1;
                if (!lastIndex) {
                    inspectedItem.text(',');
                }
            });

            if (multipleLines) {
                output.text('[').nl().indentLines();

                forEach(inspectedItems, function (inspectedItem, index) {
                    output.i().block(inspectedItem).nl();
                });

                output.outdentLines().text(']');
            } else {
                output.text('[ ');
                forEach(inspectedItems, function (inspectedItem, index) {
                    output.append(inspectedItem);
                    var lastIndex = index === inspectedItems.length - 1;
                    if (!lastIndex) {
                        output.sp();
                    }
                });
                output.text(' ]');
            }
            return output;
        },
        toJSON: function (arr, toJSON) {
            return map(arr, toJSON);
        }
    });

    expect.addType({
        base: 'object',
        name: 'Error',
        identify: function (value) {
            return utils.isError(value);
        },
        equal: function (a, b, equal) {
            return a === b ||
                (equal(a.message, b.message) && this.baseType.equal(a, b, equal));
        },
        inspect: function (output, value, inspect) {
            var errorObject = extend({
                message: value.message
            }, value);
            return output.text('[Error: ').append(inspect(output.clone(), errorObject)).text(']');
        }
    });

    expect.addType({
        name: 'date',
        identify: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Date]';
        },
        equal: function (a, b) {
            return a.getTime() === b.getTime();
        },
        inspect: function (output, date) {
            return output.text('[Date ').text(date.toUTCString()).text(']');
        },
        toJSON: function (date) {
            return {
                $Date: expect.inspect(date)
            };
        }
    });

    expect.addType({
        name: 'function',
        identify: function (f) {
            return typeof f === 'function';
        },
        equal: function (a, b) {
            return a === b || a.toString() === b.toString();
        },
        inspect: function (output, f) {
            output.text('[Function');
            if (f.name) {
                output.text(': ').text(f.name);
            }
            output.text(']');
            return output;
        },
        toJSON: function (f) {
            return {
                $Function: expect.inspect(f)
            };
        }
    });

    expect.addType({
        name: 'regexp',
        identify: isRegExp,
        equal: function (a, b) {
            return a === b || (
                a.source === b.source &&
                    a.global === b.global &&
                    a.ignoreCase === b.ignoreCase &&
                    a.multiline === b.multiline
            );
        },
        inspect: function (output, regExp) {
            return output.text(regExp);
        },
        toJSON: function (regExp) {
            return {
                $RegExp: expect.inspect(regExp)
            };
        }
    });

    expect.addType({
        name: 'DomElement',
        identify: function (value) {
            return utils.isDOMElement(value);
        },
        inspect: function (output, value) {
            return output.text(utils.getOuterHTML(value));
        }
    });

    function getHexDumpLinesForBufferLikeObject(obj, width, digitWidth) {
        digitWidth = digitWidth || 2;
        var hexDumpLines = [];
        if (typeof width !== 'number') {
            width = 16;
        } else if (width === 0) {
            width = obj.length;
        }
        for (var i = 0 ; i < obj.length ; i += width) {
            var hexChars = '',
                asciiChars = ' |';

            for (var j = 0 ; j < width ; j += 1) {
                if (i + j < obj.length) {
                    var octet = obj[i + j];
                    hexChars += leftPad(octet.toString(16).toUpperCase(), digitWidth, '0') + ' ';
                    asciiChars += (octet >= 32 && octet < 127) ? String.fromCharCode(octet) : '.';
                } else if (digitWidth === 2) {
                    hexChars += '   ';
                }
            }

            if (digitWidth === 2) {
                asciiChars += '|';
                hexDumpLines.push(hexChars + asciiChars);
            } else {
                hexDumpLines.push(hexChars.replace(/\s+$/, ''));
            }
        }
        return hexDumpLines;
    }

    function inspectBufferLikeObject(output, buffer, digitWidth) {
        var maxLength = 20;
        if (buffer.length > maxLength) {
            output.text(getHexDumpLinesForBufferLikeObject(buffer.slice(0, maxLength), 0, digitWidth))
                .text(' (+').text(buffer.length - maxLength).text(')');

        } else {
            var hexDumpLines = getHexDumpLinesForBufferLikeObject(buffer, 0, digitWidth);
            forEach(hexDumpLines, function (line, i) {
                if (i > 0) {
                    output.nl();
                }
                output.text(line);
            });
        }
        return output;
    }

    function bufferLikeObjectsEqual(a, b) {
        if (a === b) {
            return true;
        }

        if (a.length !== b.length) return false;

        for (var i = 0; i < a.length; i += 1) {
            if (a[i] !== b[i]) return false;
        }

        return true;
    }

    if (typeof Buffer !== 'undefined') {
        expect.addType({
            name: 'Buffer',
            identify: Buffer.isBuffer,
            equal: bufferLikeObjectsEqual,
            inspect: function (output, buffer) {
                return output.text('[Buffer ')
                    .append(inspectBufferLikeObject(buffer))
                    .text(']');
            },
            toJSON: function (buffer) {
                return {
                    $Buffer: getHexDumpLinesForBufferLikeObject(buffer)
                };
            }
        });
    }

    if (typeof Uint8Array !== 'undefined') {
        expect.addType({
            name: 'Uint8Array',
            identify: function (obj) {
                return obj && obj instanceof Uint8Array;
            },
            equal: bufferLikeObjectsEqual,
            inspect: function (output, uint8Array) {
                return output.text('[Uint8Array ')
                    .append(inspectBufferLikeObject(uint8Array))
                    .text(']');
            },
            toJSON: function (uint8Array) {
                return {
                    $Uint8Array: getHexDumpLinesForBufferLikeObject(uint8Array)
                };
            }
        });
    }

    if (typeof Uint16Array !== 'undefined') {
        expect.addType({
            name: 'Uint16Array',
            identify: function (obj) {
                return obj && obj instanceof Uint16Array;
            },
            equal: bufferLikeObjectsEqual,
            inspect: function (output, uint16Array) {
                return output.text('[Uint16Array ')
                    .append(inspectBufferLikeObject(uint16Array, 8, 4, false))
                    .text(']');
            },
            toJSON: function (uint16Array) {
                return {
                    $Uint16Array: getHexDumpLinesForBufferLikeObject(uint16Array, 8, 4, false)
                };
            }
        });
    }

    expect.addType({
        name: 'string',
        identify: function (value) {
            return typeof value === 'string';
        },
        inspect: function (output, value) {
            return output.strings('\'')
                .strings(json.stringify(value).replace(/^"|"$/g, '')
                      .replace(/'/g, "\\'")
                      .replace(/\\"/g, '"'))
                .strings('\'');
        }
    });

    expect.addType({
        name: 'number',
        identify: function (value) {
            return typeof value === 'number';
        }
    });

    expect.addType({
        name: 'boolean',
        identify: function (value) {
            return typeof value === 'boolean';
        }
    });

    expect.addType({
        name: 'undefined',
        identify: function (value) {
            return typeof value === 'undefined';
        }
    });

    expect.addType({
        name: 'null',
        identify: function (value) {
            return value === null;
        }
    });
};
<commit_msg>inspectBufferLikeObject: Accept a 'maxLength' parameter.<commit_after>/*global Uint8Array, Uint16Array*/

var utils = require('./utils');
var isRegExp = utils.isRegExp;
var leftPad = utils.leftPad;
var shim = require('./shim');
var json = shim.JSON;
var every = shim.every;
var some = shim.some;
var forEach = shim.forEach;
var map = shim.map;
var getKeys = shim.getKeys;
var reduce = shim.reduce;
var extend = utils.extend;

module.exports = function (expect) {
    expect.addType({
        name: 'fallback',
        identify: function (value) {
            return true;
        },
        equal: function (a, b) {
            return a === b;
        },
        inspect: function (output, value) {
            return output.text(value);
        },
        toJSON: function (value) {
            return value;
        }
    });

    expect.addType({
        name: 'object',
        identify: function (arr) {
            return typeof arr === 'object';
        },
        equal: function (a, b, equal) {
            if (a === b) {
                return true;
            }

            // an identical "prototype" property.
            if (a.prototype !== b.prototype) {
                return false;
            }
            //~~~I've managed to break Object.keys through screwy arguments passing.
            //   Converting to array solves the problem.
            if (utils.isArguments(a)) {
                if (!utils.isArguments(b)) {
                    return false;
                }
                return equal(Array.prototype.slice.call(a), Array.prototype.slice.call(b));
            }
            var actualKeys = utils.getKeysOfDefinedProperties(a),
                expectedKeys = utils.getKeysOfDefinedProperties(b),
                key,
                i;

            // having the same number of owned properties (keys incorporates hasOwnProperty)
            if (actualKeys.length !== expectedKeys.length) {
                return false;
            }
            //the same set of keys (although not necessarily the same order),
            actualKeys.sort();
            expectedKeys.sort();
            //~~~cheap key test
            for (i = actualKeys.length - 1; i >= 0; i -= 1) {
                if (actualKeys[i] !== expectedKeys[i]) {
                    return false;
                }
            }

            //equivalent values for every corresponding key, and
            //~~~possibly expensive deep test
            for (i = actualKeys.length - 1; i >= 0; i -= 1) {
                key = actualKeys[i];
                if (!equal(a[key], b[key])) {
                    return false;
                }
            }
            return true;
        },
        inspect: function (output, obj, inspect) {
            var keys = getKeys(obj);
            if (keys.length === 0) {
                return output.text('{}');
            }

            var inspectedItems = map(keys, function (key) {
                var propertyOutput = output.clone();
                if (key.match(/["' ]/)) {
                    propertyOutput.strings(expect.inspect(key));
                } else {
                    propertyOutput.key(key);
                }
                propertyOutput.text(':');

                var hasGetter = obj.__lookupGetter__ && obj.__lookupGetter__(key);
                var hasSetter = obj.__lookupGetter__ && obj.__lookupSetter__(key);

                if (hasGetter || !hasSetter) {
                    propertyOutput.sp().append(inspect(propertyOutput.clone(), obj[key]));
                }

                if (hasGetter && hasSetter) {
                    propertyOutput.sp().text('[Getter/Setter]');
                } else if (hasGetter) {
                    propertyOutput.sp().text('[Getter]');
                } else if (hasSetter) {
                    propertyOutput.sp().text('[Setter]');
                }

                return propertyOutput;
            });

            var width = 0;
            var multipleLines = some(inspectedItems, function (o) {
                var size = o.size();
                width += size.width;
                return width > 50 || size.height > 1;
            });

            forEach(inspectedItems, function (inspectedItem, index) {
                var lastIndex = index === inspectedItems.length - 1;

                if (!lastIndex) {
                    inspectedItem.text(',');
                }
            });

            if (multipleLines) {
                output.text('{').nl().indentLines();

                forEach(inspectedItems, function (inspectedItem, index) {
                    output.i().block(inspectedItem).nl();
                });

                output.outdentLines().text('}');
            } else {
                output.text('{ ');
                forEach(inspectedItems, function (inspectedItem, index) {
                    output.append(inspectedItem);
                    var lastIndex = index === inspectedItems.length - 1;
                    if (!lastIndex) {
                        output.sp();
                    }
                });
                output.text(' }');
            }
            return output;
        },
        toJSON: function (obj, toJSON) {
            return reduce(getKeys(obj), function (result, key) {
                result[key] = toJSON(obj[key]);
                return result;
            }, {});
        }
    });

    expect.addType({
        name: 'array',
        identify: function (arr) {
            return utils.isArray(arr) || utils.isArguments(arr);
        },
        equal: function (a, b, equal) {
            return a === b || (a.length === b.length && every(a, function (v, index) {
                return equal(v, b[index]);
            }));
        },
        inspect: function (output, arr, inspect, depth) {
            if (arr.length === 0) {
                return output.text('[]');
            }

            if (depth === 1) {
                return output.text('[...]');
            }

            var inspectedItems = map(arr, function (v) {
                return inspect(output.clone(), v);
            });

            var width = 0;
            var multipleLines = some(inspectedItems, function (o) {
                var size = o.size();
                width += size.width;
                return width > 50 || o.height > 1;
            });

            forEach(inspectedItems, function (inspectedItem, index) {
                var lastIndex = index === inspectedItems.length - 1;
                if (!lastIndex) {
                    inspectedItem.text(',');
                }
            });

            if (multipleLines) {
                output.text('[').nl().indentLines();

                forEach(inspectedItems, function (inspectedItem, index) {
                    output.i().block(inspectedItem).nl();
                });

                output.outdentLines().text(']');
            } else {
                output.text('[ ');
                forEach(inspectedItems, function (inspectedItem, index) {
                    output.append(inspectedItem);
                    var lastIndex = index === inspectedItems.length - 1;
                    if (!lastIndex) {
                        output.sp();
                    }
                });
                output.text(' ]');
            }
            return output;
        },
        toJSON: function (arr, toJSON) {
            return map(arr, toJSON);
        }
    });

    expect.addType({
        base: 'object',
        name: 'Error',
        identify: function (value) {
            return utils.isError(value);
        },
        equal: function (a, b, equal) {
            return a === b ||
                (equal(a.message, b.message) && this.baseType.equal(a, b, equal));
        },
        inspect: function (output, value, inspect) {
            var errorObject = extend({
                message: value.message
            }, value);
            return output.text('[Error: ').append(inspect(output.clone(), errorObject)).text(']');
        }
    });

    expect.addType({
        name: 'date',
        identify: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Date]';
        },
        equal: function (a, b) {
            return a.getTime() === b.getTime();
        },
        inspect: function (output, date) {
            return output.text('[Date ').text(date.toUTCString()).text(']');
        },
        toJSON: function (date) {
            return {
                $Date: expect.inspect(date)
            };
        }
    });

    expect.addType({
        name: 'function',
        identify: function (f) {
            return typeof f === 'function';
        },
        equal: function (a, b) {
            return a === b || a.toString() === b.toString();
        },
        inspect: function (output, f) {
            output.text('[Function');
            if (f.name) {
                output.text(': ').text(f.name);
            }
            output.text(']');
            return output;
        },
        toJSON: function (f) {
            return {
                $Function: expect.inspect(f)
            };
        }
    });

    expect.addType({
        name: 'regexp',
        identify: isRegExp,
        equal: function (a, b) {
            return a === b || (
                a.source === b.source &&
                    a.global === b.global &&
                    a.ignoreCase === b.ignoreCase &&
                    a.multiline === b.multiline
            );
        },
        inspect: function (output, regExp) {
            return output.text(regExp);
        },
        toJSON: function (regExp) {
            return {
                $RegExp: expect.inspect(regExp)
            };
        }
    });

    expect.addType({
        name: 'DomElement',
        identify: function (value) {
            return utils.isDOMElement(value);
        },
        inspect: function (output, value) {
            return output.text(utils.getOuterHTML(value));
        }
    });

    function getHexDumpLinesForBufferLikeObject(obj, width, digitWidth) {
        digitWidth = digitWidth || 2;
        var hexDumpLines = [];
        if (typeof width !== 'number') {
            width = 16;
        } else if (width === 0) {
            width = obj.length;
        }
        for (var i = 0 ; i < obj.length ; i += width) {
            var hexChars = '',
                asciiChars = ' |';

            for (var j = 0 ; j < width ; j += 1) {
                if (i + j < obj.length) {
                    var octet = obj[i + j];
                    hexChars += leftPad(octet.toString(16).toUpperCase(), digitWidth, '0') + ' ';
                    asciiChars += (octet >= 32 && octet < 127) ? String.fromCharCode(octet) : '.';
                } else if (digitWidth === 2) {
                    hexChars += '   ';
                }
            }

            if (digitWidth === 2) {
                asciiChars += '|';
                hexDumpLines.push(hexChars + asciiChars);
            } else {
                hexDumpLines.push(hexChars.replace(/\s+$/, ''));
            }
        }
        return hexDumpLines;
    }

    function inspectBufferLikeObject(output, buffer, digitWidth, maxLength) {
        if (typeof maxLength !== 'number') {
            maxLength = 20;
        }
        if (buffer.length > maxLength) {
            output.text(getHexDumpLinesForBufferLikeObject(buffer.slice(0, maxLength), 0, digitWidth))
                .text(' (+').text(buffer.length - maxLength).text(')');

        } else {
            var hexDumpLines = getHexDumpLinesForBufferLikeObject(buffer, 0, digitWidth);
            forEach(hexDumpLines, function (line, i) {
                if (i > 0) {
                    output.nl();
                }
                output.text(line);
            });
        }
        return output;
    }

    function bufferLikeObjectsEqual(a, b) {
        if (a === b) {
            return true;
        }

        if (a.length !== b.length) return false;

        for (var i = 0; i < a.length; i += 1) {
            if (a[i] !== b[i]) return false;
        }

        return true;
    }

    if (typeof Buffer !== 'undefined') {
        expect.addType({
            name: 'Buffer',
            identify: Buffer.isBuffer,
            equal: bufferLikeObjectsEqual,
            inspect: function (output, buffer) {
                return output.text('[Buffer ')
                    .append(inspectBufferLikeObject(buffer))
                    .text(']');
            },
            toJSON: function (buffer) {
                return {
                    $Buffer: getHexDumpLinesForBufferLikeObject(buffer)
                };
            }
        });
    }

    if (typeof Uint8Array !== 'undefined') {
        expect.addType({
            name: 'Uint8Array',
            identify: function (obj) {
                return obj && obj instanceof Uint8Array;
            },
            equal: bufferLikeObjectsEqual,
            inspect: function (output, uint8Array) {
                return output.text('[Uint8Array ')
                    .append(inspectBufferLikeObject(uint8Array))
                    .text(']');
            },
            toJSON: function (uint8Array) {
                return {
                    $Uint8Array: getHexDumpLinesForBufferLikeObject(uint8Array)
                };
            }
        });
    }

    if (typeof Uint16Array !== 'undefined') {
        expect.addType({
            name: 'Uint16Array',
            identify: function (obj) {
                return obj && obj instanceof Uint16Array;
            },
            equal: bufferLikeObjectsEqual,
            inspect: function (output, uint16Array) {
                return output.text('[Uint16Array ')
                    .append(inspectBufferLikeObject(uint16Array, 8, 4, false))
                    .text(']');
            },
            toJSON: function (uint16Array) {
                return {
                    $Uint16Array: getHexDumpLinesForBufferLikeObject(uint16Array, 8, 4, false)
                };
            }
        });
    }

    expect.addType({
        name: 'string',
        identify: function (value) {
            return typeof value === 'string';
        },
        inspect: function (output, value) {
            return output.strings('\'')
                .strings(json.stringify(value).replace(/^"|"$/g, '')
                      .replace(/'/g, "\\'")
                      .replace(/\\"/g, '"'))
                .strings('\'');
        }
    });

    expect.addType({
        name: 'number',
        identify: function (value) {
            return typeof value === 'number';
        }
    });

    expect.addType({
        name: 'boolean',
        identify: function (value) {
            return typeof value === 'boolean';
        }
    });

    expect.addType({
        name: 'undefined',
        identify: function (value) {
            return typeof value === 'undefined';
        }
    });

    expect.addType({
        name: 'null',
        identify: function (value) {
            return value === null;
        }
    });
};
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const path = require('path');

const intel = require('./');
const Formatter = require('./formatter');
const Filter = require('./filter');

const INTEL_PREFIX = 'intel/';

function req(str, root) {
  if (str.indexOf(INTEL_PREFIX) === 0) {
    str = str.replace(INTEL_PREFIX, './');
  } else if (str.indexOf('./') === 0 || str.indexOf('../') === 0) {
    str = path.join(root || process.cwd(), str);
  }
  return require(str);
}

function isObject(value) {
  return Object.prototype.toString.call(value) === '[object Object]';
}

function evalFunction(source) {
  /*jshint evil:true*/
  var fn = Function('return (' + String(source).trim() + ');')();
  if (!(fn instanceof Function)) {
    throw new Error('function parameter did not parse as a function');
  }
  return fn;
}

function configureFormatter(formatter, options) {
  if (formatter.formatFn) {
    var customFormatter = new Formatter();
    if (formatter.formatFn instanceof Function) {
      customFormatter.format = formatter.formatFn;
    } else {
      customFormatter.format = evalFunction(formatter.formatFn);
    }
    return customFormatter;
  }
  var FormatterClass = formatter['class'] || Formatter;
  if (typeof FormatterClass === 'string') {
    FormatterClass = req(FormatterClass, options.root);
  }
  return new FormatterClass(formatter);
}

function configureFilter(filterOptions, options) {

  var FilterClass = filterOptions['class'] || Filter;
  if (typeof FilterClass === 'string') {
    FilterClass = req(FilterClass, options.root);
  }
  if (isObject(filterOptions)) {
    var fOpts = filterOptions;
    var re = fOpts.regexp || fOpts.regex || fOpts.re;
    var fn = fOpts.function || fOpts.fn;
    if (re) {
      filterOptions = new RegExp(re, fOpts.flags);
    } else if (fn) {
      filterOptions = evalFunction(fn);
    }
  }
  return new FilterClass(filterOptions);
}

function configureHandler(handler, options) {
  var HandlerClass = handler['class'];
  if (typeof HandlerClass === 'string') {
    HandlerClass = req(HandlerClass, options.root);
  }
  delete handler['class'];
  if (handler.formatter) {
    handler.formatter = options.formatters[handler.formatter];
  }
  var hndlr = new HandlerClass(handler);
  if (handler.filters) {
    handler.filters.forEach(function eachHandler(fname) {
      hndlr.addFilter(options.filters[fname]);
    });
  }
  return hndlr;
}

function getHandler(name, options) {
  var handler = options.handlers[name];
  if (typeof handler.handle !== 'function') {
    handler = options.handlers[name] = configureHandler(handler, options);
  }
  return handler;
}

function configureLogger(name, loggerOptions, options) {
  var logger = intel.getLogger(name);
  if (loggerOptions.level != null) {
    logger.setLevel(loggerOptions.level);
  }

  if (loggerOptions.handlers) {
    loggerOptions.handlers.forEach(function eachHandler(hName) {
      logger.addHandler(getHandler(hName, options));
    });
  }
  if (loggerOptions.filters) {
    loggerOptions.filters.forEach(function eachHandler(fname) {
      logger.addFilter(options.filters[fname]);
    });
  }

  if (loggerOptions.propagate != null) {
    logger.propagate = loggerOptions.propagate;
  }

  if (loggerOptions.handleExceptions) {
    logger.handleExceptions(loggerOptions.exitOnError);
  }
}

module.exports = function config(options) {
  // lets do formatters and filters first, since they dont depend on anything
  // then handlers, since they can depend on formatters
  // and then loggers, since they can depend on handlers

  var formatters = options.formatters || {};
  for (var f in formatters) {
    formatters[f] = configureFormatter(formatters[f], options);
  }

  var filters = options.filters || {};
  for (var fi in filters) {
    filters[fi] = configureFilter(filters[fi], options);
  }

  if (options.handlers) {
    intel.basicConfig({ null: true});
  }

  var loggers = options.loggers || {};
  for (var l in loggers) {
    configureLogger(l, loggers[l], options);
  }

  if (options.console) {
    var consoleOpts = isObject(options.console) ? options.console : {};
    consoleOpts.__trace = config;
    intel.console(consoleOpts);
  }
};
<commit_msg>Throw error if handler is not defined in config<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const path = require('path');

const intel = require('./');
const Formatter = require('./formatter');
const Filter = require('./filter');

const INTEL_PREFIX = 'intel/';

function req(str, root) {
  if (str.indexOf(INTEL_PREFIX) === 0) {
    str = str.replace(INTEL_PREFIX, './');
  } else if (str.indexOf('./') === 0 || str.indexOf('../') === 0) {
    str = path.join(root || process.cwd(), str);
  }
  return require(str);
}

function isObject(value) {
  return Object.prototype.toString.call(value) === '[object Object]';
}

function evalFunction(source) {
  /*jshint evil:true*/
  var fn = Function('return (' + String(source).trim() + ');')();
  if (!(fn instanceof Function)) {
    throw new Error('function parameter did not parse as a function');
  }
  return fn;
}

function configureFormatter(formatter, options) {
  if (formatter.formatFn) {
    var customFormatter = new Formatter();
    if (formatter.formatFn instanceof Function) {
      customFormatter.format = formatter.formatFn;
    } else {
      customFormatter.format = evalFunction(formatter.formatFn);
    }
    return customFormatter;
  }
  var FormatterClass = formatter['class'] || Formatter;
  if (typeof FormatterClass === 'string') {
    FormatterClass = req(FormatterClass, options.root);
  }
  return new FormatterClass(formatter);
}

function configureFilter(filterOptions, options) {

  var FilterClass = filterOptions['class'] || Filter;
  if (typeof FilterClass === 'string') {
    FilterClass = req(FilterClass, options.root);
  }
  if (isObject(filterOptions)) {
    var fOpts = filterOptions;
    var re = fOpts.regexp || fOpts.regex || fOpts.re;
    var fn = fOpts.function || fOpts.fn;
    if (re) {
      filterOptions = new RegExp(re, fOpts.flags);
    } else if (fn) {
      filterOptions = evalFunction(fn);
    }
  }
  return new FilterClass(filterOptions);
}

function configureHandler(handler, options) {
  var HandlerClass = handler['class'];
  if (typeof HandlerClass === 'string') {
    HandlerClass = req(HandlerClass, options.root);
  }
  delete handler['class'];
  if (handler.formatter) {
    handler.formatter = options.formatters[handler.formatter];
  }
  var hndlr = new HandlerClass(handler);
  if (handler.filters) {
    handler.filters.forEach(function eachHandler(fname) {
      hndlr.addFilter(options.filters[fname]);
    });
  }
  return hndlr;
}

function getHandler(name, options) {
  var handler = options.handlers[name];
  if (!handler) {
    var errorStr = util.format('Handler "%s" is undefined.' +
      ' Probably you shoud check your config', name);
    throw new Error(errorStr);
  }
  if (typeof handler.handle !== 'function') {
    handler = options.handlers[name] = configureHandler(handler, options);
  }
  return handler;
}

function configureLogger(name, loggerOptions, options) {
  var logger = intel.getLogger(name);
  if (loggerOptions.level != null) {
    logger.setLevel(loggerOptions.level);
  }

  if (loggerOptions.handlers) {
    loggerOptions.handlers.forEach(function eachHandler(hName) {
      logger.addHandler(getHandler(hName, options));
    });
  }
  if (loggerOptions.filters) {
    loggerOptions.filters.forEach(function eachHandler(fname) {
      logger.addFilter(options.filters[fname]);
    });
  }

  if (loggerOptions.propagate != null) {
    logger.propagate = loggerOptions.propagate;
  }

  if (loggerOptions.handleExceptions) {
    logger.handleExceptions(loggerOptions.exitOnError);
  }
}

module.exports = function config(options) {
  // lets do formatters and filters first, since they dont depend on anything
  // then handlers, since they can depend on formatters
  // and then loggers, since they can depend on handlers

  var formatters = options.formatters || {};
  for (var f in formatters) {
    formatters[f] = configureFormatter(formatters[f], options);
  }

  var filters = options.filters || {};
  for (var fi in filters) {
    filters[fi] = configureFilter(filters[fi], options);
  }

  if (options.handlers) {
    intel.basicConfig({ null: true});
  }

  var loggers = options.loggers || {};
  for (var l in loggers) {
    configureLogger(l, loggers[l], options);
  }

  if (options.console) {
    var consoleOpts = isObject(options.console) ? options.console : {};
    consoleOpts.__trace = config;
    intel.console(consoleOpts);
  }
};
<|endoftext|>