express-hbs 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +2 -2
  2. package/lib/hbs.js +7 -1
  3. package/package.json +16 -12
  4. package/.editorconfig +0 -8
  5. package/.github/workflows/publish.yml +0 -73
  6. package/.github/workflows/test.yml +0 -48
  7. package/.jsbeautifyrc +0 -17
  8. package/.nvmrc +0 -1
  9. package/.nycrc.json +0 -16
  10. package/.oxfmtrc.json +0 -19
  11. package/.oxlintrc.json +0 -32
  12. package/AGENTS.md +0 -40
  13. package/SECURITY.md +0 -9
  14. package/example/app-layoutsDir.js +0 -99
  15. package/example/app.js +0 -103
  16. package/example/public/css/style.css +0 -17
  17. package/example/views/fruits/details-layoutsDir.hbs +0 -3
  18. package/example/views/fruits/details.hbs +0 -4
  19. package/example/views/fruits/index-layoutsDir.hbs +0 -20
  20. package/example/views/fruits/index.hbs +0 -20
  21. package/example/views/index.hbs +0 -34
  22. package/example/views/layout/default.hbs +0 -13
  23. package/example/views/layout/fruit-details.hbs +0 -9
  24. package/example/views/layout/fruits.hbs +0 -6
  25. package/example/views/layout/html-struct.hbs +0 -21
  26. package/example/views/layout/veggie-details.hbs +0 -9
  27. package/example/views/layout/veggie.hbs +0 -6
  28. package/example/views/partials/scripts.hbs +0 -5
  29. package/example/views/partials/sub/comment.hbs +0 -1
  30. package/example/views/partials-other/other.hbs +0 -1
  31. package/example/views/replace.hbs +0 -6
  32. package/example/views/tos.txt +0 -3
  33. package/example/views/veggies/details.hbs +0 -1
  34. package/example/views/veggies.hbs +0 -17
  35. package/renovate.json +0 -13
  36. package/test/apps/async/index.js +0 -139
  37. package/test/apps/async/views/failer.hbs +0 -4
  38. package/test/apps/async/views/index.hbs +0 -16
  39. package/test/apps/async/views/layout.hbs +0 -1
  40. package/test/apps/i18n/index.js +0 -57
  41. package/test/apps/i18n/locales/en.json +0 -8
  42. package/test/apps/i18n/locales/fr.json +0 -8
  43. package/test/apps/i18n/views/index.hbs +0 -3
  44. package/test/asyncSpecs.js +0 -69
  45. package/test/cacheSpecs.js +0 -135
  46. package/test/customExtension.js +0 -35
  47. package/test/handlebarsSpecs.js +0 -92
  48. package/test/helperSpecs.js +0 -58
  49. package/test/helpers.js +0 -24
  50. package/test/i18nSpecs.js +0 -36
  51. package/test/issues/144/index.hbs +0 -1
  52. package/test/issues/153/index.hbs +0 -3
  53. package/test/issues/21/views/index.hbs +0 -1
  54. package/test/issues/21/views/layouts/default.hbs +0 -1
  55. package/test/issues/21/views/layouts/sub/child.hbs +0 -1
  56. package/test/issues/21/views/sub/relativeLayout.hbs +0 -1
  57. package/test/issues/21/views/sub/sub.hbs +0 -1
  58. package/test/issues/21/views/useLayoutInDir.hbs +0 -1
  59. package/test/issues/22/layout1.hbs +0 -1
  60. package/test/issues/22/layout2.hbs +0 -1
  61. package/test/issues/22/template.hbs +0 -1
  62. package/test/issues/23/empty.hbs +0 -0
  63. package/test/issues/23/emptyComment.hbs +0 -1
  64. package/test/issues/23/emptyPartial.hbs +0 -1
  65. package/test/issues/23/index.hbs +0 -4
  66. package/test/issues/23/layout.hbs +0 -1
  67. package/test/issues/23/partials/custom/comment-only.hbs +0 -1
  68. package/test/issues/23/partials/custom/empty.hbs +0 -0
  69. package/test/issues/23/partials/hello.hbs +0 -1
  70. package/test/issues/49/error.hbs +0 -1
  71. package/test/issues/49/front/error.hbs +0 -1
  72. package/test/issues/49/index.hbs +0 -2
  73. package/test/issues/49/layouts/default.hbs +0 -1
  74. package/test/issues/49/partial.hbs +0 -1
  75. package/test/issues/49/partials/partial-error.hbs +0 -1
  76. package/test/issues/53/default.hbs +0 -15
  77. package/test/issues/53/index.hbs +0 -7
  78. package/test/issues/53/partials/.gitkeep +0 -0
  79. package/test/issues/59/index.hbs +0 -2
  80. package/test/issues/62/basic.hbs +0 -2
  81. package/test/issues/62/block.hbs +0 -11
  82. package/test/issues/73/index.hbs +0 -5
  83. package/test/issues/73/partials/partial.hbs +0 -6
  84. package/test/issues/76/mypartial.hbs +0 -0
  85. package/test/issues/84/index.hbs +0 -1
  86. package/test/issues/84/partials/abc/def/ghi/test.hbs +0 -1
  87. package/test/issues.js +0 -490
  88. package/test/layoutSpecs.js +0 -116
  89. package/test/localTemplateOptions/data-access-template.hbs +0 -1
  90. package/test/localTemplateOptions/template.hbs +0 -1
  91. package/test/localTemplateOptions.js +0 -125
  92. package/test/multiple.js +0 -115
  93. package/test/nonExpress.js +0 -36
  94. package/test/optionsSpecs.js +0 -22
  95. package/test/resolverSpecs.js +0 -54
  96. package/test/views/beautify/expected.hbs +0 -9
  97. package/test/views/beautify/index.hbs +0 -2
  98. package/test/views/customExtension/layout.server.view.html +0 -1
  99. package/test/views/customExtension/partialsDir/partial1.server.view.html +0 -1
  100. package/test/views/customExtension/partialsDir/partial2.server.view.html +0 -1
  101. package/test/views/customExtension/partialsDir/subs/subPartial1.server.view.html +0 -1
  102. package/test/views/customExtension/partialsDir/subs/subPartial2.server.view.html +0 -1
  103. package/test/views/customExtension/template.server.view.html +0 -5
  104. package/test/views/disableLayoutDirective/aside.hbs +0 -1
  105. package/test/views/disableLayoutDirective/index.hbs +0 -2
  106. package/test/views/disableLayoutDirective/layouts/default.hbs +0 -1
  107. package/test/views/helpers/home/async.hbs +0 -6
  108. package/test/views/helpers/home/index.hbs +0 -3
  109. package/test/views/helpers/layouts/default.hbs +0 -1
  110. package/test/views/multiple/views1/collide.hbs +0 -1
  111. package/test/views/multiple/views1/error.hbs +0 -1
  112. package/test/views/multiple/views1/test1.hbs +0 -1
  113. package/test/views/multiple/views2/collide.hbs +0 -1
  114. package/test/views/multiple/views2/error.hbs +0 -1
  115. package/test/views/multiple/views2/test2.hbs +0 -1
  116. package/test/views/viewsDir/layouts/default.hbs +0 -1
  117. package/test/views/viewsDir/sub/directive.hbs +0 -2
  118. package/test/views/viewsDir/sub/lay.hbs +0 -1
