<commit_before>/*!
 * engine <https://github.com/jonschlinkert/engine>
 *
 * Copyright (c) 2014 <NAME>, contributors.
 * Licensed under the MIT license.
 */

'use strict';

// process.env.DEBUG = 'engine:*';

var _ = require('lodash');
var path = require('path');
var chalk = require('chalk');
var Delims = require('delims');
var forOwn = require('for-own');
var Layouts = require('layouts');
var Router = require('en-route');
var Cache = require('config-cache');
var Helpers = require('helper-cache');
var Engines = require('engine-cache');
var engineLodash = require('engine-lodash');
var engineNoop = require('engine-noop');
var parserMatter = require('parser-front-matter');
var parserNoop = require('parser-noop');
var Loader = require('load-templates');
var decorate = require('./lib/decorate');
var utils = require('./lib/utils');
var debug = require('./lib/debug');
var extend = _.extend;


/**
 * Create a new instance of `Engine`, optionally passing
 * default `options` to initialize with.
 *
 * **Example:**
 *
 * ```js
 * var Engine = require('engine');
 * var engine = new Engine();
 * ```
 *
 * @class `Engine`
 * @param {Object} `options` Options to initialize with.
 * @api public
 */

var Engine = module.exports = Cache.extend({
  constructor: function(options) {
    Engine.__super__.constructor.call(this, options);
    this.initEngine();
  }
});

Engine.extend = Cache.extend;


/**
 * Initialize defaults.
 *
 * @api private
 */

Engine.prototype.initEngine = function() {
  this.engines = this.engines || {};
  this.delims = this.delims || {};

  this._ = {};
  this.templateType = {};
  this.templateType.partial = [];
  this.templateType.renderable = [];
  this.templateType.layout = [];
  this.layoutSettings = {};

  this.defaultConfig();
  this.defaultOptions();
  this.defaultDelimiters();
  this.defaultRoutes();
  this.defaultTemplates();
  this.defaultEngines();
};


/**
 * Initialize the default configuration.
 *
 * @api private
 */

Engine.prototype.defaultConfig = function() {
  this._.delims = new Delims(this.options);
  this._.engines = new Engines(this.engines);
  this._.helpers = new Helpers({
    bindFunctions: true,
    thisArg: this
  });

  this._.asyncHelpers = new Helpers({
    bindFunctions: true,
    thisArg: this
  });

  this.set('mixins', {});
  this.set('locals', {});
  this.set('imports', {});
  this.set('layouts', {});
  this.set('partials', {});
  this.set('anonymous', {});
  this.set('pages', {});
};


/**
 * Initialize default options.
 *
 * @api private
 */

Engine.prototype.defaultOptions = function() {
  this.option('cache', true);
  this.option('strictErrors', true);
  this.option('pretty', false);

  this.option('cwd', process.cwd());
  this.option('ext', '*');
  this.option('defaultExts', ['md', 'html', 'hbs']);
  this.option('destExt', '.html');
  this.option('delims', ['<%', '%>']);
  this.option('viewEngine', '*');
  this.option('layoutTag', 'body');
  this.option('layoutDelims', ['{%', '%}']);
  this.option('layoutExt', null);
  this.option('layout', null);

  this.enable('built-in:engines');

  this.option('preprocess', true);
  this.option('preferLocals', false);
  this.option('partialLayout', null);
  this.option('mergePartials', true);
  this.option('mergeFunction', extend);
  this.option('bindHelpers', true);

  // Custom function for naming partial keys
  this.option('partialsKey', function (fp) {
    return path.basename(fp, path.extname(fp));
  });

  // Custom function for all other template keys
  this.option('renameKey', function (fp) {
    return path.basename(fp);
  });
};


/**
 * Load default routes / middleware
 *
 *   - `.md`: parse front matter in markdown files
 *   - `.hbs`: parse front matter in handlebars templates
 *   - `.*`: use the noop engine for any unclaimed extensions. This just
 *           passes files through but adds expected properties to the
 *           template object if they don't already exist.
 *
 * @api private
 */

Engine.prototype.defaultRoutes = function() {
  this.route(/\.(md|hbs)$/, function route(src, dest, next) {
    parserMatter.parse(src, function(err) {
      if (err) {
        console.log('route [md|hbs]:', chalk.red(err));
        next(err);
        return;
      }
      next();
    });
  });

  this.route(/.*/, function route(src, dest, next) {
    parserNoop.parse(src, function(err) {
      if (err) {
        console.log('route [*]:', chalk.red(err));
        next(err);
        return;
      }
      next();
    });
  });
};


/**
 * Load default engines.
 *
 *   - `*` The noop engine is used as a pass-through when no other engine matches.
 *   - `md|html|hbs`. [engine-lodash] will process templates  in any files with these
 *                    extensions. To change or negate these extensions, just do
 *                    `engine.option('defaultExts', ['foo', 'bar', 'baz'])`.
 *
 * @api private
 */

