<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var url = require('url');
var send = require('send');
var mime = require('mime');
var merge = require('merge');
var async = require('async');

var memCache = require('./mem-cache');

var options = {};

/**
 * Compiles a template
 * @param  {object}   engine the engine
 * @param  {string}   file   the template file
 * @param  {function} cb     callback
 * @api private
 */
function compile(engine, file, cb) {
  options.cache.get(file, function(err, cache) {
    if (err) return cb(err);

    // Actual build function
    function build() {
      // Read the content of the template
      fs.readFile(file, { encoding: 'utf8' }, function(err, data) {
        if (err) return cb(err);
        // Ask engine to compile
        engine.compile(file, data, 'utf8', function(err, data, files) {
          if (err) return cb(err);
          // Set default files
          files = files || [file];

          // Update cache
          options.cache.set(file, {
            files: files,
            created: new Date(),
            data: data
          }, function(err) {
            if (err) {
              console.err('WARNING: error saving \'' + file + '\' to cache (error: \'' + err.message + '\')');
            }
          });

          // Callback with compiled template
          cb(err, data, false);

        });
      });
    }

    // Rebuild if cache is invalid
    if (!cache || !cache.created || !cache.files || !cache.data) {
      return build();
    }

    // Figure if cache is outdated
    // - enumerate files used
    var newest;
    async.eachSeries(cache.files, function(file, next) {
      fs.stat(file, function(err, stats) {
        // If stat fails we rebuild
        if (err) return build();
        newest = (!newest ? stats.mtime : Math.max(newest, stats.mtime));
        next();
      });
    }, function(err) {
      if (err) return cb(err);
      // If files are newer - rebuild
      if (newest > cache.created) return build();
      // - else returned cached compiled data
      cb(null, cache.data, true);
    });

  });
}

/**
 * Renders a template
 * @param  {string}   file url to render
 * @param  {function} cb   callback with rendered url
 * @api public
 */
function render(u, cb) {

  // Check arguments
  if (!u || typeof u !== 'string') {
    throw new TypeError('url is required and must be a string');
  }

  if (!cb || typeof cb !== 'function') {
    throw new TypeError('cb is required and must be a function');
  }

  var pathname = url.parse(u).pathname;
  var extname = path.extname(pathname);

  // We do not support files without extension.
  if (!extname || extname == '') return cb();

  // Iterate engines
  async.eachSeries(options.engines, function(engine, nextEngine) {
    // Iterate extensions
    async.forEachOfSeries(engine.map, function(templateExt, targetExt, nextExt) {

      // If not correct extension - continue
      if (extname != targetExt) return nextExt();

      // Build actual local filename
      var basename = path.basename(pathname, extname);
      var dirname = path.dirname(pathname);
      var templateFile = path.normalize(options.root + '/' + dirname + '/' + basename + templateExt);

      // Check if template file exists
      fs.exists(templateFile, function(exists) {

        // If not found - continue with next ext
        if (!exists) return nextExt();

        // Compile
        compile(engine, templateFile, function(err, data, cached) {

          // If compile did not succeed - continue with next ext.
          if (err || !data || data == '') return nextExt(err);

          // Ask engine to render and return
          engine.render(url, data, 'utf8', function(err, source) {
            cb(err, source, cached);
          });

        });

      })

    }, nextEngine)

  }, cb);

}

/**
 * Returns a route for smart static
 * @param  {string}   root root of static directory
 * @param  {object}   opt  options
 * @return {function}      the route
 * @api public
 */
