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}