| |
| |
| |
| |
| |
| |
| |
| |
| 'use strict'; |
|
|
| |
| |
| |
| |
|
|
| var debug = require('debug')('express:view'); |
| var path = require('node:path'); |
| var fs = require('node:fs'); |
|
|
| |
| |
| |
| |
|
|
| var dirname = path.dirname; |
| var basename = path.basename; |
| var extname = path.extname; |
| var join = path.join; |
| var resolve = path.resolve; |
|
|
| |
| |
| |
| |
|
|
| module.exports = View; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function View(name, options) { |
| var opts = options || {}; |
|
|
| this.defaultEngine = opts.defaultEngine; |
| this.ext = extname(name); |
| this.name = name; |
| this.root = opts.root; |
|
|
| if (!this.ext && !this.defaultEngine) { |
| throw new Error('No default engine was specified and no extension was provided.'); |
| } |
|
|
| var fileName = name; |
|
|
| if (!this.ext) { |
| |
| this.ext = this.defaultEngine[0] !== '.' |
| ? '.' + this.defaultEngine |
| : this.defaultEngine; |
|
|
| fileName += this.ext; |
| } |
|
|
| if (!opts.engines[this.ext]) { |
| |
| var mod = this.ext.slice(1) |
| debug('require "%s"', mod) |
|
|
| |
| var fn = require(mod).__express |
|
|
| if (typeof fn !== 'function') { |
| throw new Error('Module "' + mod + '" does not provide a view engine.') |
| } |
|
|
| opts.engines[this.ext] = fn |
| } |
|
|
| |
| this.engine = opts.engines[this.ext]; |
|
|
| |
| this.path = this.lookup(fileName); |
| } |
|
|
| |
| |
| |
| |
| |
| |
|
|
| View.prototype.lookup = function lookup(name) { |
| var path; |
| var roots = [].concat(this.root); |
|
|
| debug('lookup "%s"', name); |
|
|
| for (var i = 0; i < roots.length && !path; i++) { |
| var root = roots[i]; |
|
|
| |
| var loc = resolve(root, name); |
| var dir = dirname(loc); |
| var file = basename(loc); |
|
|
| |
| path = this.resolve(dir, file); |
| } |
|
|
| return path; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| View.prototype.render = function render(options, callback) { |
| var sync = true; |
|
|
| debug('render "%s"', this.path); |
|
|
| |
| this.engine(this.path, options, function onRender() { |
| if (!sync) { |
| return callback.apply(this, arguments); |
| } |
|
|
| |
| var args = new Array(arguments.length); |
| var cntx = this; |
|
|
| for (var i = 0; i < arguments.length; i++) { |
| args[i] = arguments[i]; |
| } |
|
|
| |
| return process.nextTick(function renderTick() { |
| return callback.apply(cntx, args); |
| }); |
| }); |
|
|
| sync = false; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| View.prototype.resolve = function resolve(dir, file) { |
| var ext = this.ext; |
|
|
| |
| var path = join(dir, file); |
| var stat = tryStat(path); |
|
|
| if (stat && stat.isFile()) { |
| return path; |
| } |
|
|
| |
| path = join(dir, basename(file, ext), 'index' + ext); |
| stat = tryStat(path); |
|
|
| if (stat && stat.isFile()) { |
| return path; |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| function tryStat(path) { |
| debug('stat "%s"', path); |
|
|
| try { |
| return fs.statSync(path); |
| } catch (e) { |
| return undefined; |
| } |
| } |
|
|