function smartStatic(root, opt) {

  // Check arguments
  if (typeof root !== 'string') {
    throw new TypeError('root is required and must be a string');
  }

  if (opt && typeof opt !== 'object') {
    throw new TypeError('opt must be an object');
  }

  // Copy options
  options = merge(true, opt || {}, {});

  // Setup default options
  options.index = options.index || ['index.html'];
  options.root = root;

  // Add engines
  options.engines = [];
  opt.engines.forEach(function(e) {
    engine(e);
  });
  
  // Set default memory cache
  options.cache = options.cache || memCache;

  // Check cache object
  if (!options.cache.get || typeof options.cache.get !== 'function') {
    throw new TypeError('cache must have a get function');
  }

  if (!options.cache.set || typeof options.cache.set !== 'function') {
    throw new TypeError('cache must have a set function');
  }

  // We make sure root exists and is a directory
  var rootStat = fs.statSync(root); // Will throw if root does not exist
  if (!rootStat.isDirectory()) throw new Error('root is not a directory');

  // Return the route
  return function route(req, res, next) {

    // Ignore non-GET and non-HEAD requests.
    if (req.method != 'GET' && req.method != 'HEAD') return next();

    // Map to local file
    var file = path.normalize(root + '/' + url.parse(req.url).pathname);
<commit_msg>Fixed crash when options contained no engines.<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var url = require('url');
var send = require('send');
var mime = require('mime');
var merge = require('merge');
var async = require('async');

var memCache = require('./mem-cache');

var options = {};

/**
 * Compiles a template
 * @param  {object}   engine the engine
 * @param  {string}   file   the template file
 * @param  {function} cb     callback
 * @api private
 */
function compile(engine, file, cb) {
  options.cache.get(file, function(err, cache) {
    if (err) return cb(err);

    // Actual build function
    function build() {
      // Read the content of the template
      fs.readFile(file, { encoding: 'utf8' }, function(err, data) {
        if (err) return cb(err);
        // Ask engine to compile
        engine.compile(file, data, 'utf8', function(err, data, files) {
          if (err) return cb(err);
          // Set default files
          files = files || [file];

          // Update cache
          options.cache.set(file, {
            files: files,
            created: new Date(),
            data: data
          }, function(err) {
            if (err) {
              console.err('WARNING: error saving \'' + file + '\' to cache (error: \'' + err.message + '\')');
            }
          });

          // Callback with compiled template
          cb(err, data, false);

        });
      });
    }

    // Rebuild if cache is invalid
    if (!cache || !cache.created || !cache.files || !cache.data) {
      return build();
    }

    // Figure if cache is outdated
    // - enumerate files used
    var newest;
    async.eachSeries(cache.files, function(file, next) {
      fs.stat(file, function(err, stats) {
        // If stat fails we rebuild
        if (err) return build();
        newest = (!newest ? stats.mtime : Math.max(newest, stats.mtime));
        next();
      });
    }, function(err) {
      if (err) return cb(err);
      // If files are newer - rebuild
      if (newest > cache.created) return build();
      // - else returned cached compiled data
      cb(null, cache.data, true);
    });

  });
}

/**
 * Renders a template
 * @param  {string}   file url to render
 * @param  {function} cb   callback with rendered url
 * @api public
 */
function render(u, cb) {

  // Check arguments
  if (!u || typeof u !== 'string') {
    throw new TypeError('url is required and must be a string');
  }

  if (!cb || typeof cb !== 'function') {
    throw new TypeError('cb is required and must be a function');
  }

  var pathname = url.parse(u).pathname;
  var extname = path.extname(pathname);

  // We do not support files without extension.
  if (!extname || extname == '') return cb();

  // Iterate engines
  async.eachSeries(options.engines, function(engine, nextEngine) {
    // Iterate extensions
    async.forEachOfSeries(engine.map, function(templateExt, targetExt, nextExt) {

      // If not correct extension - continue
      if (extname != targetExt) return nextExt();

      // Build actual local filename
      var basename = path.basename(pathname, extname);
      var dirname = path.dirname(pathname);
      var templateFile = path.normalize(options.root + '/' + dirname + '/' + basename + templateExt);

      // Check if template file exists
      fs.exists(templateFile, function(exists) {

        // If not found - continue with next ext
        if (!exists) return nextExt();

        // Compile
        compile(engine, templateFile, function(err, data, cached) {

          // If compile did not succeed - continue with next ext.
          if (err || !data || data == '') return nextExt(err);

          // Ask engine to render and return
          engine.render(url, data, 'utf8', function(err, source) {
            cb(err, source, cached);
          });

        });

      })

    }, nextEngine)

  }, cb);

}

/**
 * Returns a route for smart static
 * @param  {string}   root root of static directory
 * @param  {object}   opt  options
 * @return {function}      the route
 * @api public
 */
function smartStatic(root, opt) {

  // Check arguments
  if (typeof root !== 'string') {
    throw new TypeError('root is required and must be a string');
  }

  if (opt && typeof opt !== 'object') {
    throw new TypeError('opt must be an object');
  }

  // Copy options
  options = merge(true, opt || {}, {});

  // Setup default options
  options.index = options.index || ['index.html'];
  options.root = root;

  // Add engines
  options.engines = [];
  ((opt || {}).engines || []).forEach(function(e) {
    engine(e);
  });

  // Set default memory cache
  options.cache = options.cache || memCache;

  // Check cache object
  if (!options.cache.get || typeof options.cache.get !== 'function') {
    throw new TypeError('cache must have a get function');
  }

  if (!options.cache.set || typeof options.cache.set !== 'function') {
    throw new TypeError('cache must have a set function');
  }

  // We make sure root exists and is a directory
  var rootStat = fs.statSync(root); // Will throw if root does not exist
  if (!rootStat.isDirectory()) throw new Error('root is not a directory');

  // Return the route
  return function route(req, res, next) {

    // Ignore non-GET and non-HEAD requests.
    if (req.method != 'GET' && req.method != 'HEAD') return next();

    // Map to local file
    var file = path.normalize(root + '/' + url.parse(req.url).pathname);

{'fraction_non_alphanumeric': 0.14209705531602607, 'fraction_numerical': 0.0005504082194294101, 'mean_word_length': 2.4090056285178236, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '1261580', 'n_tokens_mistral': 3203, 'n_tokens_neox': 3125, 'n_words': 1050}