express-hbs 2.5.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +50 -22
  3. package/lib/hbs.js +89 -91
  4. package/lib/resolver.js +10 -9
  5. package/package.json +32 -21
  6. package/.editorconfig +0 -8
  7. package/.eslintrc +0 -42
  8. package/.github/workflows/test.yml +0 -24
  9. package/.jsbeautifyrc +0 -17
  10. package/SECURITY.md +0 -9
  11. package/example/app-layoutsDir.js +0 -106
  12. package/example/app.js +0 -110
  13. package/example/public/css/style.css +0 -17
  14. package/example/views/fruits/details-layoutsDir.hbs +0 -3
  15. package/example/views/fruits/details.hbs +0 -4
  16. package/example/views/fruits/index-layoutsDir.hbs +0 -20
  17. package/example/views/fruits/index.hbs +0 -20
  18. package/example/views/index.hbs +0 -34
  19. package/example/views/layout/default.hbs +0 -13
  20. package/example/views/layout/fruit-details.hbs +0 -9
  21. package/example/views/layout/fruits.hbs +0 -6
  22. package/example/views/layout/html-struct.hbs +0 -21
  23. package/example/views/layout/veggie-details.hbs +0 -9
  24. package/example/views/layout/veggie.hbs +0 -6
  25. package/example/views/partials/scripts.hbs +0 -5
  26. package/example/views/partials/sub/comment.hbs +0 -1
  27. package/example/views/partials-other/other.hbs +0 -1
  28. package/example/views/replace.hbs +0 -6
  29. package/example/views/tos.txt +0 -3
  30. package/example/views/veggies/details.hbs +0 -1
  31. package/example/views/veggies.hbs +0 -17
  32. package/renovate.json +0 -6
  33. package/test/apps/async/index.js +0 -127
  34. package/test/apps/async/views/failer.hbs +0 -4
  35. package/test/apps/async/views/index.hbs +0 -16
  36. package/test/apps/async/views/layout.hbs +0 -1
  37. package/test/apps/i18n/index.js +0 -55
  38. package/test/apps/i18n/locales/en.json +0 -8
  39. package/test/apps/i18n/locales/fr.json +0 -8
  40. package/test/apps/i18n/views/index.hbs +0 -3
  41. package/test/asyncSpecs.js +0 -69
  42. package/test/cacheSpecs.js +0 -138
  43. package/test/customExtension.js +0 -36
  44. package/test/handlebarsSpecs.js +0 -95
  45. package/test/helperSpecs.js +0 -56
  46. package/test/helpers.js +0 -24
  47. package/test/i18nSpecs.js +0 -34
  48. package/test/issues/144/index.hbs +0 -1
  49. package/test/issues/153/index.hbs +0 -3
  50. package/test/issues/21/views/index.hbs +0 -1
  51. package/test/issues/21/views/layouts/default.hbs +0 -1
  52. package/test/issues/21/views/layouts/sub/child.hbs +0 -1
  53. package/test/issues/21/views/sub/relativeLayout.hbs +0 -1
  54. package/test/issues/21/views/sub/sub.hbs +0 -1
  55. package/test/issues/21/views/useLayoutInDir.hbs +0 -1
  56. package/test/issues/22/layout1.hbs +0 -1
  57. package/test/issues/22/layout2.hbs +0 -1
  58. package/test/issues/22/template.hbs +0 -1
  59. package/test/issues/23/empty.hbs +0 -0
  60. package/test/issues/23/emptyComment.hbs +0 -1
  61. package/test/issues/23/emptyPartial.hbs +0 -1
  62. package/test/issues/23/index.hbs +0 -4
  63. package/test/issues/23/layout.hbs +0 -1
  64. package/test/issues/23/partials/custom/comment-only.hbs +0 -1
  65. package/test/issues/23/partials/custom/empty.hbs +0 -0
  66. package/test/issues/23/partials/hello.hbs +0 -1
  67. package/test/issues/49/error.hbs +0 -1
  68. package/test/issues/49/front/error.hbs +0 -1
  69. package/test/issues/49/index.hbs +0 -2
  70. package/test/issues/49/layouts/default.hbs +0 -1
  71. package/test/issues/49/partial.hbs +0 -1
  72. package/test/issues/49/partials/partial-error.hbs +0 -1
  73. package/test/issues/53/default.hbs +0 -15
  74. package/test/issues/53/index.hbs +0 -7
  75. package/test/issues/53/partials/.gitkeep +0 -0
  76. package/test/issues/59/index.hbs +0 -2
  77. package/test/issues/62/basic.hbs +0 -2
  78. package/test/issues/62/block.hbs +0 -11
  79. package/test/issues/73/index.hbs +0 -5
  80. package/test/issues/73/partials/partial.hbs +0 -6
  81. package/test/issues/76/mypartial.hbs +0 -0
  82. package/test/issues/84/index.hbs +0 -1
  83. package/test/issues/84/partials/abc/def/ghi/test.hbs +0 -1
  84. package/test/issues.js +0 -476
  85. package/test/layoutSpecs.js +0 -118
  86. package/test/localTemplateOptions/data-access-template.hbs +0 -1
  87. package/test/localTemplateOptions/template.hbs +0 -1
  88. package/test/localTemplateOptions.js +0 -127
  89. package/test/multiple.js +0 -119
  90. package/test/nonExpress.js +0 -37
  91. package/test/optionsSpecs.js +0 -24
  92. package/test/views/beautify/expected.hbs +0 -9
  93. package/test/views/beautify/index.hbs +0 -2
  94. package/test/views/customExtension/layout.server.view.html +0 -1
  95. package/test/views/customExtension/partialsDir/partial1.server.view.html +0 -1
  96. package/test/views/customExtension/partialsDir/partial2.server.view.html +0 -1
  97. package/test/views/customExtension/partialsDir/subs/subPartial1.server.view.html +0 -1
  98. package/test/views/customExtension/partialsDir/subs/subPartial2.server.view.html +0 -1
  99. package/test/views/customExtension/template.server.view.html +0 -5
  100. package/test/views/disableLayoutDirective/aside.hbs +0 -1
  101. package/test/views/disableLayoutDirective/index.hbs +0 -2
  102. package/test/views/disableLayoutDirective/layouts/default.hbs +0 -1
  103. package/test/views/helpers/home/async.hbs +0 -6
  104. package/test/views/helpers/home/index.hbs +0 -3
  105. package/test/views/helpers/layouts/default.hbs +0 -1
  106. package/test/views/multiple/views1/collide.hbs +0 -1
  107. package/test/views/multiple/views1/error.hbs +0 -1
  108. package/test/views/multiple/views1/test1.hbs +0 -1
  109. package/test/views/multiple/views2/collide.hbs +0 -1
  110. package/test/views/multiple/views2/error.hbs +0 -1
  111. package/test/views/multiple/views2/test2.hbs +0 -1
  112. package/test/views/viewsDir/layouts/default.hbs +0 -1
  113. package/test/views/viewsDir/sub/directive.hbs +0 -2
  114. package/test/views/viewsDir/sub/lay.hbs +0 -1
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2012-2023 Barc, Inc., Ghost Foundation
3
+ Copyright (c) 2012-2025 Barc, Inc., Ghost Foundation
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
package/README.md CHANGED
@@ -1,6 +1,17 @@
1
1
  # express-hbs
