express-hbs 2.4.2 → 3.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.
@@ -0,0 +1,73 @@
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,24 +1,48 @@
1
1
  name: Test
2
+
2
3
  on:
3
4
  pull_request:
4
5
  push:
5
6
  branches:
6
7
  - main
7
- - 'renovate/*'
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
8
15
 
9
16
  jobs:
10
17
  build:
11
18
  runs-on: ubuntu-latest
12
- if: github.event_name == 'push' || (github.event_name == 'pull_request' && !startsWith(github.head_ref, 'renovate/'))
19
+ timeout-minutes: 20
13
20
  strategy:
21
+ fail-fast: false
14
22
  matrix:
15
- node: [ 14, 16, 18 ]
23
+ node: [20, 22, 24]
16
24
  name: Node ${{ matrix.node }}
17
25
  steps:
18
- - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4
19
- - uses: actions/setup-node@v3
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
20
31
  with:
21
32
  node-version: ${{ matrix.node }}
22
- - run: yarn
23
- - run: yarn lint
24
- - run: yarn test
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/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 20
package/.nycrc.json ADDED
@@ -0,0 +1,16 @@
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 ADDED
@@ -0,0 +1,19 @@
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 ADDED
@@ -0,0 +1,32 @@
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 ADDED
@@ -0,0 +1,40 @@
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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2012-2023 Barc, Inc., Ghost Foundation
3
+ Copyright (c) 2012-2025 Barc, Inc., Ghost Foundation
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
package/README.md CHANGED
@@ -1,6 +1,17 @@
1
1
  # express-hbs
2
2
 
3
- Express handlebars template engine with multiple layouts, blocks and cached partials.
3
+ Express Handlebars template engine for Express apps, with nested layouts,
4
+ named content blocks, cached partials, i18n helpers, and asynchronous helpers.
5
+
6
+ `express-hbs` exposes Express-compatible view engine functions and a shared
7
+ Handlebars instance. It is used by apps that want Handlebars templates with
8
+ layout inheritance and block-style content regions while keeping the familiar
9
+ `app.engine()` integration.
10
+
11
+ ## Requirements
12
+
13
+ - Node.js 20 or later
14
+ - pnpm 10 when working on this repository
4
15
 
5
16
  ## v2.0.0
6
17
 
@@ -17,7 +28,8 @@ If you're upgrading from v0.8.4 to v1.0.0 there are some potentially breaking ch
17
28
 
18
29
  ## Usage
19
30
 
20
- To use with express 4.
31
+ Register the engine with Express using `hbs.express4()`.
32
+
21
33
  ```js
22
34
  var hbs = require('express-hbs');
23
35
 
@@ -28,7 +40,9 @@ app.engine('hbs', hbs.express4({
28
40
  app.set('view engine', 'hbs');
29
41
  app.set('views', __dirname + '/views');
30
42
  ```
31
- To use with express 3 is the same as above, except use hbs.express3
43
+
44
+ `hbs.express3()` is still available for legacy apps and accepts the same
45
+ options:
32
46
 
33
47
  ```js
34
48
  app.engine('hbs', hbs.express3({
@@ -36,13 +50,14 @@ app.engine('hbs', hbs.express3({
36
50
  }));
37
51
  ```
38
52
 
39
- Options for `#express3` and `#express4`
53
+ Options for `express3()` and `express4()`:
40
54
 
