/*! * Jade * Copyright(c) 2010 TJ Holowaychuk * MIT Licensed */ /** * Module dependencies. */ var Parser = require('./parser') , Lexer = require('./lexer') , Compiler = require('./compiler') , runtime = require('./runtime') , addWith = require('with') , fs = require('fs'); /** * Expose self closing tags. */ exports.selfClosing = require('./self-closing'); /** * Default supported doctypes. */ exports.doctypes = require('./doctypes'); /** * Text filters. */ exports.filters = require('./filters'); /** * Utilities. */ exports.utils = require('./utils'); /** * Expose `Compiler`. */ exports.Compiler = Compiler; /** * Expose `Parser`. */ exports.Parser = Parser; /** * Expose `Lexer`. */ exports.Lexer = Lexer; /** * Nodes. */ exports.nodes = require('./nodes'); /** * Jade runtime helpers. */ exports.runtime = runtime; /** * Template function cache. */ exports.cache = {}; /** * Parse the given `str` of jade and return a function body. * * @param {String} str * @param {Object} options * @return {String} * @api private */ function parse(str, options){ try { // Parse var parser = new (options.parser || Parser)(str, options.filename, options); // Compile var compiler = new (options.compiler || Compiler)(parser.parse(), options) , js = compiler.compile(); // Debug compiler if (options.debug) { console.error('\nCompiled Function:\n\n\033[90m%s\033[0m', js.replace(/^/gm, ' ')); } return '' + 'var buf = [];\n' + (options.self ? 'var self = locals || {};\n' + js : addWith('locals || {}', js, ['jade', 'buf'])) + ';' + 'return buf.join("");'; } catch (err) { parser = parser.context(); runtime.rethrow(err, parser.filename, parser.lexer.lineno, str); } } /** * Strip any UTF-8 BOM off of the start of `str`, if it exists. * * @param {String} str * @return {String} * @api private */ function stripBOM(str){ return 0xFEFF == str.charCodeAt(0) ? str.substring(1) : str; } /** * Compile a `Function` representation of the given jade `str`. * * Options: * * - `compileDebug` when `false` debugging code is stripped from the compiled template, when it is explicitly `true`, the source code is included in the compiled template for better accuracy. * - `filename` used to improve errors when `compileDebug` is not `false` * * @param {String} str * @param {Options} options * @return {Function} * @api public */ exports.compile = function(str, options){ var options = options || {} , filename = options.filename ? JSON.stringify(options.filename) : 'undefined' , fn; str = stripBOM(String(str)); if (options.compileDebug !== false) { fn = [ 'jade.debug = [{ lineno: 1, filename: ' + filename + ' }];' , 'try {' , parse(str, options) , '} catch (err) {' , ' jade.rethrow(err, jade.debug[0].filename, jade.debug[0].lineno' + (options.compileDebug === true ? ',' + JSON.stringify(str) : '') + ');' , '}' ].join('\n'); } else { fn = parse(str, options); } if (options.client) return new Function('locals', fn) fn = new Function('locals, jade', fn) return function(locals){ return fn(locals, Object.create(runtime)) } }; /** * Render the given `str` of jade and invoke * the callback `fn(err, str)`. * * Options: * * - `cache` enable template caching * - `filename` filename required for `include` / `extends` and caching * * @param {String} str * @param {Object|Function} options or fn * @param {Function} fn * @api public */ exports.render = function(str, options, fn){ // swap args if ('function' == typeof options) { fn = options, options = {}; } // cache requires .filename if (options.cache && !options.filename) { return fn(new Error('the "filename" option is required for caching')); } try { var path = options.filename; var tmpl = options.cache ? exports.cache[path] || (exports.cache[path] = exports.compile(str, options)) : exports.compile(str, options); fn(null, tmpl(options)); } catch (err) { fn(err); } }; /** * Render a Jade file at the given `path` and callback `fn(err, str)`. * * @param {String} path * @param {Object|Function} options or callback * @param {Function} fn * @api public */ exports.renderFile = function(path, options, fn){ var key = path + ':string'; if ('function' == typeof options) { fn = options, options = {}; } try { options.filename = path; var str = options.cache ? exports.cache[key] || (exports.cache[key] = fs.readFileSync(path, 'utf8')) : fs.readFileSync(path, 'utf8'); exports.render(str, options, fn); } catch (err) { fn(err); } }; /** * Express support. */ exports.__express = exports.renderFile;