2
2
 
3
- Express handlebars template engine with multiple layouts, blocks and cached partials.
3
+ Express Handlebars template engine for Express apps, with nested layouts,
4
+ named content blocks, cached partials, i18n helpers, and asynchronous helpers.
5
+
6
+ `express-hbs` exposes Express-compatible view engine functions and a shared
7
+ Handlebars instance. It is used by apps that want Handlebars templates with
8
+ layout inheritance and block-style content regions while keeping the familiar
9
+ `app.engine()` integration.
10
+
11
+ ## Requirements
12
+
13
+ - Node.js 22 or later
14
+ - the pinned pnpm version when working on this repository
4
15
 
5
16
  ## v2.0.0
6
17
 
@@ -17,7 +28,8 @@ If you're upgrading from v0.8.4 to v1.0.0 there are some potentially breaking ch
17
28
 
18
29
  ## Usage
19
30
 
20
- To use with express 4.
31
+ Register the engine with Express using `hbs.express4()`.
32
+
21
33
  ```js
22
34
  var hbs = require('express-hbs');
23
35
 
@@ -28,7 +40,9 @@ app.engine('hbs', hbs.express4({
28
40
  app.set('view engine', 'hbs');
29
41
  app.set('views', __dirname + '/views');
30
42
  ```
31
- To use with express 3 is the same as above, except use hbs.express3
43
+
44
+ `hbs.express3()` is still available for legacy apps and accepts the same
45
+ options:
32
46
 
33
47
  ```js
34
48
  app.engine('hbs', hbs.express3({
@@ -36,7 +50,7 @@ app.engine('hbs', hbs.express3({
36
50
  }));
37
51
  ```
38
52
 
39
- Options for `#express3` and `#express4`
53
+ Options for `express3()` and `express4()`:
40
54
 