Engine.prototype.defaultEngines = function() {
  if (this.enabled('built-in:engines')) {
    this.engine(this.option('defaultExts'), engineLodash, {
      layoutDelims: ['{%', '%}'],
      destExt: '.html'
    });
    this.engine('*', engineNoop, {
      layoutDelims: ['{%', '%}'],
      destExt: '.html'
    });
  }
};


/**
 * Register default template delimiters.
 *
 *   - engine delimiters: Delimiters used in templates process by [engine-lodash], the default engine.
 *   - layout delimiters: Delimiters used in layouts.
 *
 * @api private
 */

Engine.prototype.defaultDelimiters = function() {
  this.addDelims('*', ['<%', '%>'], ['{%', '%}']);
};


/**
 * Register default template types.
 *
 * @api private
 */

Engine.prototype.defaultTemplates = function() {
  this.create('page', { isRenderable: true });
  this.create('layout', { isLayout: true });
  this.create('partial');
};


/**
 * Create helpers for each default template `type`.
 *
 * @api private
 */

Engine.prototype.typeHelpers = function(type, plural) {
  this.addHelper(type, function (key, locals) {
    var partial = this.cache[plural][key];

    partial = this.stashLocals(type, partial, locals);

    var content = this.renderSync(partial, locals);
    if (content instanceof Error) {
      throw content;
    }
    return content;
  });
};


/**
 * Create async helpers for each default template `type`.
 *
 * @param {String} `type` The type of template.
 * @param {String} `plural` Plural form of `type`.
 * @api private
 */

