UNPKG

7.72 kBJavaScriptView Raw
1'use strict';
2
3var fs = require('fs');
4var utils = require('lazy-cache')(require);
5var fn = require;
6require = utils; // eslint-disable-line
7
8/**
9 * Lazily invoked module dependencies
10 */
11
12require('array-sort', 'sortBy');
13require('async-each', 'each');
14require('base-data');
15require('base-engines', 'engines');
16require('base-helpers', 'helpers');
17require('base-option', 'option');
18require('base-plugins', 'plugin');
19require('base-routes', 'routes');
20require('deep-bind', 'bindAll');
21require('define-property', 'define');
22require('engine-base', 'engine');
23require('extend-shallow', 'extend');
24require('falsey', 'isFalsey');
25require('get-value', 'get');
26require('get-view');
27require('group-array', 'groupBy');
28require('has-glob');
29require('has-value', 'has');
30require('inflection', 'inflect');
31require('is-valid-app', 'isValid');
32require('layouts');
33require('match-file');
34require('mixin-deep', 'merge');
35require('paginationator');
36require('pascalcase', 'pascal');
37require('set-value', 'set');
38require('template-error', 'rethrow');
39require = fn; // eslint-disable-line
40
41/**
42 * Expose default router methods used in all Template instances
43 */
44
45utils.methods = [
46 'onLoad',
47 'preCompile',
48 'preLayout',
49 'onLayout',
50 'postLayout',
51 'onMerge',
52 'onStream',
53 'postCompile',
54 'preRender',
55 'postRender',
56 'preWrite',
57 'postWrite'
58];
59
60utils.constructorKeys = [
61 'Collection',
62 'Group',
63 'Item',
64 'List',
65 'View',
66 'Views'
67];
68
69/**
70 * Options keys
71 */
72
73utils.optsKeys = [
74 'renameKey',
75 'namespaceData',
76 'mergePartials',
77 'rethrow',
78 'nocase',
79 'nonull',
80 'rename',
81 'cwd'
82];
83
84utils.endsWith = function(str, sub) {
85 return str.slice(-sub.length) === sub;
86};
87
88/**
89 * Return true if file exists and is not a directory.
90 */
91
92utils.fileExists = function(filepath) {
93 try {
94 return fs.statSync(filepath).isDirectory() === false;
95 } catch (err) {}
96 return false;
97};
98
99/**
100 * Keep a history of engines used to compile a view, and
101 * the compiled function for each, allowing the compiled
102 * function to be used again the next time the same view
103 * is rendered by the same engine.
104 *
105 * @param {Object} view
106 * @param {String} engine
107 * @param {Function} fn
108 */
109
110utils.engineStack = function(view, engine, fn, content) {
111 if (typeof view.engineStack === 'undefined') {
112 view.engineStack = {};
113 }
114 if (engine && engine.charAt(0) !== '.') {
115 engine = '.' + engine;
116 }
117 view.engineStack[engine] = fn;
118 view.engineStack[engine].content = content;
119};
120
121/**
122 * Return true if a template is a partial
123 */
124
125utils.isPartial = function(view) {
126 if (typeof view.isType !== 'function') {
127 return false;
128 }
129 if (typeof view.options === 'undefined') {
130 return false;
131 }
132 if (typeof view.options.viewType === 'undefined') {
133 return false;
134 }
135 return view.isType('partial');
136};
137
138/**
139 * Return true if a template is renderable, and not a partial or layout
140 */
141
142utils.isRenderable = function(view) {
143 if (typeof view.isType !== 'function') {
144 return false;
145 }
146 if (typeof view.options === 'undefined') {
147 return false;
148 }
149 if (typeof view.options.viewType === 'undefined') {
150 return false;
151 }
152 return view.isType('renderable') && view.viewType.length === 1;
153};
154
155/**
156 * When a constructor is defined after init, update any underlying
157 * properties that may rely on that option (constructor).
158 */
159
160utils.updateOptions = function(app, key, value) {
161 var k = utils.constructorKeys;
162 if (k.indexOf(key) > -1) {
163 app.define(key, value);
164 }
165 if (key === 'layout') {
166 app.viewTypes.renderable.forEach(function(name) {
167 app[name].option('layout', value);
168 });
169 }
170};
171
172/**
173 * Return the given value as-is.
174 */
175
176utils.identity = function(val) {
177 return val;
178};
179
180/**
181 * Arrayify the given value by casting it to an array.
182 */
183
184utils.arrayify = function(val) {
185 return val ? (Array.isArray(val) ? val : [val]) : [];
186};
187
188/**
189 * Return the last element in an array or array-like object.
190 */
191
192utils.last = function(array, n) {
193 return array[array.length - (n || 1)];
194};
195
196/**
197 * Return true if the given value is an object.
198 * @return {Boolean}
199 */
200
201utils.isObject = function(val) {
202 if (!val || Array.isArray(val)) {
203 return false;
204 }
205 return typeof val === 'function'
206 || typeof val === 'object';
207};
208
209/**
210 * Return true if the given value is a string.
211 */
212
213utils.isString = function(val) {
214 return val && typeof val === 'string';
215};
216
217/**
218 * Return true if the given value is a buffer
219 */
220
221utils.isBuffer = function(val) {
222 if (val && val.constructor && typeof val.constructor.isBuffer === 'function') {
223 return val.constructor.isBuffer(val);
224 }
225 return false;
226};
227
228/**
229 * Return true if the given value is a stream.
230 */
231
232utils.isStream = function(val) {
233 return utils.isObject(val)
234 && (typeof val.pipe === 'function')
235 && (typeof val.on === 'function');
236};
237
238/**
239 * Return true if the given value is an object.
240 * @return {Boolean}
241 */
242
243utils.isOptions = function(val) {
244 return utils.isObject(val) && val.hasOwnProperty('hash');
245};
246
247/**
248 * Format a helper error.
249 * TODO: create an error class for helpers
250 */
251
252utils.helperError = function(app, helperName, viewName, cb) {
253 var err = new Error('helper "' + helperName + '" cannot find "' + viewName + '"');
254 app.emit('error', err);
255 if (typeof cb === 'function') {
256 return cb(err);
257 } else {
258 throw err;
259 }
260};
261
262/**
263 * Convenience method for setting `this.isFoo` and `this._name = 'Foo'
264 * on the given `app`
265 */
266
267utils.setInstanceNames = function setInstanceNames(app, name) {
268 utils.define(app, 'is' + utils.pascal(name), true);
269 utils.define(app, '_name', name);
270};
271
272/**
273 * Singularize the given `name`
274 */
275
276utils.single = function single(name) {
277 return utils.inflect.singularize(name);
278};
279
280/**
281 * Pluralize the given `name`
282 */
283
284utils.plural = function plural(name) {
285 return utils.inflect.pluralize(name);
286};
287
288/**
289 * Ensure file extensions are formatted properly for lookups.
290 *
291 * ```js
292 * utils.formatExt('hbs');
293 * //=> '.hbs'
294 *
295 * utils.formatExt('.hbs');
296 * //=> '.hbs'
297 * ```
298 *
299 * @param {String} `ext` File extension
300 * @return {String}
301 * @api public
302 */
303
304utils.formatExt = function formatExt(ext) {
305 if (typeof ext !== 'string') {
306 throw new Error('utils.formatExt() expects `ext` to be a string.');
307 }
308 if (ext.charAt(0) !== '.') {
309 return '.' + ext;
310 }
311 return ext;
312};
313
314/**
315 * Return true if the given value looks like a
316 * `view` object.
317 */
318
319utils.isItem = utils.isView = function(val) {
320 if (!utils.isObject(val)) return false;
321 return val.hasOwnProperty('content')
322 || val.hasOwnProperty('contents')
323 || val.hasOwnProperty('path');
324};
325
326/**
327 * Get locals from helper arguments.
328 *
329 * @param {Object} `locals`
330 * @param {Object} `options`
331 */
332
333utils.getLocals = function(locals, options) {
334 options = options || {};
335 locals = locals || {};
336 var ctx = {};
337
338 if (options.hasOwnProperty('hash')) {
339 utils.extend(ctx, options.hash);
340 delete options.hash;
341 }
342 if (locals.hasOwnProperty('hash')) {
343 utils.extend(ctx, locals.hash);
344 delete locals.hash;
345 }
346 utils.extend(ctx, options);
347 utils.extend(ctx, locals);
348 return ctx;
349};
350
351/**
352 * Used on `collection` or `app` to resolve the name of the engine to use, or the file
353 * extension to use for the engine.
354 *
355 * If `options.resolveEngine` is a function, it will be
356 * used to resolve the engine.
357 *
358 * @param {Object} `view`
359 * @param {Object} `locals`
360 * @param {Object} `opts`
361 * @return {String}
362 */
363
364utils.resolveEngineExt = function(view, locals, opts) {
365 var engine = locals.engine || view.engine || opts.engine;
366 var fn = opts.resolveEngine
367 || locals.resolveEngine
368 || utils.identity;
369 return fn(engine);
370};
371
372/**
373 * Expose utils
374 */
375
376module.exports = utils;