41
55
  ```js
42
56
  hbs.express4({
@@ -67,15 +81,15 @@ hbs.express4({
67
81
 
68
82
  ## Syntax
69
83
 
70
- To mark where layout should insert page
84
+ To mark where a layout should insert page content:
71
85
 
72
86
  {{{body}}}
73
87
 
74
- To declare a block placeholder in layout
88
+ To declare a block placeholder in a layout:
75
89
 
76
90
  {{{block "pageScripts"}}}
77
91
 
78
- To define block content in a page
92
+ To define block content in a page:
79
93
 
80
94
  {{#contentFor "pageScripts"}}
81
95
  CONTENT HERE
@@ -83,7 +97,7 @@ To define block content in a page
83
97
 
84
98
  ## Layouts
85
99
 
86
- There are three ways to use a layout, listed in precedence order
100
+ There are three ways to use a layout, listed in precedence order:
87
101
 
88
102
  1. Declarative within a page. Use handlebars comment
89
103
 
@@ -100,9 +114,10 @@ There are three ways to use a layout, listed in precedence order
100
114
 
101
115
  2. As an option to render
102
116
 
103
- ## ⚠️ This creates a potential security vulnerability if used without a `restrictLayoutsTo`:
104
-
105
- The `restrictLayoutsTo` option will restrict reading layouts to a particular directory, if you do not pass this option then do not use the `layout` option in conjunction with passing user submitted data to res.render e.g. `res.render('index', req.query)`. This allows users to read arbitrary files from your filesystem!
117
+ ⚠️ Passing user-controlled data into the `layout` option can read arbitrary
118
+ files unless `restrictLayoutsTo` confines layout resolution to a safe
119
+ directory. Do not call `res.render('index', req.query)` or similar without
120
+ setting `restrictLayoutsTo`.
106
121
 
107
122
  ```js
108
123
  res.render('veggies', {
@@ -112,7 +127,8 @@ There are three ways to use a layout, listed in precedence order
112
127
  });
113
128
  ```
114
129
 
115
- This option also allows for layout suppression (both the default layout and when specified declaratively in a page) by passing in a falsey Javascript value as the value of the `layout` property:
130
+ This option also allows layout suppression, including the default layout and
131
+ template-declared layouts, by passing a falsey JavaScript value:
116
132
 
117
133
  ```js
