Switch templating to hyperfast This also means that highland is not (currently) required
Alan Pearce alan@alanpearce.eu
Sat, 01 Jul 2017 11:49:18 +0200
7 files changed, 107 insertions(+), 154 deletions(-)
M package.json → package.json
@@ -46,14 +46,13 @@ }, "dependencies": { "configly": "^4.1.0", "gray-matter": "^2.1.1", - "highland": "^2.11.0", + "hyperfast": "^2.1.0", "indent-string": "^3.1.0", "koa": "^2.2.0", "koa-helmet": "^3.2.0", "koa-router": "^7.2.1", "koa-send": "^4.1.0", "markdown-it": "^8.3.1", - "rheo": "^2.2.0", "toml": "^2.3.2" } }
M src/actions.js → src/actions.js
@@ -1,46 +1,41 @@-'use strict' +"use strict"; -const send = require('koa-send') -const h = require('highland') -const responders = require('./responders') +const send = require("koa-send"); +const responders = require("./responders"); -function toArrayStream (iterator) { - return h(iterator.entries()) +function home(config, posts) { + const postsArray = Array.from(posts.values()); + return async function(ctx, next) { + responders.home(ctx, config, postsArray); + }; } -function home (config, posts) { - const postsStream = toArrayStream(posts) - return async function (ctx, next) { - responders.home(ctx, config, postsStream) - } -} +function post(config, posts) { + return async function(ctx, next) { + ctx.assert(posts.has(ctx.params.filename), 404, "Post not found"); + const post = posts.get(ctx.params.filename); -function post (config, posts) { - return async function (ctx, next) { - ctx.assert(posts.has(ctx.params.filename), 404, 'Post not found') - const post = posts.get(ctx.params.filename) - - responders.post(ctx, config, post) - } + responders.post(ctx, config, post); + }; } -function taxonGenerator (config, term, items) { - return async function (ctx, next) { - const value = ctx.params.value - ctx.assert(items.has(ctx.params.value), 404, `${term} ${value} not found`) +function taxonGenerator(config, term, items) { + return async function(ctx, next) { + const value = ctx.params.value; + ctx.assert(items.has(ctx.params.value), 404, `${term} ${value} not found`); - const taxonItems = toArrayStream(items.get(value)) + const taxonItems = items.get(value); - responders.taxon(ctx, config, taxonItems) - } + responders.taxon(ctx, config, taxonItems); + }; } -const prefix = /^\/static\// -async function serveFiles (ctx) { +const prefix = /^\/static\//; +async function serveFiles(ctx) { if (prefix.test(ctx.path)) { - await send(ctx, ctx.path.replace(prefix, ''), { - root: './static' - }) + await send(ctx, ctx.path.replace(prefix, ""), { + root: "./static" + }); } } @@ -49,4 +44,4 @@ home, post, taxonGenerator, serveFiles -} +};
M src/responders.js → src/responders.js
@@ -1,13 +1,8 @@ "use strict"; -const h = require("highland"); const fs = require("fs"); -const rheo = require("rheo"); +const hyperfast = require("hyperfast"); const indent = require("indent-string"); -const PassThrough = require("stream").PassThrough; - -const toLines = string => - string.split("\n").map((s, i, arr) => (i === arr.length - 1 ? s : s + "\n")); const getTemplate = name => fs.readFileSync(`${__dirname}/templates/${name}.html`, "utf8"); @@ -15,21 +10,15 @@ const findMain = /^(\s+)<main/m; const baseIndentLevel = findMain.exec(getTemplate("layout"))[1].length; const postIndentLevel = - baseIndentLevel + findMain.exec(getTemplate("post"))[1].length; + baseIndentLevel + + /^(\s+)<div class="post-content/m.exec(getTemplate("post"))[1].length; function indentForTemplate(text, indentLevel) { return indent(text, indentLevel).slice(indentLevel).replace(/\n+$/, ""); } function templateReader(template, indentLevel) { - const content = toLines( - indentForTemplate(getTemplate(template), indentLevel) - ); - return () => h(content); -} - -function prependDoctype(stream) { - return h(["<!DOCTYPE html>"]).concat(stream).pipe(new PassThrough()); + return indentForTemplate(getTemplate(template), indentLevel); } const templates = { @@ -39,29 +28,27 @@ post: templateReader("post", baseIndentLevel), taxon: templateReader("taxon", baseIndentLevel) }; -function setTitle(siteTitle, pageTitle) { - return rheo.template(function(s) { - return s - .inner( - "title", - rheo(pageTitle ? `${pageTitle} · ${siteTitle}` : siteTitle) - ) - .inner("body header h1", rheo(siteTitle)); - }); +function title(siteTitle, pageTitle) { + return pageTitle ? `${pageTitle} · ${siteTitle}` : siteTitle; } -function renderPostListItem(ctx) { - return function(template, [, post]) { - return template - .attribute("a", "href", () => ctx.getURL("post", post.basename)) - .inner("a", () => rheo(post.data.get("title"))); - }; -} +const renderPostListItem = ctx => post => ({ + a: { + href: ctx.getURL(post, post.basename), + _text: post.data.get("title") + } +}); -function showPage(name) { - return function(els) { - return rheo(templates[name]()); - }; +function layout(config, pageTitle, pageElement) { + return hyperfast(templates.layout, { + title: title(config.site.author.name, pageTitle), + "body > header .p-name": config.site.author.name, + "body > header .u-photo": { + alt: config.site.author.name, + src: config.site.author.photo + }, + "body > main": pageElement + }).outerHTML.trim(); } module.exports = { @@ -69,51 +56,42 @@ baseIndentLevel, postIndentLevel, indentForTemplate, - home(ctx, config, postsStream) { + home(ctx, config, posts) { ctx.type = "html"; - ctx.body = prependDoctype( - templates - .layout() - .pipe(rheo()) - .attribute(".u-photo", "alt", config.site.author.name) - .attribute(".u-photo", "src", config.site.author.photo) - .outer("main", showPage("home")) - .inner(".posts", function(postsTemplate) { - return postsStream.pipe(postsTemplate.map(renderPostListItem(ctx))); - }) - .pipe(setTitle(config.site.author.name)) - .render() + ctx.body = layout( + config, + null, + hyperfast(templates.home, { + ".post": posts.map(renderPostListItem(ctx)) + }) ); }, post(ctx, config, post) { ctx.type = "html"; - ctx.body = prependDoctype( - templates - .layout() - .pipe(rheo()) - .outer("main", showPage("post")) - .inner("article h1", rheo(post.data.get("title"))) - .outer("article main", rheo(post.body)) - .pipe(setTitle(config.site.author.name, post.data.get("title"))) - .render() + + ctx.body = layout( + config, + post.data.get("title"), + hyperfast(templates.post, { + "article h1": post.data.get("title"), + "article .post-content": { + _html: post.body + } + }) ); }, taxon(ctx, config, taxonItems) { ctx.type = "html"; - ctx.body = prependDoctype( - templates - .layout() - .pipe(rheo()) - .outer("main", showPage("taxon")) - .inner("h1", rheo(config.site.author.name)) - .inner(".posts", function(postsTemplate) { - return taxonItems.pipe(postsTemplate.map(renderPostListItem(ctx))); - }) - .pipe(setTitle(config.site.author.name)) - .render() + + ctx.body = layout( + config, + null, + hyperfast(templates.taxon, { + ".post": taxonItems.map(renderPostListItem(ctx)) + }) ); } };
M src/templates/home.html → src/templates/home.html
@@ -1,7 +1,5 @@-<main class="home"> - <ul class="posts"> - <li class="post"> - <a href="/">Test post please ignore</a> - </li> - </ul> -</main> +<ul class="posts"> + <li class="post"> + <a href="/">Test post please ignore</a> + </li> +</ul>
M src/templates/post.html → src/templates/post.html
@@ -1,11 +1,9 @@-<main class="post"> - <article> - <h1>post title</h1> - <main> - 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. - </main> - </article> -</main> +<article> + <h1>post title</h1> + <div class="post-content"> + 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. + </div> +</article>
M test/app.test.js → test/app.test.js
@@ -32,6 +32,7 @@ .then($ => { t.is($("head > title").text(), "John Doe"); t.is($("h1").text(), "John Doe"); t.is($("main").length, 1); + t.is($("main .posts").length, 1); return $; }) .then(toMicroformatsOptions)
M yarn.lock → yarn.lock
@@ -1153,7 +1153,7 @@ 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" -css-select@~1.2.0: +css-select@^1.1.0, css-select@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" dependencies: @@ -1165,12 +1165,6 @@ css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" - -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" @@ -1399,7 +1393,7 @@ dependencies: dom-serializer "0" domelementtype "1" -domutils@^1.5.1: +domutils@^1.5.0, domutils@^1.5.1: version "1.6.2" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff" dependencies: @@ -1415,10 +1409,6 @@ 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" @@ -1466,6 +1456,10 @@ version "1.0.0" resolved "https://registry.yarnpkg.com/enhance-visitors/-/enhance-visitors-1.0.0.tgz#aa945d05da465672a1ebd38fee2ed3da8518e95a" dependencies: lodash "^4.13.1" + +ent@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" entities@^1.1.1, entities@~1.1.1: version "1.1.1" @@ -2359,12 +2353,6 @@ hide-powered-by@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.0.0.tgz#4a85ad65881f62857fc70af7174a1184dccce32b" -highland@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/highland/-/highland-2.11.0.tgz#4d156709c5f10bc31cab6a97c7feb6c373e2466d" - dependencies: - util-deprecate "^1.0.2" - hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" @@ -2390,7 +2378,7 @@ resolved "https://registry.yarnpkg.com/hsts/-/hsts-2.0.0.tgz#a52234c6070decf214b2b6b70bb144d07e4776c7" dependencies: core-util-is "1.0.2" -htmlparser2@^3.8.3, htmlparser2@^3.9.1: +htmlparser2@^3.8.2, htmlparser2@^3.9.1: version "3.9.2" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" dependencies: @@ -2460,6 +2448,15 @@ chalk "^1.1.3" find-parent-dir "^0.3.0" is-ci "^1.0.9" normalize-path "^1.0.0" + +hyperfast@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hyperfast/-/hyperfast-2.1.0.tgz#72c91f87126c54e6fe0ad2264ece278b3a6c0a56" + dependencies: + css-select "^1.1.0" + domutils "^1.5.0" + ent "^2.2.0" + htmlparser2 "^3.8.2" iconv-lite@^0.4.17: version "0.4.18" @@ -4445,15 +4442,6 @@ dependencies: 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" @@ -4854,7 +4842,7 @@ dependencies: readable-stream "^2.1.5" xtend "~4.0.1" -through@X.X.X, through@^2.3.6: +through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -5005,7 +4993,7 @@ resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" dependencies: os-homedir "^1.0.0" -util-deprecate@^1.0.2, util-deprecate@~1.0.1: +util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -5033,10 +5021,6 @@ version "1.3.6" resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" 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"