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,671 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+ var readdirp = require('readdirp');
6
+ var handlebars = require('handlebars');
7
+ var resolver = require('./resolver');
8
+ var _ = require('lodash');
9
+
10
+ /**
11
+ * Regex pattern for layout directive. {{!< layout }}
12
+ */
13
+ var layoutPattern = /{{!<\s+([A-Za-z0-9._\-/]+)\s*}}/;
14
+
15
+ /**
16
+ * Constructor
17
+ */
18
+ var ExpressHbs = function () {
19
+ this.handlebars = handlebars.create();
20
+ this.SafeString = this.handlebars.SafeString;
21
+ this.Utils = this.handlebars.Utils;
22
+ this.beautify = null;
23
+ this.beautifyrc = null;
24
+ this.cwd = process.cwd();
25
+ };
26
+
27
+ /**
28
+ * Defines content for a named block declared in layout.
29
+ *
30
+ * @example
31
+ *
32
+ * {{#contentFor "pageStylesheets"}}
33
+ * <link rel="stylesheet" href='{{{URL "css/style.css"}}}' />
34
+ * {{/contentFor}}
35
+ */
36
+ ExpressHbs.prototype.content = function (name, options, context) {
37
+ var block = options.data.root.blockCache[name] || (options.data.root.blockCache[name] = []);
38
+ block.push(options.fn(context));
39
+ };
40
+
41
+ /**
42
+ * Returns the layout filepath given the template filename and layout used.
43
+ * Backward compatible with specifying layouts in locals like 'layouts/foo',
44
+ * but if you have specified a layoutsDir you can specify layouts in locals with just the layout name.
45
+ *
46
+ * @param {String} filename Path to template file.
47
+ * @param {String} layout Layout path.
48
+ */
49
+ ExpressHbs.prototype.layoutPath = function (filename, layout) {
50
+ var dirs, layoutPath;
51
+ if (layout[0] === '.') {
52
+ dirs = path.dirname(filename);
53
+ } else if (this.layoutsDir) {
54
+ dirs = this.layoutsDir;
55
+ } else {
56
+ dirs = this.viewsDir;
57
+ }
58
+ [].concat(dirs).forEach(function (dir) {
59
+ if (!layoutPath) {
60
+ layoutPath = path.resolve(dir, layout);
61
+ }
62
+ });
63
+ return layoutPath;
64
+ };
65
+
66
+ /**
67
+ * Find the path of the declared layout in `str`, if any
68
+ *
69
+ * @param {String} str The template string to parse
70
+ * @param {String} filename Path to template
71
+ * @returns {String|undefined} Returns the path to layout.
72
+ */
73
+ ExpressHbs.prototype.declaredLayoutFile = function (str, filename) {
74
+ var matches = str.match(layoutPattern);
75
+ if (matches) {
76
+ var layout = matches[1];
77
+ // behave like `require`, if '.' then relative, else look in
78
+ // usual location (layoutsDir)
79
+ if (this.layoutsDir && layout[0] !== '.') {
80
+ layout = path.resolve(this.layoutsDir, layout);
81
+ }
82
+ return path.resolve(path.dirname(filename), layout);
83
+ }
84
+ };
85
+
86
+ /**
87
+ * Compiles a layout file.
88
+ *
89
+ * The function checks whether the layout file declares a parent layout.
90
+ * If it does, the parent layout is loaded recursively and checked as well
91
+ * for a parent layout, and so on, until the top layout is reached.
92
+ * All layouts are then returned as a stack to the caller via the callback.
93
+ *
94
+ * @param {String} layoutFile The path to the layout file to compile
95
+ * @param {Boolean} useCache Cache the compiled layout?
96
+ * @param {Function} cb Callback called with layouts stack
97
+ */
98
+ ExpressHbs.prototype.cacheLayout = function (layoutFile, useCache, cb) {
99
+ var self = this;
100
+
101
+ if (this.restrictLayoutsTo) {
102
+ var relativePath = path.relative(this.restrictLayoutsTo, layoutFile);
103
+ var outside =
104
+ relativePath === '..' ||
105
+ relativePath.startsWith('..' + path.sep) ||
106
+ path.isAbsolute(relativePath);
107
+
108
+ if (outside) {
109
+ var err = new Error(
110
+ 'Cannot read ' + layoutFile + ' it does not reside in ' + this.restrictLayoutsTo,
111
+ );
112
+ return cb(err, null);
113
+ }
114
+ }
115
+
116
+ // assume hbs extension
117
+ if (path.extname(layoutFile) === '') layoutFile += this._options.extname;
118
+
119
+ // path is relative in directive, make it absolute
120
+ var layoutTemplates = this.cache[layoutFile];
121
+ if (layoutTemplates) return cb(null, layoutTemplates);
122
+
123
+ fs.readFile(layoutFile, 'utf8', function (err, str) {
124
+ if (err) return cb(err);
125
+
126
+ // File path of eventual declared parent layout
127
+ var parentLayoutFile = self.declaredLayoutFile(str, layoutFile);
128
+
129
+ // This function returns the current layout stack to the caller
130
+ var _returnLayouts = function (layouts) {
131
+ var currentLayout;
132
+ layouts = layouts.slice(0);
133
+ currentLayout = self.compile(str, layoutFile);
134
+ layouts.push(currentLayout);
135
+ if (useCache) {
136
+ self.cache[layoutFile] = layouts.slice(0);
137
+ }
138
+ cb(null, layouts);
139
+ };
140
+
141
+ if (parentLayoutFile) {
142
+ // Recursively compile/cache parent layouts
143
+ self.cacheLayout(parentLayoutFile, useCache, function (err, parentLayouts) {
144
+ if (err) return cb(err);
145
+ _returnLayouts(parentLayouts);
146
+ });
147
+ } else {
148
+ // No parent layout: return current layout with an empty stack
149
+ _returnLayouts([]);
150
+ }
151
+ });
152
+ };
153
+
154
+ /**
155
+ * Cache partial templates found under directories configure in partialsDir.
156
+ */
157
+ ExpressHbs.prototype.cachePartials = function (cb) {
158
+ var self = this;
159
+
160
+ if (!(this.partialsDir instanceof Array)) {
161
+ this.partialsDir = [this.partialsDir];
162
+ }
163
+
164
+ // Use to iterate all folder in series
165
+ var count = 0;
166
+
167
+ function readNext() {
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
+ });
198
+ }
199
+
200
+ readNext();
201
+ };
202
+
203
+ /**
204
+ * Express 3.x template engine compliance.
205
+ *
206
+ * @param {Object} options = {
207
+ * handlebars: "override handlebars",
208
+ * defaultLayout: "path to default layout",
209
+ * partialsDir: "absolute path to partials (one path or an array of paths)",
210
+ * layoutsDir: "absolute path to the layouts",
211
+ * extname: "extension to use",
212
+ * contentHelperName: "contentFor",
213
+ * blockHelperName: "block",
214
+ * beautify: "{Boolean} whether to pretty print HTML",
215
+ * onCompile: function(self, source, filename) {
216
+ * return self.handlebars.compile(source);
217
+ * }
218
+ * }
219
+ */
220
+ ExpressHbs.prototype.express3 = function (options) {
221
+ var self = this;
222
+
223
+ // Set defaults
224
+ if (!options) options = {};
225
+ if (!options.extname) options.extname = '.hbs';
226
+ if (!options.contentHelperName) options.contentHelperName = 'contentFor';
227
+ if (!options.blockHelperName) options.blockHelperName = 'block';
228
+ if (!options.templateOptions) options.templateOptions = {};
229
+ if (options.handlebars) this.handlebars = options.handlebars;
230
+ if (options.onCompile) this.onCompile = options.onCompile;
231
+
232
+ this._options = options;
233
+ if (this._options.handlebars) this.handlebars = this._options.handlebars;
234
+
235
+ if (options.i18n) {
236
+ var i18n = options.i18n;
237
+ this.handlebars.registerHelper('__', function () {
238
+ var args = Array.prototype.slice.call(arguments);
239
+ var options = args.pop();
240
+ return i18n.__.apply(options.data.root, args);
241
+ });
242
+ this.handlebars.registerHelper('__n', function () {
243
+ var args = Array.prototype.slice.call(arguments);
244
+ var options = args.pop();
245
+ return i18n.__n.apply(options.data.root, args);
246
+ });
247
+ }
248
+
249
+ this.handlebars.registerHelper(this._options.blockHelperName, function (name, options) {
250
+ var val = options.data.root.blockCache[name];
251
+ if (val === undefined && typeof options.fn === 'function') {
252
+ val = options.fn(this);
253
+ }
254
+ if (Array.isArray(val)) {
255
+ val = val.join('\n');
256
+ }
257
+ return val;
258
+ });
259
+
260
+ // Pass 'this' as context of helper function to don't lose context call of helpers.
261
+ this.handlebars.registerHelper(this._options.contentHelperName, function (name, options) {
262
+ return self.content(name, options, this);
263
+ });
264
+
265
+ // Absolute path to partials directory.
266
+ this.partialsDir = this._options.partialsDir;
267
+
268
+ // Absolute path to the layouts directory
269
+ this.layoutsDir = this._options.layoutsDir;
270
+
271
+ this.restrictLayoutsTo = this._options.restrictLayoutsTo;
272
+
273
+ // express passes this through ___express func, gulp pass in an option
274
+ this.viewsDir = null;
275
+ this.viewsDirOpt = this._options.viewsDir;
276
+
277
+ // Cache for templates, express 3.x doesn't do this for us
278
+ this.cache = {};
279
+
280
+ // Holds the default compiled layout if specified in options configuration.
281
+ this.defaultLayoutTemplates = null;
282
+
283
+ // Keep track of if partials have been cached already or not.
284
+ this.isPartialCachingComplete = false;
285
+
286
+ return this.___express.bind(this);
287
+ };
288
+
289
+ /**
290
+ * Express 4.x template engine compliance.
291
+ *
292
+ * @param {Object} options = {
293
+ * handlebars: "override handlebars",
294
+ * defaultLayout: "path to default layout",
295
+ * partialsDir: "absolute path to partials (one path or an array of paths)",
296
+ * layoutsDir: "absolute path to the layouts",
297
+ * extname: "extension to use",
298
+ * contentHelperName: "contentFor",
299
+ * blockHelperName: "block",
300
+ * beautify: "{Boolean} whether to pretty print HTML"
301
+ * }
302
+ */
303
+ ExpressHbs.prototype.express4 = ExpressHbs.prototype.express3;
304
+
305
+ /**
306
+ * Tries to load the default layout.
307
+ *
308
+ * @param {Boolean} useCache Whether to cache.
309
+ */
310
+ ExpressHbs.prototype.loadDefaultLayout = function (useCache, cb) {
311
+ var self = this;
312
+ if (!this._options.defaultLayout) return cb();
313
+ if (useCache && this.defaultLayoutTemplates) return cb(null, this.defaultLayoutTemplates);
314
+
315
+ this.cacheLayout(this._options.defaultLayout, useCache, function (err, templates) {
316
+ if (err) return cb(err);
317
+ self.defaultLayoutTemplates = templates.slice(0);
318
+ return cb(null, templates);
319
+ });
320
+ };
321
+
322
+ /**
323
+ * Expose useful methods.
324
+ */
325
+ ExpressHbs.prototype.registerHelper = function (name, fn) {
326
+ this.handlebars.registerHelper(name, fn);
327
+ };
328
+
329
+ /**
330
+ * Registers a partial.
331
+ *
332
+ * @param {String} name The name of the partial as used in a template.
333
+ * @param {String} source String source of the partial.
334
+ */
335
+ ExpressHbs.prototype.registerPartial = function (name, source, filename) {
336
+ this.handlebars.registerPartial(name, this.compile(source, filename));
337
+ };
338
+
339
+ /**
340
+ * Compiles a string.
341
+ *
342
+ * @param {String} source The source to compile.
343
+ * @param {String} filename The path used to embed into __filename for errors.
344
+ */
345
+ ExpressHbs.prototype.compile = function (source, filename) {
346
+ // Handlebars has a bug with comment only partial causes errors. This must
347
+ // be a string so the block below can add a space.
348
+ if (typeof source !== 'string') {
349
+ throw new Error('registerPartial must be a string for empty comment workaround');
350
+ }
351
+ if (source.indexOf('}}') === source.length - 2) {
352
+ source += ' ';
353
+ }
354
+
355
+ var compiled;
356
+ if (this.onCompile) {
357
+ compiled = this.onCompile(this, source, filename);
358
+ } else {
359
+ compiled = this.handlebars.compile(source);
360
+ }
361
+
362
+ if (filename) {
363
+ if (Array.isArray(this.viewsDir) && this.viewsDir.length > 0) {
364
+ compiled.__filename = path.relative(this.cwd, filename).replace(path.sep, '/');
365
+ } else {
366
+ compiled.__filename = path.relative(this.viewsDir || '', filename).replace(path.sep, '/');
367
+ }
368
+ }
369
+ return compiled;
370
+ };
371
+
372
+ /**
373
+ * Registers an asynchronous helper.
374
+ *
375
+ * @param {String} name The name of the partial as used in a template.
376
+ * @param {String} fn The `function(options, cb)`
377
+ */
378
+ ExpressHbs.prototype.registerAsyncHelper = function (name, fn) {
379
+ this.handlebars.registerHelper(name, function (context, options) {
380
+ var resolverCache =
381
+ this.resolverCache ||
382
+ _.get(context, 'data.root.resolverCache') ||
383
+ _.get(options, 'data.root.resolverCache');
384
+ if (!resolverCache) {
385
+ throw new Error('Could not find resolver cache in async helper ' + name + '.');
386
+ }
387
+ if (options && fn.length > 2) {
388
+ var resolveFunc = function (arr, cb) {
389
+ return fn.call(this, arr[0], arr[1], cb);
390
+ };
391
+
392
+ return resolver.resolve(resolverCache, resolveFunc.bind(this), [context, options]);
393
+ }
394
+ return resolver.resolve(resolverCache, fn.bind(this), context);
395
+ });
396
+ };
397
+
398
+ ExpressHbs.prototype.getTemplateOptions = function () {
399
+ return this._options.templateOptions;
400
+ };
401
+
402
+ ExpressHbs.prototype.updateTemplateOptions = function (templateOptions) {
403
+ this._options.templateOptions = templateOptions;
404
+ };
405
+
406
+ ExpressHbs.prototype.getLocalTemplateOptions = function (locals) {
407
+ return locals._templateOptions || {};
408
+ };
409
+
410
+ ExpressHbs.prototype.updateLocalTemplateOptions = function (locals, localTemplateOptions) {
411
+ return (locals._templateOptions = localTemplateOptions);
412
+ };
413
+
414
+ /**
415
+ * Creates a new instance of ExpressHbs.
416
+ */
417
+ ExpressHbs.prototype.create = function () {
418
+ return new ExpressHbs();
419
+ };
420
+
421
+ /**
422
+ * express 3.x, 4.x template engine compliance
423
+ *
424
+ * @param {String} filename Full path to template.
425
+ * @param {Object} options Is the context or locals for templates. {
426
+ * {Object} settings - subset of Express settings, `settings.views` is
427
+ * the views directory
428
+ * }
429
+ * @param {Function} cb The callback expecting the rendered template as a string.
430
+ *
431
+ * @example
432
+ *
433
+ * Example options from express
434
+ *
435
+ * {
436
+ * settings: {
437
+ * 'x-powered-by': true,
438
+ * env: 'production',
439
+ * views: '/home/coder/barc/code/express-hbs/example/views',
440
+ * 'jsonp callback name': 'callback',
441
+ * 'view cache': true,
442
+ * 'view engine': 'hbs'
443
+ * },
444
+ * cache: true,
445
+ *
446
+ * // the rest are app-defined locals
447
+ * title: 'My favorite veggies',
448
+ * layout: 'layout/veggie'
449
+ * }
450
+ */
451
+ ExpressHbs.prototype.___express = function ___express(filename, source, options, cb) {
452
+ // support running as a gulp/grunt filter outside of express
453
+ if (arguments.length === 3) {
454
+ cb = options;
455
+ options = source;
456
+ source = null;
457
+ }
458
+
459
+ options.blockCache = {};
460
+ options.resolverCache = {};
461
+
462
+ this.viewsDir = options.settings.views || this.viewsDirOpt;
463
+ var self = this;
464
+
465
+ /**
466
+ * Allow a layout to be declared as a handlebars comment to remain spec
467
+ * compatible with handlebars.
468
+ *
469
+ * Valid directives
470
+ *
471
+ * {{!< foo}} # foo.hbs in same directory as template
472
+ * {{!< ../layouts/default}} # default.hbs in parent layout directory
473
+ * {{!< ../layouts/default.html}} # default.html in parent layout directory
474
+ */
475
+ function parseLayout(str, filename, cb) {
476
+ var layoutFile = self.declaredLayoutFile(str, filename);
477
+ if (layoutFile) {
478
+ self.cacheLayout(layoutFile, options.cache, cb);
479
+ } else {
480
+ cb(null, null);
481
+ }
482
+ }
483
+
484
+ /**
485
+ * Renders `template` with given `locals` and calls `cb` with the
486
+ * resulting HTML string.
487
+ *
488
+ * @param template
489
+ * @param locals
490
+ * @param cb
491
+ */
492
+ function renderTemplate(template, locals, cb) {
493
+ var res;
494
+
495
+ try {
496
+ var localTemplateOptions = self.getLocalTemplateOptions(locals);
497
+ var localsClone = _.extend({}, locals);
498
+ self.updateLocalTemplateOptions(localsClone, undefined);
499
+ res = template(localsClone, _.merge({}, self._options.templateOptions, localTemplateOptions));
500
+ } catch (err) {
501
+ if (err.message) {
502
+ err.message = '[' + template.__filename + '] ' + err.message;
503
+ } else if (typeof err === 'string') {
504
+ return cb('[' + template.__filename + '] ' + err, null);
505
+ }
506
+ return cb(err, null);
507
+ }
508
+ cb(null, res);
509
+ }
510
+
511
+ /**
512
+ * Renders `template` with an optional set of nested `layoutTemplates` using
513
+ * data in `locals`.
514
+ */
515
+ function render(template, locals, layoutTemplates, cb) {
516
+ if (!layoutTemplates) layoutTemplates = [];
517
+
518
+ // We'll render templates from bottom to top of the stack, each template
519
+ // being passed the rendered string of the previous ones as `body`
520
+ var i = layoutTemplates.length - 1;
521
+
522
+ var _stackRenderer = function (err, htmlStr) {
523
+ if (err) return cb(err);
524
+
525
+ if (i >= 0) {
526
+ locals.body = htmlStr;
527
+ renderTemplate(layoutTemplates[i--], locals, _stackRenderer);
528
+ } else {
529
+ cb(null, htmlStr);
530
+ }
531
+ };
532
+
533
+ // Start the rendering with the innermost page template
534
+ renderTemplate(template, locals, _stackRenderer);
535
+ }
536
+
537
+ /**
538
+ * Lazy loads js-beautify, which should not be used in production env.
539
+ */
540
+ function loadBeautify() {
541
+ if (!self.beautify) {
542
+ self.beautify = require('js-beautify').html;
543
+ var rc = path.join(process.cwd(), '.jsbeautifyrc');
544
+ if (fs.existsSync(rc)) {
545
+ self.beautifyrc = JSON.parse(fs.readFileSync(rc, 'utf8'));
546
+ }
547
+ }
548
+ }
549
+
550
+ /**
551
+ * Gets the source and compiled template for filename either from the cache
552
+ * or compiling it on the fly.
553
+ */
554
+ function getSourceTemplate(cb) {
555
+ if (options.cache) {
556
+ var info = self.cache[filename];
557
+ if (info) {
558
+ return cb(null, info.source, info.template);
559
+ }
560
+ }
561
+
562
+ fs.readFile(filename, 'utf8', function (err, source) {
563
+ if (err) return cb(err);
564
+
565
+ var template = self.compile(source, filename);
566
+ if (options.cache) {
567
+ self.cache[filename] = {
568
+ source: source,
569
+ template: template,
570
+ };
571
+ }
572
+ return cb(null, source, template);
573
+ });
574
+ }
575
+
576
+ /**
577
+ * Compiles a file into a template and a layoutTemplate, then renders it above.
578
+ */
579
+ function compileFile(locals, cb) {
580
+ getSourceTemplate(function (err, source, template) {
581
+ if (err) return cb(err);
582
+
583
+ // Try to get the layout
584
+ parseLayout(source, filename, function (err, layoutTemplates) {
585
+ if (err) return cb(err);
586
+
587
+ function renderIt(layoutTemplates) {
588
+ if (self._options.beautify) {
589
+ return render(template, locals, layoutTemplates, function (err, html) {
590
+ if (err) return cb(err);
591
+ loadBeautify();
592
+ return cb(null, self.beautify(html, self.beautifyrc));
593
+ });
594
+ }
595
+ return render(template, locals, layoutTemplates, cb);
596
+ }
597
+
598
+ // Determine which layout to use
599
+
600
+ if (typeof options.layout !== 'undefined' && !options.layout) {
601
+ // If options.layout is falsy, behave as if no layout should be used - suppress defaults
602
+ renderIt(null);
603
+ } else if (layoutTemplates) {
604
+ // 1. Layout specified in template
605
+ renderIt(layoutTemplates);
606
+ } else if (typeof options.layout !== 'undefined' && options.layout) {
607
+ // 2. Layout specified by options from render
608
+ var layoutFile = self.layoutPath(filename, options.layout);
609
+ self.cacheLayout(layoutFile, options.cache, function (err, layoutTemplates) {
610
+ if (err) return cb(err);
611
+ renderIt(layoutTemplates);
612
+ });
613
+ } else if (self.defaultLayoutTemplates) {
614
+ // 3. Default layout specified when middleware was configured.
615
+ renderIt(self.defaultLayoutTemplates);
616
+ } else {
617
+ // render without a template
618
+ renderIt(null);
619
+ }
620
+ });
621
+ });
622
+ }
623
+
624
+ function replaceValue(values, text) {
625
+ if (typeof text === 'string') {
626
+ Object.keys(values).forEach(function (id) {
627
+ text = text.replace(id, function () {
628
+ return values[id];
629
+ });
630
+ text = text.replace(self.Utils.escapeExpression(id), function () {
631
+ return self.Utils.escapeExpression(values[id]);
632
+ });
633
+ });
634
+ }
635
+ return text;
636
+ }
637
+
638
+ // Handles waiting for async helpers
639
+ function handleAsync(err, res) {
640
+ if (err) return cb(err);
641
+ resolver.done(options.resolverCache, function (err, values) {
642
+ if (err) return cb(err);
643
+ Object.keys(values).forEach(function (key) {
644
+ values[key] = replaceValue(values, values[key]);
645
+ });
646
+ res = replaceValue(values, res);
647
+ if (resolver.hasResolvers(res)) {
648
+ return handleAsync(null, res);
649
+ }
650
+ cb(null, res);
651
+ });
652
+ }
653
+
654
+ // kick it off by loading default template (if any)
655
+ this.loadDefaultLayout(options.cache, function (err) {
656
+ if (err) return cb(err);
657
+
658
+ // Force reloading of all partials if caching is not used. Inefficient but there
659
+ // is no loading partial event.
660
+ if (self.partialsDir && (!options.cache || !self.isPartialCachingComplete)) {
661
+ return self.cachePartials(function (err) {
662
+ if (err) return cb(err);
663
+ return compileFile(options, handleAsync);
664
+ });
665
+ }
666
+
667
+ return compileFile(options, handleAsync);
668
+ });
669
+ };
670
+
671
+ module.exports = new ExpressHbs();