UNPKG

3.19 kBJavaScriptView Raw
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 var mod = this.ext.substr(1)
79 debug('require "%s"', mod)
80 opts.engines[this.ext] = require(mod).__express
81 }
82
83 // store loaded engine
84 this.engine = opts.engines[this.ext];
85
86 // lookup path
87 this.path = this.lookup(fileName);
88}
89
90/**
91 * Lookup view by the given `name`
92 *
93 * @param {string} name
94 * @private
95 */
96
97View.prototype.lookup = function lookup(name) {
98 var path;
99 var roots = [].concat(this.root);
100
101 debug('lookup "%s"', name);
102
103 for (var i = 0; i < roots.length && !path; i++) {
104 var root = roots[i];
105
106 // resolve the path
107 var loc = resolve(root, name);
108 var dir = dirname(loc);
109 var file = basename(loc);
110
111 // resolve the file
112 path = this.resolve(dir, file);
113 }
114
115 return path;
116};
117
118/**
119 * Render with the given options.
120 *
121 * @param {object} options
122 * @param {function} callback
123 * @private
124 */
125
126View.prototype.render = function render(options, callback) {
127 debug('render "%s"', this.path);
128 this.engine(this.path, options, callback);
129};
130
131/**
132 * Resolve the file within the given directory.
133 *
134 * @param {string} dir
135 * @param {string} file
136 * @private
137 */
138
139View.prototype.resolve = function resolve(dir, file) {
140 var ext = this.ext;
141
142 // <path>.<ext>
143 var path = join(dir, file);
144 var stat = tryStat(path);
145
146 if (stat && stat.isFile()) {
147 return path;
148 }
149
150 // <path>/index.<ext>
151 path = join(dir, basename(file, ext), 'index' + ext);
152 stat = tryStat(path);
153
154 if (stat && stat.isFile()) {
155 return path;
156 }
157};
158
159/**
160 * Return a stat, maybe.
161 *
162 * @param {string} path
163 * @return {fs.Stats}
164 * @private
165 */
166
167function tryStat(path) {
168 debug('stat "%s"', path);
169
170 try {
171 return fs.statSync(path);
172 } catch (e) {
173 return undefined;
174 }
175}