From f9fd0d795fb7bff10cb759f2300200d50b0fbab3 Mon Sep 17 00:00:00 2001 From: Alan Pearce Date: Fri, 23 Jun 2017 23:06:43 +0200 Subject: feat: Streaming DOM templates via rheo --- package.json | 3 +- posts/testfile.md | 6 ++ src/index.js | 82 +++++++++++++++++++--------- src/templates/index.html | 36 ++++++++++++ src/views/index.njk | 17 ------ src/views/layouts/main.njk | 13 ----- src/views/post.njk | 8 --- src/views/term.njk | 7 --- test/index.test.js | 2 +- yarn.lock | 133 +++++++++++++++++++++++++++++++-------------- 10 files changed, 193 insertions(+), 114 deletions(-) create mode 100644 posts/testfile.md create mode 100644 src/templates/index.html delete mode 100644 src/views/index.njk delete mode 100644 src/views/layouts/main.njk delete mode 100644 src/views/post.njk delete mode 100644 src/views/term.njk diff --git a/package.json b/package.json index c844414..4c3d91b 100644 --- a/package.json +++ b/package.json @@ -32,10 +32,11 @@ "configly": "^4.1.0", "gray-matter": "^2.1.1", "koa": "^2.2.0", - "koa-nunjucks-next": "^1.1.3", "koa-router": "^7.2.1", "koa-send": "^4.1.0", "markdown-it": "^8.3.1", + "rheo": "^2.2.0", + "stream-array": "^1.1.2", "toml": "^2.3.2" } } diff --git a/posts/testfile.md b/posts/testfile.md new file mode 100644 index 0000000..bafc456 --- /dev/null +++ b/posts/testfile.md @@ -0,0 +1,6 @@ ++++ +Title = "This is a test" +Description = "Test file" +Tags = ["a", "b"] ++++ +# Lorem ipsum diff --git a/src/index.js b/src/index.js index fe0351d..93d61a4 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,9 @@ const Koa = require('koa') const app = new Koa() +const fs = require('fs') +const streamify = require('stream-array') + const send = require('koa-send') const config = require('./modules/config.js') @@ -12,51 +15,80 @@ const PORT = process.env.PORT || config.server.port const Router = require('koa-router') const router = new Router() -const view = require('koa-nunjucks-next') +const rheo = require('rheo') const Posts = require('./modules/posts.js') const posts = Posts.getFolder(config.posts.folder) -app.use( - view(`${__dirname}/views`, { - extname: 'njk', - globals: { - site: config.site, - url: (...args) => router.url(...args) - } - }) -) +const index = () => fs.createReadStream(`${__dirname}/templates/index.html`) -const postsArray = Array.from(posts.entries()) -router.get('home', '/', async function (ctx) { - await ctx.render('index', { - posts: postsArray - }) +function setTitle (pageTitle) { + return rheo.template(s => + s.inner('title', rheo((pageTitle ? ' · ' : '') + config.site.title)) + ) +} + +function renderPostListItem (template, [, post]) { + return template + .attribute('a', 'href', () => router.url('post', post.basename)) + .inner('a', () => rheo(post.data.get('title'))) +} + +function toArrayStream (iterator) { + return streamify(Array.from(iterator.entries())) +} + +const postsStream = toArrayStream(posts) +router.get('home', '/', async function (ctx, next) { + ctx.set('Content-Type', 'text/html') + ctx.body = index() + .pipe(rheo()) + .inner('body', pages => pages.find('main.homepage')) + .inner('h1', rheo(config.site.title)) + .inner('.posts', function (postsTemplate) { + return postsStream.pipe(postsTemplate.map(renderPostListItem)) + }) + .pipe(setTitle()) + .render() }) -router.get('post', '/post/:filename', async function (ctx) { +router.get('post', '/post/:filename', async function (ctx, next) { ctx.assert(posts.has(ctx.params.filename), 404, 'Post not found') const post = posts.get(ctx.params.filename) post.body = Posts.render(post) - await ctx.render('post', { - post: post - }) + + ctx.set('Content-Type', 'text/html') + ctx.body = index() + .pipe(rheo()) + .inner('body', pages => pages.find('main.post')) + .inner('article h1', rheo(post.data.get('title'))) + .inner('article main', rheo(post.body)) + .pipe(setTitle()) + .render() }) const taxonomies = Posts.taxonomise(config.taxonomies, posts) for (let [term, items] of taxonomies) { - router.get(`taxon-${term}`, `/${term}/:value`, async function (ctx) { + router.get(`taxon-${term}`, `/${term}/:value`, async function (ctx, next) { const value = ctx.params.value ctx.assert( items.has(ctx.params.value), 404, `Could not find ${term} ${value}` ) - await ctx.render('term', { - term: term, - [term]: value, - posts: items.get(ctx.params.value) - }) + + ctx.set('Content-Type', 'text/html') + ctx.body = index() + .pipe(rheo()) + .inner('body', pages => pages.find('main.taxon')) + .inner('h1', rheo(config.site.title)) + .inner('.posts', function (postsTemplate) { + return toArrayStream(items.get(value)).pipe( + postsTemplate.map(renderPostListItem) + ) + }) + .pipe(setTitle()) + .render() }) } diff --git a/src/templates/index.html b/src/templates/index.html new file mode 100644 index 0000000..412b4ec --- /dev/null +++ b/src/templates/index.html @@ -0,0 +1,36 @@ + + + + + + + +
+

