summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkpdecker <kpdecker@gmail.com>2014-01-01 19:14:51 -0600
committerkpdecker <kpdecker@gmail.com>2014-01-01 19:14:51 -0600
commit6e4e1f84040d8c7ee415ef711be7572ec94f3eb6 (patch)
tree90445963d0a48378bc200158d8d85fd4ce124dad
parent3c85720137c039cefc38046541f05731d151a506 (diff)
downloadhandlebars.js-6e4e1f84040d8c7ee415ef711be7572ec94f3eb6.zip
handlebars.js-6e4e1f84040d8c7ee415ef711be7572ec94f3eb6.tar.gz
handlebars.js-6e4e1f84040d8c7ee415ef711be7572ec94f3eb6.tar.bz2
Include line info in compiler thrown exceptions
Fixes #636
-rw-r--r--lib/handlebars/base.js2
-rw-r--r--lib/handlebars/compiler/ast.js19
-rw-r--r--lib/handlebars/compiler/compiler.js5
-rw-r--r--lib/handlebars/exception.js16
-rw-r--r--lib/handlebars/runtime.js6
-rw-r--r--spec/ast.js18
6 files changed, 41 insertions, 25 deletions
diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js
index 027f052..11667af 100644
--- a/lib/handlebars/base.js
+++ b/lib/handlebars/base.js
@@ -53,7 +53,7 @@ function registerDefaultHelpers(instance) {
if(arguments.length === 2) {
return undefined;
} else {
- throw new Error("Missing helper: '" + arg + "'");
+ throw new Exception("Missing helper: '" + arg + "'");
}
});
diff --git a/lib/handlebars/compiler/ast.js b/lib/handlebars/compiler/ast.js
index 09bf927..002fd3b 100644
--- a/lib/handlebars/compiler/ast.js
+++ b/lib/handlebars/compiler/ast.js
@@ -106,12 +106,12 @@ var AST = {
},
BlockNode: function(mustache, program, inverse, close, locInfo) {
+ LocationInfo.call(this, locInfo);
+
if(mustache.sexpr.id.original !== close.path.original) {
- throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original);
+ throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original, this);
}
- LocationInfo.call(this, locInfo);
-
this.type = 'block';
this.mustache = mustache;
this.program = program;
@@ -155,11 +155,16 @@ var AST = {
original += (parts[i].separator || '') + part;
if (part === ".." || part === "." || part === "this") {
- if (dig.length > 0) { throw new Exception("Invalid path: " + original); }
- else if (part === "..") { depth++; }
- else { this.isScoped = true; }
+ if (dig.length > 0) {
+ throw new Exception("Invalid path: " + original, this);
+ } else if (part === "..") {
+ depth++;
+ } else {
+ this.isScoped = true;
+ }
+ } else {
+ dig.push(part);
}
- else { dig.push(part); }
}
this.original = original;
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index 0b25ba5..461f98b 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -279,7 +279,7 @@ Compiler.prototype = {
if (this.options.knownHelpers[name]) {
this.opcode('invokeKnownHelper', params.length, name);
} else if (this.options.knownHelpersOnly) {
- throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
+ throw new Exception("You specified knownHelpersOnly, but used the unknown helper " + name, sexpr);
} else {
this.opcode('invokeHelper', params.length, name, sexpr.isRoot);
}
@@ -316,7 +316,7 @@ Compiler.prototype = {
DATA: function(data) {
this.options.data = true;
if (data.id.isScoped || data.id.depth) {
- throw new Exception('Scoped data references are not supported: ' + data.original);
+ throw new Exception('Scoped data references are not supported: ' + data.original, data);
}
this.opcode('lookupData');
@@ -350,7 +350,6 @@ Compiler.prototype = {
},
addDepth: function(depth) {
- if(isNaN(depth)) { throw new Error("EWOT"); }
if(depth === 0) { return; }
if(!this.depths[depth]) {
diff --git a/lib/handlebars/exception.js b/lib/handlebars/exception.js
index 6de9cfd..8c5c2f6 100644
--- a/lib/handlebars/exception.js
+++ b/lib/handlebars/exception.js
@@ -1,13 +1,25 @@
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
-function Exception(/* message */) {
- var tmp = Error.prototype.constructor.apply(this, arguments);
+function Exception(message, node) {
+ var line;
+ if (node && node.firstLine) {
+ line = node.firstLine;
+
+ message += ' - ' + line + ':' + node.firstColumn;
+ }
+
+ var tmp = Error.prototype.constructor.call(this, message);
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (var idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
+
+ if (line) {
+ this.lineNumber = line;
+ this.column = node.firstColumn;
+ }
}
Exception.prototype = new Error();
diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js
index 5c63645..5ae8d8a 100644
--- a/lib/handlebars/runtime.js
+++ b/lib/handlebars/runtime.js
@@ -10,11 +10,11 @@ export function checkRevision(compilerInfo) {
if (compilerRevision < currentRevision) {
var runtimeVersions = REVISION_CHANGES[currentRevision],
compilerVersions = REVISION_CHANGES[compilerRevision];
- throw new Error("Template was precompiled with an older version of Handlebars than the current runtime. "+
+ throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").");
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
- throw new Error("Template was precompiled with a newer version of Handlebars than the current runtime. "+
+ throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+
"Please update your runtime to a newer version ("+compilerInfo[1]+").");
}
}
@@ -24,7 +24,7 @@ export function checkRevision(compilerInfo) {
export function template(templateSpec, env) {
if (!env) {
- throw new Error("No environment passed to template");
+ throw new Exception("No environment passed to template");
}
// Note: Using env.VM references rather than local var references throughout this section to allow
diff --git a/spec/ast.js b/spec/ast.js
index 0cb1103..46f0131 100644
--- a/spec/ast.js
+++ b/spec/ast.js
@@ -78,8 +78,8 @@ describe('ast', function() {
shouldThrow(function() {
var sexprNode = new handlebarsEnv.AST.SexprNode([{ original: 'foo'}], null);
var mustacheNode = new handlebarsEnv.AST.MustacheNode(sexprNode, null, '{{', {});
- new handlebarsEnv.AST.BlockNode(mustacheNode, {}, {}, {path: {original: 'bar'}});
- }, Handlebars.Exception, "foo doesn't match bar");
+ new handlebarsEnv.AST.BlockNode(mustacheNode, {}, {}, {path: {original: 'bar'}}, {first_line: 2, first_column: 2});
+ }, Handlebars.Exception, "foo doesn't match bar - 2:2");
});
it('stores location info', function(){
@@ -102,22 +102,22 @@ describe('ast', function() {
{part: 'foo'},
{part: '..'},
{part: 'bar'}
- ]);
- }, Handlebars.Exception, "Invalid path: foo..");
+ ], {first_line: 1, first_column: 1});
+ }, Handlebars.Exception, "Invalid path: foo.. - 1:1");
shouldThrow(function() {
new handlebarsEnv.AST.IdNode([
{part: 'foo'},
{part: '.'},
{part: 'bar'}
- ]);
- }, Handlebars.Exception, "Invalid path: foo.");
+ ], {first_line: 1, first_column: 1});
+ }, Handlebars.Exception, "Invalid path: foo. - 1:1");
shouldThrow(function() {
new handlebarsEnv.AST.IdNode([
{part: 'foo'},
{part: 'this'},
{part: 'bar'}
- ]);
- }, Handlebars.Exception, "Invalid path: foothis");
+ ], {first_line: 1, first_column: 1});
+ }, Handlebars.Exception, "Invalid path: foothis - 1:1");
});
it('stores location info', function(){
@@ -216,7 +216,7 @@ describe('ast', function() {
firstColumn: 0,
lastColumn: 0
};
- var pn = new handlebarsEnv.AST.ProgramNode([], {strip: {}}, [ clone ], LOCATION_INFO);
+ pn = new handlebarsEnv.AST.ProgramNode([], {strip: {}}, [ clone ], LOCATION_INFO);
testLocationInfoStorage(pn);
// Assert that the newly created ProgramNode has the same location