-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdust-x.js
More file actions
192 lines (159 loc) · 6.57 KB
/
dust-x.js
File metadata and controls
192 lines (159 loc) · 6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// dust.js -- Express integration for the DustJS template engine
//
// Based in part on express-dust by Dav Glass
// https://github.com/davglass/express-dust
var dust = require('dust')
, http = require('http')
, View = require('express/lib/view.js')
// ------------------------------------------------------------------------
var DEBUG = false
var debug = DEBUG
? console.error.bind(console, 'DUSTX DEBUG')
: function() {}
var summary = function(str) {
return str.split('\n')[0].slice(0, 50)+'...'
}
// ------------------------------------------------------------------------
// TODO: This needs a setter
// Disable whitespace compression.
dust.optimizers.format = function(context, node) {
return node;
};
// Make Dust use Express to load templates for partials.
// This is used when a Dust template references another template, e.g. with:
// {<template}
dust.onLoad = function onLoad(name, context, callback) {
debug('onLoad', name)
// var v = view.lookup(name, context.stack.head)
var v = view_lookup(name, context.stack.head)
debug('onLoad ->', summary(v.contents))
callback(null, v.contents)
}
// Monkey-patch Dust's load function to pass context through to onLoad.
// Needed so dust.onLoad() can pass the required settings and options
// to Express for template loading.
dust.load = function(name, chunk, context) {
debug('load', name)
var tmpl = dust.cache[name];
if (tmpl) {
return tmpl(chunk, context);
} else {
if (dust.onLoad) {
return chunk.map(function(chunk) {
//dust.onLoad(name, function(err, src) {// PATCH:
dust.onLoad(name, context, function(err, src) {
if (err) {
debug('ERROR:', 'onLoad callback', err)
return chunk.setError(err);
}
if (!dust.cache[name]) dust.loadSource(dust.compile(src, name));
dust.cache[name](chunk, context).end();
});
});
}
debug('ERROR: no onLoad defined')
return chunk.setError(new Error("Template Not Found: " + name));
}
};
// ------------------------------------------------------------------------
// The Express / Dust interface.
// Note: this is called by Express when rendering a view/partial, but is
// *not* called by Dust for partials referenced by Dust's built-in syntax.
this.compile = function(str, opts) {
var name = opts.filename
debug('compile', name, '\n\t', summary(str))
// If template caching is disabled, we need to completely clear the cache.
// Just removing `name` from the cache isn't sufficient, as Dust doesn't
// go through this function for loading template partials (see above).
dust.cache = {} // XXX make dependant on opts.cache / app.env == dev / ???
dust.loadSource(dust.compile(str, name))
return function render(opts) {
debug('render', name)
var _dust = opts._dust
, res = _dust.res
, next = _dust.next
, layout = _dust.layout
, partial = _dust.partial
, callback = _dust.callback
dust.render(name, opts, function onrender(err, html) {
debug('onrender', name, '->', summary(html))
if (err) return next(err)
// async version of tail end of res._render logic:
if (layout) {
opts.isLayout = true
opts.layout = false
opts.body = html
this.render(layout, options, callback, view, true) // fixme ??? needs testing
} else if (partial) {
callback(null, html) // fixme is this the right thing to do here?
} else if (callback) {
callback(null, html)
} else {
res.send(html)
}
})
}
}
// We need to monkey-patch Express's _render, too, so we can access the
// request, response and next() function from this.compile() and prevent
// Express from res.send()'ing the result of the template function, which
// we need to do asyncronously.
var _render = http.ServerResponse.prototype.render
, _onerror = function(e) { if (e) throw(e) }
http.ServerResponse.prototype.render = function render(xview, opts, fn, parent, sub) {
if (typeof(options) == 'function') {
fn = opts;
opts = null;
}
// pass additional context we'll need in compile/render/load. we need
// to store opts.layout and opts.isPartial and set both to false to
// prevent Express from handling those syncronously, then handle them
// during render
opts = opts || {}
opts._dust = {}
opts._dust.res = this
opts._dust.req = this.req
opts._dust.next = this.req.next
opts._dust.callback = fn
opts._dust.layout = opts.layout
opts.layout = false
opts._dust.isPartial = opts.isPartial
opts.isPartial = false
// call default render, passing a no-op callbackto prevent Express from
// calling res.send()
_render.call(this, xview, opts, _onerror, parent, sub)
}
// ------------------------------------------------------------------------
// view.lookup doesn't exist yet in the released version of Express, so
// it's reproduced here:
function view_lookup(name, options) {
function hintAtViewPaths(view, options) {
console.error();
console.error('failed to locate view "' + view.view + '", tried:');
console.error(' - ' + new View(view.path, options).path);
console.error(' - ' + new View(view.prefixPath, options).path);
console.error(' - ' + new View(view.indexPath, options).path);
if (!options.isLayout) console.error(' - ' + new View(view.upIndexPath, options).path);
if (options.isLayout) console.error(' - ' + new View(view.rootPath, options).path);
console.error();
}
// Populate view
var view, orig = view = new View(name, options);
// Try _ prefix ex: ./views/_user.jade
if (!view.exists) view = new View(orig.prefixPath, options);
// Try index ex: ./views/user/index.jade
if (!view.exists) view = new View(orig.indexPath, options);
// Try ../<name>/index ex: ../user/index.jade
// when calling partial('user') within the same dir
if (!view.exists && !options.isLayout) view = new View(orig.upIndexPath, options);
// Try layout relative to the "views" dir
if (!view.exists && options.isLayout) view = new View(orig.rootPath, options);
// Does not exist
if (!view.exists) {
// if (app.enabled('hints'))
hintAtViewPaths(orig, options);
throw new Error('failed to locate view "' + orig.view + '"');
}
return view
}
// ------------------------------------------------------------------------