Engine.prototype.typeHelpersAsync = function(type, plural) {
  this.addHelperAsync(type, function (name, locals, next) {
    debug.helper('#{creating async type helper}:', name);
    var last = _.last(arguments);

    if (typeof locals === 'function') {
      next = locals;
      locals = {};
    }
    if (typeof next !== 'function') {
      next = last;
    }

    var partial = this.cache[plural][name];
    this.stashLocals('typeHelpersAsync', partial, locals);

    if (!partial) {
      var msg = chalk.red('helper {{' + type + ' "' + name + '"}} not found.');
      console.log(msg);
      next(null, '');
      return;
    }

    partial.locals = extend({}, partial.locals, locals);
    debug.helper('#{async helper partial}:', partial);

    this.render(partial, {}, function (err, content) {
      debug.helper('#{async helper rendering}:', content);
      if (err) {
        console.log('asyncHelpers:', chalk.red(err));
        next(err);
        return;<commit_msg>fix locals handling in `.typeHelpersAsync()`<commit_after>/*!
 * engine <https://github.com/jonschlinkert/engine>
 *
 * Copyright (c) 2014 <NAME>, contributors.
 * Licensed under the MIT license.
 */

'use strict';

// process.env.DEBUG = 'engine:*';

var _ = require('lodash');
var path = require('path');
var chalk = require('chalk');
var Delims = require('delims');
var forOwn = require('for-own');
var Layouts = require('layouts');
var Router = require('en-route');
var Cache = require('config-cache');
var Helpers = require('helper-cache');
var Engines = require('engine-cache');
var engineLodash = require('engine-lodash');
var engineNoop = require('engine-noop');
var parserMatter = require('parser-front-matter');
var parserNoop = require('parser-noop');
var Loader = require('load-templates');
var decorate = require('./lib/decorate');
var utils = require('./lib/utils');
var debug = require('./lib/debug');
var extend = _.extend;


/**
 * Create a new instance of `Engine`, optionally passing
 * default `options` to initialize with.
 *
 * **Example:**
 *
 * ```js
 * var Engine = require('engine');
 * var engine = new Engine();
 * ```
 *
 * @class `Engine`
 * @param {Object} `options` Options to initialize with.
 * @api public
 */

var Engine = module.exports = Cache.extend({
  constructor: function(options) {
    Engine.__super__.constructor.call(this, options);
    this.initEngine();
  }
});

Engine.extend = Cache.extend;


/**
 * Initialize defaults.
 *
 * @api private
 */

Engine.prototype.initEngine = function() {
  this.engines = this.engines || {};
  this.delims = this.delims || {};

  this._ = {};
  this.templateType = {};
  this.templateType.partial = [];
  this.templateType.renderable = [];
  this.templateType.layout = [];
  this.layoutSettings = {};

  this.defaultConfig();
  this.defaultOptions();
  this.defaultDelimiters();
  this.defaultRoutes();
  this.defaultTemplates();
  this.defaultEngines();
};


/**
 * Initialize the default configuration.
 *
 * @api private
 */

Engine.prototype.defaultConfig = function() {
  this._.delims = new Delims(this.options);
  this._.engines = new Engines(this.engines);
  this._.helpers = new Helpers({
    bindFunctions: true,
    thisArg: this
  });

  this._.asyncHelpers = new Helpers({
    bindFunctions: true,
    thisArg: this
  });

  this.set('mixins', {});
  this.set('locals', {});
  this.set('imports', {});
  this.set('layouts', {});
  this.set('partials', {});
  this.set('anonymous', {});
  this.set('pages', {});
};


/**
 * Initialize default options.
 *
 * @api private
 */

Engine.prototype.defaultOptions = function() {
  this.option('cache', true);
  this.option('strictErrors', true);
  this.option('pretty', false);

  this.option('cwd', process.cwd());
  this.option('ext', '*');
  this.option('defaultExts', ['md', 'html', 'hbs']);
  this.option('destExt', '.html');
  this.option('delims', ['<%', '%>']);
  this.option('viewEngine', '*');
  this.option('layoutTag', 'body');
  this.option('layoutDelims', ['{%', '%}']);
  this.option('layoutExt', null);
  this.option('layout', null);

  this.enable('built-in:engines');

  this.option('preprocess', true);
  this.option('preferLocals', false);
  this.option('partialLayout', null);
  this.option('mergePartials', true);
  this.option('mergeFunction', extend);
  this.option('bindHelpers', true);

  // Custom function for naming partial keys
  this.option('partialsKey', function (fp) {
    return path.basename(fp, path.extname(fp));
  });

  // Custom function for all other template keys
  this.option('renameKey', function (fp) {
    return path.basename(fp);
  });
};


/**
 * Load default routes / middleware
 *
 *   - `.md`: parse front matter in markdown files
 *   - `.hbs`: parse front matter in handlebars templates
 *   - `.*`: use the noop engine for any unclaimed extensions. This just
 *           passes files through but adds expected properties to the
 *           template object if they don't already exist.
 *
 * @api private
 */

Engine.prototype.defaultRoutes = function() {
  this.route(/\.(md|hbs)$/, function route(src, dest, next) {
    parserMatter.parse(src, function(err) {
      if (err) {
        console.log('route [md|hbs]:', chalk.red(err));
        next(err);
        return;
      }
      next();
    });
  });

  this.route(/.*/, function route(src, dest, next) {
    parserNoop.parse(src, function(err) {
      if (err) {
        console.log('route [*]:', chalk.red(err));
        next(err);
        return;
      }
      next();
    });
  });
};


/**
 * Load default engines.
 *
 *   - `*` The noop engine is used as a pass-through when no other engine matches.
 *   - `md|html|hbs`. [engine-lodash] will process templates  in any files with these
 *                    extensions. To change or negate these extensions, just do
 *                    `engine.option('defaultExts', ['foo', 'bar', 'baz'])`.
 *
 * @api private
 */

Engine.prototype.defaultEngines = function() {
  if (this.enabled('built-in:engines')) {
    this.engine(this.option('defaultExts'), engineLodash, {
      layoutDelims: ['{%', '%}'],
      destExt: '.html'
    });
    this.engine('*', engineNoop, {
      layoutDelims: ['{%', '%}'],
      destExt: '.html'
    });
  }
};


/**
 * Register default template delimiters.
 *
 *   - engine delimiters: Delimiters used in templates process by [engine-lodash], the default engine.
 *   - layout delimiters: Delimiters used in layouts.
 *
 * @api private
 */

Engine.prototype.defaultDelimiters = function() {
  this.addDelims('*', ['<%', '%>'], ['{%', '%}']);
};


/**
 * Register default template types.
 *
 * @api private
 */

Engine.prototype.defaultTemplates = function() {
  this.create('page', { isRenderable: true });
  this.create('layout', { isLayout: true });
  this.create('partial');
};


/**
 * Create helpers for each default template `type`.
 *
 * @api private
 */

Engine.prototype.typeHelpers = function(type, plural) {
  this.addHelper(type, function (key, locals) {
    var partial = this.cache[plural][key];

    partial = this.stashLocals(type, partial, locals);

    var content = this.renderSync(partial, locals);
    if (content instanceof Error) {
      throw content;
    }
    return content;
  });
};


/**
 * Create async helpers for each default template `type`.
 *
 * @param {String} `type` The type of template.
 * @param {String} `plural` Plural form of `type`.
 * @api private
 */

Engine.prototype.typeHelpersAsync = function(type, plural) {
  this.addHelperAsync(type, function (name, locals, next) {
    debug.helper('#{creating async type helper}:', name);
    var last = _.last(arguments);

    if (typeof locals === 'function') {
      next = locals;
      locals = {};
    }
    if (typeof next !== 'function') {
      next = last;
    }

    var partial = this.cache[plural][name];
    this.stashLocals('typeHelpersAsync', partial, locals);

    if (!partial) {
      var msg = chalk.red('helper {{' + type + ' "' + name + '"}} not found.');
      console.log(msg);
      next(null, '');
      return;
    }

    partial.locals = extend({}, partial.locals, partial.data, locals);
    debug.helper('#{async helper partial}:', partial);

    this.render(partial, partial.locals, function (err, content) {
      debug.helper('#{async helper rendering}:', content);
      if (err) {
        console.log('asyncHelpers:', chalk.red(err));
        next(err);
        return;
{'fraction_non_alphanumeric': 0.18149874991553483, 'fraction_numerical': 0.0005405770660179742, 'mean_word_length': 3.361921603300914, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 2, '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': '1899915', 'n_tokens_mistral': 4707, 'n_tokens_neox': 4605, 'n_words': 1093}