all repos — archive/homestead @ da9ff90d3e1b1348c43cd378c0806e159a700810

My future indieweb platform

feat: Parse front matter as metadata
Alan Pearce alan@alanpearce.eu
Sun, 18 Jun 2017 12:24:24 +0200
commit

da9ff90d3e1b1348c43cd378c0806e159a700810

parent

3bda5fcfb7a23619ba66bef029d89bbd18d2c915

M package.jsonpackage.json
@@ -31,6 +31,7 @@ },   "dependencies": {
     "koa": "^2.2.0",
     "koa-nunjucks-next": "^1.1.3",
-    "koa-router": "^7.2.1"
+    "koa-router": "^7.2.1",
+    "gray-matter": "^2.1.1"
   }
 }
M src/index.jssrc/index.js
@@ -10,10 +10,15 @@ const router = new Router() 
 const view = require('koa-nunjucks-next')
 
+const metadata = require('./modules/metadata.js')
+const postMetadata = metadata.getFolderMetadata(process.env.POST_DIR)
+
 app.use(view(`${__dirname}/views`))
 
 router.get('/', async function (ctx, next) {
-  await ctx.render('index')
+  await ctx.render('index', {
+    posts: postMetadata
+  })
 })
 
 app.use(router.routes()).use(router.allowedMethods())
A src/modules/metadata.js
@@ -0,0 +1,46 @@+'use strict'
+
+const fs = require('fs')
+const path = require('path')
+const matter = require('gray-matter')
+
+const options = {
+  lang: 'toml',
+  delims: '+++'
+}
+
+function* lowercaseKeys (iterator) {
+  for (let [k, v] of iterator) {
+    yield [String(k).toLowerCase(), v]
+  }
+}
+
+function canonicaliseMetadata (meta) {
+  if (meta.data) {
+    meta.data = new Map(lowercaseKeys(Object.entries(meta.data)))
+  } else {
+    meta.data = new Map()
+  }
+  return meta
+}
+
+function readFileMetadata (filename) {
+  return canonicaliseMetadata(matter.read(filename, options))
+}
+
+function getFileMetadata (filename) {
+  const result = readFileMetadata(filename)
+  return result && result.data
+}
+
+function getFolderMetadata (folder) {
+  return fs
+    .readdirSync(folder)
+    .map(f => path.resolve(folder, f))
+    .map(readFileMetadata)
+}
+
+module.exports = {
+  getFileMetadata,
+  getFolderMetadata
+}
M src/views/index.htmlsrc/views/index.html
@@ -1,1 +1,5 @@ hello world
+
+{% for post in posts %}
+  {{ post.data.get('title') }}
+{% endfor %}
A test/data/testfile.md
@@ -0,0 +1,5 @@++++
+Title = "This is a test"
+Description = "Test file"
+Tags = ["a", "b"]
++++
M test/index.test.jstest/index.test.js
@@ -1,12 +1,15 @@ const test = require('ava')
+const path = require('path')
 const request = require('supertest')
 
+process.env.POST_DIR = path.resolve(__dirname, '../test/data/')
 const app = require('../src/index.js')
 
-test(t =>
-  request(app.listen())
+test(t => {
+  return request(app.listen())
     .get('/')
     .expect(200)
     .expect(/hello world/)
+    .expect(/This is a test/)
     .then(() => t.pass())
-)
+})
A test/modules/metadata.test.js
@@ -0,0 +1,33 @@+const test = require('ava')
+const path = require('path')
+
+const metadata = require('../../src/modules/metadata.js')
+
+test('getFileMetadata', t => {
+  const expected = new Map(
+    Object.entries({
+      title: 'This is a test',
+      description: 'Test file',
+      tags: ['a', 'b']
+    })
+  )
+  t.deepEqual(
+    metadata.getFileMetadata(path.resolve(__dirname, '../data/testfile.md')),
+    expected
+  )
+})
+
+test('getFolderMetadata', t => {
+  const expected = new Map(
+    Object.entries({
+      title: 'This is a test',
+      description: 'Test file',
+      tags: ['a', 'b']
+    })
+  )
+  const actual = metadata.getFolderMetadata(path.resolve(__dirname, '../data/'))
+  t.true(Array.isArray(actual), 'must return an array')
+  t.true(actual.length > 0, 'must return a non-empty array')
+  t.is(actual[0].path, path.resolve(__dirname, '../data/testfile.md'))
+  t.deepEqual(actual[0].data, expected)
+})
M yarn.lockyarn.lock
@@ -83,6 +83,12 @@ ansi-escapes@^1.0.0, ansi-escapes@^1.1.0:   version "1.4.0"
   resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
 
+ansi-red@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c"
+  dependencies:
+    ansi-wrap "0.1.0"
+
 ansi-regex@^2.0.0, ansi-regex@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@@ -100,6 +106,10 @@ ansi-styles@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178"
+
+ansi-wrap@0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
 
 any-promise@^1.1.0:
   version "1.3.0"
@@ -887,6 +897,10 @@ code-point-at@^1.0.0:   version "1.1.0"
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
 
+coffee-script@^1.12.4:
+  version "1.12.6"
+  resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.6.tgz#285a3f7115689065064d6bf9ef4572db66695cbf"
+
 color-convert@^1.0.0:
   version "1.9.0"
   resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
@@ -1568,6 +1582,12 @@ resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337"   dependencies:
     fill-range "^2.1.0"
 
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+  dependencies:
+    is-extendable "^0.1.0"
+
 extend@^3.0.0, extend@~3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@@ -1907,6 +1927,16 @@ "graceful-readlink@>= 1.0.0":   version "1.0.1"
   resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
 
+gray-matter@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-2.1.1.tgz#3042d9adec2a1ded6a7707a9ed2380f8a17a430e"
+  dependencies:
+    ansi-red "^0.1.1"
+    coffee-script "^1.12.4"
+    extend-shallow "^2.0.1"
+    js-yaml "^3.8.1"
+    toml "^2.3.2"
+
 har-schema@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
@@ -2160,7 +2190,7 @@ is-error@^2.2.0:   version "2.2.1"
   resolved "https://registry.yarnpkg.com/is-error/-/is-error-2.2.1.tgz#684a96d84076577c98f4cdb40c6d26a5123bf19c"
 
-is-extendable@^0.1.1:
+is-extendable@^0.1.0, is-extendable@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
 
@@ -2400,7 +2430,7 @@ js-tokens@^3.0.0:   version "3.0.1"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
 
-js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.8.2:
+js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.8.1, js-yaml@^3.8.2:
   version "3.8.4"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6"
   dependencies:
@@ -4112,6 +4142,10 @@ to-fast-properties@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+
+toml@^2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/toml/-/toml-2.3.2.tgz#5eded5ca42887924949fd06eb0e955656001e834"
 
 tough-cookie@~2.3.0:
   version "2.3.2"