express-hbs 2.4.2 → 2.5.1

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 (269) hide show
  1. package/.claude/worktrees/node-20-26-migration-7de0c3/.editorconfig +8 -0
  2. package/.claude/worktrees/node-20-26-migration-7de0c3/.github/workflows/publish.yml +73 -0
  3. package/.claude/worktrees/node-20-26-migration-7de0c3/.github/workflows/test.yml +48 -0
  4. package/.claude/worktrees/node-20-26-migration-7de0c3/.jsbeautifyrc +17 -0
  5. package/.claude/worktrees/node-20-26-migration-7de0c3/.nvmrc +1 -0
  6. package/.claude/worktrees/node-20-26-migration-7de0c3/.nycrc.json +16 -0
  7. package/.claude/worktrees/node-20-26-migration-7de0c3/.oxfmtrc.json +19 -0
  8. package/.claude/worktrees/node-20-26-migration-7de0c3/.oxlintrc.json +32 -0
  9. package/.claude/worktrees/node-20-26-migration-7de0c3/AGENTS.md +40 -0
  10. package/.claude/worktrees/node-20-26-migration-7de0c3/LICENSE +21 -0
  11. package/.claude/worktrees/node-20-26-migration-7de0c3/README.md +333 -0
  12. package/.claude/worktrees/node-20-26-migration-7de0c3/SECURITY.md +9 -0
  13. package/.claude/worktrees/node-20-26-migration-7de0c3/example/app-layoutsDir.js +99 -0
  14. package/.claude/worktrees/node-20-26-migration-7de0c3/example/app.js +103 -0
  15. package/.claude/worktrees/node-20-26-migration-7de0c3/example/public/css/style.css +17 -0
  16. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/fruits/details-layoutsDir.hbs +3 -0
  17. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/fruits/details.hbs +4 -0
  18. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/fruits/index-layoutsDir.hbs +20 -0
  19. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/fruits/index.hbs +20 -0
  20. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/index.hbs +34 -0
  21. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/layout/default.hbs +13 -0
  22. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/layout/fruit-details.hbs +9 -0
  23. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/layout/fruits.hbs +6 -0
  24. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/layout/html-struct.hbs +21 -0
  25. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/layout/veggie-details.hbs +9 -0
  26. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/layout/veggie.hbs +6 -0
  27. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/partials/scripts.hbs +5 -0
  28. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/partials/sub/comment.hbs +1 -0
  29. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/partials-other/other.hbs +1 -0
  30. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/replace.hbs +6 -0
  31. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/tos.txt +3 -0
  32. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/veggies/details.hbs +1 -0
  33. package/.claude/worktrees/node-20-26-migration-7de0c3/example/views/veggies.hbs +17 -0
  34. package/.claude/worktrees/node-20-26-migration-7de0c3/index.js +1 -0
  35. package/.claude/worktrees/node-20-26-migration-7de0c3/lib/generate-id.js +16 -0
  36. package/.claude/worktrees/node-20-26-migration-7de0c3/lib/hbs.js +671 -0
  37. package/.claude/worktrees/node-20-26-migration-7de0c3/lib/resolver.js +59 -0
  38. package/.claude/worktrees/node-20-26-migration-7de0c3/package.json +61 -0
  39. package/.claude/worktrees/node-20-26-migration-7de0c3/pnpm-lock.yaml +3424 -0
  40. package/.claude/worktrees/node-20-26-migration-7de0c3/pnpm-workspace.yaml +6 -0
  41. package/.claude/worktrees/node-20-26-migration-7de0c3/renovate.json +13 -0
  42. package/.claude/worktrees/node-20-26-migration-7de0c3/test/apps/async/index.js +139 -0
  43. package/.claude/worktrees/node-20-26-migration-7de0c3/test/apps/async/views/failer.hbs +4 -0
  44. package/.claude/worktrees/node-20-26-migration-7de0c3/test/apps/async/views/index.hbs +16 -0
  45. package/.claude/worktrees/node-20-26-migration-7de0c3/test/apps/async/views/layout.hbs +1 -0
  46. package/.claude/worktrees/node-20-26-migration-7de0c3/test/apps/i18n/index.js +57 -0
  47. package/.claude/worktrees/node-20-26-migration-7de0c3/test/apps/i18n/locales/en.json +8 -0
  48. package/.claude/worktrees/node-20-26-migration-7de0c3/test/apps/i18n/locales/fr.json +8 -0
  49. package/.claude/worktrees/node-20-26-migration-7de0c3/test/apps/i18n/views/index.hbs +3 -0
  50. package/.claude/worktrees/node-20-26-migration-7de0c3/test/asyncSpecs.js +69 -0
  51. package/.claude/worktrees/node-20-26-migration-7de0c3/test/cacheSpecs.js +135 -0
  52. package/.claude/worktrees/node-20-26-migration-7de0c3/test/customExtension.js +35 -0
  53. package/.claude/worktrees/node-20-26-migration-7de0c3/test/handlebarsSpecs.js +92 -0
  54. package/.claude/worktrees/node-20-26-migration-7de0c3/test/helperSpecs.js +58 -0
  55. package/.claude/worktrees/node-20-26-migration-7de0c3/test/helpers.js +24 -0
  56. package/.claude/worktrees/node-20-26-migration-7de0c3/test/i18nSpecs.js +36 -0
  57. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/144/index.hbs +1 -0
  58. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/153/index.hbs +3 -0
  59. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/21/views/index.hbs +1 -0
  60. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/21/views/layouts/default.hbs +1 -0
  61. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/21/views/layouts/sub/child.hbs +1 -0
  62. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/21/views/sub/relativeLayout.hbs +1 -0
  63. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/21/views/sub/sub.hbs +1 -0
  64. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/21/views/useLayoutInDir.hbs +1 -0
  65. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/22/layout1.hbs +1 -0
  66. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/22/layout2.hbs +1 -0
  67. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/22/template.hbs +1 -0
  68. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/23/empty.hbs +0 -0
  69. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/23/emptyComment.hbs +1 -0
  70. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/23/emptyPartial.hbs +1 -0
  71. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/23/index.hbs +4 -0
  72. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/23/layout.hbs +1 -0
  73. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/23/partials/custom/comment-only.hbs +1 -0
  74. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/23/partials/custom/empty.hbs +0 -0
  75. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/23/partials/hello.hbs +1 -0
  76. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/49/error.hbs +1 -0
  77. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/49/front/error.hbs +1 -0
  78. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/49/index.hbs +2 -0
  79. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/49/layouts/default.hbs +1 -0
  80. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/49/partial.hbs +1 -0
  81. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/49/partials/partial-error.hbs +1 -0
  82. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/53/default.hbs +15 -0
  83. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/53/index.hbs +7 -0
  84. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/53/partials/.gitkeep +0 -0
  85. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/59/index.hbs +2 -0
  86. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/62/basic.hbs +2 -0
  87. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/62/block.hbs +11 -0
  88. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/73/index.hbs +5 -0
  89. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/73/partials/partial.hbs +6 -0
  90. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/76/mypartial.hbs +0 -0
  91. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/84/index.hbs +1 -0
  92. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues/84/partials/abc/def/ghi/test.hbs +1 -0
  93. package/.claude/worktrees/node-20-26-migration-7de0c3/test/issues.js +490 -0
  94. package/.claude/worktrees/node-20-26-migration-7de0c3/test/layoutSpecs.js +146 -0
  95. package/.claude/worktrees/node-20-26-migration-7de0c3/test/localTemplateOptions/data-access-template.hbs +1 -0
  96. package/.claude/worktrees/node-20-26-migration-7de0c3/test/localTemplateOptions/template.hbs +1 -0
  97. package/.claude/worktrees/node-20-26-migration-7de0c3/test/localTemplateOptions.js +125 -0
  98. package/.claude/worktrees/node-20-26-migration-7de0c3/test/multiple.js +115 -0
  99. package/.claude/worktrees/node-20-26-migration-7de0c3/test/nonExpress.js +36 -0
  100. package/.claude/worktrees/node-20-26-migration-7de0c3/test/optionsSpecs.js +22 -0
  101. package/.claude/worktrees/node-20-26-migration-7de0c3/test/resolverSpecs.js +54 -0
  102. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/beautify/expected.hbs +9 -0
  103. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/beautify/index.hbs +2 -0
  104. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/customExtension/layout.server.view.html +1 -0
  105. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/customExtension/partialsDir/partial1.server.view.html +1 -0
  106. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/customExtension/partialsDir/partial2.server.view.html +1 -0
  107. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/customExtension/partialsDir/subs/subPartial1.server.view.html +1 -0
  108. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/customExtension/partialsDir/subs/subPartial2.server.view.html +1 -0
  109. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/customExtension/template.server.view.html +5 -0
  110. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/disableLayoutDirective/aside.hbs +1 -0
  111. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/disableLayoutDirective/index.hbs +2 -0
  112. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/disableLayoutDirective/layouts/default.hbs +1 -0
  113. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/helpers/home/async.hbs +6 -0
  114. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/helpers/home/index.hbs +3 -0
  115. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/helpers/layouts/default.hbs +1 -0
  116. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/multiple/views1/collide.hbs +1 -0
  117. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/multiple/views1/error.hbs +1 -0
  118. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/multiple/views1/test1.hbs +1 -0
  119. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/multiple/views2/collide.hbs +1 -0
  120. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/multiple/views2/error.hbs +1 -0
  121. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/multiple/views2/test2.hbs +1 -0
  122. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/viewsDir/layouts/default.hbs +1 -0
  123. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/viewsDir/sub/directive.hbs +2 -0
  124. package/.claude/worktrees/node-20-26-migration-7de0c3/test/views/viewsDir/sub/lay.hbs +1 -0
  125. package/.claude/worktrees/security-patches-review-c621ea/.editorconfig +8 -0
  126. package/.claude/worktrees/security-patches-review-c621ea/.github/workflows/publish.yml +73 -0
  127. package/.claude/worktrees/security-patches-review-c621ea/.github/workflows/test.yml +48 -0
  128. package/.claude/worktrees/security-patches-review-c621ea/.jsbeautifyrc +17 -0
  129. package/.claude/worktrees/security-patches-review-c621ea/.nvmrc +1 -0
  130. package/.claude/worktrees/security-patches-review-c621ea/.nycrc.json +16 -0
  131. package/.claude/worktrees/security-patches-review-c621ea/.oxfmtrc.json +19 -0
  132. package/.claude/worktrees/security-patches-review-c621ea/.oxlintrc.json +32 -0
  133. package/.claude/worktrees/security-patches-review-c621ea/AGENTS.md +40 -0
  134. package/.claude/worktrees/security-patches-review-c621ea/LICENSE +21 -0
  135. package/.claude/worktrees/security-patches-review-c621ea/README.md +333 -0
  136. package/.claude/worktrees/security-patches-review-c621ea/SECURITY.md +9 -0
  137. package/.claude/worktrees/security-patches-review-c621ea/example/app-layoutsDir.js +99 -0
  138. package/.claude/worktrees/security-patches-review-c621ea/example/app.js +103 -0
  139. package/.claude/worktrees/security-patches-review-c621ea/example/public/css/style.css +17 -0
  140. package/.claude/worktrees/security-patches-review-c621ea/example/views/fruits/details-layoutsDir.hbs +3 -0
  141. package/.claude/worktrees/security-patches-review-c621ea/example/views/fruits/details.hbs +4 -0
  142. package/.claude/worktrees/security-patches-review-c621ea/example/views/fruits/index-layoutsDir.hbs +20 -0
  143. package/.claude/worktrees/security-patches-review-c621ea/example/views/fruits/index.hbs +20 -0
  144. package/.claude/worktrees/security-patches-review-c621ea/example/views/index.hbs +34 -0
  145. package/.claude/worktrees/security-patches-review-c621ea/example/views/layout/default.hbs +13 -0
  146. package/.claude/worktrees/security-patches-review-c621ea/example/views/layout/fruit-details.hbs +9 -0
  147. package/.claude/worktrees/security-patches-review-c621ea/example/views/layout/fruits.hbs +6 -0
  148. package/.claude/worktrees/security-patches-review-c621ea/example/views/layout/html-struct.hbs +21 -0
  149. package/.claude/worktrees/security-patches-review-c621ea/example/views/layout/veggie-details.hbs +9 -0
  150. package/.claude/worktrees/security-patches-review-c621ea/example/views/layout/veggie.hbs +6 -0
  151. package/.claude/worktrees/security-patches-review-c621ea/example/views/partials/scripts.hbs +5 -0
  152. package/.claude/worktrees/security-patches-review-c621ea/example/views/partials/sub/comment.hbs +1 -0
  153. package/.claude/worktrees/security-patches-review-c621ea/example/views/partials-other/other.hbs +1 -0
  154. package/.claude/worktrees/security-patches-review-c621ea/example/views/replace.hbs +6 -0
  155. package/.claude/worktrees/security-patches-review-c621ea/example/views/tos.txt +3 -0
  156. package/.claude/worktrees/security-patches-review-c621ea/example/views/veggies/details.hbs +1 -0
  157. package/.claude/worktrees/security-patches-review-c621ea/example/views/veggies.hbs +17 -0
  158. package/.claude/worktrees/security-patches-review-c621ea/index.js +1 -0
  159. package/.claude/worktrees/security-patches-review-c621ea/lib/generate-id.js +16 -0
  160. package/.claude/worktrees/security-patches-review-c621ea/lib/hbs.js +665 -0
  161. package/.claude/worktrees/security-patches-review-c621ea/lib/resolver.js +59 -0
  162. package/.claude/worktrees/security-patches-review-c621ea/package.json +57 -0
  163. package/.claude/worktrees/security-patches-review-c621ea/pnpm-lock.yaml +3424 -0
  164. package/.claude/worktrees/security-patches-review-c621ea/pnpm-workspace.yaml +6 -0
  165. package/.claude/worktrees/security-patches-review-c621ea/renovate.json +6 -0
  166. package/.claude/worktrees/security-patches-review-c621ea/test/apps/async/index.js +139 -0
  167. package/.claude/worktrees/security-patches-review-c621ea/test/apps/async/views/failer.hbs +4 -0
  168. package/.claude/worktrees/security-patches-review-c621ea/test/apps/async/views/index.hbs +16 -0
  169. package/.claude/worktrees/security-patches-review-c621ea/test/apps/async/views/layout.hbs +1 -0
  170. package/.claude/worktrees/security-patches-review-c621ea/test/apps/i18n/index.js +57 -0
  171. package/.claude/worktrees/security-patches-review-c621ea/test/apps/i18n/locales/en.json +8 -0
  172. package/.claude/worktrees/security-patches-review-c621ea/test/apps/i18n/locales/fr.json +8 -0
  173. package/.claude/worktrees/security-patches-review-c621ea/test/apps/i18n/views/index.hbs +3 -0
  174. package/.claude/worktrees/security-patches-review-c621ea/test/asyncSpecs.js +69 -0
  175. package/.claude/worktrees/security-patches-review-c621ea/test/cacheSpecs.js +135 -0
  176. package/.claude/worktrees/security-patches-review-c621ea/test/customExtension.js +35 -0
  177. package/.claude/worktrees/security-patches-review-c621ea/test/handlebarsSpecs.js +92 -0
  178. package/.claude/worktrees/security-patches-review-c621ea/test/helperSpecs.js +58 -0
  179. package/.claude/worktrees/security-patches-review-c621ea/test/helpers.js +24 -0
  180. package/.claude/worktrees/security-patches-review-c621ea/test/i18nSpecs.js +36 -0
  181. package/.claude/worktrees/security-patches-review-c621ea/test/issues/144/index.hbs +1 -0
  182. package/.claude/worktrees/security-patches-review-c621ea/test/issues/153/index.hbs +3 -0
  183. package/.claude/worktrees/security-patches-review-c621ea/test/issues/21/views/index.hbs +1 -0
  184. package/.claude/worktrees/security-patches-review-c621ea/test/issues/21/views/layouts/default.hbs +1 -0
  185. package/.claude/worktrees/security-patches-review-c621ea/test/issues/21/views/layouts/sub/child.hbs +1 -0
  186. package/.claude/worktrees/security-patches-review-c621ea/test/issues/21/views/sub/relativeLayout.hbs +1 -0
  187. package/.claude/worktrees/security-patches-review-c621ea/test/issues/21/views/sub/sub.hbs +1 -0
  188. package/.claude/worktrees/security-patches-review-c621ea/test/issues/21/views/useLayoutInDir.hbs +1 -0
  189. package/.claude/worktrees/security-patches-review-c621ea/test/issues/22/layout1.hbs +1 -0
  190. package/.claude/worktrees/security-patches-review-c621ea/test/issues/22/layout2.hbs +1 -0
  191. package/.claude/worktrees/security-patches-review-c621ea/test/issues/22/template.hbs +1 -0
  192. package/.claude/worktrees/security-patches-review-c621ea/test/issues/23/empty.hbs +0 -0
  193. package/.claude/worktrees/security-patches-review-c621ea/test/issues/23/emptyComment.hbs +1 -0
  194. package/.claude/worktrees/security-patches-review-c621ea/test/issues/23/emptyPartial.hbs +1 -0
  195. package/.claude/worktrees/security-patches-review-c621ea/test/issues/23/index.hbs +4 -0
  196. package/.claude/worktrees/security-patches-review-c621ea/test/issues/23/layout.hbs +1 -0
  197. package/.claude/worktrees/security-patches-review-c621ea/test/issues/23/partials/custom/comment-only.hbs +1 -0
  198. package/.claude/worktrees/security-patches-review-c621ea/test/issues/23/partials/custom/empty.hbs +0 -0
  199. package/.claude/worktrees/security-patches-review-c621ea/test/issues/23/partials/hello.hbs +1 -0
  200. package/.claude/worktrees/security-patches-review-c621ea/test/issues/49/error.hbs +1 -0
  201. package/.claude/worktrees/security-patches-review-c621ea/test/issues/49/front/error.hbs +1 -0
  202. package/.claude/worktrees/security-patches-review-c621ea/test/issues/49/index.hbs +2 -0
  203. package/.claude/worktrees/security-patches-review-c621ea/test/issues/49/layouts/default.hbs +1 -0
  204. package/.claude/worktrees/security-patches-review-c621ea/test/issues/49/partial.hbs +1 -0
  205. package/.claude/worktrees/security-patches-review-c621ea/test/issues/49/partials/partial-error.hbs +1 -0
  206. package/.claude/worktrees/security-patches-review-c621ea/test/issues/53/default.hbs +15 -0
  207. package/.claude/worktrees/security-patches-review-c621ea/test/issues/53/index.hbs +7 -0
  208. package/.claude/worktrees/security-patches-review-c621ea/test/issues/53/partials/.gitkeep +0 -0
  209. package/.claude/worktrees/security-patches-review-c621ea/test/issues/59/index.hbs +2 -0
  210. package/.claude/worktrees/security-patches-review-c621ea/test/issues/62/basic.hbs +2 -0
  211. package/.claude/worktrees/security-patches-review-c621ea/test/issues/62/block.hbs +11 -0
  212. package/.claude/worktrees/security-patches-review-c621ea/test/issues/73/index.hbs +5 -0
  213. package/.claude/worktrees/security-patches-review-c621ea/test/issues/73/partials/partial.hbs +6 -0
  214. package/.claude/worktrees/security-patches-review-c621ea/test/issues/76/mypartial.hbs +0 -0
  215. package/.claude/worktrees/security-patches-review-c621ea/test/issues/84/index.hbs +1 -0
  216. package/.claude/worktrees/security-patches-review-c621ea/test/issues/84/partials/abc/def/ghi/test.hbs +1 -0
  217. package/.claude/worktrees/security-patches-review-c621ea/test/issues.js +490 -0
  218. package/.claude/worktrees/security-patches-review-c621ea/test/layoutSpecs.js +116 -0
  219. package/.claude/worktrees/security-patches-review-c621ea/test/localTemplateOptions/data-access-template.hbs +1 -0
  220. package/.claude/worktrees/security-patches-review-c621ea/test/localTemplateOptions/template.hbs +1 -0
  221. package/.claude/worktrees/security-patches-review-c621ea/test/localTemplateOptions.js +125 -0
  222. package/.claude/worktrees/security-patches-review-c621ea/test/multiple.js +115 -0
  223. package/.claude/worktrees/security-patches-review-c621ea/test/nonExpress.js +36 -0
  224. package/.claude/worktrees/security-patches-review-c621ea/test/optionsSpecs.js +22 -0
  225. package/.claude/worktrees/security-patches-review-c621ea/test/resolverSpecs.js +54 -0
  226. package/.claude/worktrees/security-patches-review-c621ea/test/views/beautify/expected.hbs +9 -0
  227. package/.claude/worktrees/security-patches-review-c621ea/test/views/beautify/index.hbs +2 -0
  228. package/.claude/worktrees/security-patches-review-c621ea/test/views/customExtension/layout.server.view.html +1 -0
  229. package/.claude/worktrees/security-patches-review-c621ea/test/views/customExtension/partialsDir/partial1.server.view.html +1 -0
  230. package/.claude/worktrees/security-patches-review-c621ea/test/views/customExtension/partialsDir/partial2.server.view.html +1 -0
  231. package/.claude/worktrees/security-patches-review-c621ea/test/views/customExtension/partialsDir/subs/subPartial1.server.view.html +1 -0
  232. package/.claude/worktrees/security-patches-review-c621ea/test/views/customExtension/partialsDir/subs/subPartial2.server.view.html +1 -0
  233. package/.claude/worktrees/security-patches-review-c621ea/test/views/customExtension/template.server.view.html +5 -0
  234. package/.claude/worktrees/security-patches-review-c621ea/test/views/disableLayoutDirective/aside.hbs +1 -0
  235. package/.claude/worktrees/security-patches-review-c621ea/test/views/disableLayoutDirective/index.hbs +2 -0
  236. package/.claude/worktrees/security-patches-review-c621ea/test/views/disableLayoutDirective/layouts/default.hbs +1 -0
  237. package/.claude/worktrees/security-patches-review-c621ea/test/views/helpers/home/async.hbs +6 -0
  238. package/.claude/worktrees/security-patches-review-c621ea/test/views/helpers/home/index.hbs +3 -0
  239. package/.claude/worktrees/security-patches-review-c621ea/test/views/helpers/layouts/default.hbs +1 -0
  240. package/.claude/worktrees/security-patches-review-c621ea/test/views/multiple/views1/collide.hbs +1 -0
  241. package/.claude/worktrees/security-patches-review-c621ea/test/views/multiple/views1/error.hbs +1 -0
  242. package/.claude/worktrees/security-patches-review-c621ea/test/views/multiple/views1/test1.hbs +1 -0
  243. package/.claude/worktrees/security-patches-review-c621ea/test/views/multiple/views2/collide.hbs +1 -0
  244. package/.claude/worktrees/security-patches-review-c621ea/test/views/multiple/views2/error.hbs +1 -0
  245. package/.claude/worktrees/security-patches-review-c621ea/test/views/multiple/views2/test2.hbs +1 -0
  246. package/.claude/worktrees/security-patches-review-c621ea/test/views/viewsDir/layouts/default.hbs +1 -0
  247. package/.claude/worktrees/security-patches-review-c621ea/test/views/viewsDir/sub/directive.hbs +2 -0
  248. package/.claude/worktrees/security-patches-review-c621ea/test/views/viewsDir/sub/lay.hbs +1 -0
  249. package/README.md +3 -2
  250. package/example/app-layoutsDir.js +2 -1
  251. package/example/app.js +2 -1
  252. package/lib/hbs.js +24 -4
  253. package/lib/resolver.js +10 -3
  254. package/package.json +2 -2
  255. package/test/apps/async/index.js +2 -1
  256. package/test/apps/i18n/index.js +2 -1
  257. package/test/asyncSpecs.js +60 -0
  258. package/test/customExtension.js +2 -1
  259. package/test/handlebarsSpecs.js +19 -0
  260. package/test/helperSpecs.js +4 -2
  261. package/test/issues.js +48 -20
  262. package/test/layoutSpecs.js +48 -0
  263. package/test/localTemplateOptions.js +12 -4
  264. package/test/multiple.js +3 -1
  265. package/test/nonExpress.js +4 -2
  266. package/test/optionsSpecs.js +1 -1
  267. package/test/resolverSpecs.js +70 -0
  268. package/test/views/asyncPrefix/body.hbs +1 -0
  269. package/test/views/asyncPrefix/index.hbs +1 -0
