UNPKG

5.47 kBJavaScriptView Raw
1
2/*!
3 * Jade
4 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
5 * MIT Licensed
6 */
7
8/**
9 * Module dependencies.
10 */
11
12var Parser = require('./parser')
13 , Compiler = require('./compiler')
14 , runtime = require('./runtime')
15// if node
16 , fs = require('fs');
17// end
18
19/**
20 * Library version.
21 */
22
23exports.version = '0.14.1';
24
25/**
26 * Intermediate JavaScript cache.
27 */
28
29var cache = exports.cache = {};
30
31/**
32 * Expose self closing tags.
33 */
34
35exports.selfClosing = require('./self-closing');
36
37/**
38 * Default supported doctypes.
39 */
40
41exports.doctypes = require('./doctypes');
42
43/**
44 * Text filters.
45 */
46
47exports.filters = require('./filters');
48
49/**
50 * Utilities.
51 */
52
53exports.utils = require('./utils');
54
55/**
56 * Expose `Compiler`.
57 */
58
59exports.Compiler = Compiler;
60
61/**
62 * Expose `Parser`.
63 */
64
65exports.Parser = Parser;
66
67/**
68 * Nodes.
69 */
70
71exports.nodes = require('./nodes');
72
73/**
74 * Jade runtime helpers.
75 */
76
77exports.runtime = runtime;
78
79/**
80 * Parse the given `str` of jade and return a function body.
81 *
82 * @param {String} str
83 * @param {Object} options
84 * @return {String}
85 * @api private
86 */
87
88function parse(str, options){
89 var filename = options.filename
90 , inline = false !== options.inline
91 , inlined = '';
92
93 if (inline) {
94 inlined += runtime.attrs.toString() + '\n';
95 inlined += runtime.escape.toString() + '\n';
96 } else {
97 inlined = 'var attrs = jade.attrs, escape = jade.escape;\n';
98 }
99
100 try {
101 // Parse
102 var parser = new Parser(str, filename, options);
103 if (options.debug) parser.debug();
104
105 // Compile
106 var compiler = new (options.compiler || Compiler)(parser.parse(), options)
107 , js = compiler.compile();
108
109 // Debug compiler
110 if (options.debug) {
111 console.log('\n\x1b[1mCompiled Function\x1b[0m:\n\n%s', js.replace(/^/gm, ' '));
112 }
113
114 try {
115 return ''
116 + inlined
117 + 'var buf = [];\n'
118 + (options.self
119 ? 'var self = locals || {}, __ = __ || locals.__;\n' + js
120 : 'with (locals || {}) {\n' + js + '\n}\n')
121 + 'return buf.join("");';
122
123 } catch (err) {
124 process.compile(js, filename || 'Jade');
125 return;
126 }
127 } catch (err) {
128 runtime.rethrow(err, str, filename, parser.lexer.lineno);
129 }
130}
131
132/**
133 * Compile a `Function` representation of the given jade `str`.
134 *
135 * Options:
136 *
137 * - `compileDebug` when `false` debugging code is stripped from the compiled template
138 * - `inline` when `false` helpers are not inlined, and `jade.<helper>` is used
139 *
140 * @param {String} str
141 * @param {Options} options
142 * @return {Function}
143 * @api public
144 */
145
146exports.compile = function(str, options){
147 var options = options || {}
148 , input = JSON.stringify(str)
149 , inline = false !== options.inline
150 , filename = options.filename
151 ? JSON.stringify(options.filename)
152 : 'undefined'
153 , inlined = ''
154 , fn;
155
156 if (inline) {
157 inlined = runtime.rethrow.toString();
158 } else {
159 inlined = 'var rethrow = jade.rethrow;';
160 }
161
162 if (options.compileDebug !== false) {
163 // Reduce closure madness by injecting some locals
164 fn = [
165 'var __ = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };'
166 , inlined
167 , 'try {'
168 , parse(String(str), options || {})
169 , '} catch (err) {'
170 , ' rethrow(err, __.input, __.filename, __.lineno);'
171 , '}'
172 ].join('\n');
173 } else {
174 fn = parse(String(str), options || {});
175 }
176
177 return new Function('locals', fn);
178};
179
180/**
181 * Render the given `str` of jade.
182 *
183 * Options:
184 *
185 * - `scope` Evaluation scope (`this`)
186 * - `locals` Local variable object
187 * - `filename` Used in exceptions, and required by `cache`
188 * - `cache` Cache intermediate JavaScript in memory keyed by `filename`
189 * - `compiler` Compiler to replade jade's default
190 * - `doctype` Specify the default doctype
191 *
192 * @param {String|Buffer} str
193 * @param {Object} options
194 * @return {String}
195 * @api public
196 */
197
198exports.render = function(str, options){
199 var fn
200 , options = options || {}
201 , filename = options.filename;
202
203 // Accept Buffers
204 str = String(str);
205
206 // Cache support
207 if (options.cache) {
208 if (filename) {
209 if (cache[filename]) {
210 fn = cache[filename];
211 } else {
212 fn = cache[filename] = new Function('locals', parse(str, options));
213 }
214 } else {
215 throw new Error('filename is required when using the cache option');
216 }
217 } else {
218 fn = new Function('locals', parse(str, options));
219 }
220
221 // Render the template
222 try {
223 var locals = options.locals || {}
224 , meta = { lineno: 1 };
225 locals.__ = meta;
226 return fn.call(options.scope, locals);
227 } catch (err) {
228 runtime.rethrow(err, str, filename, meta.lineno);
229 }
230};
231
232/**
233 * Render jade template at the given `path`.
234 *
235 * @param {String} path
236 * @param {Object} options
237 * @param {Function} fn
238 * @api public
239 */
240
241exports.renderFile = function(path, options, fn){
242 var ret;
243
244 if (typeof options === 'function') {
245 fn = options;
246 options = {};
247 }
248 options.filename = path;
249
250 // Primed cache
251 if (options.cache && cache[path]) {
252 try {
253 ret = exports.render('', options);
254 } catch (err) {
255 return fn(err);
256 }
257 fn(null, ret);
258 } else {
259 fs.readFile(path, 'utf8', function(err, str){
260 if (err) return fn(err);
261 try {
262 ret = exports.render(str, options);
263 } catch (err) {
264 return fn(err);
265 }
266 fn(null, ret);
267 });
268 }
269};
\No newline at end of file