<commit_before>var _ = require('lodash');
var fs = require('fs');
var Handlebars = require('handlebars');
var path = require('path');

var colors = require('./colors');

var getLineNumber = function(error) {
	var line = error.line;

	if (Array.isArray(line)) {
		line = line[0];
	}

	return line;
};

var sortErrors = function(a, b) {
	var aNum = getLineNumber(a);
	var bNum = getLineNumber(b);

	var retVal = 0;

	if (aNum < bNum) {
		retVal = -1;
	}
	else if (aNum > bNum) {
		retVal = 1;
	}

	return retVal;
};

var REGEX_NON_SPACE = /\S/;

var Logger = function() {
	this.fileErrors = {};

	this.testStats = {
		failures: 0
	};

	this.verboseDetails = {};
};

Logger.prototype = {
	TPL: '',
	TPL_PATH: path.join(__dirname, 'tpl/cli.tpl'),

	Logger: Logger,

	getErrors: function(file) {
		var fileErrors;

		if (_.isUndefined(file)) {
			fileErrors = this.fileErrors;
		}
		else {
			fileErrors = this.fileErrors[file] || [];
		}

		return fileErrors;
	},

	log: function(line, msg, file, type, props) {
		var fileErrors = this.fileErrors;

		var errors = fileErrors[file];

		this.testStats.failures++;

		if (!errors) {
			errors = [];

			fileErrors[file] = errors;
		}

		var error = {
			line: line,
			msg: msg,
			type: type
		};

		if (_.isObject(props)) {
			_.defaults(error, props);
		}

		errors.push(error);
	},

	render: function(file, config) {
		config = config || {};

		if (!this.TPL) {
			this.TPL = fs.readFileSync(this.TPL_PATH, 'utf-8');
		}

		var logTpl = Handlebars.compile(this.TPL);

		var errors = this.getErrors(file);

		errors.sort(sortErrors);

		var tplContext = {
			errors: errors,
			file: this._getFilePath(file, config),
			showBanner: errors.length || !config.showBanner,
			showLintIds: config.showLintIds
		};

		var out = logTpl(tplContext);

		if (!REGEX_NON_SPACE.test(out)) {
			out = '';
		}

		return out;
	},

	renderFileNames: function(file, config) {
		config = config || {};

		var errors = this.getErrors(file);

		var out = '';

		if (errors.length) {
			out = this._getFilePath(file, config);
		}

		return out;
	},

	_getFilePath: function(file, config) {
		var relative = config.relative;

		if (relative) {
			file = path.relative(relative, file);
		}

		return file;
	}
};
<commit_msg>Speed up performance; we don't need to recompile on every invocation<commit_after>var _ = require('lodash');
var fs = require('fs');
var Handlebars = require('handlebars');
var path = require('path');

var colors = require('./colors');

var getLineNumber = function(error) {
	var line = error.line;

	if (Array.isArray(line)) {
		line = line[0];
	}

	return line;
};

var sortErrors = function(a, b) {
	var aNum = getLineNumber(a);
	var bNum = getLineNumber(b);

	var retVal = 0;

	if (aNum < bNum) {
		retVal = -1;
	}
	else if (aNum > bNum) {
		retVal = 1;
	}

	return retVal;
};

var REGEX_NON_SPACE = /\S/;

var Logger = function() {
	this.TPL_PATH = path.join(__dirname, 'tpl/cli.tpl');

	this.fileErrors = {};

	this.testStats = {
		failures: 0
	};

	this.verboseDetails = {};
};

Logger.prototype = {
	Logger: Logger,

	getErrors: function(file) {
		var fileErrors;

		if (_.isUndefined(file)) {
			fileErrors = this.fileErrors;
		}
		else {
			fileErrors = this.fileErrors[file] || [];
		}

		return fileErrors;
	},

	log: function(line, msg, file, type, props) {
		var fileErrors = this.fileErrors;

		var errors = fileErrors[file];

		this.testStats.failures++;

		if (!errors) {
			errors = [];

			fileErrors[file] = errors;
		}

		var error = {
			line: line,
			msg: msg,
			type: type
		};

		if (_.isObject(props)) {
			_.defaults(error, props);
		}

		errors.push(error);
	},

	render: function(file, config) {
		config = config || {};

		var logTpl = this._getTPL();



		var errors = this.getErrors(file);

		errors.sort(sortErrors);

		var tplContext = {
			errors: errors,
			file: this._getFilePath(file, config),
			showBanner: errors.length || !config.showBanner,
			showLintIds: config.showLintIds
		};

		var out = logTpl(tplContext);

		if (!REGEX_NON_SPACE.test(out)) {
			out = '';
		}

		return out;
	},

	renderFileNames: function(file, config) {
		config = config || {};

		var errors = this.getErrors(file);

		var out = '';

		if (errors.length) {
			out = this._getFilePath(file, config);
		}

		return out;
	},

	_getFilePath: function(file, config) {
		var relative = config.relative;

		if (relative) {
			file = path.relative(relative, file);
		}

		return file;
	},

	_getTPL: function() {
		var tplFn = this.TPL_FN;

		if (!tplFn) {
			var tplContent = this._getTPLContent();

			tplFn = Handlebars.compile(tplContent);

			this.TPL_FN = tplFn;
		}

		return tplFn;
	},

	_getTPLContent: function() {
		var tpl = this.TPL;

		if (!tpl) {
			tpl = fs.readFileSync(this.TPL_PATH, 'utf-8');

			this.TPL = tpl;
		}

		return tpl;
	}
};

Object.defineProperty(
	Logger.prototype,
	'TPL',
	{
		get: function() {
			return this._TPL;
		},
		set: function(val) {
			this._TPL = val;

			this.TPL_FN = null;
		}
	}
);

Object.defineProperty(
	Logger.prototype,
	'TPL_PATH',
	{
		get: function() {
			return this._TPL_PATH;
		},
		set: function(val) {
			this._TPL_PATH = val;

			this.TPL = null;
		}
	}
);

{'fraction_non_alphanumeric': 0.15869649055148477, 'fraction_numerical': 0.0023139220979560356, 'mean_word_length': 3.162921348314607, 'pattern_counts': {'":': 0, '<': 5, '<?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': '6762179', 'n_tokens_mistral': 2206, 'n_tokens_neox': 1945, 'n_words': 422}