express-hbs 2.5.0 → 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 (257) 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/lib/hbs.js +16 -5
  250. package/lib/resolver.js +10 -3
  251. package/package.json +1 -1
  252. package/test/asyncSpecs.js +60 -0
  253. package/test/handlebarsSpecs.js +19 -0
  254. package/test/layoutSpecs.js +30 -0
  255. package/test/resolverSpecs.js +70 -0
  256. package/test/views/asyncPrefix/body.hbs +1 -0
  257. 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/lib/hbs.js CHANGED
@@ -100,7 +100,12 @@ ExpressHbs.prototype.cacheLayout = function(layoutFile, useCache, cb) {
100
100
  var self = this;
101
101
 
102
102
  if (this.restrictLayoutsTo) {
103
- if (!layoutFile.startsWith(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) {
104
109
  var err = new Error('Cannot read ' + layoutFile + ' it does not reside in ' + this.restrictLayoutsTo);
105
110
  return cb(err, null);
106
111
  }
@@ -341,7 +346,7 @@ ExpressHbs.prototype.compile = function(source, filename) {
341
346
  if (typeof source !== 'string') {
342
347
  throw new Error('registerPartial must be a string for empty comment workaround');
343
348
  }
344
- if (source.indexOf('}}') === source.length - 2) {
349
+ if (source.length >= 2 && source.indexOf('}}') === source.length - 2) {
345
350
  source += ' ';
346
351
  }
347
352
 
@@ -645,10 +650,16 @@ ExpressHbs.prototype.___express = function ___express(filename, source, options,
645
650
  Object.keys(values).forEach(function(key) {
646
651
  values[key] = replaceValue(values, values[key]);
647
652
  });
648
- res = replaceValue(values, res);
649
- if (resolver.hasResolvers(res)) {
650
- 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);
651
661
  }
662
+ res = replaced;
652
663
  cb(null, res);
653
664
  });
654
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.5.0",
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",
@@ -5,6 +5,7 @@ var assert = require('assert');
5
5
  var hbs = require('..');
6
6
  var asyncApp = require('./apps/async');
7
7
  var resolver = require('../lib/resolver');
8
+ var H = require('./helpers');
8
9
 
9
10
  function makeUserRequest(app, user, cb) {
10
11
  request(app)
@@ -66,4 +67,63 @@ describe('async', function() {
66
67
  done();
67
68
  });
68
69
  });
70
+
71
+ describe('literal async id prefix in data', function () {
72
+ var dirname = __dirname + '/views/asyncPrefix';
73
+
74
+ function createRender() {
75
+ var hb = hbs.create();
76
+ hb.registerAsyncHelper('async', function (s, cb) {
77
+ setTimeout(function () {
78
+ cb(new hb.SafeString('async-' + s));
79
+ }, 1);
80
+ });
81
+ return hb.express4({ viewsDir: dirname });
82
+ }
83
+
84
+ function renderValue(file, locals, cb) {
85
+ var render = createRender();
86
+ render(dirname + '/' + file, H.createLocals('express4', dirname, locals), cb);
87
+ }
88
+
89
+ it('renders prefix text in the middle of data verbatim', function (done) {
90
+ renderValue('body.hbs', { body: 'x __aSyNcId__ y' }, function (err, html) {
91
+ if (err) return done(err);
92
+ assert.equal(html.trim(), 'hello x __aSyNcId__ y');
93
+ done();
94
+ });
95
+ });
96
+
97
+ [
98
+ '__aSyNcId__',
99
+ '__aSyNcId__ start',
100
+ 'middle __aSyNcId__ text',
101
+ '__aSyNcId__<_abcdefgh__',
102
+ 'x __aSyNcId__<_abcdefgh__ y',
103
+ ].forEach(function (value) {
104
+ it('renders ' + JSON.stringify(value) + ' escaped and unescaped', function (done) {
105
+ renderValue('index.hbs', { value: value }, function (err, html) {
106
+ if (err) return done(err);
107
+ var escaped = hbs.Utils.escapeExpression(value);
108
+ assert.equal(html.trim(), 'hello ' + escaped + '|' + value + '|async-a');
109
+ done();
110
+ });
111
+ });
112
+ });
113
+
114
+ it('renders prefix text in data alongside nested async helpers', function (done) {
115
+ var app = asyncApp.create(hbs.create(), 'production');
116
+ request(app)
117
+ .get('/')
118
+ .set('Cookie', 'user=__aSyNcId__')
119
+ .end(function (err, res) {
120
+ if (err) return done(err);
121
+ assert.notEqual(res.text.indexOf('Hello, __aSyNcId__'), -1);
122
+ assert.notEqual(res.text.indexOf('This should welcome __aSyNcId__.'), -1);
123
+ assert.notEqual(res.text.indexOf('Title 1 Comment 1'), -1);
124
+ assert.equal(res.text.indexOf('This should not show!'), -1);
125
+ done();
126
+ });
127
+ });
128
+ });
69
129
  });
@@ -92,4 +92,23 @@ describe('express-hbs', function() {
92
92
  assert(hbs !== hbs2 && hbs !== hbs3 && hbs2 !== hbs3);
93
93
  });
94
94
  });
95
+
96
+ describe('compile', function () {
97
+ it('should pad a template that ends with its only closing tag', function () {
98
+ var hb = hbs.create();
99
+ hb.onCompile = function (exhbs, source) {
100
+ return source;
101
+ };
102
+ assert.equal(hb.compile('{{! comment }}'), '{{! comment }} ');
103
+ });
104
+
105
+ it('should not pad a one-character template', function () {
106
+ var hb = hbs.create();
107
+ hb.onCompile = function (exhbs, source) {
108
+ return source;
109
+ };
110
+ assert.equal(hb.compile('x'), 'x');
111
+ assert.equal(hb.compile(''), '');
112
+ });
113
+ });
95
114
  });
@@ -102,6 +102,36 @@ describe('layouts', function() {
102
102
  });
103
103
  });
104
104
 
105
+ it('should allow a layout inside a relative restrictLayoutsTo', function(done) {
106
+ var render = hbs.create().express3({
107
+ restrictLayoutsTo: path.relative(process.cwd(), dirname)
108
+ });
109
+ var locals = createLocals('express3', dirname, { layout: 'layouts/default' });
110
+
111
+ render(dirname + '/aside.hbs', locals, function(err, html) {
112
+ if (err) return done(err);
113
+ assert.equal('<dld>aside</dld>', stripWs(html));
114
+ done();
115
+ });
116
+ });
117
+
118
+ it('should error when a layout is in a sibling directory sharing the restrictLayoutsTo prefix', function(done) {
119
+ var restrictLayoutsTo = path.join(dirname, 'lay');
120
+ var render = hbs.create().express3({
121
+ restrictLayoutsTo: restrictLayoutsTo
122
+ });
123
+ var locals = createLocals('express3', dirname, { layout: 'layouts/default' });
124
+
125
+ render(dirname + '/aside.hbs', locals, function(err) {
126
+ assert.ok(err, 'expected an error');
127
+ assert.ok(
128
+ err.message.includes('does not reside in ' + restrictLayoutsTo),
129
+ 'unexpected error: ' + err.message
130
+ );
131
+ done();
132
+ });
133
+ });
134
+
105
135
  it ('should not process template-specified layout when options.layout is falsy', function(done) {
106
136
  var render = hbs.create().express3({
107
137
  restrictLayoutsTo: dirname