41
55
  ```js
42
56
  hbs.express4({
43
57
  partialsDir: "{String/Array} [Required] Path to partials templates, one or several directories",
44
58
 
45
59
  // OPTIONAL settings
60
+ restrictLayoutsTo: "{String} Absolute path to a directory to restrict layout directive reading from",
46
61
  blockHelperName: "{String} Override 'block' helper name.",
47
62
  contentHelperName: "{String} Override 'contentFor' helper name.",
48
63
  defaultLayout: "{String} Absolute path to default layout template",
@@ -66,15 +81,15 @@ hbs.express4({
66
81
 
67
82
  ## Syntax
68
83
 
69
- To mark where layout should insert page
84
+ To mark where a layout should insert page content:
70
85
 
71
86
  {{{body}}}
72
87
 
73
- To declare a block placeholder in layout
88
+ To declare a block placeholder in a layout:
74
89
 
75
90
  {{{block "pageScripts"}}}
76
91
 
77
- To define block content in a page
92
+ To define block content in a page:
78
93
 
79
94
  {{#contentFor "pageScripts"}}
80
95
  CONTENT HERE
@@ -82,7 +97,7 @@ To define block content in a page
82
97
 
83
98
  ## Layouts
84
99
 
85
- There are three ways to use a layout, listed in precedence order
100
+ There are three ways to use a layout, listed in precedence order:
86
101
 
87
102
  1. Declarative within a page. Use handlebars comment
88
103
 
@@ -99,9 +114,10 @@ There are three ways to use a layout, listed in precedence order
99
114
 
100
115
  2. As an option to render
101
116
 
102
- ## ⚠️ This creates a potential security vulnerability:
103
-
104
- Do not use this option in conjunction with passing user submitted data to res.render e.g. `res.render('index', req.query)`. This allows users to read arbitrary files from your filesystem!
117
+ ⚠️ Passing user-controlled data into the `layout` option can read arbitrary
118
+ files unless `restrictLayoutsTo` confines layout resolution to a safe
119
+ directory. Do not call `res.render('index', req.query)` or similar without
120
+ setting `restrictLayoutsTo`.
105
121
 
106
122
  ```js
107
123
  res.render('veggies', {
@@ -111,7 +127,8 @@ There are three ways to use a layout, listed in precedence order
111
127
  });
112
128
  ```
113
129
 
114
- This option also allows for layout suppression (both the default layout and when specified declaratively in a page) by passing in a falsey Javascript value as the value of the `layout` property:
130
+ This option also allows layout suppression, including the default layout and
131
+ template-declared layouts, by passing a falsey JavaScript value:
115
132
 
116
133
  ```js