@@ -0,0 +1,125 @@
1
+ 'use strict';
2
+ var assert = require('assert');
3
+ var path = require('path');
4
+ var hbs = require('..');
5
+ var H = require('./helpers');
6
+
7
+ describe('local template options', function () {
8
+ var dirname = path.join(__dirname, 'localTemplateOptions');
9
+
10
+ describe('express3', function () {
11
+ it('merges res.locals._templateOptions with the self._templateOptions', function (done) {
12
+ var instance = hbs.create();
13
+ var render = instance.express3({
14
+ restrictLayoutsTo: dirname,
15
+ });
16
+ instance.updateTemplateOptions({
17
+ data: {
18
+ greeting: 'Hello,',
19
+ firstName: 'Freddy',
20
+ lastName: 'Krueger',
21
+ },
22
+ });
23
+
24
+ var locals = H.createLocals('express3', dirname, {
25
+ _templateOptions: {
26
+ data: {
27
+ lastName: 'Mercury',
28
+ },
29
+ },
30
+ });
31
+
32
+ render(path.join(dirname, 'template.hbs'), locals, function (err, html) {
33
+ assert.ifError(err);
34
+ assert.strictEqual(H.stripWs(html), H.stripWs('Hello, Freddy Mercury'));
35
+ done(err);
36
+ });
37
+ });
38
+
39
+ it('removes _templateOptions from the locals data', function (done) {
40
+ var instance = hbs.create();
41
+ var render = instance.express3({
42
+ restrictLayoutsTo: dirname,
43
+ });
44
+ instance.updateTemplateOptions({
45
+ data: {
46
+ greeting: 'Hello,',
47
+ firstName: 'Freddy',
48
+ lastName: 'Krueger',
49
+ },
50
+ });
51
+
52
+ var locals = H.createLocals('express3', dirname, {
53
+ _templateOptions: {
54
+ data: {
55
+ lastName: 'Mercury',
56
+ },
57
+ },
58
+ });
59
+
60
+ render(path.join(dirname, 'data-access-template.hbs'), locals, function (err, html) {
61
+ assert.ifError(err);
62
+ assert.strictEqual(H.stripWs(html), H.stripWs(''));
63
+ done(err);
64
+ });
65
+ });
66
+ });
67
+
68
+ describe('express4', function () {
69
+ it('merges res.locals._templateOptions with the self._templateOptions', function (done) {
70
+ var instance = hbs.create();
71
+ var render = instance.express4({
72
+ restrictLayoutsTo: dirname,
73
+ });
74
+ instance.updateTemplateOptions({
75
+ data: {
76
+ greeting: 'Hello,',
77
+ firstName: 'Freddy',
78
+ lastName: 'Krueger',
79
+ },
80
+ });
81
+
82
+ var locals = H.createLocals('express4', dirname, {
83
+ _templateOptions: {
84
+ data: {
85
+ lastName: 'Mercury',
86
+ },
87
+ },
88
+ });
89
+
90
+ render(path.join(dirname, 'template.hbs'), locals, function (err, html) {
91
+ assert.ifError(err);
92
+ assert.strictEqual(H.stripWs(html), H.stripWs('Hello, Freddy Mercury'));
93
+ done(err);
94
+ });
95
+ });
96
+
97
+ it('removes _templateOptions from the locals data', function (done) {
98
+ var instance = hbs.create();
99
+ var render = instance.express3({
100
+ restrictLayoutsTo: dirname,
101
+ });
102
+ instance.updateTemplateOptions({
103
+ data: {
104
+ greeting: 'Hello,',
105
+ firstName: 'Freddy',
106
+ lastName: 'Krueger',
107
+ },
108
+ });
109
+
110
+ var locals = H.createLocals('express3', dirname, {
111
+ _templateOptions: {
112
+ data: {
113
+ lastName: 'Mercury',
114
+ },
115
+ },
116
+ });
117
+
118
+ render(path.join(dirname, 'data-access-template.hbs'), locals, function (err, html) {
119
+ assert.ifError(err);
120
+ assert.strictEqual(H.stripWs(html), H.stripWs(''));
121
+ done(err);
122
+ });
123
+ });
124
+ });
125
+ });
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ var request = require('supertest');
4
+ var assert = require('assert');
5
+ var express = require('express');
6
+ var hbs = require('..');
7
+
8
+ describe('multiple directories', function () {
9
+ var app;
10
+
11
+ beforeEach(function () {
12
+ app = express();
13
+ app.engine(
14
+ 'hbs',
15
+ hbs.express3({
16
+ restrictLayoutsTo: './test/views/multiple',
17
+ }),
18
+ );
19
+ app.set('view engine', 'hbs');
20
+ app.get('/test1', function (req, res) {
21
+ res.render('test1');
22
+ });
23
+ app.get('/test2', function (req, res) {
24
+ res.render('test2');
25
+ });
26
+ app.get('/collide', function (req, res) {
27
+ res.render('collide');
28
+ });
29
+ app.get('/error', function (req, res) {
30
+ res.render('error');
31
+ });
32
+ });
33
+
34
+ it('should handle single folder', function (done) {
35
+ app.set('views', './test/views/multiple/views1');
36
+ request(app)
37
+ .get('/test1')
38
+ .end(function (err, res) {
39
+ assert.ifError(err);
40
+ var expected = '<h1>test1</h1>\n';
41
+ assert.equal(res.text, expected);
42
+ done();
43
+ });
44
+ });
45
+
46
+ it('should handle multiple folders', function (done) {
47
+ app.set('views', ['./test/views/multiple/views1', './test/views/multiple/views2']);
48
+ request(app)
49
+ .get('/test2')
50
+ .end(function (err, res) {
51
+ assert.ifError(err);
52
+ var expected = '<h1>test2</h1>\n';
53
+ assert.equal(res.text, expected);
54
+ done();
55
+ });
56
+ });
57
+
58
+ describe('should handle multiple folders in specific order', function () {
59
+ it('views1, views2', function (done) {
60
+ app.set('views', ['./test/views/multiple/views1', './test/views/multiple/views2']);
61
+ request(app)
62
+ .get('/collide')
63
+ .end(function (err, res) {
64
+ assert.ifError(err);
65
+ var expected = '<h1>collide1</h1>\n';
66
+ assert.equal(res.text, expected);
67
+ done();
68
+ });
69
+ });
70
+
71
+ it('views2, views1', function (done) {
72
+ app.set('views', ['./test/views/multiple/views2', './test/views/multiple/views1']);
73
+ request(app)
74
+ .get('/collide')
75
+ .end(function (err, res) {
76
+ assert.ifError(err);
77
+ var expected = '<h1>collide2</h1>\n';
78
+ assert.equal(res.text, expected);
79
+ done();
80
+ });
81
+ });
82
+ });
83
+
84
+ describe('should report the filename in error', function () {
85
+ it('should report from first folder', function (done) {
86
+ app.set('views', ['./test/views/multiple/views1', './test/views/multiple/views2']);
87
+ app.use(function (err, req, res, next) {
88
+ res.status(500).send(err.stack);
89
+ });
90
+
91
+ request(app)
92
+ .get('/error')
93
+ .end(function (err, res) {
94
+ assert.ifError(err);
95
+ assert(res.error.text.indexOf('views1/error.hbs]') > 0);
96
+ done();
97
+ });
98
+ });
99
+
100
+ it('should report from second folder', function (done) {
101
+ app.set('views', ['./test/views/multiple/views2', './test/views/multiple/views1']);
102
+ app.use(function (err, req, res, next) {
103
+ res.status(500).send(err.stack);
104
+ });
105
+
106
+ request(app)
107
+ .get('/error')
108
+ .end(function (err, res) {
109
+ assert.ifError(err);
110
+ assert(res.error.text.indexOf('views2/error.hbs]') > 0);
111
+ done();
112
+ });
113
+ });
114
+ });
115
+ });
@@ -0,0 +1,36 @@
1
+ var assert = require('assert');
2
+ var hbs = require('..');
3
+ var H = require('./helpers');
4
+
5
+ describe('non-express', function () {
6
+ describe('viewsDir', function () {
7
+ var dirname = __dirname + '/views/viewsDir';
8
+
9
+ it('should use viewsDir options', function (done) {
10
+ var render = hbs.create().express3({
11
+ viewsDir: dirname,
12
+ restrictLayoutsTo: dirname,
13
+ });
14
+ var locals = H.createLocals('express3', dirname);
15
+
16
+ render(dirname + '/sub/directive.hbs', locals, function (err, html) {
17
+ assert.equal('<vd>directive</vd>', H.stripWs(html));
18
+ done();
19
+ });
20
+ });
21
+
22
+ it('should work with layoutsDir', function (done) {
23
+ var render = hbs.create().express3({
24
+ viewsDir: dirname,
25
+ layoutsDir: dirname + '/layouts',
26
+ restrictLayoutsTo: dirname,
27
+ });
28
+ var locals = H.createLocals('express3', dirname, { layout: 'default.hbs' });
29
+
30
+ render(dirname + '/sub/lay.hbs', locals, function (err, html) {
31
+ assert.equal('<vd>lay</vd>', H.stripWs(html));
32
+ done();
33
+ });
34
+ });
35
+ });
36
+ });
@@ -0,0 +1,22 @@
1
+ var assert = require('assert');
2
+ var hbs = require('..');
3
+ var path = require('path');
4
+ var H = require('./helpers');
5
+ var fs = require('fs');
6
+
7
+ describe('options', function () {
8
+ var dirname = path.join(__dirname, 'views/beautify');
9
+
10
+ it('should pretty print HTML', function (done) {
11
+ var hb = hbs.create();
12
+ var render = hb.express3({ beautify: true, restrictLayoutsTo: dirname });
13
+ var locals = H.createLocals('express3', dirname, {});
14
+
15
+ render(dirname + '/index.hbs', locals, function (err, html) {
16
+ assert.ifError(err);
17
+ var expected = fs.readFileSync(dirname + '/expected.hbs', 'utf8');
18
+ assert.equal(html.trim(), expected.trim());
19
+ done();
20
+ });
21
+ });
22
+ });
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ var assert = require('assert');
4
+ var generateId = require('../lib/generate-id');
5
+ var resolver = require('../lib/resolver');
6
+
7
+ describe('resolver', function () {
8
+ it('detects an unresolved helper at the start of rendered output', function () {
9
+ assert.equal(resolver.hasResolvers('__aSyNcId__<_12345678__'), true);
10
+ });
11
+
12
+ it('does not report rendered output without a resolver marker', function () {
13
+ assert.equal(resolver.hasResolvers('rendered output'), false);
14
+ });
15
+
16
+ it('resolves asynchronous helper output with its context', function (done) {
17
+ var cache = {};
18
+ var id = resolver.resolve(
19
+ cache,
20
+ function (context, callback) {
21
+ callback(context.value);
22
+ },
23
+ { value: 'resolved' },
24
+ );
25
+
26
+ resolver.done(cache, function (err, values) {
27
+ assert.ifError(err);
28
+ assert.equal(values[id], 'resolved');
29
+ done();
30
+ });
31
+ });
32
+
33
+ it('reports synchronous helper failures', function (done) {
34
+ var cache = {};
35
+ resolver.resolve(cache, function () {
36
+ throw new Error('helper failed');
37
+ });
38
+
39
+ resolver.done(cache, function (err) {
40
+ assert.equal(err.message, 'helper failed');
41
+ done();
42
+ });
43
+ });
44
+ });
45
+
46
+ describe('generateId', function () {
47
+ it('uses an eight-character default', function () {
48
+ assert.match(generateId(), /^[A-Za-z_]{8}$/);
49
+ });
50
+
51
+ it('uses the requested length', function () {
52
+ assert.match(generateId(16), /^[A-Za-z_]{16}$/);
53
+ });
54
+ });
@@ -0,0 +1,9 @@
1
+ <html>
2
+
3
+ <head>head</head>
4
+
5
+ <body>
6
+ <h1>heading 1</h1>
7
+ <h2>heading 2</h2>
8
+ </body>
9
+ <html>
@@ -0,0 +1,2 @@
1
+ <html>
2
+ <head>head</head> <body><h1>heading 1</h1><h2>heading 2</h2></body> <html>
@@ -0,0 +1,5 @@
1
+ {{!< layout}}
2
+ {{> subs/subPartial1}}
3
+ {{> partial1}}
4
+ {{> subs/subPartial2}}
5
+ {{> partial2}}
@@ -0,0 +1,6 @@
1
+ {{!< ../layouts/default}}
2
+ asynctemplate
3
+ {{async "foo"}}
4
+ {{async "bar"}}
5
+ {{async "bah"}}
6
+ {{async "baz"}}
@@ -0,0 +1,3 @@
1
+ {{!< ../layouts/default}}
2
+ index
3
+ {{sync "index"}}
@@ -0,0 +1,2 @@
1
+ {{!< ../layouts/default}}
2
+ directive
package/README.md CHANGED
@@ -43,6 +43,7 @@ hbs.express4({
43
43
  partialsDir: "{String/Array} [Required] Path to partials templates, one or several directories",
44
44
 
45
45
  // OPTIONAL settings
46
+ restrictLayoutsTo: "{String} Absolute path to a directory to restrict layout directive reading from",
46
47
  blockHelperName: "{String} Override 'block' helper name.",
47
48
  contentHelperName: "{String} Override 'contentFor' helper name.",
48
49
  defaultLayout: "{String} Absolute path to default layout template",
@@ -99,9 +100,9 @@ There are three ways to use a layout, listed in precedence order
99
100
 
100
101
  2. As an option to render
101
102
 
102
- ## ⚠️ This creates a potential security vulnerability:
103
+ ## ⚠️ This creates a potential security vulnerability if used without a `restrictLayoutsTo`:
103
104
 
104
- Do not use this 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!
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!
105
106
 
106
107
  ```js
107
108
  res.render('veggies', {
@@ -19,7 +19,8 @@ app.use(express.static(relative('public')));
19
19
  app.engine('hbs', hbs.express4({
20
20
  partialsDir: [relative('views/partials'), relative('views/partials-other')],
21
21
  layoutsDir: relative('views/layout'),
22
- defaultLayout: relative('views/layout/default.hbs')
22
+ defaultLayout: relative('views/layout/default.hbs'),
23
+ restrictLayoutsTo: relative('views/layout')
23
24
  }));
24
25
  app.set('view engine', 'hbs');
25
26
  app.set('views', relative('views'));
package/example/app.js CHANGED
@@ -20,7 +20,8 @@ function create(hbs, env) {
20
20
  // Hook in express-hbs and tell it where known directories reside
21
21
  app.engine('hbs', hbs.express4({
22
22
  partialsDir: [relative('views/partials'), relative('views/partials-other')],
23
- defaultLayout: relative('views/layout/default.hbs')
23
+ defaultLayout: relative('views/layout/default.hbs'),
24
+ restrictLayoutsTo: viewsDir
24
25
  }));
25
26
  app.set('view engine', 'hbs');
26
27
  app.set('views', viewsDir);
package/lib/hbs.js CHANGED
@@ -99,6 +99,18 @@ ExpressHbs.prototype.declaredLayoutFile = function(str, filename) {
99
99
  ExpressHbs.prototype.cacheLayout = function(layoutFile, useCache, cb) {
100
100
  var self = this;
101
101
 
102
+ if (this.restrictLayoutsTo) {
103
+ var relativePath = path.relative(this.restrictLayoutsTo, layoutFile);
104
+ var outside = relativePath === '..' ||
105
+ relativePath.startsWith('..' + path.sep) ||
106
+ path.isAbsolute(relativePath);
107
+
108
+ if (outside) {
109
+ var err = new Error('Cannot read ' + layoutFile + ' it does not reside in ' + this.restrictLayoutsTo);
110
+ return cb(err, null);
111
+ }
112
+ }
113
+
102
114
  // assume hbs extension
103
115
  if (path.extname(layoutFile) === '') layoutFile += this._options.extname;
104
116
 
@@ -254,6 +266,8 @@ ExpressHbs.prototype.express3 = function(options) {
254
266
  // Absolute path to the layouts directory
255
267
  this.layoutsDir = this._options.layoutsDir;
256
268
 
269
+ this.restrictLayoutsTo = this._options.restrictLayoutsTo;
270
+
257
271
  // express passes this through ___express func, gulp pass in an option
258
272
  this.viewsDir = null;
259
273
  this.viewsDirOpt = this._options.viewsDir;
@@ -332,7 +346,7 @@ ExpressHbs.prototype.compile = function(source, filename) {
332
346
  if (typeof source !== 'string') {
333
347
  throw new Error('registerPartial must be a string for empty comment workaround');
334
348
  }
335
- if (source.indexOf('}}') === source.length - 2) {
349
+ if (source.length >= 2 && source.indexOf('}}') === source.length - 2) {
336
350
  source += ' ';
337
351
  }
338
352
 
@@ -636,10 +650,16 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
636
650
  Object.keys(values).forEach(function(key) {
637
651
  values[key] = replaceValue(values, values[key]);
638
652
  });
639
- res = replaceValue(values, res);
640
- if (resolver.hasResolvers(res)) {
641
- return handleAsync(null, res);
653
+ var replaced = replaceValue(values, res);
654
+ // Re-check against the live cache: nested async helpers may have registered new IDs
655
+ // while resolving. Stop if a pass made no progress so unknown IDs can never loop forever.
656
+ if (
657
+ replaced !== res &&
658
+ resolver.hasResolvers(replaced, options.resolverCache, self.Utils.escapeExpression)
659
+ ) {
660
+ return handleAsync(null, replaced);
642
661
  }
662
+ res = replaced;
643
663
  cb(null, res);
644
664
  });
645
665
  }
package/lib/resolver.js CHANGED
@@ -41,14 +41,21 @@ function done(cache, callback) {
41
41
  });
42
42
  }
43
43
 
44
- function hasResolvers(text) {
44
+ function hasResolvers(text, cache, escape) {
45
45
  // NOTE: We specifically search the text for the ID_PREFIX **NOT** including the escapable character
46
46
  // This is because that character can be escaped in the text, and lead us to not finding unresolved
47
47
  // async helper outputs.
48
- if (text.search(ID_PREFIX) > 0) {
48
+ if (text.indexOf(ID_PREFIX) < 0) {
49
+ return false;
50
+ }
51
+ if (!cache) {
49
52
  return true;
50
53
  }
51
- return false;
54
+ // Only report IDs that were actually generated by an async helper. The prefix can also
55
+ // appear in user-supplied data, which must never be treated as an unresolved helper.
56
+ return Object.keys(cache).some(function(id) {
57
+ return text.indexOf(id) >= 0 || (escape && text.indexOf(escape(id)) >= 0);
58
+ });
52
59
  }
53
60
 
54
61
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-hbs",
3
- "version": "2.4.2",
3
+ "version": "2.5.1",
4
4
  "description": "Express handlebars template engine complete with multiple layouts, partials and blocks.",
5
5
  "keywords": [
6
6
  "express4",
@@ -30,7 +30,7 @@
30
30
  "license": "MIT",
31
31
  "devDependencies": {
32
32
  "cookie-parser": "1.4.6",
33
- "eslint": "8.48.0",
33
+ "eslint": "8.54.0",
34
34
  "express": "4.18.2",
35
35
  "i18n": "0.15.1",
36
36
  "mocha": "10.2.0",
@@ -65,7 +65,8 @@ function create(hbs, env) {
65
65
 
66
66
  // Hook in express-hbs and tell it where known directories reside
67
67
  app.engine('hbs', hbs.express4({
68
- defaultLayout: path.join(viewsDir, "layout.hbs")
68
+ defaultLayout: path.join(viewsDir, 'layout.hbs'),
69
+ restrictLayoutsTo: viewsDir
69
70
  }));
70
71
  app.set('view engine', 'hbs');
71
72
  app.set('views', viewsDir);
@@ -31,7 +31,8 @@ function create(hbs, env) {
31
31
 
32
32
  // Hook in express-hbs and tell it where known directories reside
33
33
  app.engine('hbs', hbs.express3({
34
- i18n: i18n
34
+ i18n: i18n,
35
+ restrictLayoutsTo: viewsDir
35
36
  }));
36
37
  app.set('view engine', 'hbs');
37
38
  app.set('views', viewsDir);