diff --git a/README.md b/README.md index 6248f191..8ececa1c 100644 --- a/README.md +++ b/README.md @@ -728,8 +728,10 @@ getOne() { ``` To use rendering ability make sure to configure express / koa properly. -To use rendering ability with Koa you will need to use a rendering 3rd party such as [koa-views](https://github.com/queckezz/koa-views/), -koa-views is the only render middleware that has been tested. +To use rendering ability with Koa you will need to use a rendering 3rd party such as [@koa/ejs](https://github.com/koajs/ejs), +@koa/ejs is the only render middleware that has been tested. + +See [the koa render test file](./test/functional/koa-render-decorator.spec.ts) as an example. #### Throw HTTP errors diff --git a/package-lock.json b/package-lock.json index e0304d41..f34dc9ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,10 +18,12 @@ "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.15.0", "@koa/cors": "^5.0.0", + "@koa/ejs": "^5.1.0", "@types/express": "^5.0.0", "@types/express-session": "^1.18.0", "@types/jest": "^29.5.13", "@types/koa": "^2.15.0", + "@types/koa__ejs": "^5.1.0", "@types/multer": "^1.4.12", "@types/node": "^16.18.3", "@types/serve-static": "^1.15.7", @@ -1596,6 +1598,42 @@ "node": ">= 14.0.0" } }, + "node_modules/@koa/ejs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@koa/ejs/-/ejs-5.1.0.tgz", + "integrity": "sha512-cBP2uH+RUEuSb7zcRw6kKXucqjYNwQU1I9h59qGlvU+w08H1FYoj1lDor86f3PaekrpzyLyxZQAbtn0GMowe6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "ejs": "^3.1.8" + } + }, + "node_modules/@koa/ejs/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@koa/ejs/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@koa/multer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@koa/multer/-/multer-3.0.2.tgz", @@ -2110,6 +2148,13 @@ "@types/node": "*" } }, + "node_modules/@types/ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/express": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", @@ -2221,6 +2266,17 @@ "@types/node": "*" } }, + "node_modules/@types/koa__ejs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/koa__ejs/-/koa__ejs-5.1.0.tgz", + "integrity": "sha512-hS70hsJNucnTvy0vK6tc4YATMLzrtguFhOHroIlkXs4OnR+sI79T/99bxbZ6eZU1eUYF1Sr29QpFJu8YvgZShQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ejs": "*", + "@types/koa": "*" + } + }, "node_modules/@types/koa-compose": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz", @@ -11163,6 +11219,33 @@ "vary": "^1.1.2" } }, + "@koa/ejs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@koa/ejs/-/ejs-5.1.0.tgz", + "integrity": "sha512-cBP2uH+RUEuSb7zcRw6kKXucqjYNwQU1I9h59qGlvU+w08H1FYoj1lDor86f3PaekrpzyLyxZQAbtn0GMowe6w==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "ejs": "^3.1.8" + }, + "dependencies": { + "debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "@koa/multer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@koa/multer/-/multer-3.0.2.tgz", @@ -11438,6 +11521,12 @@ "@types/node": "*" } }, + "@types/ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "dev": true + }, "@types/express": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", @@ -11548,6 +11637,16 @@ "@types/node": "*" } }, + "@types/koa__ejs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/koa__ejs/-/koa__ejs-5.1.0.tgz", + "integrity": "sha512-hS70hsJNucnTvy0vK6tc4YATMLzrtguFhOHroIlkXs4OnR+sI79T/99bxbZ6eZU1eUYF1Sr29QpFJu8YvgZShQ==", + "dev": true, + "requires": { + "@types/ejs": "*", + "@types/koa": "*" + } + }, "@types/koa-compose": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz", diff --git a/package.json b/package.json index 05a1a15f..966042b3 100644 --- a/package.json +++ b/package.json @@ -52,10 +52,12 @@ "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.15.0", "@koa/cors": "^5.0.0", + "@koa/ejs": "^5.1.0", "@types/express": "^5.0.0", "@types/express-session": "^1.18.0", "@types/jest": "^29.5.13", "@types/koa": "^2.15.0", + "@types/koa__ejs": "^5.1.0", "@types/multer": "^1.4.12", "@types/node": "^16.18.3", "@types/serve-static": "^1.15.7", diff --git a/src/driver/koa/KoaDriver.ts b/src/driver/koa/KoaDriver.ts index 168c7126..0f7303b7 100644 --- a/src/driver/koa/KoaDriver.ts +++ b/src/driver/koa/KoaDriver.ts @@ -249,12 +249,11 @@ export class KoaDriver extends BaseDriver { options.response.redirect(action.redirect); } } else if (action.renderedTemplate) { - // if template is set then render it // TODO: not working in koa + // if template is set then render it const renderOptions = result && result instanceof Object ? result : {}; - - this.koa.use(async function (ctx: any, next: any) { - await ctx.render(action.renderedTemplate, renderOptions); - }); + const ctxLocals = options.context.locals || {}; + const oldNext = options.next; + options.next = () => options.context.render(action.renderedTemplate, {...ctxLocals, ...renderOptions}).then(oldNext); } else if (result === undefined) { // throw NotFoundError on undefined response if (action.undefinedResultCode instanceof Function) { diff --git a/test/functional/render-decorator.spec.ts b/test/functional/express-render-decorator.spec.ts similarity index 100% rename from test/functional/render-decorator.spec.ts rename to test/functional/express-render-decorator.spec.ts diff --git a/test/functional/koa-render-decorator.spec.ts b/test/functional/koa-render-decorator.spec.ts new file mode 100644 index 00000000..25dc5a29 --- /dev/null +++ b/test/functional/koa-render-decorator.spec.ts @@ -0,0 +1,84 @@ +import { Render } from '../../src/decorator/Render'; +import { Server as HttpServer } from 'http'; +import HttpStatusCodes from 'http-status-codes'; +import Koa from "koa"; +import { Controller } from '../../src/decorator/Controller'; +import { Get } from '../../src/decorator/Get'; +import { createKoaServer, getMetadataArgsStorage, Ctx } from '../../src/index'; +import { axios } from '../utilities/axios'; +import koaEjs from "@koa/ejs"; +import path from "path"; +import DoneCallback = jest.DoneCallback; + +describe(``, () => { + let koaServer: HttpServer; + + describe('koa template rendering', () => { + beforeAll((done: DoneCallback) => { + getMetadataArgsStorage().reset(); + + @Controller() + class RenderController { + @Get('/index') + @Render('ejs-render-test-spec') + index(): any { + return { + name: 'Routing-controllers', + }; + } + + @Get('/locals') + @Render('ejs-render-test-locals-spec') + locals(@Ctx() ctx: any): any { + ctx.locals = { + myVariable: 'my-variable' + }; + + return { + name: 'Routing-controllers', + }; + } + } + + const resourcePath: string = path.resolve(__dirname, '../resources'); + + const koaApp = createKoaServer() as Koa; + koaEjs(koaApp, { + root: resourcePath, + layout: false, + viewExt: "html", // Auto-appended to template name + cache: false, + debug: true, + }); + + koaServer = koaApp.listen(3001, done); + }); + + afterAll((done: DoneCallback) => { + koaServer.close(done); + }); + + it('should render a template and use given variables', async () => { + expect.assertions(6); + const response = await axios.get('/index'); + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toContain(''); + expect(response.data).toContain(''); + expect(response.data).toContain('Routing-controllers'); + expect(response.data).toContain(''); + expect(response.data).toContain(''); + }); + + it('should render a template with given variables and locals variables', async () => { + expect.assertions(7); + const response = await axios.get('/locals'); + expect(response.status).toEqual(HttpStatusCodes.OK); + expect(response.data).toContain(''); + expect(response.data).toContain(''); + expect(response.data).toContain('Routing-controllers'); + expect(response.data).toContain('my-variable'); + expect(response.data).toContain(''); + expect(response.data).toContain(''); + }); + }); +}); diff --git a/test/resources/ejs-render-test-locals-spec.html b/test/resources/ejs-render-test-locals-spec.html new file mode 100644 index 00000000..cc5c024c --- /dev/null +++ b/test/resources/ejs-render-test-locals-spec.html @@ -0,0 +1,8 @@ + + + + <%= name %> + <%= myVariable %> + + + \ No newline at end of file diff --git a/test/resources/ejs-render-test-spec.html b/test/resources/ejs-render-test-spec.html new file mode 100644 index 00000000..a272a646 --- /dev/null +++ b/test/resources/ejs-render-test-spec.html @@ -0,0 +1,5 @@ + + +<%= name %> + + \ No newline at end of file