toffee
Version:
A NodeJs and browser-side templating language based on CoffeeScript with slicker tokens and syntax.
321 lines (297 loc) • 12.2 kB
JavaScript
// Generated by CoffeeScript 1.12.7
(function() {
var _ppEscape, errorTypes, path, toffeeError, util,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
path = require("path");
util = require("util");
errorTypes = exports.errorTypes = {
PARSER: 0,
STR_INTERPOLATE: 1,
COFFEE_COMPILE: 2,
RUNTIME: 3
};
toffeeError = (function(superClass) {
extend(toffeeError, superClass);
function toffeeError(view, err_type, e) {
this.errType = err_type;
this.view = view;
this.e = e;
this.toffeeSrc = view.txt;
switch (this.errType) {
case errorTypes.PARSER:
this.offensiveSrc = this.toffeeSrc;
break;
case errorTypes.STR_INTERPOLATE:
this.offensiveSrc = this.toffeeSrc;
break;
case errorTypes.COFFEE_COMPILE:
this.offensiveSrc = this.view.coffeeScript;
break;
case errorTypes.RUNTIME:
this.offensiveSrc = this.view.javaScript;
}
this.toffeeSrcLines = this.toffeeSrc.split("\n");
this.offensiveSrcLines = this.offensiveSrc.split("\n");
}
toffeeError.prototype.getConvertedError = function() {
/* --------------------------------------
returns a JS style error, but with some extras
{
stack: array of lines
message: error message
line_range: line range in the toffee file
filename: filename, if available; or null
...etc...
}
------------------------------------------
*/
var line, ref, ref1, ref2, res;
res = {
stack: [],
message: "",
type: this.errType,
full_path: this.view.fileName,
dir_name: path.dirname(this.view.fileName),
file: path.basename(this.view.fileName),
line_range: null
};
if (((ref = this.e) != null ? ref.message : void 0) != null) {
res.message = this.e.message;
}
if (((ref1 = this.e) != null ? (ref2 = ref1.location) != null ? ref2.first_line : void 0 : void 0) != null) {
res.line_range = this._convertJsErrorRangeToToffeeRange(this.e.location);
}
switch (this.errType) {
case errorTypes.PARSER:
if (res.line_range == null) {
line = this._extractOffensiveLineNo(this.e.message, /on line ([0-9]+)/);
res.line_range = [line, line + 1];
}
break;
case errorTypes.STR_INTERPOLATE:
res.line_range = [this.e.relayed_line_range[0], this.e.relayed_line_range[1]];
res.message = res.message.replace('starting on line NaN', this._lineRangeToPhrase(res.line_range));
res.message = res.message.replace('missing }', 'unclosed `\#{}`');
break;
case errorTypes.COFFEE_COMPILE:
if (res.line_range == null) {
line = this._extractOffensiveLineNo(this.e.message, /on line ([0-9]+)/);
res.line_range = this._convertOffensiveLineToToffeeRange(line);
}
if (res.message.indexOf('on line') !== -1) {
res.message = res.message.replace(/on line [0-9]+/, this._lineRangeToPhrase(res.line_range));
} else {
res.message += " " + this._lineRangeToPhrase(res.line_range);
}
break;
case errorTypes.RUNTIME:
if (res.line_range == null) {
res.line_range = [0, 0];
}
if (this.e.stack) {
res.stack = this.e.stack.split("\n");
this._convertRuntimeStackLines(res);
}
}
return res;
};
toffeeError.prototype._convertRuntimeStackLines = function(converted_err) {
/*
a little more complicated, so extracted. Returns an array
of dictionaries where there's extra info on each line in the stack.
*/
var at_pub_call, hit_pub_yet, i, in_src_file, k, len, line, lineno, lrange, m, results, rxx_inline, rxx_pub, stack;
hit_pub_yet = false;
stack = converted_err.stack;
results = [];
for (i = k = 0, len = stack.length; k < len; i = ++k) {
line = stack[i];
rxx_pub = /Object[\.].*?pub[\s]\(undefined\:([0-9]+)\:[0-9]+|tmpl[\.]render[\.]tmpl[\.]pub.*\(.*\:([0-9]+)\:[0-9]+/;
m = line.match(rxx_pub);
in_src_file = false;
lrange = [null, null];
at_pub_call = false;
if ((m != null ? m.length : void 0) >= 2) {
line = line.replace("undefined", converted_err.full_path);
lineno = this._extractOffensiveLineNo(line, /([0-9]+)\:[0-9]+/);
lrange = this._convertOffensiveLineToToffeeRange(lineno);
line = line.replace(/\:[0-9]+\:[0-9]+/, "");
hit_pub_yet = true;
in_src_file = true;
at_pub_call = true;
}
rxx_inline = /at[\s]undefined\:([0-9]+)\:[0-9]+/;
m = line.match(rxx_inline);
if ((m != null ? m.length : void 0) >= 2) {
line = line.replace("undefined", converted_err.full_path);
lineno = this._extractOffensiveLineNo(line, /([0-9]+)\:[0-9]+/);
lrange = this._convertOffensiveLineToToffeeRange(lineno);
line = line.replace(/\:[0-9]+\:[0-9]+/, "");
in_src_file = true;
}
stack[i] = {
line: line,
above_pub_call: !hit_pub_yet,
at_pub_call: at_pub_call,
in_src_file: in_src_file,
line_range: lrange
};
if (stack[i].line_range[0] && !converted_err.line_range[0]) {
results.push(converted_err.line_range = stack[i].line_range);
} else {
results.push(void 0);
}
}
return results;
};
toffeeError.prototype.getPrettyPrintText = function() {
/*
returns a TEXT only blob explaining the error
*/
var cerr, count, header, i, item, k, len, ref, ref1, res;
cerr = this.getConvertedError();
header = cerr.dir_name + "/" + cerr.file + ": " + cerr.message;
res = "ERROR\n=====\n" + header;
if ((ref = cerr.stack) != null ? ref.length : void 0) {
res += "\n\nSTACK\n=====\n";
count = 0;
ref1 = cerr.stack;
for (i = k = 0, len = ref1.length; k < len; i = ++k) {
item = ref1[i];
if (i === 0) {
res += (count++) + " " + item.line;
} else if (item.in_src_file && (item.above_pub_call || item.at_pub_call)) {
res += (count++) + " [" + (this._lineRangeToPhrase(item.line_range)) + "] " + cerr.dir_name + "/" + cerr.file;
} else if (item.in_src_file) {
continue;
} else {
res += "" + (count++) + item.line;
}
if (i < cerr.stack.length - 1) {
res += "\n";
}
}
}
res += "\n";
return res;
};
toffeeError.prototype.getPrettyPrint = function() {
/*
returns an HTML blob explaining the error
with lines highlighted
*/
var cerr, count, extra, header, i, item, j, k, l, len, line, padding, padding_len, ref, ref1, ref2, ref3, ref4, res;
cerr = this.getConvertedError();
res = "";
header = cerr.dir_name + "/<span style=\"background-color:#fde\"><b>" + cerr.file + "</b>: " + (_ppEscape(cerr.message)) + "</span>";
res += "<div style=\"line-height:13px;border:1px solid #999;margin:10px;padding:10px;background-color:#fff;position:fixed;top:0;left:0;max-width:90%;z-index:9999;max-height:90%;overflow:scroll;\">\n \n<pre>" + header + "</pre>\n \n<hr />\n \n<div style=\"font-family:courier new;font-size:12px;color:#900;width:100%;\">";
if ((ref = cerr.stack) != null ? ref.length : void 0) {
res += "<div style=\"border:1px solid #000;background-color:#eee;width:100%;\">";
count = 0;
ref1 = cerr.stack;
for (i = k = 0, len = ref1.length; k < len; i = ++k) {
item = ref1[i];
if (i === 0) {
res += "<div style=\"color:#333;width:100%\">" + (count++) + " " + item.line + "</div>";
} else if (item.in_src_file && (item.above_pub_call || item.at_pub_call)) {
res += "<div style=\"color:#000;width:100%\">" + (count++) + " [" + (this._lineRangeToPhrase(item.line_range)) + "] " + cerr.dir_name + "/" + cerr.file + "</div>";
} else if (item.in_src_file) {
continue;
} else {
res += "<div style=\"color:#999;width:100%\">" + (count++) + item.line + "</div>";
}
}
res += "</div>";
}
for (i = l = ref2 = cerr.line_range[0] - 3, ref3 = cerr.line_range[1] + 1; ref2 <= ref3 ? l < ref3 : l > ref3; i = ref2 <= ref3 ? ++l : --l) {
if ((i < 0) || i > this.toffeeSrcLines.length - 1) {
continue;
}
line = _ppEscape(this.toffeeSrcLines[i]);
padding_len = 5 - ("" + (i + 1)).length;
padding = ((function() {
var n, ref4, results;
results = [];
for (j = n = 0, ref4 = padding_len; 0 <= ref4 ? n < ref4 : n > ref4; j = 0 <= ref4 ? ++n : --n) {
results.push(" ");
}
return results;
})()).join("");
if (((cerr.line_range[0] - 1) <= (ref4 = i) && ref4 < cerr.line_range[1])) {
extra = "<span style=\"background-color:#fde\">";
} else {
extra = "<span>";
}
res += extra + "\n" + (i + 1) + ": " + padding + " " + line + "</span><br />";
}
res += " \n</div>\n\n</div>";
return res;
};
toffeeError.prototype._lineRangeToPhrase = function(lrange) {
if (lrange[0] === lrange[1]) {
return "on line " + lrange[0];
} else {
return "between lines " + lrange[0] + " and " + lrange[1];
}
};
toffeeError.prototype._extractOffensiveLineNo = function(msg, rxx) {
var m;
m = msg.match(rxx);
if (!((m != null ? m.length : void 0) >= 2)) {
return null;
}
return parseInt(m[1]);
};
toffeeError.prototype._convertJsErrorRangeToToffeeRange = function(loc) {
var range, range2;
range = this._convertOffensiveLineToToffeeRange(loc.first_line);
if (loc.last_line != null) {
range2 = this._convertOffensiveLineToToffeeRange(loc.last_line);
range[1] = range2[1];
}
return range;
};
toffeeError.prototype._convertOffensiveLineToToffeeRange = function(lineno) {
/*
Given the error line in a converted file, hunts for surrounding
__toffee.lineno calls and returns a pair array with the error position
range in the original toffee file.
*/
var next, next_matches, ol, prev, prev_matches, res, tl;
ol = this.offensiveSrcLines;
tl = this.toffeeSrcLines;
if ((lineno == null) || isNaN(lineno)) {
return [1, tl.length];
}
prev = ol.slice(0, lineno).join("\n");
next = ol.slice(lineno).join("\n");
prev_matches = prev.match(/_ln[ ]*\(?[ ]*([0-9]+)/g);
next_matches = next.match(/_ln[ ]*\(?[ ]*([0-9]+)/g);
res = [1, tl.length];
if (prev_matches != null ? prev_matches.length : void 0) {
res[0] = parseInt(prev_matches[prev_matches.length - 1].match(/[0-9]+/)[0]);
}
if (next_matches != null ? next_matches.length : void 0) {
res[1] = parseInt(next_matches[0].match(/[0-9]+/)[0]);
}
return res;
};
return toffeeError;
})(Error);
exports.toffeeError = toffeeError;
_ppEscape = function(txt) {
var i, m;
txt = txt.replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''');
m = txt.match(/^[\t ]*/);
txt = txt.replace(m[0], ((function() {
var k, ref, results;
results = [];
for (i = k = 0, ref = m[0].length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
results.push(" ");
}
return results;
})()).join(""));
return txt;
};
}).call(this);