You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					713 lines
				
				20 KiB
			
		
		
			
		
	
	
					713 lines
				
				20 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const fp = require('fastify-plugin')
							 | 
						||
| 
								 | 
							
								const readFile = require('fs').readFile
							 | 
						||
| 
								 | 
							
								const accessSync = require('fs').accessSync
							 | 
						||
| 
								 | 
							
								const existsSync = require('fs').existsSync
							 | 
						||
| 
								 | 
							
								const mkdirSync = require('fs').mkdirSync
							 | 
						||
| 
								 | 
							
								const readdirSync = require('fs').readdirSync
							 | 
						||
| 
								 | 
							
								const resolve = require('path').resolve
							 | 
						||
| 
								 | 
							
								const join = require('path').join
							 | 
						||
| 
								 | 
							
								const { basename, dirname, extname } = require('path')
							 | 
						||
| 
								 | 
							
								const HLRU = require('hashlru')
							 | 
						||
| 
								 | 
							
								const supportedEngines = ['ejs', 'nunjucks', 'pug', 'handlebars', 'mustache', 'art-template', 'twig', 'liquid', 'dot', 'eta']
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function fastifyView (fastify, opts, next) {
							 | 
						||
| 
								 | 
							
								  if (!opts.engine) {
							 | 
						||
| 
								 | 
							
								    next(new Error('Missing engine'))
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  const type = Object.keys(opts.engine)[0]
							 | 
						||
| 
								 | 
							
								  if (supportedEngines.indexOf(type) === -1) {
							 | 
						||
| 
								 | 
							
								    next(new Error(`'${type}' not yet supported, PR? :)`))
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  const charset = opts.charset || 'utf-8'
							 | 
						||
| 
								 | 
							
								  const propertyName = opts.propertyName || 'view'
							 | 
						||
| 
								 | 
							
								  const engine = opts.engine[type]
							 | 
						||
| 
								 | 
							
								  const globalOptions = opts.options || {}
							 | 
						||
| 
								 | 
							
								  const templatesDir = resolveTemplateDir(opts)
							 | 
						||
| 
								 | 
							
								  const lru = HLRU(opts.maxCache || 100)
							 | 
						||
| 
								 | 
							
								  const includeViewExtension = opts.includeViewExtension || false
							 | 
						||
| 
								 | 
							
								  const viewExt = opts.viewExt || ''
							 | 
						||
| 
								 | 
							
								  const prod = typeof opts.production === 'boolean' ? opts.production : process.env.NODE_ENV === 'production'
							 | 
						||
| 
								 | 
							
								  const defaultCtx = opts.defaultContext || {}
							 | 
						||
| 
								 | 
							
								  const globalLayoutFileName = opts.layout
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function templatesDirIsValid (_templatesDir) {
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(_templatesDir) && type !== 'nunjucks') {
							 | 
						||
| 
								 | 
							
								      throw new Error('Only Nunjucks supports the "templates" option as an array')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function layoutIsValid (_layoutFileName) {
							 | 
						||
| 
								 | 
							
								    if (type !== 'dot' && type !== 'handlebars' && type !== 'ejs' && type !== 'eta') {
							 | 
						||
| 
								 | 
							
								      throw new Error('Only Dot, Handlebars, EJS, and Eta support the "layout" option')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!hasAccessToLayoutFile(_layoutFileName, getDefaultExtension(type))) {
							 | 
						||
| 
								 | 
							
								      throw new Error(`unable to access template "${_layoutFileName}"`)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    templatesDirIsValid(templatesDir)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (globalLayoutFileName) {
							 | 
						||
| 
								 | 
							
								      layoutIsValid(globalLayoutFileName)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } catch (error) {
							 | 
						||
| 
								 | 
							
								    next(error)
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const dotRender = type === 'dot' ? viewDot.call(fastify, preProcessDot.call(fastify, templatesDir, globalOptions)) : null
							 | 
						||
| 
								 | 
							
								  const nunjucksEnv = type === 'nunjucks' ? engine.configure(templatesDir, globalOptions) : null
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const renders = {
							 | 
						||
| 
								 | 
							
								    ejs: withLayout(viewEjs, globalLayoutFileName),
							 | 
						||
| 
								 | 
							
								    handlebars: withLayout(viewHandlebars, globalLayoutFileName),
							 | 
						||
| 
								 | 
							
								    mustache: viewMustache,
							 | 
						||
| 
								 | 
							
								    nunjucks: viewNunjucks,
							 | 
						||
| 
								 | 
							
								    'art-template': viewArtTemplate,
							 | 
						||
| 
								 | 
							
								    twig: viewTwig,
							 | 
						||
| 
								 | 
							
								    liquid: viewLiquid,
							 | 
						||
| 
								 | 
							
								    dot: withLayout(dotRender, globalLayoutFileName),
							 | 
						||
| 
								 | 
							
								    eta: withLayout(viewEta, globalLayoutFileName),
							 | 
						||
| 
								 | 
							
								    _default: view
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const renderer = renders[type] ? renders[type] : renders._default
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewDecorator () {
							 | 
						||
| 
								 | 
							
								    const args = Array.from(arguments)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let done
							 | 
						||
| 
								 | 
							
								    if (typeof args[args.length - 1] === 'function') {
							 | 
						||
| 
								 | 
							
								      done = args.pop()
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const promise = new Promise((resolve, reject) => {
							 | 
						||
| 
								 | 
							
								      renderer.apply({
							 | 
						||
| 
								 | 
							
								        getHeader: () => { },
							 | 
						||
| 
								 | 
							
								        header: () => { },
							 | 
						||
| 
								 | 
							
								        send: result => {
							 | 
						||
| 
								 | 
							
								          if (result instanceof Error) {
							 | 
						||
| 
								 | 
							
								            reject(result)
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          resolve(result)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }, args)
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (done && typeof done === 'function') {
							 | 
						||
| 
								 | 
							
								      promise.then(done.bind(null, null), done)
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return promise
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  viewDecorator.clearCache = function () {
							 | 
						||
| 
								 | 
							
								    lru.clear()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  fastify.decorate(propertyName, viewDecorator)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  fastify.decorateReply(propertyName, function () {
							 | 
						||
| 
								 | 
							
								    renderer.apply(this, arguments)
							 | 
						||
| 
								 | 
							
								    return this
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function getPage (page, extension) {
							 | 
						||
| 
								 | 
							
								    const pageLRU = `getPage-${page}-${extension}`
							 | 
						||
| 
								 | 
							
								    let result = lru.get(pageLRU)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (typeof result === 'string') {
							 | 
						||
| 
								 | 
							
								      return result
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const filename = basename(page, extname(page))
							 | 
						||
| 
								 | 
							
								    result = join(dirname(page), filename + getExtension(page, extension))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    lru.set(pageLRU, result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return result
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function getDefaultExtension (type) {
							 | 
						||
| 
								 | 
							
								    const mappedExtensions = {
							 | 
						||
| 
								 | 
							
								      'art-template': 'art',
							 | 
						||
| 
								 | 
							
								      handlebars: 'hbs',
							 | 
						||
| 
								 | 
							
								      nunjucks: 'njk'
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return viewExt || (mappedExtensions[type] || type)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function getExtension (page, extension) {
							 | 
						||
| 
								 | 
							
								    let filextension = extname(page)
							 | 
						||
| 
								 | 
							
								    if (!filextension) {
							 | 
						||
| 
								 | 
							
								      filextension = '.' + getDefaultExtension(type)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return viewExt ? `.${viewExt}` : (includeViewExtension ? `.${extension}` : filextension)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function isPathExcludedMinification (currentPath, pathsToExclude) {
							 | 
						||
| 
								 | 
							
								    return (pathsToExclude && Array.isArray(pathsToExclude)) ? pathsToExclude.includes(currentPath) : false
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function useHtmlMinification (globalOpts, requestedPath) {
							 | 
						||
| 
								 | 
							
								    return globalOptions.useHtmlMinifier &&
							 | 
						||
| 
								 | 
							
								      (typeof globalOptions.useHtmlMinifier.minify === 'function') &&
							 | 
						||
| 
								 | 
							
								      !isPathExcludedMinification(requestedPath, globalOptions.pathsToExcludeHtmlMinifier)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function getRequestedPath (fastify) {
							 | 
						||
| 
								 | 
							
								    return (fastify && fastify.request) ? fastify.request.context.config.url : null
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  // Gets template as string (or precompiled for Handlebars)
							 | 
						||
| 
								 | 
							
								  // from LRU cache or filesystem.
							 | 
						||
| 
								 | 
							
								  const getTemplate = function (file, callback, requestedPath) {
							 | 
						||
| 
								 | 
							
								    const data = lru.get(file)
							 | 
						||
| 
								 | 
							
								    if (data && prod) {
							 | 
						||
| 
								 | 
							
								      callback(null, data)
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      readFile(join(templatesDir, file), 'utf-8', (err, data) => {
							 | 
						||
| 
								 | 
							
								        if (err) {
							 | 
						||
| 
								 | 
							
								          callback(err, null)
							 | 
						||
| 
								 | 
							
								          return
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (useHtmlMinification(globalOptions, requestedPath)) {
							 | 
						||
| 
								 | 
							
								          data = globalOptions.useHtmlMinifier.minify(data, globalOptions.htmlMinifierOptions || {})
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (type === 'handlebars') {
							 | 
						||
| 
								 | 
							
								          data = engine.compile(data)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        lru.set(file, data)
							 | 
						||
| 
								 | 
							
								        callback(null, data)
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Gets partials as collection of strings from LRU cache or filesystem.
							 | 
						||
| 
								 | 
							
								  const getPartials = function (page, { partials, requestedPath }, callback) {
							 | 
						||
| 
								 | 
							
								    const cacheKey = getPartialsCacheKey(page, partials, requestedPath)
							 | 
						||
| 
								 | 
							
								    const partialsObj = lru.get(cacheKey)
							 | 
						||
| 
								 | 
							
								    if (partialsObj && prod) {
							 | 
						||
| 
								 | 
							
								      callback(null, partialsObj)
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      let filesToLoad = Object.keys(partials).length
							 | 
						||
| 
								 | 
							
								      if (filesToLoad === 0) {
							 | 
						||
| 
								 | 
							
								        callback(null, {})
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      let error = null
							 | 
						||
| 
								 | 
							
								      const partialsHtml = {}
							 | 
						||
| 
								 | 
							
								      Object.keys(partials).forEach((key, index) => {
							 | 
						||
| 
								 | 
							
								        readFile(join(templatesDir, partials[key]), 'utf-8', (err, data) => {
							 | 
						||
| 
								 | 
							
								          if (err) {
							 | 
						||
| 
								 | 
							
								            error = err
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          if (useHtmlMinification(globalOptions, requestedPath)) {
							 | 
						||
| 
								 | 
							
								            data = globalOptions.useHtmlMinifier.minify(data, globalOptions.htmlMinifierOptions || {})
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          partialsHtml[key] = data
							 | 
						||
| 
								 | 
							
								          if (--filesToLoad === 0) {
							 | 
						||
| 
								 | 
							
								            lru.set(cacheKey, partialsHtml)
							 | 
						||
| 
								 | 
							
								            callback(error, partialsHtml)
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function getPartialsCacheKey (page, partials, requestedPath) {
							 | 
						||
| 
								 | 
							
								    let cacheKey = page
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for (const key of Object.keys(partials)) {
							 | 
						||
| 
								 | 
							
								      cacheKey += `|${key}:${partials[key]}`
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    cacheKey += `|${requestedPath}-Partials`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return cacheKey
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function readCallback (that, page, data) {
							 | 
						||
| 
								 | 
							
								    return function _readCallback (err, html) {
							 | 
						||
| 
								 | 
							
								      const requestedPath = getRequestedPath(that)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (err) {
							 | 
						||
| 
								 | 
							
								        that.send(err)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      let compiledPage
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        if ((type === 'ejs') && viewExt && !globalOptions.includer) {
							 | 
						||
| 
								 | 
							
								          globalOptions.includer = (originalPath, parsedPath) => {
							 | 
						||
| 
								 | 
							
								            return {
							 | 
						||
| 
								 | 
							
								              filename: parsedPath || join(templatesDir, originalPath + '.' + viewExt)
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        globalOptions.filename = join(templatesDir, page)
							 | 
						||
| 
								 | 
							
								        compiledPage = engine.compile(html, globalOptions)
							 | 
						||
| 
								 | 
							
								      } catch (error) {
							 | 
						||
| 
								 | 
							
								        that.send(error)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      lru.set(page, compiledPage)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (!that.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								        that.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      let cachedPage
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        cachedPage = lru.get(page)(data)
							 | 
						||
| 
								 | 
							
								      } catch (error) {
							 | 
						||
| 
								 | 
							
								        cachedPage = error
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (useHtmlMinification(globalOptions, requestedPath)) {
							 | 
						||
| 
								 | 
							
								        cachedPage = globalOptions.useHtmlMinifier.minify(cachedPage, globalOptions.htmlMinifierOptions || {})
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      that.send(cachedPage)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function preProcessDot (templatesDir, options) {
							 | 
						||
| 
								 | 
							
								    // Process all templates to in memory functions
							 | 
						||
| 
								 | 
							
								    // https://github.com/olado/doT#security-considerations
							 | 
						||
| 
								 | 
							
								    const destinationDir = options.destination || join(__dirname, 'out')
							 | 
						||
| 
								 | 
							
								    if (!existsSync(destinationDir)) {
							 | 
						||
| 
								 | 
							
								      mkdirSync(destinationDir)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const renderer = engine.process(Object.assign(
							 | 
						||
| 
								 | 
							
								      {},
							 | 
						||
| 
								 | 
							
								      options,
							 | 
						||
| 
								 | 
							
								      {
							 | 
						||
| 
								 | 
							
								        path: templatesDir,
							 | 
						||
| 
								 | 
							
								        destination: destinationDir
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    ))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // .jst files are compiled to .js files so we need to require them
							 | 
						||
| 
								 | 
							
								    for (const file of readdirSync(destinationDir, { withFileTypes: false })) {
							 | 
						||
| 
								 | 
							
								      renderer[basename(file, '.js')] = require(resolve(join(destinationDir, file)))
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (Object.keys(renderer).length === 0) {
							 | 
						||
| 
								 | 
							
								      this.log.warn(`WARN: no template found in ${templatesDir}`)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return renderer
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function view (page, data) {
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // append view extension
							 | 
						||
| 
								 | 
							
								    page = getPage(page, type)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const toHtml = lru.get(page)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (toHtml && prod) {
							 | 
						||
| 
								 | 
							
								      if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								        this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      this.send(toHtml(data))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    readFile(join(templatesDir, page), 'utf8', readCallback(this, page, data))
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewEjs (page, data, opts) {
							 | 
						||
| 
								 | 
							
								    if (opts && opts.layout) {
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        layoutIsValid(opts.layout)
							 | 
						||
| 
								 | 
							
								        const that = this
							 | 
						||
| 
								 | 
							
								        return withLayout(viewEjs, opts.layout).call(that, page, data)
							 | 
						||
| 
								 | 
							
								      } catch (error) {
							 | 
						||
| 
								 | 
							
								        this.send(error)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // append view extension
							 | 
						||
| 
								 | 
							
								    page = getPage(page, type)
							 | 
						||
| 
								 | 
							
								    const requestedPath = getRequestedPath(this)
							 | 
						||
| 
								 | 
							
								    getTemplate(page, (err, template) => {
							 | 
						||
| 
								 | 
							
								      if (err) {
							 | 
						||
| 
								 | 
							
								        this.send(err)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      const toHtml = lru.get(page)
							 | 
						||
| 
								 | 
							
								      if (toHtml && prod && (typeof (toHtml) === 'function')) {
							 | 
						||
| 
								 | 
							
								        if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								          this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.send(toHtml(data))
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      readFile(join(templatesDir, page), 'utf8', readCallback(this, page, data))
							 | 
						||
| 
								 | 
							
								    }, requestedPath)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewArtTemplate (page, data) {
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // Append view extension.
							 | 
						||
| 
								 | 
							
								    page = getPage(page, 'art')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const defaultSetting = {
							 | 
						||
| 
								 | 
							
								      debug: process.env.NODE_ENV !== 'production',
							 | 
						||
| 
								 | 
							
								      root: templatesDir
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // merge engine options
							 | 
						||
| 
								 | 
							
								    const confs = Object.assign({}, defaultSetting, globalOptions)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function render (filename, data) {
							 | 
						||
| 
								 | 
							
								      confs.filename = join(templatesDir, filename)
							 | 
						||
| 
								 | 
							
								      const render = engine.compile(confs)
							 | 
						||
| 
								 | 
							
								      return render(data)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      const html = render(page, data)
							 | 
						||
| 
								 | 
							
								      if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								        this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      this.send(html)
							 | 
						||
| 
								 | 
							
								    } catch (error) {
							 | 
						||
| 
								 | 
							
								      this.send(error)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewNunjucks (page, data) {
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (typeof globalOptions.onConfigure === 'function') {
							 | 
						||
| 
								 | 
							
								      globalOptions.onConfigure(nunjucksEnv)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // Append view extension.
							 | 
						||
| 
								 | 
							
								    page = getPage(page, 'njk')
							 | 
						||
| 
								 | 
							
								    nunjucksEnv.render(page, data, (err, html) => {
							 | 
						||
| 
								 | 
							
								      const requestedPath = getRequestedPath(this)
							 | 
						||
| 
								 | 
							
								      if (err) return this.send(err)
							 | 
						||
| 
								 | 
							
								      if (useHtmlMinification(globalOptions, requestedPath)) {
							 | 
						||
| 
								 | 
							
								        html = globalOptions.useHtmlMinifier.minify(html, globalOptions.htmlMinifierOptions || {})
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								      this.send(html)
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewHandlebars (page, data, opts) {
							 | 
						||
| 
								 | 
							
								    if (opts && opts.layout) {
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        layoutIsValid(opts.layout)
							 | 
						||
| 
								 | 
							
								        const that = this
							 | 
						||
| 
								 | 
							
								        return withLayout(viewHandlebars, opts.layout).call(that, page, data)
							 | 
						||
| 
								 | 
							
								      } catch (error) {
							 | 
						||
| 
								 | 
							
								        this.send(error)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const options = Object.assign({}, globalOptions)
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // append view extension
							 | 
						||
| 
								 | 
							
								    page = getPage(page, 'hbs')
							 | 
						||
| 
								 | 
							
								    const requestedPath = getRequestedPath(this)
							 | 
						||
| 
								 | 
							
								    getTemplate(page, (err, template) => {
							 | 
						||
| 
								 | 
							
								      if (err) {
							 | 
						||
| 
								 | 
							
								        this.send(err)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (prod) {
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          const html = template(data)
							 | 
						||
| 
								 | 
							
								          if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								            this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          this.send(html)
							 | 
						||
| 
								 | 
							
								        } catch (e) {
							 | 
						||
| 
								 | 
							
								          this.send(e)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        getPartials(type, { partials: options.partials || {}, requestedPath: requestedPath }, (err, partialsObject) => {
							 | 
						||
| 
								 | 
							
								          if (err) {
							 | 
						||
| 
								 | 
							
								            this.send(err)
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          try {
							 | 
						||
| 
								 | 
							
								            Object.keys(partialsObject).forEach((name) => {
							 | 
						||
| 
								 | 
							
								              engine.registerPartial(name, engine.compile(partialsObject[name]))
							 | 
						||
| 
								 | 
							
								            })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            const html = template(data)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								              this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            this.send(html)
							 | 
						||
| 
								 | 
							
								          } catch (e) {
							 | 
						||
| 
								 | 
							
								            this.send(e)
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }, requestedPath)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewMustache (page, data, opts) {
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const options = Object.assign({}, opts)
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // append view extension
							 | 
						||
| 
								 | 
							
								    page = getPage(page, 'mustache')
							 | 
						||
| 
								 | 
							
								    const requestedPath = getRequestedPath(this)
							 | 
						||
| 
								 | 
							
								    getTemplate(page, (err, templateString) => {
							 | 
						||
| 
								 | 
							
								      if (err) {
							 | 
						||
| 
								 | 
							
								        this.send(err)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      getPartials(page, { partials: options.partials || {}, requestedPath: requestedPath }, (err, partialsObject) => {
							 | 
						||
| 
								 | 
							
								        if (err) {
							 | 
						||
| 
								 | 
							
								          this.send(err)
							 | 
						||
| 
								 | 
							
								          return
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        const html = engine.render(templateString, data, partialsObject)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								          this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.send(html)
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    }, requestedPath)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewTwig (page, data, opts) {
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, globalOptions, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // Append view extension.
							 | 
						||
| 
								 | 
							
								    page = getPage(page, 'twig')
							 | 
						||
| 
								 | 
							
								    engine.renderFile(join(templatesDir, page), data, (err, html) => {
							 | 
						||
| 
								 | 
							
								      const requestedPath = getRequestedPath(this)
							 | 
						||
| 
								 | 
							
								      if (err) {
							 | 
						||
| 
								 | 
							
								        return this.send(err)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (useHtmlMinification(globalOptions, requestedPath)) {
							 | 
						||
| 
								 | 
							
								        html = globalOptions.useHtmlMinifier.minify(html, globalOptions.htmlMinifierOptions || {})
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								        this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      this.send(html)
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewLiquid (page, data, opts) {
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // Append view extension.
							 | 
						||
| 
								 | 
							
								    page = getPage(page, 'liquid')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    engine.renderFile(join(templatesDir, page), data, opts)
							 | 
						||
| 
								 | 
							
								      .then((html) => {
							 | 
						||
| 
								 | 
							
								        const requestedPath = getRequestedPath(this)
							 | 
						||
| 
								 | 
							
								        if (useHtmlMinification(globalOptions, requestedPath)) {
							 | 
						||
| 
								 | 
							
								          html = globalOptions.useHtmlMinifier.minify(html, globalOptions.htmlMinifierOptions || {})
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								          this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        this.send(html)
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								      .catch((err) => {
							 | 
						||
| 
								 | 
							
								        this.send(err)
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewDot (renderModule) {
							 | 
						||
| 
								 | 
							
								    return function _viewDot (page, data, opts) {
							 | 
						||
| 
								 | 
							
								      if (opts && opts.layout) {
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          layoutIsValid(opts.layout)
							 | 
						||
| 
								 | 
							
								          const that = this
							 | 
						||
| 
								 | 
							
								          return withLayout(dotRender, opts.layout).call(that, page, data)
							 | 
						||
| 
								 | 
							
								        } catch (error) {
							 | 
						||
| 
								 | 
							
								          this.send(error)
							 | 
						||
| 
								 | 
							
								          return
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (!page) {
							 | 
						||
| 
								 | 
							
								        this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								      let html = renderModule[page](data)
							 | 
						||
| 
								 | 
							
								      const requestedPath = getRequestedPath(this)
							 | 
						||
| 
								 | 
							
								      if (useHtmlMinification(globalOptions, requestedPath)) {
							 | 
						||
| 
								 | 
							
								        html = globalOptions.useHtmlMinifier.minify(html, globalOptions.htmlMinifierOptions || {})
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (!this.getHeader('content-type')) {
							 | 
						||
| 
								 | 
							
								        this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      this.send(html)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function viewEta (page, data, opts) {
							 | 
						||
| 
								 | 
							
								    if (opts && opts.layout) {
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        layoutIsValid(opts.layout)
							 | 
						||
| 
								 | 
							
								        const that = this
							 | 
						||
| 
								 | 
							
								        return withLayout(viewEta, opts.layout).call(that, page, data)
							 | 
						||
| 
								 | 
							
								      } catch (error) {
							 | 
						||
| 
								 | 
							
								        this.send(error)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!page) {
							 | 
						||
| 
								 | 
							
								      this.send(new Error('Missing page'))
							 | 
						||
| 
								 | 
							
								      return
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    lru.define = lru.set
							 | 
						||
| 
								 | 
							
								    engine.configure({
							 | 
						||
| 
								 | 
							
								      templates: globalOptions.templates ? globalOptions.templates : lru
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const config = Object.assign({
							 | 
						||
| 
								 | 
							
								      cache: prod,
							 | 
						||
| 
								 | 
							
								      views: templatesDir
							 | 
						||
| 
								 | 
							
								    }, globalOptions)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								    // Append view extension (Eta will append '.eta' by default,
							 | 
						||
| 
								 | 
							
								    // but this also allows custom extensions)
							 | 
						||
| 
								 | 
							
								    page = getPage(page, 'eta')
							 | 
						||
| 
								 | 
							
								    engine.renderFile(page, data, config, (err, html) => {
							 | 
						||
| 
								 | 
							
								      if (err) return this.send(err)
							 | 
						||
| 
								 | 
							
								      if (
							 | 
						||
| 
								 | 
							
								        config.useHtmlMinifier &&
							 | 
						||
| 
								 | 
							
								        typeof config.useHtmlMinifier.minify === 'function' &&
							 | 
						||
| 
								 | 
							
								        !isPathExcludedMinification(getRequestedPath(this), config.pathsToExcludeHtmlMinifier)
							 | 
						||
| 
								 | 
							
								      ) {
							 | 
						||
| 
								 | 
							
								        html = config.useHtmlMinifier.minify(
							 | 
						||
| 
								 | 
							
								          html,
							 | 
						||
| 
								 | 
							
								          config.htmlMinifierOptions || {}
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      this.header('Content-Type', 'text/html; charset=' + charset)
							 | 
						||
| 
								 | 
							
								      this.send(html)
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (prod && type === 'handlebars' && globalOptions.partials) {
							 | 
						||
| 
								 | 
							
								    getPartials(type, { partials: globalOptions.partials || {}, requestedPath: getRequestedPath(this) }, (err, partialsObject) => {
							 | 
						||
| 
								 | 
							
								      if (err) {
							 | 
						||
| 
								 | 
							
								        next(err)
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      Object.keys(partialsObject).forEach((name) => {
							 | 
						||
| 
								 | 
							
								        engine.registerPartial(name, engine.compile(partialsObject[name]))
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								      next()
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    next()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function withLayout (render, layout) {
							 | 
						||
| 
								 | 
							
								    if (layout) {
							 | 
						||
| 
								 | 
							
								      return function (page, data, opts) {
							 | 
						||
| 
								 | 
							
								        if (opts && opts.layout) throw new Error('A layout can either be set globally or on render, not both.')
							 | 
						||
| 
								 | 
							
								        const that = this
							 | 
						||
| 
								 | 
							
								        data = Object.assign({}, defaultCtx, this.locals, data)
							 | 
						||
| 
								 | 
							
								        render.call({
							 | 
						||
| 
								 | 
							
								          getHeader: () => { },
							 | 
						||
| 
								 | 
							
								          header: () => { },
							 | 
						||
| 
								 | 
							
								          send: (result) => {
							 | 
						||
| 
								 | 
							
								            if (result instanceof Error) {
							 | 
						||
| 
								 | 
							
								              throw result
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            data = Object.assign((data || {}), { body: result })
							 | 
						||
| 
								 | 
							
								            render.call(that, layout, data, opts)
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }, page, data, opts)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return render
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function resolveTemplateDir (_opts) {
							 | 
						||
| 
								 | 
							
								    if (_opts.root) {
							 | 
						||
| 
								 | 
							
								      return _opts.root
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return Array.isArray(_opts.templates)
							 | 
						||
| 
								 | 
							
								      ? _opts.templates.map((dir) => resolve(dir))
							 | 
						||
| 
								 | 
							
								      : resolve(_opts.templates || './')
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function hasAccessToLayoutFile (fileName, ext) {
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      accessSync(join(templatesDir, getPage(fileName, ext)))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      return true
							 | 
						||
| 
								 | 
							
								    } catch (e) {
							 | 
						||
| 
								 | 
							
								      return false
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = fp(fastifyView, {
							 | 
						||
| 
								 | 
							
								  fastify: '3.x',
							 | 
						||
| 
								 | 
							
								  name: 'point-of-view'
							 | 
						||
| 
								 | 
							
								})
							 |