117
134
  res.render('veggies', {
@@ -214,9 +231,12 @@ var instance2 = hbs.create();
214
231
 
215
232
  ## Template options
216
233
 
217
- The main use case for template options is setting the handlebars "data" object - this creates global template variables accessible with an `@` prefix.
234
+ The main use case for template options is setting the Handlebars `data` object,
235
+ which creates global template variables accessible with an `@` prefix.
218
236
 
219
- Template options can be set in 3 ways. When setting global template options they can be [passed as config on creation of an instance](https://github.com/barc/express-hbs#usage), and they can also be updated used the `updateTemplateOptions(templateOptions)` method of an instance. To set template options for an individual request they can be set on `res.locals` using the helper method `updateLocalTemplateOptions(locals, templateOptions)`.
237
+ Template options can be passed when creating an engine instance, updated with
238
+ `updateTemplateOptions(templateOptions)`, or set for one request with
239
+ `updateLocalTemplateOptions(locals, templateOptions)` on `res.locals`.
220
240
 
221
241
  Both of these methods have a companion method `getTemplateOptions()` and `getLocalTemplateOptions(locals)`, which should be used when extending or merging the current options.
222
242
 
@@ -274,20 +294,29 @@ File `views/index.hbs`
274
294
 
275
295
  To run example project
276
296
 
277
- npm install -d
278
- node example/app.js
297
+ ```sh
298
+ pnpm install
299
+ node example/app.js
300
+ ```
279
301
 
302
+ The example server listens on <http://localhost:3000>.
280
303
 
281
304
  ## Testing
282
305
 
283
- The test suite requires the `grunt-cli` package:
306
+ Install dependencies and run the test suite:
284
307
 
285
- npm install -g grunt-cli
286
- npm install -d
308
+ ```sh
309
+ pnpm install
310
+ pnpm test
311
+ ```
287
312
 
288
- Once everything's installed, just run:
313
+ Useful maintainer commands:
289
314
 
290
- npm test
315
+ ```sh
316
+ pnpm lint # oxlint plus oxfmt --check
317
+ pnpm lint:fix # apply safe lint fixes and formatting
318
+ pnpm coverage # mocha under nyc with enforced coverage thresholds
319
+ ```
291
320
 
292
321
 
293
322
  ## Credits
@@ -301,4 +330,4 @@ Big thanks to all [CONTRIBUTORS](https://github.com/TryGhost/express-hbs/contrib
301
330
 
302
331
  The MIT License (MIT)
303
332
 
304
- Copyright (c) 2012-2023 Barc, Inc., Ghost Foundation - Released under the [MIT license](LICENSE).
333
+ Copyright (c) 2012-2026 Barc, Inc., Ghost Foundation - Released under the [MIT license](LICENSE).
@@ -1,4 +1,4 @@
1
- // npm install express express-hbs
1
+ // pnpm add express express-hbs
2
2
 
3
3
  'use strict';
4
4
 
@@ -16,17 +16,20 @@ var viewsDir = relative('views');
16
16
  app.use(express.static(relative('public')));
17
17
 
18
18
  // Hook in express-hbs and tell it where known directories reside
19
- app.engine('hbs', hbs.express4({
20
- partialsDir: [relative('views/partials'), relative('views/partials-other')],
21
- layoutsDir: relative('views/layout'),
22
- defaultLayout: relative('views/layout/default.hbs')
23
- }));
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
+ );
24
28
  app.set('view engine', 'hbs');
25
29
  app.set('views', relative('views'));
26
30
 
27
-
28
31
  // Register sync helper
29
- hbs.registerHelper('link', function(text, options) {
32
+ hbs.registerHelper('link', function (text, options) {
30
33
  var attrs = [];
31
34
  for (var prop in options.hash) {
32
35
  attrs.push(prop + '="' + options.hash[prop] + '"');
@@ -35,66 +38,57 @@ hbs.registerHelper('link', function(text, options) {
35
38
  });
36
39
 
37
40
  // Register Async helpers
38
- hbs.registerAsyncHelper('readFile', function(filename, cb) {
39
- fs.readFile(fp.join(viewsDir, filename), 'utf8', function(err, content) {
41
+ hbs.registerAsyncHelper('readFile', function (filename, cb) {
42
+ fs.readFile(fp.join(viewsDir, filename), 'utf8', function (err, content) {
40
43
  if (err) console.error(err);
41
44
  cb(new hbs.SafeString(content));
42
45
  });
43
46
  });
44
47
 
45
- var fruits = [
46
- {name: 'apple'},
47
- {name: 'orange'},
48
- {name: 'pear'}
49
- ];
50
-
48
+ var fruits = [{ name: 'apple' }, { name: 'orange' }, { name: 'pear' }];
51
49
 
52
- var veggies = [
53
- {name: 'asparagus'},
54
- {name: 'carrot'},
55
- {name: 'spinach'}
56
- ];
50
+ var veggies = [{ name: 'asparagus' }, { name: 'carrot' }, { name: 'spinach' }];
57
51
 
58
- app.get('/', function(req, res) {
52
+ app.get('/', function (req, res) {
59
53
  res.render('index', {
60
- title: 'express-hbs example'
54
+ title: 'express-hbs example',
61
55
  });
62
56
  });
63
57
 
64
- app.get('/fruits', function(req, res) {
58
+ app.get('/fruits', function (req, res) {
65
59
  res.render('fruits/index-layoutsDir', {
66
60
  title: 'My favorite fruits',
67
- fruits: fruits
61
+ fruits: fruits,
68
62
  });
69
63
  });
70
64
 
71
- app.get('/fruits/:name', function(req, res) {
72
- res.render('fruits/details-layoutsDir', {
73
- fruit: req.params.name
74
- });
65
+ app.get('/fruits/:name', function (req, res) {
66
+ res.render('fruits/details-layoutsDir', {
67
+ fruit: req.params.name,
68
+ });
75
69
  });
76
70
 
77
- app.get('/veggies', function(req, res) {
71
+ app.get('/veggies', function (req, res) {
78
72
  res.render('veggies', {
79
73
  title: 'My favorite veggies',
80
74
  veggies: veggies,
81
- layout: 'veggie'
75
+ layout: 'veggie',
82
76
  });
83
77
  });
84
78
 
85
- app.get('/veggies/explicit-dir', function(req, res) {
79
+ app.get('/veggies/explicit-dir', function (req, res) {
86
80
  res.render('veggies', {
87
81
  title: 'My favorite veggies',
88
82
  veggies: veggies,
89
- layout: relative('views/layout/veggie')
83
+ layout: relative('views/layout/veggie'),
90
84
  });
91
85
  });
92
86
 
93
- app.get('/veggies/:name', function(req, res) {
94
- res.render('veggies/details', {
95
- veggie: req.params.name,
96
- layout: 'veggie-details'
97
- });
87
+ app.get('/veggies/:name', function (req, res) {
88
+ res.render('veggies/details', {
89
+ veggie: req.params.name,
90
+ layout: 'veggie-details',
91
+ });
98
92
  });
99
93
 
100
94
  if (require.main === module) {
package/example/app.js CHANGED
@@ -1,5 +1,5 @@
1
1
  'use strict';
2
- // npm install express express-hbs
2
+ // pnpm add express express-hbs
3
3
 
4
4
  function create(hbs, env) {
5
5
  if (env) process.env.NODE_ENV = env;
@@ -18,81 +18,75 @@ function create(hbs, env) {
18
18
  app.use(express.static(relative('public')));
19
19
 
20
20
  // Hook in express-hbs and tell it where known directories reside
21
- app.engine('hbs', hbs.express4({
22
- partialsDir: [relative('views/partials'), relative('views/partials-other')],
23
- defaultLayout: relative('views/layout/default.hbs')
24
- }));
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
+ );
25
29
  app.set('view engine', 'hbs');
26
30
  app.set('views', viewsDir);
27
31
 
28
32
  // Register sync helper
29
- hbs.registerHelper('link', function(text, options) {
33
+ hbs.registerHelper('link', function (text, options) {
30
34
  var attrs = [];
31
35
  for (var prop in options.hash) {
32
36
  attrs.push(prop + '="' + options.hash[prop] + '"');
33
37
  }
34
- return new hbs.SafeString(
35
- '<a ' + attrs.join(' ') + '>' + text + '</a>'
36
- );
38
+ return new hbs.SafeString('<a ' + attrs.join(' ') + '>' + text + '</a>');
37
39
  });
38
40
 
39
41
  // Register Async helpers
40
- hbs.registerAsyncHelper('readFile', function(filename, cb) {
41
- fs.readFile(fp.join(viewsDir, filename), 'utf8', function(err, content) {
42
+ hbs.registerAsyncHelper('readFile', function (filename, cb) {
43
+ fs.readFile(fp.join(viewsDir, filename), 'utf8', function (err, content) {
42
44
  if (err) console.error(err);
43
45
  cb(new hbs.SafeString(content));
44
46
  });
45
47
  });
46
48
 
47
- var fruits = [
48
- {name: 'apple'},
49
- {name: 'orange'},
50
- {name: 'pear'}
51
- ];
49
+ var fruits = [{ name: 'apple' }, { name: 'orange' }, { name: 'pear' }];
52
50
 
53
- var veggies = [
54
- {name: 'asparagus'},
55
- {name: 'carrot'},
56
- {name: 'spinach'}
57
- ];
51
+ var veggies = [{ name: 'asparagus' }, { name: 'carrot' }, { name: 'spinach' }];
58
52
 
59
- app.get('/', function(req, res) {
53
+ app.get('/', function (req, res) {
60
54
  res.render('index', {
61
- title: 'express-hbs example'
55
+ title: 'express-hbs example',
62
56
  });
63
57
  });
64
58
 
65
- app.get('/replace', function(req, res) {
59
+ app.get('/replace', function (req, res) {
66
60
  res.render('replace', {
67
- title: 'express-hbs example'
61
+ title: 'express-hbs example',
68
62
  });
69
63
  });
70
64
 
71
- app.get('/fruits', function(req, res) {
65
+ app.get('/fruits', function (req, res) {
72
66
  res.render('fruits/index', {
73
67
  title: 'My favorite fruits',
74
- fruits: fruits
68
+ fruits: fruits,
75
69
  });
76
70
  });
77
71
 
78
- app.get('/fruits/:name', function(req, res) {
72
+ app.get('/fruits/:name', function (req, res) {
79
73
  res.render('fruits/details', {
80
- fruit: req.params.name
74
+ fruit: req.params.name,
81
75
  });
82
76
  });
83
77
 
84
- app.get('/veggies', function(req, res) {
78
+ app.get('/veggies', function (req, res) {
85
79
  res.render('veggies', {
86
80
  title: 'My favorite veggies',
87
81
  veggies: veggies,
88
- layout: 'layout/veggie'
82
+ layout: 'layout/veggie',
89
83
  });
90
84
  });
91
85
 
92
- app.get('/veggies/:name', function(req, res) {
86
+ app.get('/veggies/:name', function (req, res) {
93
87
  res.render('veggies/details', {
94
88
  veggie: req.params.name,
95
- layout: 'layout/veggie-details'
89
+ layout: 'layout/veggie-details',
96
90
  });
97
91
  });
98
92