Skip to content

Commit

Permalink
Implement Dust Debugging
Browse files Browse the repository at this point in the history
  • Loading branch information
Prayrit Jain committed Oct 21, 2013
1 parent 4238f6c commit 6a6d936
Show file tree
Hide file tree
Showing 8 changed files with 4,977 additions and 46 deletions.
812 changes: 812 additions & 0 deletions dist/dust-core-2.1.0.js

Large diffs are not rendered by default.

3,788 changes: 3,788 additions & 0 deletions dist/dust-full-2.1.0.js

Large diffs are not rendered by default.

215 changes: 184 additions & 31 deletions lib/dust.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,92 @@ function getGlobal(){

(function(dust) {

if(!dust) {
return;
}
var ERROR = 'ERROR',
WARN = 'WARN',
INFO = 'INFO',
DEBUG = 'DEBUG',
levels = [DEBUG, INFO, WARN, ERROR],
logger = function() {};

dust.isDebug = false;
dust.debugLevel = INFO;

// Try to find the console logger in window scope (browsers) or top level scope (node.js)
if (typeof window !== 'undefined' && window && window.console && window.console.log) {
logger = window.console.log;
} else if (typeof console !== 'undefined' && console && console.log) {
logger = console.log;
}

/**
* If dust.isDebug is true, Log dust debug statements, info statements, warning statements, and errors.
* This default implementation will print to the console if it exists.
* @param {String} message the message to print
* @param {String} type the severity of the message(ERROR, WARN, INFO, or DEBUG)
* @public
*/
dust.log = function(message, type) {
var type = type || INFO;
if(dust.isDebug && levels.indexOf(type) >= levels.indexOf(dust.debugLevel)) {
if(!dust.logQueue) {
dust.logQueue = [];
}
dust.logQueue.push({message: message, type: type});
logger.call(console || window.console, "[DUST " + type + "]: " + message);
}
};

/**
* If debugging is turned on(dust.isDebug=true) log the error message and throw it.
* Otherwise try to keep rendering. This is useful to fail hard in dev mode, but keep rendering in production.
* @param {Error} error the error message to throw
* @param {Object} chunk the chunk the error was thrown from
* @public
*/
dust.onError = function(error, chunk) {
dust.log(error.message || error, ERROR);
if(dust.isDebug) {
throw error;
} else {
return chunk;
}
};

dust.helpers = {};

dust.cache = {};

dust.register = function(name, tmpl) {
if (!name) return;
if (!name) {
dust.log('Template name is undefined', 'WARN');
return;
}
if(typeof tmpl !== 'function') {
dust.onError(new Error('Template [' + name + '] cannot be resolved to a Dust function'));
}
dust.cache[name] = tmpl;
};

dust.render = function(name, context, callback) {
var chunk = new Stub(callback).head;
dust.load(name, chunk, Context.wrap(context, name)).end();
try {
dust.load(name, chunk, Context.wrap(context, name)).end();
} catch (err) {
dust.onError(err, chunk);
}
};

dust.stream = function(name, context) {
var stream = new Stream();
dust.nextTick(function() {
dust.load(name, stream.head, Context.wrap(context, name)).end();
try {
dust.load(name, stream.head, Context.wrap(context, name)).end();
} catch (err) {
dust.onError(err, stream.head);
}
});
return stream;
};
Expand All @@ -39,7 +107,12 @@ dust.compileFn = function(source, name) {
return function(context, callback) {
var master = callback ? new Stub(callback) : new Stream();
dust.nextTick(function() {
tmpl(master.head, Context.wrap(context, name)).end();
if(typeof tmpl === 'function') {
tmpl(master.head, Context.wrap(context, name)).end();
}
else {
dust.onError(new Error('Template [' + name + '] cannot be resolved to a Dust function'));
}
});
return master;
};
Expand Down Expand Up @@ -71,7 +144,7 @@ if (Array.isArray) {
dust.isArray = Array.isArray;
} else {
dust.isArray = function(arr) {
return Object.prototype.toString.call(arr) == "[object Array]";
return Object.prototype.toString.call(arr) === "[object Array]";
};
}

Expand All @@ -98,11 +171,14 @@ dust.filter = function(string, auto, filters) {
var name = filters[i];
if (name === "s") {
auto = null;
dust.log('Using unescape filter on [' + string + ']', DEBUG);
}
// fail silently for invalid filters
else if (typeof dust.filters[name] === 'function') {
string = dust.filters[name](string);
}
else {
dust.onError(new Error('Invalid filter [' + name + ']'));
}
}
}
// by default always apply the h filter, unless asked to unescape with |s
Expand All @@ -117,8 +193,21 @@ dust.filters = {
j: function(value) { return dust.escapeJs(value); },
u: encodeURI,
uc: encodeURIComponent,
js: function(value) { if (!JSON) { return value; } return JSON.stringify(value); },
jp: function(value) { if (!JSON) { return value; } return JSON.parse(value); }
js: function(value) {
if (!JSON) {
dust.log('JSON is undefined. JSON stringify has not been used on [' + value + ']', WARN);
return value;
} else {
return JSON.stringify(value);
}
},
jp: function(value) {
if (!JSON) {dust.log('JSON is undefined. JSON parse has not been used on [' + value + ']', WARN);
return value;
} else {
return JSON.parse(value);
}
}
};

function Context(stack, global, blocks, templateName) {
Expand All @@ -140,25 +229,37 @@ Context.wrap = function(context, name) {
};

Context.prototype.get = function(key) {
var ctx = this.stack, value;
var ctx = this.stack, value, globalValue;

while(ctx) {
if (ctx.isObject) {
value = ctx.head[key];
if(ctx.head) {
value = ctx.head[key];
} else {
dust.log('Context head is undefined for [{' + key + '}] in template [' + this.templateName + ']', WARN);
}
if (!(value === undefined)) {
return value;
}
}
else {
dust.log('Current context is not an object. Cannot find value for [{' + key + '}] in template [' + this.templateName + ']', DEBUG);
}
ctx = ctx.tail;
}
return this.global ? this.global[key] : undefined;
dust.log('Looking for [{' + key + '}] in the globals in template [' + this.templateName + ']', DEBUG);
globalValue = this.global ? this.global[key] : undefined;
if (typeof globalValue === 'undefined') {
dust.log('Cannot find the value for reference [{' + key + '}] in template [' + this.templateName + ']', DEBUG);
}
return globalValue;
};

//supports dot path resolution, function wrapped apply, and searching global paths
Context.prototype.getPath = function(cur, down) {
var ctx = this.stack, ctxThis,
len = down.length,
tail = cur ? undefined : this.stack.tail;
tail = cur ? undefined : this.stack.tail;

if (cur && len === 0) return ctx.head;
ctx = ctx.head;
Expand Down Expand Up @@ -197,11 +298,25 @@ Context.prototype.getPath = function(cur, down) {
};

Context.prototype.push = function(head, idx, len) {
return new Context(new Stack(head, this.stack, idx, len), this.global, this.blocks, this.templateName);
var context = new Context(new Stack(head, this.stack, idx, len), this.global, this.blocks, this.templateName);
if(context) {
return context;
}
else {
dust.log('Head [' + head + '] could not be resolved to a context for the template [' + this.templateName + ']', WARN);
return Context.wrap(context);
}
};

Context.prototype.rebase = function(head) {
return new Context(new Stack(head), this.global, this.blocks, this.templateName);
var context = new Context(new Stack(head), this.global, this.blocks, this.templateName);
if(context) {
return context;
}
else {
dust.log('Head [' + head + '] could not be resolved to a context for the template [' + this.templateName + ']', WARN);
return Context.wrap(context);
}
};

Context.prototype.current = function() {
Expand All @@ -216,7 +331,10 @@ Context.prototype.getBlock = function(key, chk, ctx) {

var blocks = this.blocks;

if (!blocks) return;
if (!blocks) {
dust.log('No blocks for context[{' + key + '}] in template [' + this.templateName + ']', DEBUG);
return;
}
var len = blocks.length, fn;
while (len--) {
fn = blocks[len][key];
Expand All @@ -242,7 +360,13 @@ Context.prototype.shiftBlocks = function(locals) {
function Stack(head, tail, idx, len) {
this.tail = tail;
this.isObject = !dust.isArray(head) && head && typeof head === "object";
this.head = head;
if(head != null) {
this.head = head;
}
else {
dust.log('Head was undefined. Defaulting to {}', DEBUG);
this.head = {};
}
this.index = idx;
this.of = len;
}
Expand All @@ -261,6 +385,7 @@ Stub.prototype.flush = function() {
this.out += chunk.data.join(""); //ie7 perf
} else if (chunk.error) {
this.callback(chunk.error);
dust.onError(new Error('Chunk error [' + chunk.error + '] thrown. Ceasing to render this template.'));
this.flush = function() {};
return;
} else {
Expand All @@ -284,6 +409,7 @@ Stream.prototype.flush = function() {
this.emit('data', chunk.data.join("")); //ie7 perf
} else if (chunk.error) {
this.emit('error', chunk.error);
dust.onError(new Error('Chunk error [' + chunk.error + '] thrown. Ceasing to render this template.'));
this.flush = function() {};
return;
} else {
Expand All @@ -296,16 +422,24 @@ Stream.prototype.flush = function() {
};

Stream.prototype.emit = function(type, data) {
if (!this.events) return false;
if (!this.events) {
dust.log('No events to emit', INFO);
return false;
}
var handler = this.events[type];
if (!handler) return false;
if (typeof handler == 'function') {
if (!handler) {
dust.log('Event type [' + type + '] does not exist', WARN);
return false;
}
if (typeof handler === 'function') {
handler(data);
} else {
} else if (dust.isArray(handler)) {
var listeners = handler.slice(0);
for (var i = 0, l = listeners.length; i < l; i++) {
listeners[i](data);
}
} else {
dust.onError(new Error('Event Handler [' + handler + '] is not of a type that is handled by emit'));
}
};

Expand All @@ -314,7 +448,12 @@ Stream.prototype.on = function(type, callback) {
this.events = {};
}
if (!this.events[type]) {
this.events[type] = callback;
dust.log('Event type [' + type + '] does not exist. Using just the specified callback.', WARN);
if(callback) {
this.events[type] = callback;
} else {
dust.log('Callback for type [' + type + '] does not exist. Listener not registered.', WARN);
}
} else if(typeof this.events[type] === 'function') {
this.events[type] = [this.events[type], callback];
} else {
Expand All @@ -325,9 +464,17 @@ Stream.prototype.on = function(type, callback) {

Stream.prototype.pipe = function(stream) {
this.on("data", function(data) {
stream.write(data, "utf8");
try {
stream.write(data, "utf8");
} catch (err) {
dust.onError(err, stream.head);
}
}).on("end", function() {
stream.end();
try {
return stream.end();
} catch (err) {
dust.onError(err, stream.head);
}
}).on("error", function(err) {
stream.error(err);
});
Expand Down Expand Up @@ -438,9 +585,7 @@ Chunk.prototype.section = function(elem, context, bodies, params) {
if (len > 0) {
// any custom helper can blow up the stack
// and store a flattened context, guard defensively
if(context.stack.head) {
context.stack.head['$len'] = len;
}
context.stack.head['$len'] = len;
for (var i=0; i<len; i++) {
if(context.stack.head) {
context.stack.head['$idx'] = i;
Expand Down Expand Up @@ -474,7 +619,8 @@ Chunk.prototype.section = function(elem, context, bodies, params) {
// undefined are all falsy
} else if (skip) {
return skip(this, context);
}
}
dust.log('Cannot render a section tag in template [' + context.templateName + ']', WARN);
return this;
};

Expand All @@ -487,6 +633,7 @@ Chunk.prototype.exists = function(elem, context, bodies) {
} else if (skip) {
return skip(this, context);
}
dust.log('Not rendering body or else block for exists check', DEBUG);
return this;
};

Expand All @@ -499,6 +646,7 @@ Chunk.prototype.notexists = function(elem, context, bodies) {
} else if (skip) {
return skip(this, context);
}
dust.log('Not rendering body or else block for not exists check', DEBUG);
return this;
};

Expand Down Expand Up @@ -550,11 +698,16 @@ Chunk.prototype.partial = function(elem, context, params) {
};

Chunk.prototype.helper = function(name, context, bodies, params) {
var chunk = this;
// handle invalid helpers, similar to invalid filters
if( dust.helpers[name]){
return dust.helpers[name](this, context, bodies, params);
} else {
return this;
try {
if(dust.helpers[name]) {
return dust.helpers[name](chunk, context, bodies, params);
} else {
return dust.onError(new Error('Invalid helper [' + name + ']'), chunk);
}
} catch (err) {
return dust.onError(err, chunk);
}
};

Expand Down
Loading

0 comments on commit 6a6d936

Please sign in to comment.