hello world

+ + +
+
+
+

post title

+
+ Fringilla ut morbi tincidunt augue interdum velit euismod! + Interdum velit laoreet id donec ultrices tincidunt arcu, non + sodales neque sodales ut etiam sit amet nisl purus, in + mollis nunc sed. +
+
+
+
+ +
+ + diff --git a/src/views/index.njk b/src/views/index.njk deleted file mode 100644 index 9a7cc80..0000000 --- a/src/views/index.njk +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "layouts/main.njk" %} - -{% block body %} - -

hello world

- - - -{% endblock %} diff --git a/src/views/layouts/main.njk b/src/views/layouts/main.njk deleted file mode 100644 index 1fe1a93..0000000 --- a/src/views/layouts/main.njk +++ /dev/null @@ -1,13 +0,0 @@ - - - - - {% block title %}{{ site.title }}{% endblock %} - - -
- {% block body %} - {% endblock %} -
- - diff --git a/src/views/post.njk b/src/views/post.njk deleted file mode 100644 index d6a4f92..0000000 --- a/src/views/post.njk +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "layouts/main.njk" %} - -{% block body %} - -{{ post.data.title }} - -{{ post.body | safe }} -{% endblock %} diff --git a/src/views/term.njk b/src/views/term.njk deleted file mode 100644 index 6ac932e..0000000 --- a/src/views/term.njk +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "layouts/main.njk" %} - -{% block body %} -{% for post in posts %} - {{ post.data.get('title') }} -{% endfor %} -{% endblock %} diff --git a/test/index.test.js b/test/index.test.js index 73bbd19..769097f 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -12,7 +12,7 @@ test('homepage', t => { .get('/') .expect(200) .expect(/Test Site<\/title>/) - .expect(/hello world/) + .expect(/<h1>Test Site<\/h1>/) .expect(/This is a test/) .then(() => t.pass()) }) diff --git a/yarn.lock b/yarn.lock index 0179800..5536609 100644 --- a/yarn.lock +++ b/yarn.lock @@ -190,10 +190,6 @@ arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" -asap@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" - asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -708,6 +704,10 @@ buf-compare@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/buf-compare/-/buf-compare-1.0.1.tgz#fef28da8b8113a0a0db4430b0b6467b69730b34a" +buffer-shims@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -750,7 +750,7 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase@^2.0.0, camelcase@^2.0.1: +camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -792,7 +792,7 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chokidar@1.6.1, chokidar@^1.4.2, chokidar@^1.6.0: +chokidar@1.6.1, chokidar@^1.4.2: version "1.6.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" dependencies: @@ -869,7 +869,7 @@ cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" -cliui@^3.0.3, cliui@^3.2.0: +cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" dependencies: @@ -1083,6 +1083,12 @@ crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" +cssauron@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8" + dependencies: + through X.X.X + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -1247,12 +1253,44 @@ doctrine@^2.0.0: esutils "^2.0.2" isarray "^1.0.0" +dom-serializer@0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" + dependencies: + domelementtype "1" + +domutils@^1.5.1: + version "1.6.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff" + dependencies: + dom-serializer "0" + domelementtype "1" + dot-prop@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.1.1.tgz#a8493f0b7b5eeec82525b5c7587fa7de7ca859c1" dependencies: is-obj "^1.0.0" +double-ended-queue@^0.9.7: + version "0.9.7" + resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-0.9.7.tgz#8ae0a7265df66cdc3f07dce558e9716adb586ab8" + duplexer2@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -1284,7 +1322,7 @@ empower-core@^0.6.1: call-signature "0.0.2" core-js "^2.0.0" -entities@~1.1.1: +entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" @@ -2042,6 +2080,17 @@ hosted-git-info@^2.1.4: version "2.4.2" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" +htmlparser2@^3.8.3: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + http-assert@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.3.0.tgz#a31a5cf88c873ecbb5796907d4d6f132e8c01e4a" @@ -2558,13 +2607,6 @@ koa-is-json@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" -koa-nunjucks-next@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/koa-nunjucks-next/-/koa-nunjucks-next-1.1.3.tgz#20ec177bb5790153beaefae77e4151db586c66df" - dependencies: - debug "^2.2.0" - nunjucks "^2.4.2" - koa-router@^7.2.1: version "7.2.1" resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-7.2.1.tgz#b40a4ab3c6adb4b40895debd00a9c640304e3039" @@ -3116,14 +3158,6 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" -nunjucks@^2.4.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-2.5.2.tgz#ea7d346e785b8a4874666c3cca9e18c577fba22c" - dependencies: - asap "^2.0.3" - chokidar "^1.6.0" - yargs "^3.32.0" - oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -3608,7 +3642,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2: +readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2: version "2.2.11" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a8088d34aa17c0f72" dependencies: @@ -3620,6 +3654,18 @@ readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable string_decoder "~1.0.0" util-deprecate "~1.0.1" +readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@~2.1.0: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + readable-stream@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" @@ -3860,6 +3906,15 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +rheo@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/rheo/-/rheo-2.2.0.tgz#614187095b12847f49f284e2f804ad59623ca243" + dependencies: + cssauron "^1.2.0" + double-ended-queue "^0.9.7" + htmlparser2 "^3.8.3" + void-elements "^2.0.1" + rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" @@ -4043,6 +4098,12 @@ standard@^10.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" +stream-array@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/stream-array/-/stream-array-1.1.2.tgz#9e5f7345f2137c30ee3b498b9114e80b52bb7eb5" + dependencies: + readable-stream "~2.1.0" + stream-to-observable@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" @@ -4225,7 +4286,7 @@ through2@^2.0.0: readable-stream "^2.1.5" xtend "~4.0.1" -through@^2.3.6: +through@X.X.X, through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -4391,6 +4452,10 @@ verror@1.3.6: dependencies: extsprintf "1.0.2" +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" @@ -4413,10 +4478,6 @@ widest-line@^1.0.0: dependencies: string-width "^1.0.1" -window-size@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - window-size@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" @@ -4484,7 +4545,7 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" -y18n@^3.2.0, y18n@^3.2.1: +y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" @@ -4516,15 +4577,3 @@ yargs@6.3.0: window-size "^0.2.0" y18n "^3.2.1" yargs-parser "^4.0.2" - -yargs@^3.32.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" -- cgit 1.4.1