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(/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