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.
- package/LICENSE +1 -1
- package/README.md +50 -22
- package/lib/hbs.js +89 -91
- package/lib/resolver.js +10 -9
- package/package.json +32 -21
- package/.editorconfig +0 -8
- package/.eslintrc +0 -42
- package/.github/workflows/test.yml +0 -24
- package/.jsbeautifyrc +0 -17
- package/SECURITY.md +0 -9
- package/example/app-layoutsDir.js +0 -106
- package/example/app.js +0 -110
- package/example/public/css/style.css +0 -17
- package/example/views/fruits/details-layoutsDir.hbs +0 -3
- package/example/views/fruits/details.hbs +0 -4
- package/example/views/fruits/index-layoutsDir.hbs +0 -20
- package/example/views/fruits/index.hbs +0 -20
- package/example/views/index.hbs +0 -34
- package/example/views/layout/default.hbs +0 -13
- package/example/views/layout/fruit-details.hbs +0 -9
- package/example/views/layout/fruits.hbs +0 -6
- package/example/views/layout/html-struct.hbs +0 -21
- package/example/views/layout/veggie-details.hbs +0 -9
- package/example/views/layout/veggie.hbs +0 -6
- package/example/views/partials/scripts.hbs +0 -5
- package/example/views/partials/sub/comment.hbs +0 -1
- package/example/views/partials-other/other.hbs +0 -1
- package/example/views/replace.hbs +0 -6
- package/example/views/tos.txt +0 -3
- package/example/views/veggies/details.hbs +0 -1
- package/example/views/veggies.hbs +0 -17
- package/renovate.json +0 -6
- package/test/apps/async/index.js +0 -127
- package/test/apps/async/views/failer.hbs +0 -4
- package/test/apps/async/views/index.hbs +0 -16
- package/test/apps/async/views/layout.hbs +0 -1
- package/test/apps/i18n/index.js +0 -55
- package/test/apps/i18n/locales/en.json +0 -8
- package/test/apps/i18n/locales/fr.json +0 -8
- package/test/apps/i18n/views/index.hbs +0 -3
- package/test/asyncSpecs.js +0 -69
- package/test/cacheSpecs.js +0 -138
- package/test/customExtension.js +0 -36
- package/test/handlebarsSpecs.js +0 -95
- package/test/helperSpecs.js +0 -56
- package/test/helpers.js +0 -24
- package/test/i18nSpecs.js +0 -34
- package/test/issues/144/index.hbs +0 -1
- package/test/issues/153/index.hbs +0 -3
- package/test/issues/21/views/index.hbs +0 -1
- package/test/issues/21/views/layouts/default.hbs +0 -1
- package/test/issues/21/views/layouts/sub/child.hbs +0 -1
- package/test/issues/21/views/sub/relativeLayout.hbs +0 -1
- package/test/issues/21/views/sub/sub.hbs +0 -1
- package/test/issues/21/views/useLayoutInDir.hbs +0 -1
- package/test/issues/22/layout1.hbs +0 -1
- package/test/issues/22/layout2.hbs +0 -1
- package/test/issues/22/template.hbs +0 -1
- package/test/issues/23/empty.hbs +0 -0
- package/test/issues/23/emptyComment.hbs +0 -1
- package/test/issues/23/emptyPartial.hbs +0 -1
- package/test/issues/23/index.hbs +0 -4
- package/test/issues/23/layout.hbs +0 -1
- package/test/issues/23/partials/custom/comment-only.hbs +0 -1
- package/test/issues/23/partials/custom/empty.hbs +0 -0
- package/test/issues/23/partials/hello.hbs +0 -1
- package/test/issues/49/error.hbs +0 -1
- package/test/issues/49/front/error.hbs +0 -1
- package/test/issues/49/index.hbs +0 -2
- package/test/issues/49/layouts/default.hbs +0 -1
- package/test/issues/49/partial.hbs +0 -1
- package/test/issues/49/partials/partial-error.hbs +0 -1
- package/test/issues/53/default.hbs +0 -15
- package/test/issues/53/index.hbs +0 -7
- package/test/issues/53/partials/.gitkeep +0 -0
- package/test/issues/59/index.hbs +0 -2
- package/test/issues/62/basic.hbs +0 -2
- package/test/issues/62/block.hbs +0 -11
- package/test/issues/73/index.hbs +0 -5
- package/test/issues/73/partials/partial.hbs +0 -6
- package/test/issues/76/mypartial.hbs +0 -0
- package/test/issues/84/index.hbs +0 -1
- package/test/issues/84/partials/abc/def/ghi/test.hbs +0 -1
- package/test/issues.js +0 -476
- package/test/layoutSpecs.js +0 -118
- package/test/localTemplateOptions/data-access-template.hbs +0 -1
- package/test/localTemplateOptions/template.hbs +0 -1
- package/test/localTemplateOptions.js +0 -127
- package/test/multiple.js +0 -119
- package/test/nonExpress.js +0 -37
- package/test/optionsSpecs.js +0 -24
- package/test/views/beautify/expected.hbs +0 -9
- package/test/views/beautify/index.hbs +0 -2
- package/test/views/customExtension/layout.server.view.html +0 -1
- package/test/views/customExtension/partialsDir/partial1.server.view.html +0 -1
- package/test/views/customExtension/partialsDir/partial2.server.view.html +0 -1
- package/test/views/customExtension/partialsDir/subs/subPartial1.server.view.html +0 -1
- package/test/views/customExtension/partialsDir/subs/subPartial2.server.view.html +0 -1
- package/test/views/customExtension/template.server.view.html +0 -5
- package/test/views/disableLayoutDirective/aside.hbs +0 -1
- package/test/views/disableLayoutDirective/index.hbs +0 -2
- package/test/views/disableLayoutDirective/layouts/default.hbs +0 -1
- package/test/views/helpers/home/async.hbs +0 -6
- package/test/views/helpers/home/index.hbs +0 -3
- package/test/views/helpers/layouts/default.hbs +0 -1
- package/test/views/multiple/views1/collide.hbs +0 -1
- package/test/views/multiple/views1/error.hbs +0 -1
- package/test/views/multiple/views1/test1.hbs +0 -1
- package/test/views/multiple/views2/collide.hbs +0 -1
- package/test/views/multiple/views2/error.hbs +0 -1
- package/test/views/multiple/views2/test2.hbs +0 -1
- package/test/views/viewsDir/layouts/default.hbs +0 -1
- package/test/views/viewsDir/sub/directive.hbs +0 -2
- 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-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
279
|
-
|
|
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
|
-
|
|
306
|
+
Install dependencies and run the test suite:
|
|
285
307
|
|
|
286
|
-
|
|
287
|
-
|
|
308
|
+
```sh
|
|
309
|
+
pnpm install
|
|
310
|
+
pnpm test
|
|
311
|
+
```
|
|
288
312
|
|
|
289
|
-
|
|
313
|
+
Useful maintainer commands:
|
|
290
314
|
|
|
291
|
-
|
|
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-
|
|
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
|
|
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
|
-
|
|
104
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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 =
|
|
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
|
-
|
|
40
|
-
|
|
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)
|
|
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
|
};
|