package/README.md CHANGED
@@ -10,8 +10,8 @@ layout inheritance and block-style content regions while keeping the familiar
10
10
 
11
11
  ## Requirements
12
12
 
13
- - Node.js 20 or later
14
- - pnpm 10 when working on this repository
13
+ - Node.js 22 or later
14
+ - the pinned pnpm version when working on this repository
15
15
 
16
16
  ## v2.0.0
17
17
 
package/lib/hbs.js CHANGED
@@ -99,7 +99,13 @@ ExpressHbs.prototype.cacheLayout = function (layoutFile, useCache, cb) {
99
99
  var self = this;
100
100
 
101
101
  if (this.restrictLayoutsTo) {
102
- if (!layoutFile.startsWith(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) {
103
109
  var err = new Error(
104
110
  'Cannot read ' + layoutFile + ' it does not reside in ' + this.restrictLayoutsTo,
105
111
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-hbs",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "description": "Express handlebars template engine complete with multiple layouts, partials and blocks.",
5
5
  "keywords": [
6
6
  "express4",
@@ -10,8 +10,12 @@
10
10
  "partials"
11
11
  ],
12
12
  "main": "index.js",
13
+ "files": [
14
+ "index.js",
15
+ "lib"
16
+ ],
13
17
  "engines": {
14
- "node": ">=20"
18
+ "node": ">=22"
15
19
  },
16
20
  "directories": {
17
21
  "example": "example"
@@ -24,21 +28,21 @@
24
28
  "author": "Mario Gutierrez <mario@barc.com>",
25
29
  "license": "MIT",
26
30
  "devDependencies": {
31
+ "@tryghost/pro-ship": "2.0.1",
27
32
  "cookie-parser": "1.4.7",
28
- "express": "5.2.1",
29
- "i18n": "0.15.3",
30
- "mocha": "11.7.6",
33
+ "express": "5.3.0",
34
+ "i18n": "0.15.4",
35
+ "mocha": "12.0.3",
31
36
  "nyc": "18.0.0",
32
- "oxfmt": "0.57.0",
33
- "oxlint": "1.72.0",
37
+ "oxfmt": "0.76.0",
38
+ "oxlint": "1.91.0",
34
39
  "rewire": "9.0.1",
35
- "supertest": "7.2.2"
40
+ "supertest": "7.3.2"
36
41
  },
37
42
  "dependencies": {
38
- "@tryghost/pro-ship": "1.1.5",
39
- "handlebars": "4.7.9",
40
- "lodash": "4.18.1",
41
- "readdirp": "3.6.0"
43
+ "handlebars": "^4.7.10",
44
+ "lodash": "^4.18.1",
45
+ "readdirp": "^3.6.0"
42
46
  },
43
47
  "optionalDependencies": {
44
48
  "js-beautify": "^2.0.0"
package/.editorconfig DELETED
@@ -1,8 +0,0 @@
1
- ; indicate this is the root of the project
2
-
3
- [*]
4
- indent_style = space
5
- end_of_line = lf
6
-
7
- [*.js]
8
- indent_size = 2
@@ -1,73 +0,0 @@
1
- name: Publish
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
- paths:
8
- - 'package.json'
9
- workflow_dispatch:
10
- inputs:
11
- dry-run:
12
- description: 'Preview what would be published without actually publishing'
13
- required: false
14
- type: boolean
15
- default: true
16
-
17
- concurrency:
18
- group: ${{ github.workflow }}-${{ github.ref }}
19
-
20
- jobs:
21
- publish:
22
- name: Publish to npm
23
- runs-on: ubuntu-latest
24
-
25
- permissions:
26
- # OIDC trusted publishing to npm. Provenance is enabled because this
27
- # repository is public; public repos can generate a provenance
28
- # attestation at publish time.
29
- id-token: write
30
- contents: read
31
-
32
- env:
33
- FORCE_COLOR: 1
34
- CI: true
35
- NPM_CONFIG_PROVENANCE: true
36
- NPM_CONFIG_REGISTRY: 'https://registry.npmjs.org'
37
-
38
- steps:
39
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
40
- with:
41
- persist-credentials: false
42
-
43
- - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
44
- with:
45
- node-version: '24.14.1'
46
- registry-url: 'https://registry.npmjs.org'
47
-
48
- - name: Install Corepack
49
- run: npm install --global corepack@0.34.6
50
- - name: Enable corepack
51
- run: corepack enable
52
-
53
- - name: Install dependencies
54
- run: pnpm install --frozen-lockfile --prefer-offline
55
-
56
- - name: Determine whether to publish
57
- id: pub
58
- run: |
59
- name=$(jq -r .name package.json)
60
- version=$(jq -r .version package.json)
61
- if [ -n "$(npm view "$name@$version" version 2>/dev/null)" ]; then
62
- echo "$name@$version is already published; nothing to do"
63
- echo "publish=false" >> "$GITHUB_OUTPUT"
64
- else
65
- echo "queueing $name@$version"
66
- echo "publish=true" >> "$GITHUB_OUTPUT"
67
- fi
68
-
69
- - name: Publish to npm
70
- if: steps.pub.outputs.publish == 'true'
71
- # --no-git-checks is required because actions/checkout leaves the repo on
72
- # a detached HEAD, which pnpm's default publish branch check rejects.
73
- run: pnpm publish --no-git-checks ${{ (github.event.inputs['dry-run'] == 'true' && '--dry-run') || '' }}
@@ -1,48 +0,0 @@
1
- name: Test
2
-
3
- on:
4
- pull_request:
5
- push:
6
- branches:
7
- - main
8
-
9
- permissions:
10
- contents: read
11
-
12
- concurrency:
13
- group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
14
- cancel-in-progress: true
15
-
16
- jobs:
17
- build:
18
- runs-on: ubuntu-latest
19
- timeout-minutes: 20
20
- strategy:
21
- fail-fast: false
22
- matrix:
23
- node: [20, 22, 24]
24
- name: Node ${{ matrix.node }}
25
- steps:
26
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
27
- with:
28
- persist-credentials: false
29
- - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
30
- - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
31
- with:
32
- node-version: ${{ matrix.node }}
33
- cache: pnpm
34
- - run: pnpm install --frozen-lockfile
35
- - run: pnpm lint
36
- - name: Run tests with coverage
37
- run: pnpm coverage
38
-
39
- required-checks-pass:
40
- name: Required checks pass
41
- if: always()
42
- needs: [build]
43
- runs-on: ubuntu-latest
44
- timeout-minutes: 5
45
- steps:
46
- - name: Verify all required jobs succeeded
47
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
48
- run: exit 1
package/.jsbeautifyrc DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "indent_size": 2,
3
- "indent_char": " ",
4
- "indent_level": 0,
5
- "indent_with_tabs": false,
6
- "preserve_newlines": true,
7
- "max_preserve_newlines": 10,
8
- "jslint_happy": false,
9
- "brace_style": "collapse",
10
- "keep_array_indentation": false,
11
- "keep_function_indentation": false,
12
- "space_before_conditional": true,
13
- "break_chained_methods": false,
14
- "eval_code": false,
15
- "unescape_strings": false,
16
- "wrap_line_length": 0
17
- }
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- 20
package/.nycrc.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "all": true,
3
- "include": [
4
- "index.js",
5
- "lib/**/*.js"
6
- ],
7
- "branches": 80,
8
- "functions": 80,
9
- "lines": 80,
10
- "statements": 80,
11
- "reporter": [
12
- "text",
13
- "json-summary",
14
- "lcov"
15
- ]
16
- }
package/.oxfmtrc.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "$schema": "./node_modules/oxfmt/configuration_schema.json",
3
- "singleQuote": true,
4
- "tabWidth": 2,
5
- "sortPackageJson": false,
6
- "ignorePatterns": [
7
- "**/node_modules/**",
8
- "**/coverage/**",
9
- "pnpm-lock.yaml",
10
- "**/*.json",
11
- "**/*.jsonc",
12
- "**/*.yml",
13
- "**/*.yaml",
14
- "**/*.md",
15
- "**/*.hbs",
16
- "**/*.html",
17
- "**/*.css"
18
- ]
19
- }
package/.oxlintrc.json DELETED
@@ -1,32 +0,0 @@
1
- {
2
- "$schema": "./node_modules/oxlint/configuration_schema.json",
3
- "categories": {
4
- "correctness": "error",
5
- "suspicious": "warn"
6
- },
7
- "env": {
8
- "node": true,
9
- "es2024": true
10
- },
11
- "rules": {
12
- "no-console": "error"
13
- },
14
- "overrides": [
15
- {
16
- "files": ["test/**/*.js"],
17
- "env": {
18
- "mocha": true
19
- },
20
- "rules": {
21
- "no-console": "off",
22
- "no-unused-vars": "off"
23
- }
24
- },
25
- {
26
- "files": ["lib/hbs.js", "example/**/*.js"],
27
- "rules": {
28
- "no-console": "off"
29
- }
30
- }
31
- ]
32
- }
package/AGENTS.md DELETED
@@ -1,40 +0,0 @@
1
- # express-hbs agent notes
2
-
3
- `express-hbs` is a CommonJS Express view-engine library. Read `README.md` for
4
- the public API and usage examples before changing behavior.
5
-
6
- ## Commands
7
-
8
- Use pnpm 10 on Node.js 20 or later.
9
-
10
- ```sh
11
- pnpm install --frozen-lockfile
12
- pnpm lint
13
- pnpm test
14
- pnpm coverage
15
- ```
16
-
17
- `pnpm test` runs `mocha` and then `pnpm lint` through the `posttest` script.
18
- `pnpm coverage` runs the same test suite under `nyc` and enforces the coverage
19
- thresholds in `package.json`.
20
-
21
- ## Boundaries
22
-
23
- - Do not edit `pnpm-lock.yaml` by hand. Regenerate it with `pnpm install`.
24
- - Keep `lib/hbs.js` and `lib/resolver.js` CommonJS; this package does not use
25
- ESM.
26
- - Treat files under `test/views/**`, `test/issues/**`, and `example/views/**`
27
- as rendering fixtures. Seemingly odd whitespace, malformed markup, or partial
28
- names may be intentional test coverage.
29
- - Keep the security warning around user-controlled `layout` values and
30
- `restrictLayoutsTo` in sync with behavior.
31
-
32
- ## Style notes
33
-
34
- - Formatting is enforced by `oxfmt`; run `pnpm lint:fix` rather than doing large
35
- manual formatting sweeps.
36
- - The formatter intentionally ignores Handlebars, HTML, CSS, lockfile, and
37
- fixture data files. Preserve their existing shape unless the test expectation
38
- itself is being changed.
39
- - Add or update tests for layout resolution, partial loading, async helpers,
40
- template options, and caching changes.
package/SECURITY.md DELETED
@@ -1,9 +0,0 @@
1
- # Reporting Security Vulnerabilities
2
-
3
- Potential security vulnerabilities can be reported directly us at `security@ghost.org`. The Ghost Security Team communicates privately and works in a secured, isolated repository for tracking, testing, and resolving security-related issues.
4
-
5
- The full, up-to-date details of our security policy and procedure can always be found in our documentation:
6
-
7
- https://ghost.org/docs/security/
8
-
9
- Please refer to this before emailing us. Thanks for helping make Ghost safe for everyone 🙏.
@@ -1,99 +0,0 @@
1
- // pnpm add express express-hbs
2
-
3
- 'use strict';
4
-
5
- var express = require('express');
6
- var app = express();
7
- var hbs = require('..'); // should be `require('express-hbs')` outside of this example
8
- var fs = require('fs');
9
- var fp = require('path');
10
-
11
- function relative(path) {
12
- return fp.join(__dirname, path);
13
- }
14
- var viewsDir = relative('views');
15
-
16
- app.use(express.static(relative('public')));
17
-
18
- // Hook in express-hbs and tell it where known directories reside
19
- app.engine(
20
- 'hbs',
21
- hbs.express4({
22
- partialsDir: [relative('views/partials'), relative('views/partials-other')],
23
- layoutsDir: relative('views/layout'),
24
- defaultLayout: relative('views/layout/default.hbs'),
25
- restrictLayoutsTo: relative('views/layout'),
26
- }),
27
- );
28
- app.set('view engine', 'hbs');
29
- app.set('views', relative('views'));
30
-
31
- // Register sync helper
32
- hbs.registerHelper('link', function (text, options) {
33
- var attrs = [];
34
- for (var prop in options.hash) {
35
- attrs.push(prop + '="' + options.hash[prop] + '"');
36
- }
37
- return new hbs.SafeString('<a ' + attrs.join(' ') + '>' + text + '</a>');
38
- });
39
-
40
- // Register Async helpers
41
- hbs.registerAsyncHelper('readFile', function (filename, cb) {
42
- fs.readFile(fp.join(viewsDir, filename), 'utf8', function (err, content) {
43
- if (err) console.error(err);
44
- cb(new hbs.SafeString(content));
45
- });
46
- });
47
-
48
- var fruits = [{ name: 'apple' }, { name: 'orange' }, { name: 'pear' }];
49
-
50
- var veggies = [{ name: 'asparagus' }, { name: 'carrot' }, { name: 'spinach' }];
51
-
52
- app.get('/', function (req, res) {
53
- res.render('index', {
54
- title: 'express-hbs example',
55
- });
56
- });
57
-
58
- app.get('/fruits', function (req, res) {
59
- res.render('fruits/index-layoutsDir', {
60
- title: 'My favorite fruits',
61
- fruits: fruits,
62
- });
63
- });
64
-
65
- app.get('/fruits/:name', function (req, res) {
66
- res.render('fruits/details-layoutsDir', {
67
- fruit: req.params.name,
68
- });
69
- });
70
-
71
- app.get('/veggies', function (req, res) {
72
- res.render('veggies', {
73
- title: 'My favorite veggies',
74
- veggies: veggies,
75
- layout: 'veggie',
76
- });
77
- });
78
-
79
- app.get('/veggies/explicit-dir', function (req, res) {
80
- res.render('veggies', {
81
- title: 'My favorite veggies',
82
- veggies: veggies,
83
- layout: relative('views/layout/veggie'),
84
- });
85
- });
86
-
87
- app.get('/veggies/:name', function (req, res) {
88
- res.render('veggies/details', {
89
- veggie: req.params.name,
90
- layout: 'veggie-details',
91
- });
92
- });
93
-
94
- if (require.main === module) {
95
- app.listen(3000);
96
- console.log('Express server listening on port 3000');
97
- } else {
98
- module.exports = app;
99
- }
package/example/app.js DELETED
@@ -1,103 +0,0 @@
1
- 'use strict';
2
- // pnpm add express express-hbs
3
-
4
- function create(hbs, env) {
5
- if (env) process.env.NODE_ENV = env;
6
-
7
- var express = require('express');
8
- var app = express();
9
- var fs = require('fs');
10
- var fp = require('path');
11
-
12
- function relative(path) {
13
- return fp.join(__dirname, path);
14
- }
15
-
16
- var viewsDir = relative('views');
17
-
18
- app.use(express.static(relative('public')));
19
-
20
- // Hook in express-hbs and tell it where known directories reside
21
- app.engine(
22
- 'hbs',
23
- hbs.express4({
24
- partialsDir: [relative('views/partials'), relative('views/partials-other')],
25
- defaultLayout: relative('views/layout/default.hbs'),
26
- restrictLayoutsTo: viewsDir,
27
- }),
28
- );
29
- app.set('view engine', 'hbs');
30
- app.set('views', viewsDir);
31
-
32
- // Register sync helper
33
- hbs.registerHelper('link', function (text, options) {
34
- var attrs = [];
35
- for (var prop in options.hash) {
36
- attrs.push(prop + '="' + options.hash[prop] + '"');
37
- }
38
- return new hbs.SafeString('<a ' + attrs.join(' ') + '>' + text + '</a>');
39
- });
40
-
41
- // Register Async helpers
42
- hbs.registerAsyncHelper('readFile', function (filename, cb) {
43
- fs.readFile(fp.join(viewsDir, filename), 'utf8', function (err, content) {
44
- if (err) console.error(err);
45
- cb(new hbs.SafeString(content));
46
- });
47
- });
48
-
49
- var fruits = [{ name: 'apple' }, { name: 'orange' }, { name: 'pear' }];
50
-
51
- var veggies = [{ name: 'asparagus' }, { name: 'carrot' }, { name: 'spinach' }];
52
-
53
- app.get('/', function (req, res) {
54
- res.render('index', {
55
- title: 'express-hbs example',
56
- });
57
- });
58
-
59
- app.get('/replace', function (req, res) {
60
- res.render('replace', {
61
- title: 'express-hbs example',
62
- });
63
- });
64
-
65
- app.get('/fruits', function (req, res) {
66
- res.render('fruits/index', {
67
- title: 'My favorite fruits',
68
- fruits: fruits,
69
- });
70
- });
71
-
72
- app.get('/fruits/:name', function (req, res) {
73
- res.render('fruits/details', {
74
- fruit: req.params.name,
75
- });
76
- });
77
-
78
- app.get('/veggies', function (req, res) {
79
- res.render('veggies', {
80
- title: 'My favorite veggies',
81
- veggies: veggies,
82
- layout: 'layout/veggie',
83
- });
84
- });
85
-
86
- app.get('/veggies/:name', function (req, res) {
87
- res.render('veggies/details', {
88
- veggie: req.params.name,
89
- layout: 'layout/veggie-details',
90
- });
91
- });
92
-
93
- return app;
94
- }
95
-
96
- if (require.main === module) {
97
- var hbs = require('..');
98
- var app = create(hbs);
99
- app.listen(3000);
100
- console.log('Express server listening on port 3000');
101
- } else {
102
- exports.create = create;
103
- }
@@ -1,17 +0,0 @@
1
- * {
2
- font-family: Georgia, serif;
3
- }
4
-
5
- h1, h2 {
6
- font-family: Helvetica, Arial, sans;
7
- }
8
-
9
- .veggie-details {
10
- border: 5px solid green;
11
- padding: 10px;
12
- }
13
-
14
- .fruit-details {
15
- border: 5px solid red;
16
- padding: 10px;
17
- }
@@ -1,3 +0,0 @@
1
- {{!< fruit-details}}
2
-
3
- {{fruit}}
@@ -1,4 +0,0 @@
1
- {{!< ../layout/fruit-details}}
2
-
3
- {{fruit}}
4
-
@@ -1,20 +0,0 @@
1
- <!-- name of layout -->
2
- {{!< fruits}}
3
-
4
- {{#contentFor 'pageStyles'}}
5
- <style>
6
- li a {
7
- color: #800;
8
- }
9
- </style>
10
- {{/contentFor}}
11
-
12
-
13
- <h1>{{title}}</h1>
14
- <ul>
15
- {{#each fruits}}
16
- <li>
17
- <a href="/fruits/{{this.name}}">{{this.name}}</a>
18
- </li>
19
- {{/each}}
20
- </ul>
@@ -1,20 +0,0 @@
1
- <!-- name of layout -->
2
- {{!< ../layout/fruits}}
3
-
4
- {{#contentFor 'pageStyles'}}
5
- <style>
6
- li a {
7
- color: #800;
8
- }
9
- </style>
10
- {{/contentFor}}
11
-
12
-
13
- <h1>{{title}}</h1>
14
- <ul>
15
- {{#each fruits}}
16
- <li>
17
- <a href="/fruits/{{this.name}}">{{this.name}}</a>
18
- </li>
19
- {{/each}}
20
- </ul>
@@ -1,34 +0,0 @@
1
- {{#contentFor 'pageStyles'}}
2
- <style>
3
- .clicker {
4
- color: blue;
5
- };
6
- </style>
7
- {{/contentFor}}
8
-
9
-
10
- {{#contentFor 'pageScripts'}}
11
- <script>
12
- $(function() {
13
- $('.clicker').click(function() {
14
- $(this).text('Hello express-hbs!');
15
- });
16
- });
17
- </script>
18
- {{/contentFor}}
19
-
20
-
21
- <h1>{{title}}</h1>
22
- <p class='clicker'>Click me!</p>
23
- <p>
24
- <a href='/fruits'>Favorite fruits</a>
25
- </p>
26
- <p>
27
- <!-- sync helper -->
28
- {{{link 'Vegetables' href='/veggies'}}}
29
- </p>
30
-
31
- <!-- async helper -->
32
- <div>
33
- {{{readFile 'tos.txt'}}}
34
- </div>
@@ -1,13 +0,0 @@
1
- {{!< html-struct}}
2
-
3
- <h2>DEFAULT LAYOUT</h2>
4
-
5
- {{#block 'default-block'}}
6
- Default block content
7
- {{/block}}
8
-
9
- {{#block 'replaced-default-block'}}
10
- Default block content
11
- {{/block}}
12
-
13
- {{{body}}}