1 | 'use strict'
|
2 |
|
3 | const { resolve } = require('path')
|
4 | const debug = require('debug')('koa-views')
|
5 | const consolidate = require('consolidate')
|
6 | const send = require('koa-send')
|
7 | const getPaths = require('get-paths')
|
8 | const pretty = require('pretty')
|
9 |
|
10 | module.exports = viewsMiddleware
|
11 |
|
12 | const bigIntReplacer = () => {
|
13 | const seen = new WeakSet()
|
14 | return (key, value) => {
|
15 | if (typeof value === 'bigint') return value.toString()
|
16 |
|
17 | if (typeof value === 'object' && value !== null) {
|
18 | if (seen.has(value)) {
|
19 | return
|
20 | }
|
21 | seen.add(value)
|
22 | }
|
23 |
|
24 | return value
|
25 | }
|
26 | }
|
27 |
|
28 | function viewsMiddleware(
|
29 | path,
|
30 | { autoRender = true, engineSource = consolidate, extension = 'html', options = {}, map } = {}
|
31 | ) {
|
32 | return function views(ctx, next) {
|
33 | function render(relPath, locals = {}) {
|
34 | if (!ctx) ctx = this
|
35 |
|
36 | return getPaths(path, relPath, extension).then(paths => {
|
37 | const suffix = paths.ext
|
38 | const state = Object.assign(locals, options, ctx.state || {})
|
39 |
|
40 | state.partials = Object.assign(Object.create(null), options.partials || {})
|
41 | if (debug.enabled) debug('render `%s` with %s', paths.rel, JSON.stringify(state, bigIntReplacer()))
|
42 | ctx.type = 'text/html'
|
43 |
|
44 | if (isHtml(suffix) && !map) {
|
45 | return send(ctx, paths.rel, {
|
46 | root: path
|
47 | })
|
48 | } else {
|
49 | const engineName = map && map[suffix] ? map[suffix] : suffix
|
50 |
|
51 | const render = engineSource[engineName]
|
52 |
|
53 | if (!engineName || !render)
|
54 | return Promise.reject(
|
55 | new Error(`Engine not found for the ".${suffix}" file extension`)
|
56 | )
|
57 |
|
58 | return render(resolve(path, paths.rel), state).then(html => {
|
59 |
|
60 |
|
61 | if (locals.pretty) {
|
62 | debug('using `pretty` package to beautify HTML')
|
63 | html = pretty(html)
|
64 | }
|
65 |
|
66 | if (autoRender) {
|
67 | ctx.body = html
|
68 | } else {
|
69 | return Promise.resolve(html)
|
70 | }
|
71 | })
|
72 | }
|
73 | })
|
74 | }
|
75 |
|
76 |
|
77 | if (!ctx) return render
|
78 |
|
79 | if (ctx.render) return next()
|
80 |
|
81 | ctx.response.render = ctx.render = render
|
82 |
|
83 | return next()
|
84 | }
|
85 | }
|
86 |
|
87 | function isHtml(ext) {
|
88 | return ext === 'html'
|
89 | }
|