118
134
  res.render('veggies', {
@@ -215,9 +231,12 @@ var instance2 = hbs.create();
215
231
 
216
232
  ## Template options
217
233
 
218
- The main use case for template options is setting the handlebars "data" object - this creates global template variables accessible with an `@` prefix.
234
+ The main use case for template options is setting the Handlebars `data` object,
235
+ which creates global template variables accessible with an `@` prefix.
219
236
 
220
- Template options can be set in 3 ways. When setting global template options they can be [passed as config on creation of an instance](https://github.com/barc/express-hbs#usage), and they can also be updated used the `updateTemplateOptions(templateOptions)` method of an instance. To set template options for an individual request they can be set on `res.locals` using the helper method `updateLocalTemplateOptions(locals, templateOptions)`.
237
+ Template options can be passed when creating an engine instance, updated with
238
+ `updateTemplateOptions(templateOptions)`, or set for one request with
239
+ `updateLocalTemplateOptions(locals, templateOptions)` on `res.locals`.
221
240
 
222
241
  Both of these methods have a companion method `getTemplateOptions()` and `getLocalTemplateOptions(locals)`, which should be used when extending or merging the current options.
223
242
 
@@ -275,20 +294,29 @@ File `views/index.hbs`
275
294
 
276
295
  To run example project
277
296
 
278
- npm install -d
279
- node example/app.js
297
+ ```sh
298
+ pnpm install
299
+ node example/app.js
300
+ ```
280
301
 
302
+ The example server listens on <http://localhost:3000>.
281
303
 
282
304
  ## Testing
283
305
 
284
- The test suite requires the `grunt-cli` package:
306
+ Install dependencies and run the test suite:
285
307
 
286
- npm install -g grunt-cli
287
- npm install -d
308
+ ```sh
309
+ pnpm install
310
+ pnpm test
311
+ ```
288
312
 
289
- Once everything's installed, just run:
313
+ Useful maintainer commands:
290
314
 
291
- npm test
315
+ ```sh
316
+ pnpm lint # oxlint plus oxfmt --check
317
+ pnpm lint:fix # apply safe lint fixes and formatting
318
+ pnpm coverage # mocha under nyc with enforced coverage thresholds
319
+ ```
292
320
 
293
321
 
294
322
  ## Credits
@@ -302,4 +330,4 @@ Big thanks to all [CONTRIBUTORS](https://github.com/TryGhost/express-hbs/contrib
302
330
 
303
331
  The MIT License (MIT)
304
332
 
305
- Copyright (c) 2012-2023 Barc, Inc., Ghost Foundation - Released under the [MIT license](LICENSE).
333
+ Copyright (c) 2012-2026 Barc, Inc., Ghost Foundation - Released under the [MIT license](LICENSE).
package/lib/hbs.js CHANGED
@@ -10,12 +10,12 @@ var _ = require('lodash');
10
10
  /**
11
11
  * Regex pattern for layout directive. {{!< layout }}
12
12
  */
13
- var layoutPattern = /{{!<\s+([A-Za-z0-9\._\-\/]+)\s*}}/;
13
+ var layoutPattern = /{{!<\s+([A-Za-z0-9._\-/]+)\s*}}/;
14
14
 
15
15
  /**
16
16
  * Constructor
17
17
  */
18
- var ExpressHbs = function() {
18
+ var ExpressHbs = function () {
19
19
  this.handlebars = handlebars.create();
20
20
  this.SafeString = this.handlebars.SafeString;
21
21
  this.Utils = this.handlebars.Utils;
@@ -33,7 +33,7 @@ var ExpressHbs = function() {
33
33
  * <link rel="stylesheet" href='{{{URL "css/style.css"}}}' />
34
34
  * {{/contentFor}}
35
35
  */
36
- ExpressHbs.prototype.content = function(name, options, context) {
36
+ ExpressHbs.prototype.content = function (name, options, context) {
37
37
  var block = options.data.root.blockCache[name] || (options.data.root.blockCache[name] = []);
38
38
  block.push(options.fn(context));
39
39
  };
@@ -46,9 +46,8 @@ ExpressHbs.prototype.content = function(name, options, context) {
46
46
  * @param {String} filename Path to template file.
47
47
  * @param {String} layout Layout path.
48
48
  */
49
- ExpressHbs.prototype.layoutPath = function(filename, layout) {
50
- var dirs,
51
- layoutPath;
49
+ ExpressHbs.prototype.layoutPath = function (filename, layout) {
50
+ var dirs, layoutPath;
52
51
  if (layout[0] === '.') {
53
52
  dirs = path.dirname(filename);
54
53
  } else if (this.layoutsDir) {
@@ -71,7 +70,7 @@ ExpressHbs.prototype.layoutPath = function(filename, layout) {
71
70
  * @param {String} filename Path to template
72
71
  * @returns {String|undefined} Returns the path to layout.
73
72
  */
74
- ExpressHbs.prototype.declaredLayoutFile = function(str, filename) {
73
+ ExpressHbs.prototype.declaredLayoutFile = function (str, filename) {
75
74
  var matches = str.match(layoutPattern);
76
75
  if (matches) {
77
76
  var layout = matches[1];
@@ -96,12 +95,20 @@ ExpressHbs.prototype.declaredLayoutFile = function(str, filename) {
96
95
  * @param {Boolean} useCache Cache the compiled layout?
97
96
  * @param {Function} cb Callback called with layouts stack
98
97
  */
99
- ExpressHbs.prototype.cacheLayout = function(layoutFile, useCache, cb) {
98
+ ExpressHbs.prototype.cacheLayout = function (layoutFile, useCache, cb) {
100
99
  var self = this;
101
100
 
102
101
  if (this.restrictLayoutsTo) {
103
- if (!layoutFile.startsWith(this.restrictLayoutsTo)) {
104
- var err = new Error('Cannot read ' + layoutFile + ' it does not reside in ' + this.restrictLayoutsTo);
102
+ var relativePath = path.relative(this.restrictLayoutsTo, layoutFile);
103
+ var outside =
104
+ relativePath === '..' ||
105
+ relativePath.startsWith('..' + path.sep) ||
106
+ path.isAbsolute(relativePath);
107
+
108
+ if (outside) {
109
+ var err = new Error(
110
+ 'Cannot read ' + layoutFile + ' it does not reside in ' + this.restrictLayoutsTo,
111
+ );
105
112
  return cb(err, null);
106
113
  }
107
114
  }
@@ -113,14 +120,14 @@ ExpressHbs.prototype.cacheLayout = function(layoutFile, useCache, cb) {
113
120
  var layoutTemplates = this.cache[layoutFile];
114
121
  if (layoutTemplates) return cb(null, layoutTemplates);
115
122
 
116
- fs.readFile(layoutFile, 'utf8', function(err, str) {
123
+ fs.readFile(layoutFile, 'utf8', function (err, str) {
117
124
  if (err) return cb(err);
118
125
 
119
126
  // File path of eventual declared parent layout
120
127
  var parentLayoutFile = self.declaredLayoutFile(str, layoutFile);
121
128
 
122
129
  // This function returns the current layout stack to the caller
123
- var _returnLayouts = function(layouts) {
130
+ var _returnLayouts = function (layouts) {
124
131
  var currentLayout;
125
132
  layouts = layouts.slice(0);
126
133
  currentLayout = self.compile(str, layoutFile);
@@ -133,7 +140,7 @@ ExpressHbs.prototype.cacheLayout = function(layoutFile, useCache, cb) {
133
140
 
134
141
  if (parentLayoutFile) {
135
142
  // Recursively compile/cache parent layouts
136
- self.cacheLayout(parentLayoutFile, useCache, function(err, parentLayouts) {
143
+ self.cacheLayout(parentLayoutFile, useCache, function (err, parentLayouts) {
137
144
  if (err) return cb(err);
138
145
  _returnLayouts(parentLayouts);
139
146
  });
@@ -147,7 +154,7 @@ ExpressHbs.prototype.cacheLayout = function(layoutFile, useCache, cb) {
147
154
  /**
148
155
  * Cache partial templates found under directories configure in partialsDir.
149
156
  */
150
- ExpressHbs.prototype.cachePartials = function(cb) {
157
+ ExpressHbs.prototype.cachePartials = function (cb) {
151
158
  var self = this;
152
159
 
153
160
  if (!(this.partialsDir instanceof Array)) {
@@ -158,36 +165,36 @@ ExpressHbs.prototype.cachePartials = function(cb) {
158
165
  var count = 0;
159
166
 
160
167
  function readNext() {
161
- readdirp(self.partialsDir[count], {fileFilter: '*' + self._options.extname})
162
- .on('warn', function(err) {
163
- console.warn('Non-fatal error in express-hbs cachePartials.', err);
164
- })
165
- .on('error', function(err) {
166
- console.error('Fatal error in express-hbs cachePartials', err);
167
- return cb(err);
168
- })
169
- .on('data', function(entry) {
170
- if (!entry) return;
171
- var source = fs.readFileSync(entry.fullPath, 'utf8');
172
- var dirname = path.dirname(entry.path);
173
- dirname = dirname === '.' ? '' : dirname + '/';
174
-
175
- var name = dirname + path.basename(entry.basename, self._options.extname);
176
- // fix the path in windows
177
- name = name.split('\\').join('/');
178
- self.registerPartial(name, source, entry.fullPath);
179
- })
180
- .on('end', function() {
181
- count += 1;
182
-
183
- // If all directories aren't read, read the next directory
184
- if (count < self.partialsDir.length) {
185
- readNext();
186
- } else {
187
- self.isPartialCachingComplete = true;
188
- if (cb) cb(null, true);
189
- }
190
- });
168
+ readdirp(self.partialsDir[count], { fileFilter: '*' + self._options.extname })
169
+ .on('warn', function (err) {
170
+ console.warn('Non-fatal error in express-hbs cachePartials.', err);
171
+ })
172
+ .on('error', function (err) {
173
+ console.error('Fatal error in express-hbs cachePartials', err);
174
+ return cb(err);
175
+ })
176
+ .on('data', function (entry) {
177
+ if (!entry) return;
178
+ var source = fs.readFileSync(entry.fullPath, 'utf8');
179
+ var dirname = path.dirname(entry.path);
180
+ dirname = dirname === '.' ? '' : dirname + '/';
181
+
182
+ var name = dirname + path.basename(entry.basename, self._options.extname);
183
+ // fix the path in windows
184
+ name = name.split('\\').join('/');
185
+ self.registerPartial(name, source, entry.fullPath);
186
+ })
187
+ .on('end', function () {
188
+ count += 1;
189
+
190
+ // If all directories aren't read, read the next directory
191
+ if (count < self.partialsDir.length) {
192
+ readNext();
193
+ } else {
194
+ self.isPartialCachingComplete = true;
195
+ if (cb) cb(null, true);
196
+ }
197
+ });
191
198
  }
192
199
 
193
200
  readNext();
@@ -210,7 +217,7 @@ ExpressHbs.prototype.cachePartials = function(cb) {
210
217
  * }
211
218
  * }
212
219
  */
213
- ExpressHbs.prototype.express3 = function(options) {
220
+ ExpressHbs.prototype.express3 = function (options) {
214
221
  var self = this;
215
222
 
216
223
  // Set defaults
@@ -227,31 +234,31 @@ ExpressHbs.prototype.express3 = function(options) {
227
234
 
228
235
  if (options.i18n) {
229
236
  var i18n = options.i18n;
230
- this.handlebars.registerHelper('__', function() {
237
+ this.handlebars.registerHelper('__', function () {
231
238
  var args = Array.prototype.slice.call(arguments);
232
239
  var options = args.pop();
233
240
  return i18n.__.apply(options.data.root, args);
234
241
  });
235
- this.handlebars.registerHelper('__n', function() {
242
+ this.handlebars.registerHelper('__n', function () {
236
243
  var args = Array.prototype.slice.call(arguments);
237
244
  var options = args.pop();
238
245
  return i18n.__n.apply(options.data.root, args);
239
246
  });
240
247
  }
241
248
 
242
- this.handlebars.registerHelper(this._options.blockHelperName, function(name, options) {
249
+ this.handlebars.registerHelper(this._options.blockHelperName, function (name, options) {
243
250
  var val = options.data.root.blockCache[name];
244
251
  if (val === undefined && typeof options.fn === 'function') {
245
252
  val = options.fn(this);
246
253
  }
247
254
  if (Array.isArray(val)) {
248
- val = val.join('\n');
255
+ val = val.join('\n');
249
256
  }
250
257
  return val;
251
258
  });
252
259
 
253
260
  // Pass 'this' as context of helper function to don't lose context call of helpers.
254
- this.handlebars.registerHelper(this._options.contentHelperName, function(name, options) {
261
+ this.handlebars.registerHelper(this._options.contentHelperName, function (name, options) {
255
262
  return self.content(name, options, this);
256
263
  });
257
264
 
@@ -300,12 +307,12 @@ ExpressHbs.prototype.express4 = ExpressHbs.prototype.express3;
300
307
  *
301
308
  * @param {Boolean} useCache Whether to cache.
302
309
  */
303
- ExpressHbs.prototype.loadDefaultLayout = function(useCache, cb) {
310
+ ExpressHbs.prototype.loadDefaultLayout = function (useCache, cb) {
304
311
  var self = this;
305
312
  if (!this._options.defaultLayout) return cb();
306
313
  if (useCache && this.defaultLayoutTemplates) return cb(null, this.defaultLayoutTemplates);
307
314
 
308
- this.cacheLayout(this._options.defaultLayout, useCache, function(err, templates) {
315
+ this.cacheLayout(this._options.defaultLayout, useCache, function (err, templates) {
309
316
  if (err) return cb(err);
310
317
  self.defaultLayoutTemplates = templates.slice(0);
311
318
  return cb(null, templates);
@@ -315,7 +322,7 @@ ExpressHbs.prototype.loadDefaultLayout = function(useCache, cb) {
315
322
  /**
316
323
  * Expose useful methods.
317
324
  */
318
- ExpressHbs.prototype.registerHelper = function(name, fn) {
325
+ ExpressHbs.prototype.registerHelper = function (name, fn) {
319
326
  this.handlebars.registerHelper(name, fn);
320
327
  };
321
328
 
@@ -325,7 +332,7 @@ ExpressHbs.prototype.registerHelper = function(name, fn) {
325
332
  * @param {String} name The name of the partial as used in a template.
326
333
  * @param {String} source String source of the partial.
327
334
  */
328
- ExpressHbs.prototype.registerPartial = function(name, source, filename) {
335
+ ExpressHbs.prototype.registerPartial = function (name, source, filename) {
329
336
  this.handlebars.registerPartial(name, this.compile(source, filename));
330
337
  };
331
338
 
@@ -335,7 +342,7 @@ ExpressHbs.prototype.registerPartial = function(name, source, filename) {
335
342
  * @param {String} source The source to compile.
336
343
  * @param {String} filename The path used to embed into __filename for errors.
337
344
  */
338
- ExpressHbs.prototype.compile = function(source, filename) {
345
+ ExpressHbs.prototype.compile = function (source, filename) {
339
346
  // Handlebars has a bug with comment only partial causes errors. This must
340
347
  // be a string so the block below can add a space.
341
348
  if (typeof source !== 'string') {
@@ -368,53 +375,46 @@ ExpressHbs.prototype.compile = function(source, filename) {
368
375
  * @param {String} name The name of the partial as used in a template.
369
376
  * @param {String} fn The `function(options, cb)`
370
377
  */
371
- ExpressHbs.prototype.registerAsyncHelper = function(name, fn) {
372
- this.handlebars.registerHelper(name, function(context, options) {
373
- var resolverCache = this.resolverCache ||
378
+ ExpressHbs.prototype.registerAsyncHelper = function (name, fn) {
379
+ this.handlebars.registerHelper(name, function (context, options) {
380
+ var resolverCache =
381
+ this.resolverCache ||
374
382
  _.get(context, 'data.root.resolverCache') ||
375
383
  _.get(options, 'data.root.resolverCache');
376
384
  if (!resolverCache) {
377
385
  throw new Error('Could not find resolver cache in async helper ' + name + '.');
378
386
  }
379
387
  if (options && fn.length > 2) {
380
- var resolveFunc = function(arr, cb) {
388
+ var resolveFunc = function (arr, cb) {
381
389
  return fn.call(this, arr[0], arr[1], cb);
382
390
  };
383
391
 
384
- return resolver.resolve(
385
- resolverCache,
386
- resolveFunc.bind(this),
387
- [context, options]
388
- );
392
+ return resolver.resolve(resolverCache, resolveFunc.bind(this), [context, options]);
389
393
  }
390
- return resolver.resolve(
391
- resolverCache,
392
- fn.bind(this),
393
- context
394
- );
394
+ return resolver.resolve(resolverCache, fn.bind(this), context);
395
395
  });
396
396
  };
397
397
 
398
- ExpressHbs.prototype.getTemplateOptions = function() {
398
+ ExpressHbs.prototype.getTemplateOptions = function () {
399
399
  return this._options.templateOptions;
400
400
  };
401
401
 
402
- ExpressHbs.prototype.updateTemplateOptions = function(templateOptions) {
402
+ ExpressHbs.prototype.updateTemplateOptions = function (templateOptions) {
403
403
  this._options.templateOptions = templateOptions;
404
404
  };
405
405
 
406
- ExpressHbs.prototype.getLocalTemplateOptions = function(locals) {
406
+ ExpressHbs.prototype.getLocalTemplateOptions = function (locals) {
407
407
  return locals._templateOptions || {};
408
408
  };
409
409
 
410
- ExpressHbs.prototype.updateLocalTemplateOptions = function(locals, localTemplateOptions) {
411
- return locals._templateOptions = localTemplateOptions;
410
+ ExpressHbs.prototype.updateLocalTemplateOptions = function (locals, localTemplateOptions) {
411
+ return (locals._templateOptions = localTemplateOptions);
412
412
  };
413
413
 
414
414
  /**
415
415
  * Creates a new instance of ExpressHbs.
416
416
  */
417
- ExpressHbs.prototype.create = function() {
417
+ ExpressHbs.prototype.create = function () {
418
418
  return new ExpressHbs();
419
419
  };
420
420
 
@@ -508,7 +508,6 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
508
508
  cb(null, res);
509
509
  }
510
510
 
511
-
512
511
  /**
513
512
  * Renders `template` with an optional set of nested `layoutTemplates` using
514
513
  * data in `locals`.
@@ -520,7 +519,7 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
520
519
  // being passed the rendered string of the previous ones as `body`
521
520
  var i = layoutTemplates.length - 1;
522
521
 
523
- var _stackRenderer = function(err, htmlStr) {
522
+ var _stackRenderer = function (err, htmlStr) {
524
523
  if (err) return cb(err);
525
524
 
526
525
  if (i >= 0) {
@@ -535,7 +534,6 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
535
534
  renderTemplate(template, locals, _stackRenderer);
536
535
  }
537
536
 
538
-
539
537
  /**
540
538
  * Lazy loads js-beautify, which should not be used in production env.
541
539
  */
@@ -561,14 +559,14 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
561
559
  }
562
560
  }
563
561
 
564
- fs.readFile(filename, 'utf8', function(err, source) {
562
+ fs.readFile(filename, 'utf8', function (err, source) {
565
563
  if (err) return cb(err);
566
564
 
567
565
  var template = self.compile(source, filename);
568
566
  if (options.cache) {
569
567
  self.cache[filename] = {
570
568
  source: source,
571
- template: template
569
+ template: template,
572
570
  };
573
571
  }
574
572
  return cb(null, source, template);
@@ -579,16 +577,16 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
579
577
  * Compiles a file into a template and a layoutTemplate, then renders it above.
580
578
  */
581
579
  function compileFile(locals, cb) {
582
- getSourceTemplate(function(err, source, template) {
580
+ getSourceTemplate(function (err, source, template) {
583
581
  if (err) return cb(err);
584
582
 
585
583
  // Try to get the layout
586
- parseLayout(source, filename, function(err, layoutTemplates) {
584
+ parseLayout(source, filename, function (err, layoutTemplates) {
587
585
  if (err) return cb(err);
588
586
 
589
587
  function renderIt(layoutTemplates) {
590
588
  if (self._options.beautify) {
591
- return render(template, locals, layoutTemplates, function(err, html) {
589
+ return render(template, locals, layoutTemplates, function (err, html) {
592
590
  if (err) return cb(err);
593
591
  loadBeautify();
594
592
  return cb(null, self.beautify(html, self.beautifyrc));
@@ -608,7 +606,7 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
608
606
  } else if (typeof options.layout !== 'undefined' && options.layout) {
609
607
  // 2. Layout specified by options from render
610
608
  var layoutFile = self.layoutPath(filename, options.layout);
611
- self.cacheLayout(layoutFile, options.cache, function(err, layoutTemplates) {
609
+ self.cacheLayout(layoutFile, options.cache, function (err, layoutTemplates) {
612
610
  if (err) return cb(err);
613
611
  renderIt(layoutTemplates);
614
612
  });
@@ -625,11 +623,11 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
625
623
 
626
624
  function replaceValue(values, text) {
627
625
  if (typeof text === 'string') {
628
- Object.keys(values).forEach(function(id) {
629
- text = text.replace(id, function() {
626
+ Object.keys(values).forEach(function (id) {
627
+ text = text.replace(id, function () {
630
628
  return values[id];
631
629
  });
632
- text = text.replace(self.Utils.escapeExpression(id), function() {
630
+ text = text.replace(self.Utils.escapeExpression(id), function () {
633
631
  return self.Utils.escapeExpression(values[id]);
634
632
  });
635
633
  });
@@ -640,9 +638,9 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
640
638
  // Handles waiting for async helpers
641
639
  function handleAsync(err, res) {
642
640
  if (err) return cb(err);
643
- resolver.done(options.resolverCache, function(err, values) {
641
+ resolver.done(options.resolverCache, function (err, values) {
644
642
  if (err) return cb(err);
645
- Object.keys(values).forEach(function(key) {
643
+ Object.keys(values).forEach(function (key) {
646
644
  values[key] = replaceValue(values, values[key]);
647
645
  });
648
646
  res = replaceValue(values, res);
@@ -654,13 +652,13 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
654
652
  }
655
653
 
656
654
  // kick it off by loading default template (if any)
657
- this.loadDefaultLayout(options.cache, function(err) {
655
+ this.loadDefaultLayout(options.cache, function (err) {
658
656
  if (err) return cb(err);
659
657
 
660
658
  // Force reloading of all partials if caching is not used. Inefficient but there
661
659
  // is no loading partial event.
662
660
  if (self.partialsDir && (!options.cache || !self.isPartialCachingComplete)) {
663
- return self.cachePartials(function(err) {
661
+ return self.cachePartials(function (err) {
664
662
  if (err) return cb(err);
665
663
  return compileFile(options, handleAsync);
666
664
  });
package/lib/resolver.js CHANGED
@@ -14,9 +14,9 @@ var ID_SUFFIX = '__';
14
14
 
15
15
  function resolve(cache, fn, context) {
16
16
  var id = ID_PREFIX + ID_ESCAPED_STRING + generateId(ID_LENGTH) + ID_SUFFIX;
17
- cache[id] = new Promise(function(passed, failed) {
17
+ cache[id] = new Promise(function (passed, failed) {
18
18
  try {
19
- fn(context, function(res) {
19
+ fn(context, function (res) {
20
20
  passed(res);
21
21
  });
22
22
  } catch (error) {
@@ -29,23 +29,24 @@ function resolve(cache, fn, context) {
29
29
  // cache is an object where the keys are cache keys and values are promises. See above.
30
30
  function done(cache, callback) {
31
31
  return Promise.all(Object.values(cache))
32
- .then(function(values) {
32
+ .then(function (values) {
33
33
  var resolvedCache = {};
34
34
  var keys = Object.keys(cache);
35
- values.forEach(function(value, index) {
35
+ values.forEach(function (value, index) {
36
36
  resolvedCache[keys[index]] = value;
37
37
  });
38
38
  callback(null, resolvedCache);
39
- }).catch(function(error) {
40
- callback(error);
41
- });
39
+ })
40
+ .catch(function (error) {
41
+ callback(error);
42
+ });
42
43
  }
43
44
 
44
45
  function hasResolvers(text) {
45
46
  // NOTE: We specifically search the text for the ID_PREFIX **NOT** including the escapable character
46
47
  // This is because that character can be escaped in the text, and lead us to not finding unresolved
47
48
  // async helper outputs.
48
- if (text.search(ID_PREFIX) > 0) {
49
+ if (text.search(ID_PREFIX) >= 0) {
49
50
  return true;
50
51
  }
51
52
  return false;
@@ -54,5 +55,5 @@ function hasResolvers(text) {
54
55
  module.exports = {
55
56
  done: done,
56
57
  hasResolvers: hasResolvers,
57
- resolve: resolve
58
+ resolve: resolve,
58
59
  };