arnaucube
arnaucube/thoughtsPublic Self-hosted repository
thoughts/node_modules/express/lib/view.js
node_modules/express/lib/view.jsRaw
  1/*!
  2 * express
  3 * Copyright(c) 2009-2013 TJ Holowaychuk
  4 * Copyright(c) 2013 Roman Shtylman
  5 * Copyright(c) 2014-2015 Douglas Christopher Wilson
  6 * MIT Licensed
  7 */
  8
  9'use strict';
 10
 11/**
 12 * Module dependencies.
 13 * @private
 14 */
 15
 16var debug = require('debug')('express:view');
 17var path = require('path');
 18var fs = require('fs');
 19var utils = require('./utils');
 20
 21/**
 22 * Module variables.
 23 * @private
 24 */
 25
 26var dirname = path.dirname;
 27var basename = path.basename;
 28var extname = path.extname;
 29var join = path.join;
 30var resolve = path.resolve;
 31
 32/**
 33 * Module exports.
 34 * @public
 35 */
 36
 37module.exports = View;
 38
 39/**
 40 * Initialize a new `View` with the given `name`.
 41 *
 42 * Options:
 43 *
 44 *   - `defaultEngine` the default template engine name
 45 *   - `engines` template engine require() cache
 46 *   - `root` root path for view lookup
 47 *
 48 * @param {string} name
 49 * @param {object} options
 50 * @public
 51 */
 52
 53function View(name, options) {
 54  var opts = options || {};
 55
 56  this.defaultEngine = opts.defaultEngine;
 57  this.ext = extname(name);
 58  this.name = name;
 59  this.root = opts.root;
 60
 61  if (!this.ext && !this.defaultEngine) {
 62    throw new Error('No default engine was specified and no extension was provided.');
 63  }
 64
 65  var fileName = name;
 66
 67  if (!this.ext) {
 68    // get extension from default engine name
 69    this.ext = this.defaultEngine[0] !== '.'
 70      ? '.' + this.defaultEngine
 71      : this.defaultEngine;
 72
 73    fileName += this.ext;
 74  }
 75
 76  if (!opts.engines[this.ext]) {
 77    // load engine
 78    opts.engines[this.ext] = require(this.ext.substr(1)).__express;
 79  }
 80
 81  // store loaded engine
 82  this.engine = opts.engines[this.ext];
 83
 84  // lookup path
 85  this.path = this.lookup(fileName);
 86}
 87
 88/**
 89 * Lookup view by the given `name`
 90 *
 91 * @param {string} name
 92 * @private
 93 */
 94
 95View.prototype.lookup = function lookup(name) {
 96  var path;
 97  var roots = [].concat(this.root);
 98
 99  debug('lookup "%s"', name);
100
101  for (var i = 0; i < roots.length && !path; i++) {
102    var root = roots[i];
103
104    // resolve the path
105    var loc = resolve(root, name);
106    var dir = dirname(loc);
107    var file = basename(loc);
108
109    // resolve the file
110    path = this.resolve(dir, file);
111  }
112
113  return path;
114};
115
116/**
117 * Render with the given options.
118 *
119 * @param {object} options
120 * @param {function} callback
121 * @private
122 */
123
124View.prototype.render = function render(options, callback) {
125  debug('render "%s"', this.path);
126  this.engine(this.path, options, callback);
127};
128
129/**
130 * Resolve the file within the given directory.
131 *
132 * @param {string} dir
133 * @param {string} file
134 * @private
135 */
136
137View.prototype.resolve = function resolve(dir, file) {
138  var ext = this.ext;
139
140  // <path>.<ext>
141  var path = join(dir, file);
142  var stat = tryStat(path);
143
144  if (stat && stat.isFile()) {
145    return path;
146  }
147
148  // <path>/index.<ext>
149  path = join(dir, basename(file, ext), 'index' + ext);
150  stat = tryStat(path);
151
152  if (stat && stat.isFile()) {
153    return path;
154  }
155};
156
157/**
158 * Return a stat, maybe.
159 *
160 * @param {string} path
161 * @return {fs.Stats}
162 * @private
163 */
164
165function tryStat(path) {
166  debug('stat "%s"', path);
167
168  try {
169    return fs.statSync(path);
170  } catch (e) {
171    return undefined;
172  }
173}