├── example ├── view │ ├── user.oc.html │ ├── async.html │ ├── content.html │ ├── content.noext.html │ ├── user.html │ ├── template.html │ └── template.oc.html ├── app.js └── app-with-custom-fs-module.js ├── .eslintrc ├── .travis.yml ├── .editorconfig ├── .nycrc ├── .gitignore ├── LICENSE ├── package.json ├── test ├── write-response.test.js └── koa-ejs.test.js ├── README.md ├── index.js └── HISTORY.md /example/view/user.oc.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/view/async.html: -------------------------------------------------------------------------------- 1 | <%= await sayHello('Jack') %> 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "env": { 4 | "mocha": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 12 4 | - 14 5 | - 16 6 | script: 7 | - npm run ci 8 | -------------------------------------------------------------------------------- /example/view/content.html: -------------------------------------------------------------------------------- 1 |
2 |

request ip is: <%= ip %>

3 | <%- include('user.html') -%> 4 |
5 | -------------------------------------------------------------------------------- /example/view/content.noext.html: -------------------------------------------------------------------------------- 1 |
2 |

request ip is: <%= ip %>

3 | <%- include ('user') -%> 4 |
5 | -------------------------------------------------------------------------------- /example/view/user.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /example/view/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | koa ejs 4 | 5 | 6 |

koa ejs

7 | <%- body %> 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/view/template.oc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | koa ejs 4 | 5 | 6 |

koa ejs

7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "all": false, 3 | "exclude": [ 4 | "**/*.test.js", 5 | "example" 6 | ], 7 | "reporter": [ 8 | "text-lcov", 9 | "text", 10 | "lcov" 11 | ], 12 | "report-dir": "./coverage", 13 | "temp-dir": "./.nyc_output" 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS # 2 | ################### 3 | .DS_Store 4 | .idea 5 | Thumbs.db 6 | tmp 7 | temp 8 | 9 | 10 | # Node.js # 11 | ################### 12 | node_modules 13 | package-lock.json 14 | yarn.lock 15 | 16 | 17 | # NYC # 18 | ################### 19 | coverage 20 | .nyc_output 21 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @koa/ejs - example/app.js 3 | * 4 | * Copyright(c) 2017 dead_horse 5 | * MIT Licensed 6 | */ 7 | 8 | 'use strict' 9 | 10 | /** 11 | * Module dependencies. 12 | */ 13 | 14 | const Koa = require('koa') 15 | const render = require('..') 16 | const path = require('path') 17 | 18 | const app = new Koa() 19 | 20 | render(app, { 21 | root: path.join(__dirname, 'view'), 22 | layout: 'template', 23 | viewExt: 'html', 24 | cache: false, 25 | debug: false 26 | }) 27 | 28 | app 29 | .use(function (ctx, next) { 30 | ctx.state = ctx.state || {} 31 | ctx.state.now = new Date() 32 | ctx.state.ip = ctx.ip 33 | ctx.state.version = '2.0.0' 34 | return next() 35 | }) 36 | .use(async function (ctx) { 37 | const users = [{ name: 'Dead Horse' }, { name: 'Imed Jaberi' }, { name: 'Tom' }] 38 | await ctx.render('content', { users }) 39 | }) 40 | 41 | if (process.env.NODE_ENV === 'test') { 42 | module.exports = app.callback() 43 | } else { 44 | app.listen(7001) 45 | console.log('open http://localhost:7001') 46 | } 47 | 48 | app.on('error', function (err) { 49 | console.log(err.stack) 50 | }) 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright(c) 2017 dead_horse 4 | Copyright(c) 2021-2022 3imed-jaberi 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/app-with-custom-fs-module.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * koa-ejs - example/app-with-custom-fs-module 3 | * 4 | * Copyright(c) 2022 3imed-jaberi 5 | * MIT Licensed 6 | */ 7 | 8 | 'use strict' 9 | 10 | /** 11 | * Module dependencies. 12 | */ 13 | 14 | const Koa = require('koa') 15 | const render = require('..') 16 | const path = require('path') 17 | 18 | const app = new Koa() 19 | 20 | render(app, { 21 | root: path.join(__dirname, 'view'), 22 | fs: require('mz/fs'), 23 | layout: 'template', 24 | viewExt: 'html', 25 | cache: false, 26 | debug: false 27 | }) 28 | 29 | app 30 | .use(function (ctx, next) { 31 | ctx.state = ctx.state || {} 32 | ctx.state.now = new Date() 33 | ctx.state.ip = ctx.ip 34 | ctx.state.version = '2.0.0' 35 | return next() 36 | }) 37 | .use(async function (ctx) { 38 | const users = [{ name: 'Dead Horse' }, { name: 'Imed Jaberi' }, { name: 'Tom' }] 39 | await ctx.render('content', { users }) 40 | }) 41 | 42 | if (process.env.NODE_ENV === 'test') { 43 | module.exports = app.callback() 44 | } else { 45 | app.listen(7001) 46 | console.log('open http://localhost:7001') 47 | } 48 | 49 | app.on('error', function (err) { 50 | console.log(err.stack) 51 | }) 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@koa/ejs", 3 | "version": "5.1.0", 4 | "description": "ejs render middleware for Koa.js", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/koajs/ejs.git" 12 | }, 13 | "scripts": { 14 | "lint": "eslint .", 15 | "pretest": "npm run lint", 16 | "test": "cross-env NODE_ENV=test mocha --exit --reporter spec --timeout 5000 --require should test/*.test.js", 17 | "precoverage": "rimraf .nyc_output coverage", 18 | "coverage": "nyc npm run test", 19 | "ci": "npm run coverage" 20 | }, 21 | "keywords": [ 22 | "koa", 23 | "middleware", 24 | "render", 25 | "ejs", 26 | "view", 27 | "template-engine" 28 | ], 29 | "author": { 30 | "name": "dead_horse", 31 | "email": "dead_horse@qq.com", 32 | "url": "http://deadhorse.me" 33 | }, 34 | "contributors": [ 35 | { 36 | "name": "Imed Jaberi", 37 | "email": "imed-jaberi@outlook.com", 38 | "url": "https://www.3imed-jaberi.com" 39 | } 40 | ], 41 | "license": "MIT", 42 | "dependencies": { 43 | "debug": "^4.3.4", 44 | "ejs": "^3.1.8" 45 | }, 46 | "devDependencies": { 47 | "cross-env": "^7.0.3", 48 | "eslint": "^7.30.0", 49 | "eslint-config-standard": "^16.0.3", 50 | "eslint-plugin-import": "^2.23.4", 51 | "eslint-plugin-node": "^11.1.0", 52 | "eslint-plugin-promise": "^5.1.0", 53 | "eslint-plugin-standard": "^5.0.0", 54 | "koa": "^2.13.4", 55 | "mocha": "^10.0.0", 56 | "mz": "^2.7.0", 57 | "nyc": "^15.1.0", 58 | "should": "^13.2.3", 59 | "supertest": "^6.2.4" 60 | }, 61 | "engine": { 62 | "node": ">= 12" 63 | }, 64 | "bugs": { 65 | "url": "https://github.com/koajs/ejs/issues" 66 | }, 67 | "homepage": "https://github.com/koajs/ejs" 68 | } 69 | -------------------------------------------------------------------------------- /test/write-response.test.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @koa/ejs - test/koa/ejs.test.js 3 | * Copyright(c) 2017 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict' 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | const render = require('..') 14 | const request = require('supertest') 15 | const Koa = require('koa') 16 | 17 | describe('test/write-response.test.js', function () { 18 | describe('writeResp option', function () { 19 | it('should return html with default configuration and writeResp option = false', function (done) { 20 | const app = new Koa() 21 | render(app, { 22 | root: 'example/view', 23 | layout: 'template.oc', 24 | viewExt: 'html', 25 | delimiter: '?' 26 | }) 27 | 28 | app.use(async function (ctx, next) { 29 | const html = await ctx.render('user.oc', { 30 | user: { name: 'Zed Gu' }, 31 | writeResp: false 32 | }) 33 | 34 | ctx.type = 'html' 35 | ctx.body = html 36 | }) 37 | 38 | request(app.callback()) 39 | .get('/') 40 | .expect(200) 41 | .expect('content-type', 'text/html; charset=utf-8') 42 | .expect(/Zed Gu/, done) 43 | }) 44 | 45 | it('should return html with configuration writeResp = false', function (done) { 46 | const app = new Koa() 47 | render(app, { 48 | root: 'example/view', 49 | layout: 'template.oc', 50 | viewExt: 'html', 51 | delimiter: '?', 52 | writeResp: false 53 | }) 54 | 55 | app.use(async function (ctx) { 56 | const html = await ctx.render('user.oc', { 57 | user: { name: 'Zed Gu' } 58 | }) 59 | 60 | ctx.type = 'html' 61 | ctx.body = html 62 | }) 63 | 64 | request(app.callback()) 65 | .get('/') 66 | .expect(200) 67 | .expect('content-type', 'text/html; charset=utf-8') 68 | .expect(/Zed Gu/, done) 69 | }) 70 | }) 71 | }) 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @koa/ejs 2 | 3 | > Koa ejs view render middleware. support all feature of [ejs](https://github.com/mde/ejs). 4 | 5 | [![Build Status](https://secure.travis-ci.org/koajs/ejs.svg)](http://travis-ci.org/koajs/ejs) 6 | 7 | [![NPM](https://nodei.co/npm/@koa/ejs.png?downloads=true)](https://nodei.co/npm/@koa/ejs/) 8 | 9 | ## Usage 10 | 11 | ### Example 12 | 13 | ```js 14 | const Koa = require("koa"); 15 | const render = require("@koa/ejs"); 16 | const path = require("path"); 17 | 18 | const app = new Koa(); 19 | render(app, { 20 | root: path.join(__dirname, "view"), 21 | layout: "template", 22 | viewExt: "html", 23 | cache: false, 24 | debug: true, 25 | }); 26 | 27 | app.use(async function (ctx) { 28 | await ctx.render("user"); 29 | }); 30 | 31 | app.listen(7001); 32 | ``` 33 | 34 | Or you can checkout the [example](https://github.com/koajs/ejs/tree/master/example). 35 | 36 | ### Settings 37 | 38 | - root: view root directory. 39 | - fs: file system module with same Node.js fs interface (default `Node.js fs module`). 40 | - layout: global layout file, default is `layout`, set `false` to disable layout. 41 | - viewExt: view file extension (default `html`). 42 | - cache: cache compiled templates (default `true`). 43 | - debug: debug flag (default `false`). 44 | - delimiter: character to use with angle brackets for open / close (default `%`). 45 | - async: When true, EJS will use an async function for rendering. Depends on async/await support in the JS runtime. 46 | - outputFunctionName: Set to a string (e.g., 'echo' or 'print') for a function to print output inside scriptlet tags. 47 | 48 | ### Layouts 49 | 50 | `@koa/ejs` supports layouts. The default layout file is `layout`. If you want to change default layout file, use `settings.layout`. Also you can specify layout by `options.layout` in `await ctx.render`. 51 | Also you can set `layout = false` to disable the layout. 52 | 53 | ``` 54 | 55 | 56 | koa ejs 57 | 58 | 59 |

koa ejs

60 | <%- body %> 61 | 62 | 63 | ``` 64 | 65 | ### Include 66 | 67 | Supports ejs includes. 68 | 69 | ``` 70 |
71 | <%- include ('user.html') -%> 72 |
73 | ``` 74 | 75 | ### State 76 | 77 | Support [`ctx.state` in koa](https://github.com/koajs/koa/blob/master/docs/api/context.md#ctxstate). 78 | 79 | 80 | ### TypeScript 81 | 82 | If you're project based on TypeScript, we recommend using [`@types/koa-ejs`](https://www.npmjs.com/package/@types/koa-ejs) until we start supporting it in the upcoming releases. 83 | 84 | ## Licences 85 | 86 | [(The MIT License)](LICENSE) 87 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @koa/ejs 3 | * 4 | * Copyright(c) 2017 dead_horse 5 | * Copyright(c) 2021-2022 3imed-jaberi 6 | * MIT Licensed 7 | */ 8 | 9 | 'use strict' 10 | 11 | /** 12 | * Module dependencies. 13 | */ 14 | 15 | const ejs = require('ejs') 16 | const path = require('path') 17 | const debug = require('debug')('@koa/ejs') 18 | 19 | /** 20 | * Temp assigned for override later 21 | */ 22 | const parentResolveInclude = ejs.resolveInclude 23 | 24 | /** 25 | * default render options 26 | * @type {Object} 27 | */ 28 | const defaultSettings = { 29 | cache: true, 30 | layout: 'layout', 31 | viewExt: 'html', 32 | locals: {}, 33 | compileDebug: false, 34 | debug: false, 35 | writeResp: true, 36 | async: false 37 | } 38 | 39 | exports = module.exports = koaEjs 40 | 41 | /** 42 | * set app.context.render 43 | * 44 | * usage: 45 | * ``` 46 | * await ctx.render('user', {name: 'dead_horse'}); 47 | * ``` 48 | * @param {Application} app koa application instance 49 | * @param {Object} settings user settings 50 | */ 51 | function koaEjs (app, settings) { 52 | if (app.context.render) return 53 | if (!settings || !settings.root) throw new Error('settings.root required') 54 | settings.root = path.resolve(process.cwd(), settings.root) 55 | // cache the generate package 56 | const cache = {} 57 | settings = { ...defaultSettings, ...settings } 58 | settings.viewExt = settings.viewExt ? '.' + settings.viewExt.replace(/^\./, '') : '' 59 | settings.fs = settings.fs || require('fs/promises') 60 | 61 | // override `ejs` node_module `resolveInclude` function 62 | ejs.resolveInclude = function (name, filename, isDir) { 63 | if (!path.extname(name)) name += settings.viewExt 64 | return parentResolveInclude(name, filename, isDir) 65 | } 66 | 67 | /** 68 | * generate html with view name and options 69 | * 70 | * @param {String} view 71 | * @param {Object} options 72 | * @return {String} html 73 | */ 74 | async function render (view, options) { 75 | view += settings.viewExt 76 | const viewPath = path.join(settings.root, view) 77 | debug(`render: ${viewPath}`) 78 | // get from cache 79 | if (settings.cache && cache[viewPath]) return cache[viewPath].call(options.scope, options) 80 | 81 | const tpl = await settings.fs.readFile(viewPath, 'utf8') 82 | 83 | const fn = ejs.compile(tpl, { 84 | filename: viewPath, 85 | _with: settings._with, 86 | compileDebug: settings.debug && settings.compileDebug, 87 | debug: settings.debug, 88 | delimiter: settings.delimiter, 89 | cache: settings.cache, 90 | async: settings.async, 91 | outputFunctionName: settings.outputFunctionName 92 | }) 93 | 94 | if (settings.cache) cache[viewPath] = fn 95 | 96 | return fn.call(options.scope, options) 97 | } 98 | 99 | app.context.render = async function (view, _context) { 100 | const ctx = this 101 | const context = { ...ctx.state, ..._context } 102 | let html = await render(view, context) 103 | 104 | const layout = context.layout === false ? false : (context.layout || settings.layout) 105 | if (layout) { 106 | // if using layout 107 | context.body = html 108 | html = await render(layout, context) 109 | } 110 | 111 | const writeResp = context.writeResp === false ? false : (context.writeResp || settings.writeResp) 112 | if (writeResp) { 113 | // normal operation 114 | ctx.type = 'html' 115 | ctx.body = html 116 | } 117 | 118 | // only return the html 119 | return html 120 | } 121 | }; 122 | 123 | /** 124 | * Expose ejs 125 | */ 126 | 127 | exports.ejs = ejs 128 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 2 | # History 3 | 4 | **[PLEASE REFERENCE THE RELEASES TAB FOR THE LATEST CHANGES](https://github.com/koajs/ejs/releases)** 5 | 6 | 4.3.0 / 2019-12-04 7 | ================== 8 | 9 | **features** 10 | * [[`9bd7c37`](http://github.com/koajs/ejs/commit/9bd7c3790ccbba3d66e3bb9c4ab12ee020cf15fd)] - feat: support for outputFunctionName settings (#52) (Vojtěch Kozák <>) 11 | 12 | 4.2.1 / 2019-12-04 13 | ================== 14 | 15 | **others** 16 | * [[`129021b`](http://github.com/koajs/ejs/commit/129021bc9bae23910434ebba7a61b82b8245f6ea)] - chore: Move definition of resolveInclude() override out of closure (Anthony Howe <>) 17 | 18 | 4.2.0 / 2018-12-27 19 | ================== 20 | 21 | **features** 22 | * [[`ca1e00e`](http://github.com/koajs/ejs/commit/ca1e00ee388a759684ce949b9c089fa56f9c9de7)] - feat: upgrade ejs@2.6.1 and add `async` option (#47) (nswbmw <>) 23 | 24 | 4.1.2 / 2018-07-19 25 | ================== 26 | 27 | **fixes** 28 | * [[`245e1ee`](http://github.com/koajs/ejs/commit/245e1eeca515caebd2e5ffd6e1c3450ec159db4b)] - fix: cache includeFile (#45) (hyj1991 <<66cfat66@gmail.com>>) 29 | 30 | 4.1.1 / 2018-03-17 31 | ================== 32 | 33 | **fixes** 34 | * [[`f12513f`](http://github.com/koajs/ejs/commit/f12513fed78c6ee2cc02624dd861039c35a82737)] - fix: Fix Wrong function reference may cause 'Maxiumn call stack size exceeded' (Runrioter <>) 35 | 36 | **others** 37 | * [[`785f28a`](http://github.com/koajs/ejs/commit/785f28ab6c6fe6989bfd6741ef4803c0754d70e4)] - chore: add missing package.files (dead-horse <>) 38 | * [[`6babd1f`](http://github.com/koajs/ejs/commit/6babd1f58751ccf9e9c0fc1c99d4ebb3848719af)] - fix typo (#43) (Dan Bergren <>) 39 | 40 | 4.1.0 / 2017-06-27 41 | ================== 42 | 43 | * feat: Make `viewExt` option support `include` directive (#38) 44 | * correct the demo in the Readme 45 | 46 | 4.0.1 / 2017-06-25 47 | ================== 48 | 49 | * Fix `debug` setting bug (#37) 50 | 51 | 4.0.0 / 2017-02-28 52 | ================== 53 | 54 | * rewrite with async function and upgrade dependencies (#34) 55 | 56 | 3.0.0 / 2015-11-19 57 | ================== 58 | 59 | * Clean open/close settings 60 | * Switch to EJS v2 61 | 62 | 2.0.1 / 2015-04-22 63 | ================== 64 | 65 | * fix: contenxt not defined. fixes #18 66 | 67 | 2.0.0 / 2015-04-19 68 | ================== 69 | 70 | * add test for 0.12 and iojs 71 | * deps: ejs@1 72 | * refactor: remove locals 73 | 74 | 1.1.3 / 2014-12-18 75 | ================== 76 | 77 | * fix viewPath' bug, improve code 78 | 79 | 1.1.2 / 2014-08-30 80 | ================== 81 | 82 | * Merge pull request #8 from zedgu/master 83 | * for resolve to absolute path 84 | 85 | 1.1.1 / 2014-08-28 86 | ================== 87 | 88 | * Merge pull request #7 from lopezchr/clopez 89 | * adding write-response test suit 90 | * Addign no auto write body option 91 | 92 | 1.1.0 / 2014-08-12 93 | ================== 94 | 95 | * Merge pull request #6 from zedgu/master 96 | * [fix] open/close tag setting is not working 97 | 98 | 1.0.2 / 2014-07-14 99 | ================== 100 | 101 | * Merge pull request #5 from its-florida/master 102 | * Honor layout overrides in options. 103 | 104 | 1.0.1 / 2014-05-04 105 | ================== 106 | 107 | * updated default settings, fixes #3 108 | 109 | 1.0.0 / 2014-04-24 110 | ================== 111 | 112 | * refactor, close #2 113 | 114 | 0.0.3 / 2014-03-17 115 | ================== 116 | 117 | * update readme 118 | * support locals as generator 119 | * Merge pull request #1 from Pana/master 120 | * small edit 121 | 122 | 0.0.2 / 2014-03-11 123 | ================== 124 | 125 | * fix charset 126 | * fix typo 127 | 128 | 0.0.1 / 2014-02-25 129 | ================== 130 | 131 | * update readme 132 | * add test 133 | * rename koa-view to koa-ejs 134 | * rename koa-render to koa-view 135 | * add example 136 | * complete koa-render 137 | * Initial commit 138 | -------------------------------------------------------------------------------- /test/koa-ejs.test.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * koa-ejs - test/koa-ejs.test.js 3 | * Copyright(c) 2017 dead_horse 4 | * MIT Licensed 5 | */ 6 | 7 | 'use strict' 8 | 9 | /** 10 | * Module dependencies. 11 | */ 12 | 13 | const render = require('..') 14 | const request = require('supertest') 15 | const Koa = require('koa') 16 | 17 | describe('test/koa-ejs.test.js', function () { 18 | describe('init()', function () { 19 | const app = new Koa() 20 | it('should throw error if no root', function () { 21 | (function () { 22 | render(app) 23 | }).should.throw('settings.root required'); 24 | 25 | (function () { 26 | render(app, {}) 27 | }).should.throw('settings.root required') 28 | }) 29 | 30 | it('should init ok', function () { 31 | render(app, { 32 | root: __dirname, 33 | open: '{{', 34 | close: '}}' 35 | }) 36 | // eslint-disable-next-line no-unused-expressions 37 | app.context.render.should.be.Function 38 | }) 39 | }) 40 | 41 | describe('server', function () { 42 | describe('with default node.js fs module', () => { 43 | it('should render page ok', function (done) { 44 | const app = require('../example/app') 45 | request(app) 46 | .get('/') 47 | .expect(200) 48 | .expect('content-type', 'text/html; charset=utf-8') 49 | .expect(/koa ejs<\/title>/) 50 | .expect(/Dead Horse/) 51 | .expect(/Imed Jaberi/, done) 52 | }) 53 | it('should render page ok with async functions', function (done) { 54 | const app = new Koa() 55 | render(app, { 56 | root: 'example/view', 57 | viewExt: 'html', 58 | layout: false, 59 | async: true 60 | }) 61 | 62 | app.use(async function (ctx) { 63 | await ctx.render('async', { 64 | async sayHello (name) { 65 | return `Hello, ${name}` 66 | } 67 | }) 68 | }) 69 | request(app.callback()) 70 | .get('/') 71 | .expect(200) 72 | .expect('content-type', 'text/html; charset=utf-8') 73 | .expect(/Hello, Jack/, done) 74 | }) 75 | it('should render page ok with custom open/close', function (done) { 76 | const app = new Koa() 77 | render(app, { 78 | root: 'example/view', 79 | layout: 'template.oc', 80 | viewExt: 'html', 81 | delimiter: '?' 82 | }) 83 | 84 | app.use(async function (ctx) { 85 | await ctx.render('user.oc', { 86 | user: { name: 'Zed Gu' } 87 | }) 88 | }) 89 | request(app.callback()) 90 | .get('/') 91 | .expect(200) 92 | .expect('content-type', 'text/html; charset=utf-8') 93 | .expect(/Zed Gu/, done) 94 | }) 95 | it('should render page ok with `viewExt` option supporting `include` directive', function (done) { 96 | const app = new Koa() 97 | render(app, { 98 | root: 'example/view', 99 | layout: 'template', 100 | viewExt: 'html', 101 | cache: false 102 | }) 103 | 104 | app.use(function (ctx, next) { 105 | ctx.state = ctx.state || {} 106 | ctx.state.ip = ctx.ip 107 | return next() 108 | }) 109 | 110 | app.use(async function (ctx) { 111 | const users = [{ name: 'Dead Horse' }, { name: 'Runrioter Wung' }] 112 | await ctx.render('content.noext', { 113 | users 114 | }) 115 | }) 116 | request(app.callback()) 117 | .get('/') 118 | .expect(200) 119 | .expect('content-type', 'text/html; charset=utf-8') 120 | .expect(/Dead Horse/) 121 | .expect(/Runrioter Wung/, done) 122 | }) 123 | }) 124 | 125 | describe('with custom node.js fs module like', () => { 126 | it('should render page ok', function (done) { 127 | const app = require('../example/app-with-custom-fs-module') 128 | request(app) 129 | .get('/') 130 | .expect(200) 131 | .expect('content-type', 'text/html; charset=utf-8') 132 | .expect(/<title>koa ejs<\/title>/) 133 | .expect(/Dead Horse/) 134 | .expect(/Imed Jaberi/, done) 135 | }) 136 | it('should render page ok with async functions', function (done) { 137 | const app = new Koa() 138 | render(app, { 139 | root: 'example/view', 140 | fs: require('mz/fs'), 141 | viewExt: 'html', 142 | layout: false, 143 | async: true 144 | }) 145 | 146 | app.use(async function (ctx) { 147 | await ctx.render('async', { 148 | async sayHello (name) { 149 | return `Hello, ${name}` 150 | } 151 | }) 152 | }) 153 | request(app.callback()) 154 | .get('/') 155 | .expect(200) 156 | .expect('content-type', 'text/html; charset=utf-8') 157 | .expect(/Hello, Jack/, done) 158 | }) 159 | it('should render page ok with custom open/close', function (done) { 160 | const app = new Koa() 161 | render(app, { 162 | root: 'example/view', 163 | fs: require('mz/fs'), 164 | layout: 'template.oc', 165 | viewExt: 'html', 166 | delimiter: '?' 167 | }) 168 | 169 | app.use(async function (ctx) { 170 | await ctx.render('user.oc', { 171 | user: { name: 'Zed Gu' } 172 | }) 173 | }) 174 | request(app.callback()) 175 | .get('/') 176 | .expect(200) 177 | .expect('content-type', 'text/html; charset=utf-8') 178 | .expect(/Zed Gu/, done) 179 | }) 180 | it('should render page ok with `viewExt` option supporting `include` directive', function (done) { 181 | const app = new Koa() 182 | render(app, { 183 | root: 'example/view', 184 | fs: require('mz/fs'), 185 | layout: 'template', 186 | viewExt: 'html', 187 | cache: false 188 | }) 189 | 190 | app.use(function (ctx, next) { 191 | ctx.state = ctx.state || {} 192 | ctx.state.ip = ctx.ip 193 | return next() 194 | }) 195 | 196 | app.use(async function (ctx) { 197 | const users = [{ name: 'Dead Horse' }, { name: 'Runrioter Wung' }] 198 | await ctx.render('content.noext', { 199 | users 200 | }) 201 | }) 202 | request(app.callback()) 203 | .get('/') 204 | .expect(200) 205 | .expect('content-type', 'text/html; charset=utf-8') 206 | .expect(/Dead Horse/) 207 | .expect(/Runrioter Wung/, done) 208 | }) 209 | }) 210 | }) 211 | }) 212 | --------------------------------------------------------------------------------