-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.js
138 lines (115 loc) · 3.85 KB
/
index.js
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
var Promise = require('native-or-bluebird')
var resolve = require('path').resolve
var extname = require('path').extname
var assert = require('assert')
var fs = require('fs')
Templation.engines = require('./engines')
module.exports = Templation
function Templation(options) {
if (!(this instanceof Templation)) return new Templation(options)
options = options || {}
// flag whether to cache templates
this._cache = typeof options.cache === 'boolean'
? options.cache
: process.env.NODE_ENV === 'production'
this.cache = Object.create(null) // cache lookup
this.engines = {} // ext -> engine map
this.root = resolve(options.root)
}
Templation.prototype.use = function (ext, engine) {
assert(engine, 'no engine defined')
if (typeof ext !== 'string') throw new TypeError('each engine must map to an extension')
if (typeof engine.compile !== 'function') throw new TypeError('each engine must define a .compile()')
if (typeof engine.render !== 'function') throw new TypeError('each engine must define a .render()')
this.engines[ext] = engine
return this
}
Templation.prototype.render = function (name, options, fn) {
if (typeof options === 'function') {
fn = options
options = {}
}
var self = this
var promise = this._compiled(name, options)
.then(function (compiled) {
return self._render(compiled.__templation_engine, compiled, options)
})
.then(validateOutput)
if (typeof fn === 'function') {
promise.then(function (out) {
fn(null, out)
}, fn)
}
return promise
}
// look up a template by a name
// these results should be cached!
Templation.prototype._lookup = function (name) {
var filename = resolve(this.root, name)
var engines = this.engines
var ext = extname(name).slice(1)
var engine
if (ext) {
assert(engine = engines[ext], 'no view engine found for: ' + ext)
return [filename, engine]
}
var exts = Object.keys(engines)
for (var i = 0; i < exts.length; i++) {
var _filename = filename + '.' + exts[i]
if (!fs.existsSync(_filename)) continue
return [_filename, engines[exts[i]]]
}
throw new Error('could not find a suitable engine for: ' + filename)
}
// asynchronously get a compiled and maybe cached template
Templation.prototype._compiled = function (name, options) {
var filename
var engine
if (!this._cache) {
var out = this._lookup(name)
filename = out[0]
engine = out[1]
return this._compile(engine, filename, options).then(compile)
}
var cache = this.cache
var compiled = cache[name]
if (compiled) return Promise.resolve(compiled)
var out = this._lookup(name)
filename = out[0]
engine = out[1]
compiled = cache[filename]
if (compiled) {
// cache `name` as well
cache[name] = compiled
return Promise.resolve(compiled)
}
return this._compile(engine, filename, options)
.then(compile)
.then(function (compiled) {
return cache[name] = cache[filename] = compiled
})
function compile(compiled) {
// memoize stuff onto this thing
// make sure you wrap the compiled template
// in a function if it uses some sort of
// constructor-prototype mechanism
compiled.__templation_filename = filename
compiled.__templation_engine = engine
return compiled
}
}
// asynchronously compile a template
Templation.prototype._compile = function (engine, filename, options) {
return Promise.resolve(engine.compile(filename, options || {}))
}
// asynchronously render a template
Templation.prototype._render = function (engine, compiled, options) {
return Promise.resolve(engine.render(compiled, options || {}))
}
function validateOutput(out) {
if (!out) return ''
if (typeof out === 'string') return out
if (typeof out.pipe === 'function') return out
if (Buffer.isBuffer(out)) return out
throw new TypeError('templates may only render strings, buffers, or streams. Got: ' + String(out))
}