From b820b35b70ed2bf528e7d389bfaad60e20ee6072 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Mon, 8 Mar 2021 18:07:55 -0600 Subject: [PATCH] Add support for view engines, PNPM --- package-lock.json | 322 +++++++++++++++++++++++ package.json | 2 + pnpm-lock.yaml | 321 ++++++++++++++++++++++ src/http/kernel/HTTPKernel.ts | 4 +- src/http/response/ViewResponseFactory.ts | 22 ++ src/index.ts | 5 + src/service/Config.ts | 8 + src/service/HTTPServer.ts | 8 +- src/service/Routing.ts | 3 + src/views/PugViewEngine.ts | 33 +++ src/views/ViewEngine.ts | 23 ++ src/views/ViewEngineFactory.ts | 66 +++++ 12 files changed, 814 insertions(+), 3 deletions(-) create mode 100644 pnpm-lock.yaml create mode 100644 src/http/response/ViewResponseFactory.ts create mode 100644 src/views/PugViewEngine.ts create mode 100644 src/views/ViewEngine.ts create mode 100644 src/views/ViewEngineFactory.ts diff --git a/package-lock.json b/package-lock.json index f899114..0b5b12a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,26 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" + }, + "@babel/parser": { + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.9.tgz", + "integrity": "sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw==" + }, + "@babel/types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz", + "integrity": "sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==", + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, "@extollo/di": { "version": "file:../di", "requires": { @@ -103,20 +123,322 @@ "resolved": "https://registry.npmjs.org/@types/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha512-c4mvXFByghezQ/eVGN5HvH/jI63vm3B7FiE81BUzDAWmuiohRecCO6ddU60dfq29oKUMiQujsoB2h0JQC7JHKA==" }, + "@types/pug": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.4.tgz", + "integrity": "sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=" + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "assert-never": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" + }, + "babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "requires": { + "@babel/types": "^7.9.6" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=", + "requires": { + "is-regex": "^1.0.3" + } + }, + "constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "requires": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" + }, "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "requires": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + }, + "is-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.1" + } + }, + "js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds=" + }, + "jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=", + "requires": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "pug": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", + "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", + "requires": { + "pug-code-gen": "^3.0.2", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "requires": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "pug-code-gen": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", + "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", + "requires": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.0.0", + "pug-runtime": "^3.0.0", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "pug-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" + }, + "pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "requires": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "requires": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "requires": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "requires": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "requires": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==" + }, + "pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "requires": { + "pug-error": "^2.0.0" + } + }, + "pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=" + }, "typescript": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==" + }, + "void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=" + }, + "with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "requires": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + } } } } diff --git a/package.json b/package.json index 92729d3..4aa1a5e 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "@extollo/di": "file:../di", "@extollo/util": "file:../util", "@types/negotiator": "^0.6.1", + "@types/pug": "^2.0.4", "dotenv": "^8.2.0", "negotiator": "^0.6.2", + "pug": "^3.0.2", "typescript": "^4.1.3" }, "devDependencies": {}, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..8a05560 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,321 @@ +dependencies: + '@extollo/di': link:../di + '@extollo/util': link:../util + '@types/negotiator': 0.6.1 + '@types/pug': 2.0.4 + dotenv: 8.2.0 + negotiator: 0.6.2 + pug: 3.0.2 + typescript: 4.2.3 +lockfileVersion: 5.2 +packages: + /@babel/helper-validator-identifier/7.12.11: + dev: false + resolution: + integrity: sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + /@babel/parser/7.13.10: + dev: false + engines: + node: '>=6.0.0' + hasBin: true + resolution: + integrity: sha512-0s7Mlrw9uTWkYua7xWr99Wpk2bnGa0ANleKfksYAES8LpWH4gW1OUr42vqKNf0us5UQNfru2wPqMqRITzq/SIQ== + /@babel/types/7.13.0: + dependencies: + '@babel/helper-validator-identifier': 7.12.11 + lodash: 4.17.21 + to-fast-properties: 2.0.0 + dev: false + resolution: + integrity: sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== + /@types/negotiator/0.6.1: + dev: false + resolution: + integrity: sha512-c4mvXFByghezQ/eVGN5HvH/jI63vm3B7FiE81BUzDAWmuiohRecCO6ddU60dfq29oKUMiQujsoB2h0JQC7JHKA== + /@types/pug/2.0.4: + dev: false + resolution: + integrity: sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI= + /acorn/7.4.1: + dev: false + engines: + node: '>=0.4.0' + hasBin: true + resolution: + integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + /asap/2.0.6: + dev: false + resolution: + integrity: sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + /assert-never/1.2.1: + dev: false + resolution: + integrity: sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw== + /babel-walk/3.0.0-canary-5: + dependencies: + '@babel/types': 7.13.0 + dev: false + engines: + node: '>= 10.0.0' + resolution: + integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw== + /call-bind/1.0.2: + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.1.1 + dev: false + resolution: + integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + /character-parser/2.2.0: + dependencies: + is-regex: 1.1.2 + dev: false + resolution: + integrity: sha1-x84o821LzZdE5f/CxfzeHHMmH8A= + /constantinople/4.0.1: + dependencies: + '@babel/parser': 7.13.10 + '@babel/types': 7.13.0 + dev: false + resolution: + integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw== + /doctypes/1.1.0: + dev: false + resolution: + integrity: sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk= + /dotenv/8.2.0: + dev: false + engines: + node: '>=8' + resolution: + integrity: sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + /function-bind/1.1.1: + dev: false + resolution: + integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + /get-intrinsic/1.1.1: + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.2 + dev: false + resolution: + integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + /has-symbols/1.0.2: + dev: false + engines: + node: '>= 0.4' + resolution: + integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + /has/1.0.3: + dependencies: + function-bind: 1.1.1 + dev: false + engines: + node: '>= 0.4.0' + resolution: + integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + /is-core-module/2.2.0: + dependencies: + has: 1.0.3 + dev: false + resolution: + integrity: sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + /is-expression/4.0.0: + dependencies: + acorn: 7.4.1 + object-assign: 4.1.1 + dev: false + resolution: + integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A== + /is-promise/2.2.2: + dev: false + resolution: + integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + /is-regex/1.1.2: + dependencies: + call-bind: 1.0.2 + has-symbols: 1.0.2 + dev: false + engines: + node: '>= 0.4' + resolution: + integrity: sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== + /js-stringify/1.0.2: + dev: false + resolution: + integrity: sha1-Fzb939lyTyijaCrcYjCufk6Weds= + /jstransformer/1.0.0: + dependencies: + is-promise: 2.2.2 + promise: 7.3.1 + dev: false + resolution: + integrity: sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM= + /lodash/4.17.21: + dev: false + resolution: + integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + /negotiator/0.6.2: + dev: false + engines: + node: '>= 0.6' + resolution: + integrity: sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + /object-assign/4.1.1: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + /path-parse/1.0.6: + dev: false + resolution: + integrity: sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + /promise/7.3.1: + dependencies: + asap: 2.0.6 + dev: false + resolution: + integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + /pug-attrs/3.0.0: + dependencies: + constantinople: 4.0.1 + js-stringify: 1.0.2 + pug-runtime: 3.0.1 + dev: false + resolution: + integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA== + /pug-code-gen/3.0.2: + dependencies: + constantinople: 4.0.1 + doctypes: 1.1.0 + js-stringify: 1.0.2 + pug-attrs: 3.0.0 + pug-error: 2.0.0 + pug-runtime: 3.0.1 + void-elements: 3.1.0 + with: 7.0.2 + dev: false + resolution: + integrity: sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg== + /pug-error/2.0.0: + dev: false + resolution: + integrity: sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ== + /pug-filters/4.0.0: + dependencies: + constantinople: 4.0.1 + jstransformer: 1.0.0 + pug-error: 2.0.0 + pug-walk: 2.0.0 + resolve: 1.20.0 + dev: false + resolution: + integrity: sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A== + /pug-lexer/5.0.1: + dependencies: + character-parser: 2.2.0 + is-expression: 4.0.0 + pug-error: 2.0.0 + dev: false + resolution: + integrity: sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w== + /pug-linker/4.0.0: + dependencies: + pug-error: 2.0.0 + pug-walk: 2.0.0 + dev: false + resolution: + integrity: sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw== + /pug-load/3.0.0: + dependencies: + object-assign: 4.1.1 + pug-walk: 2.0.0 + dev: false + resolution: + integrity: sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ== + /pug-parser/6.0.0: + dependencies: + pug-error: 2.0.0 + token-stream: 1.0.0 + dev: false + resolution: + integrity: sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw== + /pug-runtime/3.0.1: + dev: false + resolution: + integrity: sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg== + /pug-strip-comments/2.0.0: + dependencies: + pug-error: 2.0.0 + dev: false + resolution: + integrity: sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ== + /pug-walk/2.0.0: + dev: false + resolution: + integrity: sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ== + /pug/3.0.2: + dependencies: + pug-code-gen: 3.0.2 + pug-filters: 4.0.0 + pug-lexer: 5.0.1 + pug-linker: 4.0.0 + pug-load: 3.0.0 + pug-parser: 6.0.0 + pug-runtime: 3.0.1 + pug-strip-comments: 2.0.0 + dev: false + resolution: + integrity: sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw== + /resolve/1.20.0: + dependencies: + is-core-module: 2.2.0 + path-parse: 1.0.6 + dev: false + resolution: + integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + /to-fast-properties/2.0.0: + dev: false + engines: + node: '>=4' + resolution: + integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + /token-stream/1.0.0: + dev: false + resolution: + integrity: sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ= + /typescript/4.2.3: + dev: false + engines: + node: '>=4.2.0' + hasBin: true + resolution: + integrity: sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== + /void-elements/3.1.0: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-YU9/v42AHwu18GYfWy9XhXUOTwk= + /with/7.0.2: + dependencies: + '@babel/parser': 7.13.10 + '@babel/types': 7.13.0 + assert-never: 1.2.1 + babel-walk: 3.0.0-canary-5 + dev: false + engines: + node: '>= 10.0.0' + resolution: + integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w== +specifiers: + '@extollo/di': file:../di + '@extollo/util': file:../util + '@types/negotiator': ^0.6.1 + '@types/pug': ^2.0.4 + dotenv: ^8.2.0 + negotiator: ^0.6.2 + pug: ^3.0.2 + typescript: ^4.1.3 diff --git a/src/http/kernel/HTTPKernel.ts b/src/http/kernel/HTTPKernel.ts index f302c30..44b0c14 100644 --- a/src/http/kernel/HTTPKernel.ts +++ b/src/http/kernel/HTTPKernel.ts @@ -4,7 +4,7 @@ import {HTTPKernelModule} from "./HTTPKernelModule"; import {Logging} from "../../service/Logging"; import {AppClass} from "../../lifecycle/AppClass"; import {Request} from "../lifecycle/Request"; -import {http} from "../response/HTTPErrorResponseFactory"; +import {error} from "../response/ErrorResponseFactory"; /** * Interface for fluently registering kernel modules into the kernel. @@ -73,7 +73,7 @@ export class HTTPKernel extends AppClass { } } catch (e: any) { this.logging.error(e) - await http(HTTPStatus.REQUEST_TIMEOUT).write(request) + await error(e).status(HTTPStatus.INTERNAL_SERVER_ERROR).write(request) } this.logging.verbose('Finished kernel lifecycle') diff --git a/src/http/response/ViewResponseFactory.ts b/src/http/response/ViewResponseFactory.ts new file mode 100644 index 0000000..f9e72a4 --- /dev/null +++ b/src/http/response/ViewResponseFactory.ts @@ -0,0 +1,22 @@ +import {Container} from "@extollo/di"; +import {ResponseFactory} from "./ResponseFactory"; +import {Request} from "../lifecycle/Request"; +import {ViewEngine} from "../../views/ViewEngine"; + +export function view(name: string, data?: {[key: string]: any}): ViewResponseFactory { + return new ViewResponseFactory(name, data) +} + +export class ViewResponseFactory extends ResponseFactory { + constructor( + public readonly viewName: string, + public readonly data?: {[key: string]: any} + ) { super() } + + public async write(request: Request) { + const viewEngine = Container.getContainer().make(ViewEngine) + request.response.body = await viewEngine.renderByName(this.viewName, this.data || {}) + request.response.setHeader('Content-Type', 'text/html; charset=utf-8') + return request + } +} diff --git a/src/index.ts b/src/index.ts index e091b8c..6e280b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,6 +29,7 @@ export * from './http/response/JSONResponseFactory' export * from './http/response/ResponseFactory' export * from './http/response/StringResponseFactory' export * from './http/response/TemporaryRedirectResponseFactory' +export * from './http/response/ViewResponseFactory' export * from './http/routing/ActivatedRoute' export * from './http/routing/Route' @@ -51,3 +52,7 @@ export * from './service/Config' export * from './service/Controllers' export * from './service/HTTPServer' export * from './service/Routing' + +export * from './views/ViewEngine' +export * from './views/ViewEngineFactory' +export * from './views/PugViewEngine' diff --git a/src/service/Config.ts b/src/service/Config.ts index 4ad5c03..d87566b 100644 --- a/src/service/Config.ts +++ b/src/service/Config.ts @@ -6,4 +6,12 @@ export class Config extends CanonicalRecursive { protected appPath: string[] = ['configs'] protected suffix: string = '.config.js' protected canonicalItem: string = 'config' + + public async up() { + await super.up() + + if ( this.get('server.debug', false) ) { + Error.stackTraceLimit = Infinity + } + } } diff --git a/src/service/HTTPServer.ts b/src/service/HTTPServer.ts index 62d6124..0a3b371 100644 --- a/src/service/HTTPServer.ts +++ b/src/service/HTTPServer.ts @@ -11,6 +11,7 @@ import {InjectSessionHTTPModule} from "../http/kernel/module/InjectSessionHTTPMo import {PersistSessionHTTPModule} from "../http/kernel/module/PersistSessionHTTPModule"; import {MountActivatedRouteHTTPModule} from "../http/kernel/module/MountActivatedRouteHTTPModule"; import {ExecuteResolvedRouteHandlerHTTPModule} from "../http/kernel/module/ExecuteResolvedRouteHandlerHTTPModule"; +import {error} from "../http/response/ErrorResponseFactory"; @Singleton() export class HTTPServer extends Unit { @@ -76,7 +77,12 @@ export class HTTPServer extends Unit { .run() .catch(e => this.logging.error(e)) - await this.kernel.handle(extolloReq) + try { + await this.kernel.handle(extolloReq) + } catch (e) { + await error(e).write(extolloReq) + } + await extolloReq.response.send() } } diff --git a/src/service/Routing.ts b/src/service/Routing.ts index 226a374..b28c28d 100644 --- a/src/service/Routing.ts +++ b/src/service/Routing.ts @@ -4,6 +4,7 @@ import {Unit} from "../lifecycle/Unit" import {Logging} from "./Logging" import {Route} from "../http/routing/Route"; import {HTTPMethod} from "../http/lifecycle/Request"; +import {ViewEngineFactory} from "../views/ViewEngineFactory"; @Singleton() export class Routing extends Unit { @@ -13,6 +14,8 @@ export class Routing extends Unit { protected compiledRoutes: Collection = new Collection() public async up() { + this.app().registerFactory(new ViewEngineFactory()); + for await ( const entry of this.path.walk() ) { if ( !entry.endsWith('.routes.js') ) { this.logging.debug(`Skipping routes file with invalid suffix: ${entry}`) diff --git a/src/views/PugViewEngine.ts b/src/views/PugViewEngine.ts new file mode 100644 index 0000000..8b8c1d7 --- /dev/null +++ b/src/views/PugViewEngine.ts @@ -0,0 +1,33 @@ +import {ViewEngine} from "./ViewEngine" +import {Injectable} from "@extollo/di" +import * as pug from "pug" + +@Injectable() +export class PugViewEngine extends ViewEngine { + protected compileCache: {[key: string]: ((locals?: pug.LocalsObject) => string)} = {} + + public renderString(templateString: string, locals: { [p: string]: any }): string | Promise { + return pug.compile(templateString, this.getOptions())(locals) + } + + public renderByName(templateName: string, locals: { [p: string]: any }): string | Promise { + let compiled = this.compileCache[templateName] + if ( compiled ) return compiled(locals) + + if ( !templateName.endsWith('.pug') ) templateName += '.pug' + const filePath = this.path.concat(...templateName.split(':')) + compiled = pug.compileFile(filePath.toLocal, this.getOptions()) + + this.compileCache[templateName] = compiled + return compiled(locals) + } + + protected getOptions() { + return { + basedir: this.path.toLocal, + debug: this.debug, + compileDebug: this.debug, + globals: [], + } + } +} diff --git a/src/views/ViewEngine.ts b/src/views/ViewEngine.ts new file mode 100644 index 0000000..9e13320 --- /dev/null +++ b/src/views/ViewEngine.ts @@ -0,0 +1,23 @@ +import {AppClass} from "../lifecycle/AppClass" +import {Config} from "../service/Config" +import {Container} from "@extollo/di" +import {UniversalPath} from "@extollo/util" + +export abstract class ViewEngine extends AppClass { + protected readonly config: Config + protected readonly debug: boolean + + constructor() { + super() + this.config = Container.getContainer().make(Config) + this.debug = (this.config.get('server.mode', 'production') === 'development' + || this.config.get('server.debug', false)) + } + + public get path(): UniversalPath { + return this.app().appPath(...['resources', 'views']) // FIXME allow configuring + } + + public abstract renderString(templateString: string, locals: {[key: string]: any}): string | Promise + public abstract renderByName(templateName: string, locals: {[key: string]: any}): string | Promise +} diff --git a/src/views/ViewEngineFactory.ts b/src/views/ViewEngineFactory.ts new file mode 100644 index 0000000..a70a7f9 --- /dev/null +++ b/src/views/ViewEngineFactory.ts @@ -0,0 +1,66 @@ +import { + AbstractFactory, + Container, + DependencyRequirement, + PropertyDependency, + isInstantiable, + DEPENDENCY_KEYS_METADATA_KEY, + DEPENDENCY_KEYS_PROPERTY_METADATA_KEY +} from "@extollo/di" +import {Collection, ErrorWithContext} from "@extollo/util" +import {Logging} from "../service/Logging"; +import {Config} from "../service/Config"; +import {ViewEngine} from "./ViewEngine"; +import {PugViewEngine} from "./PugViewEngine"; + +export class ViewEngineFactory extends AbstractFactory { + protected readonly logging: Logging + protected readonly config: Config + + constructor() { + super({}) + this.logging = Container.getContainer().make(Logging) + this.config = Container.getContainer().make(Config) + } + + produce(dependencies: any[], parameters: any[]): ViewEngine { + return new (this.getViewEngineClass()) + } + + match(something: any) { + return something === ViewEngine + } + + getDependencyKeys(): Collection { + const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getViewEngineClass()) + if ( meta ) return meta + return new Collection() + } + + getInjectedProperties(): Collection { + const meta = new Collection() + let currentToken = this.getViewEngineClass() + + do { + const loadedMeta = Reflect.getMetadata(DEPENDENCY_KEYS_PROPERTY_METADATA_KEY, currentToken) + if ( loadedMeta ) meta.concat(loadedMeta) + currentToken = Object.getPrototypeOf(currentToken) + } while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype) + + return meta + } + + protected getViewEngineClass() { + const ViewEngineClass = this.config.get('server.view_engine.driver', PugViewEngine) + + if ( !isInstantiable(ViewEngineClass) || !(ViewEngineClass.prototype instanceof ViewEngine) ) { + const e = new ErrorWithContext('Provided session class does not extend from @extollo/lib.ViewEngine'); + e.context = { + config_key: 'server.view_engine.driver', + class: ViewEngineClass.toString(), + } + } + + return ViewEngineClass + } +}