Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@fastify/view

Package Overview
Dependencies
Maintainers
18
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fastify/view - npm Package Compare versions

Comparing version 9.0.0 to 9.1.0

benchmark/fastify-viewAsync.js

2

benchmark/fastify-ejs-minify.js

@@ -7,3 +7,3 @@ 'use strict'

options: {
useHtmlMinifier: require('html-minifier'),
useHtmlMinifier: require('html-minifier-terser'),
htmlMinifierOptions: {

@@ -10,0 +10,0 @@ removeComments: true,

@@ -6,6 +6,22 @@ 'use strict'

const { basename, dirname, extname, join, resolve } = require('node:path')
const HLRU = require('hashlru')
const { LruMap } = require('toad-cache')
const supportedEngines = ['ejs', 'nunjucks', 'pug', 'handlebars', 'mustache', 'art-template', 'twig', 'liquid', 'dot', 'eta']
const viewCache = Symbol('@fastify/view/cache')
const fastifyViewCache = fp(
async function cachePlugin (fastify, opts) {
const lru = new LruMap(opts.maxCache || 100)
fastify.decorate(viewCache, lru)
},
{
fastify: '4.x',
name: '@fastify/view/cache'
}
)
async function fastifyView (fastify, opts) {
if (fastify[viewCache] === undefined) {
await fastify.register(fastifyViewCache, opts)
}
if (!opts.engine) {

@@ -20,6 +36,6 @@ throw new Error('Missing engine')

const propertyName = opts.propertyName || 'view'
const engine = opts.engine[type]
const asyncPropertyName = opts.asyncPropertyName || `${propertyName}Async`
const engine = await opts.engine[type]
const globalOptions = opts.options || {}
const templatesDir = resolveTemplateDir(opts)
const lru = HLRU(opts.maxCache || 100)
const includeViewExtension = opts.includeViewExtension || false

@@ -96,2 +112,20 @@ const viewExt = opts.viewExt || ''

async function asyncRender (page) {
if (!page) {
throw new Error('Missing page')
}
let result = await renderer.apply(this, arguments)
if (minify && !isPathExcludedMinification(this)) {
result = await minify(result, globalOptions.htmlMinifierOptions)
}
if (this.getHeader && !this.getHeader('Content-Type')) {
this.header('Content-Type', 'text/html; charset=' + charset)
}
return result
}
function viewDecorator (page) {

@@ -105,8 +139,4 @@ const args = Array.from(arguments)

let promise = !page ? Promise.reject(new Error('Missing page')) : renderer.apply(this, args)
const promise = asyncRender.apply({}, args)
if (minify) {
promise = promise.then((result) => minify(result, globalOptions.htmlMinifierOptions))
}
if (typeof done === 'function') {

@@ -121,3 +151,3 @@ promise.then(done.bind(null, null), done)

viewDecorator.clearCache = function () {
lru.clear()
fastify[viewCache].clear()
}

@@ -127,23 +157,15 @@

fastify.decorateReply(propertyName, async function (page) {
if (!page) {
this.send(new Error('Missing page'))
}
fastify.decorateReply(propertyName, async function (page, data, opts) {
try {
const result = await renderer.apply(this, arguments)
if (!this.getHeader('Content-Type')) {
this.header('Content-Type', 'text/html; charset=' + charset)
}
if (minify && !isPathExcludedMinification(this)) {
this.send(minify(result, globalOptions.htmlMinifierOptions))
} else {
this.send(result)
}
const html = await asyncRender.call(this, page, data, opts)
this.send(html)
} catch (err) {
this.send(err)
}
return this
})
fastify.decorateReply(asyncPropertyName, asyncRender)
if (!fastify.hasReplyDecorator('locals')) {

@@ -160,3 +182,3 @@ fastify.decorateReply('locals', null)

const pageLRU = `getPage-${page}-${extension}`
let result = lru.get(pageLRU)
let result = fastify[viewCache].get(pageLRU)

@@ -170,3 +192,3 @@ if (typeof result === 'string') {

lru.set(pageLRU, result)
fastify[viewCache].set(pageLRU, result)

@@ -213,3 +235,3 @@ return result

}
lru.set(file, data)
fastify[viewCache].set(file, data)
return data

@@ -229,3 +251,3 @@ }

}
const data = lru.get(file)
const data = fastify[viewCache].get(file)
if (data && prod) {

@@ -244,3 +266,3 @@ return data

const cacheKey = getPartialsCacheKey(page, partials, requestedPath)
const partialsObj = lru.get(cacheKey)
const partialsObj = fastify[viewCache].get(cacheKey)
if (partialsObj && prod) {

@@ -257,3 +279,3 @@ return partialsObj

}))
lru.set(cacheKey, partialsHtml)
fastify[viewCache].set(cacheKey, partialsHtml)
return partialsHtml

@@ -291,3 +313,3 @@ }

lru.set(page, compiledPage)
fastify[viewCache].set(page, compiledPage)
return compiledPage

@@ -342,3 +364,3 @@ }

}
const toHtml = lru.get(page)
const toHtml = fastify[viewCache].get(page)

@@ -374,3 +396,3 @@ if (toHtml && prod) {

}
const toHtml = lru.get(page)
const toHtml = fastify[viewCache].get(page)

@@ -580,4 +602,2 @@ if (toHtml && prod) {

lru.define = lru.set
engine.configure({

@@ -651,9 +671,19 @@ views: templatesDir,

function hasAccessToLayoutFile (fileName, ext) {
const layoutKey = `layout-${fileName}-${ext}`
let result = fastify[viewCache].get(layoutKey)
if (typeof result === 'boolean') {
return result
}
try {
accessSync(join(templatesDir, getPage(fileName, ext)))
return true
result = true
} catch (e) {
return false
result = false
}
fastify[viewCache].set(layoutKey, result)
return result
}

@@ -668,1 +698,2 @@ }

module.exports.fastifyView = fastifyView
module.exports.fastifyViewCache = viewCache
{
"name": "@fastify/view",
"version": "9.0.0",
"version": "9.1.0",
"description": "Template plugin for Fastify",

@@ -44,3 +44,3 @@ "main": "index.js",

"fastify-plugin": "^4.0.0",
"hashlru": "^2.3.0"
"toad-cache": "^3.7.0"
},

@@ -59,3 +59,3 @@ "devDependencies": {

"handlebars": "^4.7.6",
"html-minifier": "^4.0.0",
"html-minifier-terser": "^7.2.0",
"liquidjs": "^10.0.0",

@@ -65,3 +65,2 @@ "mustache": "^4.0.1",

"pino": "^8.0.0",
"proxyquire": "^2.1.3",
"pug": "^3.0.0",

@@ -72,3 +71,3 @@ "simple-get": "^4.0.0",

"tap": "^16.0.0",
"tsd": "^0.30.0",
"tsd": "^0.31.0",
"twig": "^1.13.3"

@@ -75,0 +74,0 @@ },

@@ -9,3 +9,3 @@ # @fastify/view

`@fastify/view` decorates the reply interface with the `view` method for managing view engines, which can be used to render templates responses.
`@fastify/view` decorates the reply interface with the `view` and `viewAsync` methods for managing view engines, which can be used to render templates responses.

@@ -31,2 +31,6 @@ Currently supports the following templates engines:

## Recent Changes
_Note: `reply.viewAsync` added as a replacement for `reply.view` and `fastify.view`. See [Migrating from view to viewAsync](#migrating-from-view-to-viewAsync)._
_Note: [`ejs-mate`](https://github.com/JacksonTian/ejs-mate) support [has been dropped](https://github.com/fastify/point-of-view/pull/157)._

@@ -59,52 +63,62 @@

This example will render the template and provide a variable `text` to be used inside the template:
This example will render the template using the EJS engine and provide a variable `name` to be used inside the template:
```html
<!-- index.ejs --->
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<p>Hello, <%= name %>!</p>
</body>
</html>
```
```js
const fastify = require("fastify")();
// index.js:
const fastify = require("fastify")()
const fastifyView = require("@fastify/view")
fastify.register(require("@fastify/view"), {
fastify.register(fastifyView, {
engine: {
ejs: require("ejs"),
},
});
ejs: require("ejs")
}
})
// synchronous handler:
fastify.get("/", (req, reply) => {
reply.view("/templates/index.ejs", { text: "text" });
});
reply.view("index.ejs", { name: "User" });
})
// asynchronous handler:
fastify.get("/", async (req, reply) => {
return reply.viewAsync("index.ejs", { name: "User" });
})
fastify.listen({ port: 3000 }, (err) => {
if (err) throw err;
console.log(`server listening on ${fastify.server.address().port}`);
});
})
```
If your handler function is asynchronous, make sure to return the result - otherwise this will result in an `FST_ERR_PROMISE_NOT_FULFILLED` error:
```js
// This is an async function
fastify.get("/", async (req, reply) => {
// We are awaiting a function result
const t = await something();
// Note the return statement
return reply.view("/templates/index.ejs", { text: "text" });
});
```
## Configuration
`fastify.register(<engine>, <options>)` accepts an options object.
### Options
- `engine`: The template engine object - pass in the return value of `require('<engine>')`. This option is mandatory.
- `layout`: @fastify/view supports layouts for **EJS**, **Handlebars**, **Eta** and **doT**. This option lets you specify a global layout file to be used when rendering your templates. Settings like `root` or `viewExt` apply as for any other template file. Example: `./templates/layouts/main.hbs`
- `propertyName`: The property that should be used to decorate `reply` and `fastify` - E.g. `reply.view()` and `fastify.view()` where `"view"` is the property name. Default: `"view"`.
- `root`: The root path of your templates folder. The template name or path passed to the render function will be resolved relative to this path. Default: `"./"`.
- `includeViewExtension`: Setting this to `true` will automatically append the default extension for the used template engine **if omitted from the template name** . So instead of `template.hbs`, just `template` can be used. Default: `false`.
- `viewExt`: Let's you override the default extension for a given template engine. This has precedence over `includeViewExtension` and will lead to the same behavior, just with a custom extension. Default `""`. Example: `"handlebars"`.
- `defaultContext`: The template variables defined here will be available to all views. Variables provided on render have precedence and will **override** this if they have the same name. Default: `{}`. Example: `{ siteName: "MyAwesomeSite" }`.
- `maxCache`: In `production` mode, maximum number of templates file and functions caches. Default: `100`. Example: `{ maxCache: 100 }`.
| Option | Description | Default |
| ---------------------- | ----------- | ------- |
| `engine` | **Required**. The template engine object - pass in the return value of `require('<engine>')` | |
| `production` | Enables caching of template files and render functions | `NODE_ENV === "production"` |
| `maxCache` | In `production` mode, maximum number of cached template files and render functions | `100` |
| `defaultContext` | Template variables available to all views. Variables provided on render have precedence and will **override** this if they have the same name. <br><br>Example: `{ siteName: "MyAwesomeSite" }` | `{}` |
| `propertyName` | The property that should be used to decorate `reply` and `fastify` <br><br>E.g. `reply.view()` and `fastify.view()` where `"view"` is the property name | `"view"` |
| `asyncPropertyName` | The property that should be used to decorate `reply` for async handler <br><br>Defaults to `${propertyName}Async` if `propertyName` is defined | `"viewAsync"` |
| `root` | The root path of your templates folder. The template name or path passed to the render function will be resolved relative to this path | `"./"` |
| `charset` | Default charset used when setting `Content-Type` header | `"utf-8"` |
| `includeViewExtension` | Automatically append the default extension for the used template engine **if omitted from the template name** . So instead of `template.hbs`, just `template` can be used | `false` |
| `viewExt` | Override the default extension for a given template engine. This has precedence over `includeViewExtension` and will lead to the same behavior, just with a custom extension. <br><br>Example: `"handlebars"` | `""` |
| `layout` | See [Layouts](#layouts) <br><br>This option lets you specify a global layout file to be used when rendering your templates. Settings like `root` or `viewExt` apply as for any other template file. <br><br>Example: `./templates/layouts/main.hbs` | |
| `options` | See [Engine-specific settings](#engine-specific-settings) | `{}` |
Example:
### Example

@@ -127,7 +141,76 @@ ```js

## Rendering the template into a variable
## Layouts
The `fastify` object is decorated the same way as `reply` and allows you to just render a view into a variable instead of sending the result back to the browser:
@fastify/view supports layouts for **EJS**, **Handlebars**, **Eta** and **doT**. When a layout is specified, the request template is first rendered, then the layout template is rendered with the request-rendered html set on `body`.
### Example
```html
<!-- layout.ejs: -->
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<!--
Ensure body is not escaped:
EJS: <%- body %>
Handlebars: {{{ body }}}
ETA/doT: <%~ it.body %>
-->
<%- body %>
<br/>
</body>
</html>
```
```html
<!-- template.ejs: -->
<p><%= text %></p>
```
```js
// index.js:
fastify.register(fastifyView, {
engine: { ejs },
layout: "layout.ejs"
})
fastify.get('/', (req, reply) => {
const data = { text: "Hello!"}
reply.view('template.ejs', data)
})
```
### Providing a layout on render
**Please note:** Global layouts and providing layouts on render are mutually exclusive. They can not be mixed.
```js
fastify.get('/', (req, reply) => {
const data = { text: "Hello!"}
reply.view('template.ejs', data, { layout: 'layout.ejs' })
})
```
## Setting request-global variables
Sometimes, several templates should have access to the same request-specific variables. E.g. when setting the current username.
If you want to provide data, which will be depended on by a request and available in all views, you have to add property `locals` to `reply` object, like in the example below:
```js
fastify.addHook("preHandler", function (request, reply, done) {
reply.locals = {
text: getTextFromRequest(request), // it will be available in all views
};
done();
});
```
Properties from `reply.locals` will override those from `defaultContext`, but not from `data` parameter provided to `reply.view(template, data)` function.
## Rendering the template into a variable
The `fastify` object is decorated the same way as `reply` and allows you to just render a view into a variable (without request-global variables) instead of sending the result back to the browser:
```js
// Promise based, using async/await

@@ -143,2 +226,5 @@ const html = await fastify.view("/templates/index.ejs", { text: "text" });

If called within a request hook and you need request-global variables, see [Migrating from view to viewAsync](#migrating-from-view-to-viewAsync).
## Registering multiple engines

@@ -172,41 +258,11 @@

## Providing a layout on render
@fastify/view supports layouts for **EJS**, **Handlebars**, **Eta** and **doT**.
These engines also support providing a layout on render.
**Please note:** Global layouts and providing layouts on render are mutually exclusive. They can not be mixed.
```js
fastify.get('/', (req, reply) => {
reply.view('index-for-layout.ejs', data, { layout: 'layout.html' })
})
```
## Setting request-global variables
Sometimes, several templates should have access to the same request-specific variables. E.g. when setting the current username.
If you want to provide data, which will be depended on by a request and available in all views, you have to add property `locals` to `reply` object, like in the example below:
```js
fastify.addHook("preHandler", function (request, reply, done) {
reply.locals = {
text: getTextFromRequest(request), // it will be available in all views
};
done();
});
```
Properties from `reply.locals` will override those from `defaultContext`, but not from `data` parameter provided to `reply.view(template, data)` function.
## Minifying HTML on render
To utilize [`html-minifier`](https://www.npmjs.com/package/html-minifier) in the rendering process, you can add the option `useHtmlMinifier` with a reference to `html-minifier`,
and the optional `htmlMinifierOptions` option is used to specify the `html-minifier` options:
To utilize [`html-minifier-terser`](https://www.npmjs.com/package/html-minifier-terser) in the rendering process, you can add the option `useHtmlMinifier` with a reference to `html-minifier-terser`,
and the optional `htmlMinifierOptions` option is used to specify the `html-minifier-terser` options:
```js
// get a reference to html-minifier
const minifier = require('html-minifier')
// optionally defined the html-minifier options
// get a reference to html-minifier-terser
const minifier = require('html-minifier-terser')
// optionally defined the html-minifier-terser options
const minifierOpts = {

@@ -229,5 +285,5 @@ removeComments: true,

```js
// get a reference to html-minifier
const minifier = require('html-minifier')
// in options configure the use of html-minifier and set paths to exclude from minification
// get a reference to html-minifier-terser
const minifier = require('html-minifier-terser')
// in options configure the use of html-minifier-terser and set paths to exclude from minification
const options = {

@@ -739,3 +795,2 @@ useHtmlMinifier: minifier,

```
<!---

@@ -809,6 +864,80 @@ // This seems a bit random given that there was no mention of typescript before.

### Migrating from `view` to `viewAsync`
The behavior of `reply.view` is to immediately send the HTML response as soon as rendering is completed, or immediately send a 500 response with error if encountered, short-circuiting fastify's error handling hooks, whereas `reply.viewAsync` returns a promise that either resolves to the rendered HTML, or rejects on any errors. `fastify.view` has no mechanism for providing request-global variables, if needed. `reply.viewAsync` can be used in both sync and async handlers.
#### Sync handler
Previously:
```js
fastify.get('/', (req, reply) => {
reply.view('index.ejs', { text: 'text' })
})
```
Now:
```js
fastify.get('/', (req, reply) => {
return reply.viewAsync('index.ejs', { text: 'text' })
})
```
#### Async handler
Previously:
```js
// This is an async function
fastify.get("/", async (req, reply) => {
const data = await something();
reply.view("/templates/index.ejs", { data });
return
})
```
Now:
```js
// This is an async function
fastify.get("/", async (req, reply) => {
const data = await something();
return reply.viewAsync("/templates/index.ejs", { data });
})
```
#### fastify.view (when called inside a route hook)
Previously:
```js
// Promise based, using async/await
fastify.get("/", async (req, reply) => {
const html = await fastify.view("/templates/index.ejs", { text: "text" });
return html
})
```
```js
// Callback based
fastify.get("/", (req, reply) => {
fastify.view("/templates/index.ejs", { text: "text" }, (err, html) => {
if(err) {
reply.send(err)
}
else {
reply.type("application/html").send(html)
}
});
})
```
Now:
```js
// Promise based, using async/await
fastify.get("/", (req, reply) => {
const html = await fastify.viewAsync("/templates/index.ejs", { text: "text" });
return html
})
```
```js
fastify.get("/", (req, reply) => {
fastify.viewAsync("/templates/index.ejs", { text: "text" })
.then((html) => reply.type("application/html").send(html))
.catch((err) => reply.send(err))
});
})
```
## Note
By default views are served with the mime type 'text/html; charset=utf-8',
but you can specify a different value using the type function of reply, or by specifying the desired charset in the property 'charset' in the options object given to the plugin.
By default views are served with the mime type `text/html`, with the charset specified in options. You can specify a different `Content-Type` header using `reply.type`.

@@ -815,0 +944,0 @@ ## Acknowledgements

@@ -5,3 +5,3 @@ 'use strict'

const Fastify = require('fastify')
const minifier = require('html-minifier')
const minifier = require('html-minifier-terser')
const fs = require('node:fs')

@@ -30,3 +30,3 @@ const dot = require('dot')

test('reply.view with dot engine and html-minifier', t => {
test('reply.view with dot engine and html-minifier-terser', t => {
t.plan(6)

@@ -57,3 +57,3 @@ const fastify = Fastify()

url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
}, async (err, response, body) => {
t.error(err)

@@ -63,3 +63,3 @@ t.equal(response.statusCode, 200)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(dot.process(compileOptions).testdot(data), options), body.toString())
t.equal(await minifier.minify(dot.process(compileOptions).testdot(data), options), body.toString())
fastify.close()

@@ -69,3 +69,3 @@ })

})
test('reply.view with dot engine and paths excluded from html-minifier', t => {
test('reply.view with dot engine and paths excluded from html-minifier-terser', t => {
t.plan(6)

@@ -116,3 +116,3 @@ const fastify = Fastify()

test('reply.view with eta engine and html-minifier', t => {
test('reply.view with eta engine and html-minifier-terser', t => {
t.plan(6)

@@ -141,3 +141,3 @@ const fastify = Fastify()

url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
}, async (err, response, body) => {
t.error(err)

@@ -147,3 +147,3 @@ t.equal(response.statusCode, 200)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(eta.renderString(fs.readFileSync('./templates/index.eta', 'utf8'), data), options), body.toString())
t.equal(await minifier.minify(eta.renderString(fs.readFileSync('./templates/index.eta', 'utf8'), data), options), body.toString())
fastify.close()

@@ -154,3 +154,3 @@ })

test('reply.view with eta engine and async and html-minifier', t => {
test('reply.view with eta engine and async and html-minifier-terser', t => {
t.plan(6)

@@ -180,3 +180,3 @@ const fastify = Fastify()

url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
}, async (err, response, body) => {
t.error(err)

@@ -186,3 +186,3 @@ t.equal(response.statusCode, 200)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(eta.renderString(fs.readFileSync('./templates/index.eta', 'utf8'), data), options), body.toString())
t.equal(await minifier.minify(eta.renderString(fs.readFileSync('./templates/index.eta', 'utf8'), data), options), body.toString())
fastify.close()

@@ -192,3 +192,3 @@ })

})
test('reply.view with eta engine and paths excluded from html-minifier', t => {
test('reply.view with eta engine and paths excluded from html-minifier-terser', t => {
t.plan(6)

@@ -234,3 +234,3 @@ const fastify = Fastify()

test('fastify.view with handlebars engine and html-minifier', t => {
test('fastify.view with handlebars engine and html-minifier-terser', t => {
t.plan(2)

@@ -253,4 +253,4 @@ const fastify = Fastify()

fastify.view('./templates/index.html', data).then(compiled => {
t.equal(minifier.minify(handlebars.compile(fs.readFileSync('./templates/index.html', 'utf8'))(data), options), compiled)
fastify.view('./templates/index.html', data).then(async compiled => {
t.equal(await minifier.minify(handlebars.compile(fs.readFileSync('./templates/index.html', 'utf8'))(data), options), compiled)
fastify.close()

@@ -266,3 +266,3 @@ })

test('reply.view with liquid engine and html-minifier', t => {
test('reply.view with liquid engine and html-minifier-terser', t => {
t.plan(7)

@@ -298,5 +298,5 @@ const fastify = Fastify()

engine.renderFile('./templates/index.liquid', data)
.then((html) => {
.then(async (html) => {
t.error(err)
t.equal(minifier.minify(html, options), body.toString())
t.equal(await minifier.minify(html, options), body.toString())
})

@@ -307,3 +307,3 @@ fastify.close()

})
test('reply.view with liquid engine and paths excluded from html-minifier', t => {
test('reply.view with liquid engine and paths excluded from html-minifier-terser', t => {
t.plan(7)

@@ -354,3 +354,3 @@ const fastify = Fastify()

test('reply.view with nunjucks engine, full path templates folder, and html-minifier', t => {
test('reply.view with nunjucks engine, full path templates folder, and html-minifier-terser', t => {
t.plan(6)

@@ -380,3 +380,3 @@ const fastify = Fastify()

url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
}, async (err, response, body) => {
t.error(err)

@@ -387,3 +387,3 @@ t.equal(response.statusCode, 200)

// Global Nunjucks templates dir changed here.
t.equal(minifier.minify(nunjucks.render('./index.njk', data), options), body.toString())
t.equal(await minifier.minify(nunjucks.render('./index.njk', data), options), body.toString())
fastify.close()

@@ -393,3 +393,3 @@ })

})
test('reply.view with nunjucks engine, full path templates folder, and paths excluded from html-minifier', t => {
test('reply.view with nunjucks engine, full path templates folder, and paths excluded from html-minifier-terser', t => {
t.plan(6)

@@ -437,3 +437,3 @@ const fastify = Fastify()

test('reply.view with pug engine and html-minifier', t => {
test('reply.view with pug engine and html-minifier-terser', t => {
t.plan(6)

@@ -462,3 +462,3 @@ const fastify = Fastify()

url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
}, async (err, response, body) => {
t.error(err)

@@ -468,3 +468,3 @@ t.equal(response.statusCode, 200)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(pug.render(fs.readFileSync('./templates/index.pug', 'utf8'), data), options), body.toString())
t.equal(await minifier.minify(pug.render(fs.readFileSync('./templates/index.pug', 'utf8'), data), options), body.toString())
fastify.close()

@@ -474,3 +474,3 @@ })

})
test('reply.view with pug engine and paths excluded from html-minifier', t => {
test('reply.view with pug engine and paths excluded from html-minifier-terser', t => {
t.plan(6)

@@ -516,3 +516,3 @@ const fastify = Fastify()

test('reply.view with twig engine and html-minifier', t => {
test('reply.view with twig engine and html-minifier-terser', t => {
t.plan(7)

@@ -546,5 +546,5 @@ const fastify = Fastify()

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
Twig.renderFile('./templates/index.twig', data, (err, html) => {
Twig.renderFile('./templates/index.twig', data, async (err, html) => {
t.error(err)
t.equal(minifier.minify(html, options), body.toString())
t.equal(await minifier.minify(html, options), body.toString())
})

@@ -555,3 +555,3 @@ fastify.close()

})
test('reply.view with twig engine and paths excluded from html-minifier', t => {
test('reply.view with twig engine and paths excluded from html-minifier-terser', t => {
t.plan(7)

@@ -558,0 +558,0 @@ const fastify = Fastify()

@@ -8,2 +8,3 @@ 'use strict'

const fs = require('node:fs')
const minifier = require('html-minifier-terser')

@@ -81,5 +82,4 @@ test('reply.view with ejs engine and async: true (global option)', t => {

const minifier = require('html-minifier')
const minifierOpts = { collapseWhitespace: true }
test('reply.view with ejs engine, async: true (global option), and html-minifier', t => {
test('reply.view with ejs engine, async: true (global option), and html-minifier-terser', t => {
t.plan(6)

@@ -114,3 +114,3 @@ const fastify = Fastify()

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true }), minifierOpts), body.toString())
t.equal(await minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true }), minifierOpts), body.toString())
fastify.close()

@@ -150,3 +150,3 @@ })

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true })), body.toString())
t.equal(await minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true })), body.toString())
fastify.close()

@@ -191,3 +191,3 @@ })

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true }), minifierOpts), body.toString())
t.equal(await minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true }), minifierOpts), body.toString())
if (i === numTests - 1) fastify.close()

@@ -270,3 +270,3 @@ resolve()

test('reply.view with ejs engine, async: true (local override), and html-minifier', t => {
test('reply.view with ejs engine, async: true (local override), and html-minifier-terser', t => {
t.plan(6)

@@ -301,3 +301,3 @@ const fastify = Fastify()

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), { }, { async: true }), minifierOpts), body.toString())
t.equal(await minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), { }, { async: true }), minifierOpts), body.toString())
fastify.close()

@@ -308,3 +308,3 @@ })

test('reply.view with ejs engine, async: false (local override), and html-minifier', t => {
test('reply.view with ejs engine, async: false (local override), and html-minifier-terser', t => {
t.plan(6)

@@ -339,3 +339,3 @@ const fastify = Fastify()

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(await ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), { text: 'text' }, { async: false }), minifierOpts), body.toString())
t.equal(await minifier.minify(await ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), { text: 'text' }, { async: false }), minifierOpts), body.toString())
fastify.close()

@@ -346,3 +346,3 @@ })

test('reply.view with ejs engine, async: true (local override), and html-minifier in production mode', t => {
test('reply.view with ejs engine, async: true (local override), and html-minifier-terser in production mode', t => {
const numTests = 3

@@ -381,3 +381,3 @@ t.plan(numTests * 5 + 1)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true }), minifierOpts), body.toString())
t.equal(await minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true }), minifierOpts), body.toString())
if (i === numTests - 1) fastify.close()

@@ -391,3 +391,3 @@ resolve()

test('reply.view with ejs engine, async: false (local override), and html-minifier in production mode', t => {
test('reply.view with ejs engine, async: false (local override), and html-minifier-terser in production mode', t => {
const numTests = 2

@@ -426,3 +426,3 @@ t.plan(numTests * 5 + 1)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(await ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), { text: 'text' }, { async: false }), minifierOpts), body.toString())
t.equal(await minifier.minify(await ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), { text: 'text' }, { async: false }), minifierOpts), body.toString())
if (i === numTests - 1) fastify.close()

@@ -522,1 +522,69 @@ resolve()

})
test('reply.viewAsync with ejs engine and async: true (global option)', t => {
t.plan(6)
const fastify = Fastify()
const ejs = require('ejs')
fastify.register(require('../index'), {
engine: { ejs },
options: { async: true },
templates: 'templates'
})
fastify.get('/', async (req, reply) => {
return reply.viewAsync('ejs-async.ejs')
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, async (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true }), body.toString())
fastify.close()
})
})
})
test('reply.viewAsync with ejs engine, async: true (global option), and html-minifier-terser', t => {
t.plan(6)
const fastify = Fastify()
const ejs = require('ejs')
fastify.register(require('../index'), {
engine: { ejs },
options: {
async: true,
useHtmlMinifier: minifier,
htmlMinifierOptions: minifierOpts
},
templates: 'templates'
})
fastify.get('/', (req, reply) => {
return reply.viewAsync('ejs-async.ejs')
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, async (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(await minifier.minify(await ejs.render(fs.readFileSync('./templates/ejs-async.ejs', 'utf8'), {}, { async: true }), minifierOpts), body.toString())
fastify.close()
})
})
})

@@ -9,3 +9,3 @@ 'use strict'

const path = require('node:path')
const minifier = require('html-minifier')
const minifier = require('html-minifier-terser')
const minifierOpts = {

@@ -549,3 +549,3 @@ removeComments: true,

test('reply.view with ejs engine and html-minifier', t => {
test('reply.view with ejs engine and html-minifier-terser', t => {
t.plan(6)

@@ -576,3 +576,3 @@ const fastify = Fastify()

url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
}, async (err, response, body) => {
t.error(err)

@@ -582,3 +582,3 @@ t.equal(response.statusCode, 200)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), minifierOpts), body.toString())
t.equal(await minifier.minify(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), minifierOpts), body.toString())
fastify.close()

@@ -588,3 +588,3 @@ })

})
test('reply.view with ejs engine and paths excluded from html-minifier', t => {
test('reply.view with ejs engine and paths excluded from html-minifier-terser', t => {
t.plan(6)

@@ -626,3 +626,3 @@ const fastify = Fastify()

})
test('reply.view with ejs engine and html-minifier in production mode', t => {
test('reply.view with ejs engine and html-minifier-terser in production mode', t => {
const numTests = 5

@@ -660,3 +660,3 @@ t.plan(numTests * 5 + 1)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), minifierOpts), body.toString())
t.equal(await minifier.minify(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), minifierOpts), body.toString())
if (i === numTests - 1) fastify.close()

@@ -1261,1 +1261,268 @@ resolve()

})
test('reply.viewAsync with ejs engine - sync handler', t => {
t.plan(6)
const fastify = Fastify()
const ejs = require('ejs')
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
}
})
fastify.get('/', async (req, reply) => {
return reply.viewAsync('templates/index.ejs', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), body.toString())
fastify.close()
})
})
})
test('reply.viewAsync with ejs engine - async handler', t => {
t.plan(6)
const fastify = Fastify()
const ejs = require('ejs')
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
}
})
fastify.get('/', (req, reply) => {
return reply.viewAsync('templates/index.ejs', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), body.toString())
fastify.close()
})
})
})
test('reply.viewAsync should return 500 if layout is missing on render', t => {
t.plan(3)
const fastify = Fastify()
const ejs = require('ejs')
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
},
root: path.join(__dirname, '../templates')
})
fastify.get('/', (req, reply) => {
return reply.viewAsync('index-for-layout.ejs', data, { layout: 'non-existing-layout.html' })
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 500)
fastify.close()
})
})
})
test('reply.viewAsync should allow errors to be handled by custom error handler', t => {
t.plan(7)
const fastify = Fastify()
const ejs = require('ejs')
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
},
root: path.join(__dirname, '../templates')
})
fastify.get('/', (req, reply) => {
return reply.viewAsync('index-for-layout.ejs', data, { layout: 'non-existing-layout.html' })
})
fastify.setErrorHandler((err, request, reply) => {
t.ok(err instanceof Error)
t.same(reply.getHeader('Content-Type'), null)
return 'something went wrong'
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.headers['content-type'], 'text/plain; charset=utf-8')
t.equal(response.statusCode, 200)
t.equal('something went wrong', body.toString())
fastify.close()
})
})
})
test('reply.viewAsync with ejs engine and custom propertyName', t => {
t.plan(6)
const fastify = Fastify()
const ejs = require('ejs')
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
},
propertyName: 'render'
})
fastify.get('/', async (req, reply) => {
return reply.renderAsync('templates/index.ejs', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), body.toString())
fastify.close()
})
})
})
test('reply.viewAsync with ejs engine and custom asyncPropertyName', t => {
t.plan(6)
const fastify = Fastify()
const ejs = require('ejs')
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
},
asyncPropertyName: 'viewAsPromise'
})
fastify.get('/', async (req, reply) => {
return reply.viewAsPromise('templates/index.ejs', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), body.toString())
fastify.close()
})
})
})
test('reply.viewAsync with ejs engine and custom asyncPropertyName and custom propertyName', t => {
t.plan(11)
const fastify = Fastify()
const ejs = require('ejs')
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
},
asyncPropertyName: 'renderPromise',
propertyName: 'oldRenderSend'
})
fastify.get('/asyncPropertyName', async (req, reply) => {
return reply.renderPromise('templates/index.ejs', data)
})
fastify.get('/propertyName', (req, reply) => {
reply.oldRenderSend('templates/index.ejs', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/asyncPropertyName'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), body.toString())
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/propertyName'
}, (err2, response2, body2) => {
t.error(err2)
t.equal(response2.statusCode, 200)
t.equal(response2.headers['content-length'], '' + body.length)
t.equal(response2.headers['content-type'], 'text/html; charset=utf-8')
t.equal(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), body2.toString())
fastify.close()
})
})
})
})
test('reply.viewAsync with ejs engine and conflicting propertyName/asyncPropertyName', t => {
t.plan(1)
const fastify = Fastify()
const ejs = require('ejs')
fastify.register(require('../index'), {
engine: {
ejs
},
propertyName: 'render',
asyncPropertyName: 'render'
})
fastify.listen({ port: 0 }, err => {
t.ok(err instanceof Error)
})
})

@@ -9,3 +9,2 @@ 'use strict'

const { join } = require('node:path')
const proxyquire = require('proxyquire')

@@ -336,3 +335,3 @@ require('./helper').handleBarsHtmlMinifierTests(t, true)

test('fastify.view with handlebars engine with invalid layout option on render should throw', t => {
t.plan(3)
t.plan(5)

@@ -355,2 +354,7 @@ const fastify = Fastify()

})
// repeated for test coverage of layout access check lru
fastify.view('./templates/index-for-layout.hbs', data, { layout: './templates/invalid-layout.hbs' }, (err, compiled) => {
t.ok(err instanceof Error)
t.equal(err.message, 'unable to access template "./templates/invalid-layout.hbs"')
})
})

@@ -481,135 +485,2 @@ })

test('reply.view with ejs engine and includeViewExtension property as true', t => {
t.plan(6)
const fastify = Fastify()
const ejs = require('ejs')
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
},
includeViewExtension: true
})
fastify.get('/', (req, reply) => {
reply.view('./templates/index', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(ejs.render(fs.readFileSync('./templates/index.ejs', 'utf8'), data), body.toString())
fastify.close()
})
})
})
test('reply.view with ejs engine, template folder specified, include files (ejs and html) used in template, includeViewExtension property as true', t => {
t.plan(7)
const fastify = Fastify()
const ejs = require('ejs')
const resolve = require('node:path').resolve
const templatesFolder = 'templates'
const options = {
filename: resolve(templatesFolder), // needed for include files to be resolved in include directive ...
views: [__dirname] // must be put to make tests (with include files) working ...
}
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
ejs
},
includeViewExtension: true,
templates: templatesFolder,
options
})
fastify.get('/', (req, reply) => {
reply.type('text/html; charset=utf-8').view('index-linking-other-pages', data) // sample for specifying with type
// reply.view('index-with-includes', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(response.headers['content-length'], '' + body.length)
let content = null
ejs.renderFile(templatesFolder + '/index-linking-other-pages.ejs', data, options, function (err, str) {
content = str
t.error(err)
t.equal(content.length, body.length)
})
fastify.close()
})
})
})
test('reply.view with ejs engine, templates with folder specified, include files and attributes; home', t => {
t.plan(7)
const fastify = Fastify()
const ejs = require('ejs')
const resolve = require('node:path').resolve
const templatesFolder = 'templates'
const options = {
filename: resolve(templatesFolder),
views: [__dirname]
}
const data = { text: 'Hello from EJS Templates' }
fastify.register(require('../index'), {
engine: {
ejs
},
includeViewExtension: true,
templates: templatesFolder,
options
})
fastify.get('/', (req, reply) => {
reply.type('text/html; charset=utf-8').view('index', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(response.headers['content-length'], '' + body.length)
let content = null
ejs.renderFile(templatesFolder + '/index.ejs', data, options, function (err, str) {
content = str
t.error(err)
t.equal(content.length, body.length)
})
fastify.close()
})
})
})
test('reply.view with handlebars engine and includeViewExtension property as true', t => {

@@ -756,13 +627,10 @@ t.plan(6)

const handlebars = require('handlebars')
const POV = proxyquire('..', {
hashlru: function () {
return {
get: (key) => {
t.equal(key, 'handlebars|body:./templates/body.hbs|null-Partials')
},
set: (key, value) => {
t.equal(key, 'handlebars|body:./templates/body.hbs|null-Partials')
t.strictSame(value, { body: fs.readFileSync('./templates/body.hbs', 'utf8') })
}
}
const POV = require('..')
fastify.decorate(POV.fastifyViewCache, {
get: (key) => {
t.equal(key, 'handlebars|body:./templates/body.hbs|null-Partials')
},
set: (key, value) => {
t.equal(key, 'handlebars|body:./templates/body.hbs|null-Partials')
t.strictSame(value, { body: fs.readFileSync('./templates/body.hbs', 'utf8') })
}

@@ -1062,11 +930,8 @@ })

const handlebars = require('handlebars')
const POV = proxyquire('..', {
hashlru: function () {
return {
get: () => {
return () => { throw Error('Template Error') }
},
set: () => { }
}
}
const POV = require('..')
fastify.decorate(POV.fastifyViewCache, {
get: () => {
return () => { throw Error('Template Error') }
},
set: () => { }
})

@@ -1263,1 +1128,39 @@

})
test('reply.viewAsync for handlebars engine without defaultContext but with reply.locals and data-parameter, with async fastify hooks', t => {
t.plan(6)
const fastify = Fastify()
const handlebars = require('handlebars')
const localsData = { text: 'text from locals' }
const data = { text: 'text' }
fastify.register(require('../index'), {
engine: {
handlebars
}
})
fastify.addHook('preHandler', async function (request, reply) {
reply.locals = localsData
})
fastify.get('/', async (req, reply) => {
return reply.viewAsync('./templates/index.html', data)
})
fastify.listen({ port: 0 }, err => {
t.error(err)
sget({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-length'], '' + body.length)
t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(handlebars.compile(fs.readFileSync('./templates/index.html', 'utf8'))(data), body.toString())
fastify.close()
})
})
})

@@ -8,4 +8,3 @@ 'use strict'

const fs = require('node:fs')
const minifier = require('html-minifier')
const proxyquire = require('proxyquire')
const minifier = require('html-minifier-terser')
const minifierOpts = {

@@ -349,11 +348,9 @@ removeComments: true,

const data = { text: 'text' }
const POV = proxyquire('..', {
hashlru: function () {
return {
get: () => {
return '<div>Cached Response</div>'
},
set: () => { }
}
}
const POV = require('..')
fastify.decorate(POV.fastifyViewCache, {
get: () => {
return '<div>Cached Response</div>'
},
set: () => { }
})

@@ -438,3 +435,3 @@

test('reply.view with mustache engine with partials and html-minifier', t => {
test('reply.view with mustache engine with partials and html-minifier-terser', t => {
t.plan(6)

@@ -464,3 +461,3 @@ const fastify = Fastify()

url: 'http://localhost:' + fastify.server.address().port
}, (err, response, replyBody) => {
}, async (err, response, replyBody) => {
t.error(err)

@@ -470,3 +467,3 @@ t.equal(response.statusCode, 200)

t.equal(response.headers['content-type'], 'text/html; charset=utf-8')
t.equal(minifier.minify(mustache.render(fs.readFileSync('./templates/index.mustache', 'utf8'), data, { body: '<p>{{ text }}</p>' }), minifierOpts), replyBody.toString())
t.equal(await minifier.minify(mustache.render(fs.readFileSync('./templates/index.mustache', 'utf8'), data, { body: '<p>{{ text }}</p>' }), minifierOpts), replyBody.toString())
fastify.close()

@@ -477,3 +474,3 @@ })

test('reply.view with mustache engine with partials and paths excluded from html-minifier', t => {
test('reply.view with mustache engine with partials and paths excluded from html-minifier-terser', t => {
t.plan(6)

@@ -480,0 +477,0 @@ const fastify = Fastify()

@@ -8,3 +8,2 @@ 'use strict'

const fs = require('node:fs')
const proxyquire = require('proxyquire')

@@ -51,11 +50,9 @@ require('./helper').pugHtmlMinifierTests(t, true)

const pug = require('pug')
const POV = proxyquire('..', {
hashlru: function () {
return {
get: () => {
return () => '<div>Cached Response</div>'
},
set: () => { }
}
}
const POV = require('..')
fastify.decorate(POV.fastifyViewCache, {
get: () => {
return () => '<div>Cached Response</div>'
},
set: () => { }
})

@@ -62,0 +59,0 @@

@@ -1,2 +0,2 @@

import { FastifyPluginCallback } from 'fastify';
import { FastifyPluginAsync } from 'fastify';

@@ -12,2 +12,4 @@ declare module "fastify" {

view(page: string, data?: object, opts?: RouteSpecificOptions): FastifyReply;
viewAsync<T extends { [key: string]: any; }>(page: string, data: T, opts?: RouteSpecificOptions): Promise<string>;
viewAsync(page: string, data?: object, opts?: RouteSpecificOptions): Promise<string>;
}

@@ -21,3 +23,3 @@

type FastifyView = FastifyPluginCallback<fastifyView.FastifyViewOptions>
type FastifyView = FastifyPluginAsync<fastifyView.FastifyViewOptions>

@@ -49,2 +51,3 @@ declare namespace fastifyView {

propertyName?: string;
asyncPropertyName?: string;
}

@@ -59,2 +62,3 @@

export { fastifyView as default }
export const fastifyViewCache: Symbol
}

@@ -61,0 +65,0 @@

import fastify from "fastify";
import fastifyView, { PointOfViewOptions, FastifyViewOptions } from "..";
import { expectAssignable, expectDeprecated, expectType } from "tsd";
import { expectAssignable, expectNotAssignable, expectDeprecated, expectType } from "tsd";
import * as path from "path";

@@ -44,2 +44,4 @@

// reply.locals.appVersion = 1 // not a valid type
expectNotAssignable<NonNullable<(typeof reply)['locals']>['appVersion']>(1)
reply.locals.appVersion = '4.14.0'

@@ -55,2 +57,4 @@ reply.view("/index", { text: "Sample data" });

// reply.locals.appVersion = 1 // not a valid type
expectNotAssignable<NonNullable<(typeof reply)['locals']>['appVersion']>(1)
reply.locals.appVersion = '4.14.0'

@@ -64,2 +68,25 @@ reply.view<{ text: string; }>("/index", { text: "Sample data" });

app.get("/view-async", async (request, reply) => {
expectAssignable<NonNullable<(typeof reply)['locals']>['appVersion']>('4.14.0')
expectNotAssignable<NonNullable<(typeof reply)['locals']>['appVersion']>(1)
type ViewAsyncDataParamType = Parameters<typeof reply.viewAsync>[1]
expectAssignable<ViewAsyncDataParamType>({ text: "Sample data" })
expectAssignable<ViewAsyncDataParamType>({ notText: "Sample data "})
const html = await reply.viewAsync("/index", { text: "Sample data" });
expectType<string>(html)
return html
});
app.get("/view-async-generic-provided", async (request, reply) => {
type ViewAsyncDataParamType = Parameters<typeof reply.viewAsync<{ text: string; }>>[1]
expectAssignable<ViewAsyncDataParamType>({ text: "Sample data" })
expectNotAssignable<ViewAsyncDataParamType>({ notText: "Sample data "})
const html = reply.viewAsync<{ text: string; }>("/index", { text: "Sample data" });
expectType<Promise<string>>(html)
return html
});
app.listen({port: 3000}, (err, address) => {

@@ -66,0 +93,0 @@ if (err) throw err

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc