From: Lady Date: Mon, 8 Aug 2022 01:23:42 +0000 (-0700) Subject: Add s for feed/page discovery X-Git-Tag: 0.1.0~3 X-Git-Url: https://git.ladys.computer/Beorn/commitdiff_plain/8ad980140c4a432e5fa3c7a8bc0cd1ba219362cb?ds=sidebyside;hp=87e79e5a942a55f386f349242b8b554d6a50db4d Add s for feed/page discovery And other various improvements to page metadata generation. --- diff --git a/README.markdown b/README.markdown index 6cce62f..2207d3a 100644 --- a/README.markdown +++ b/README.markdown @@ -58,6 +58,9 @@ probably look something like this :— ``` +The value of `rdf:about` on the root element needs to be the absolute +U·R·L for your blog. + Your `entry.rdf` will look similar, but it needs a `sioc:content` property :— @@ -78,6 +81,9 @@ I love it! ``` +In this case, the `rdf:about` can be automatically filled in based on +the path to the entry from the blog root. + As you can see from the above example, 🧸📔 Bjørn supports a (nonstandard) `Markdown` value for `rdf:parseType` in addition to `Literal`. Some words of caution regarding this :— @@ -96,6 +102,26 @@ Alongside the metadata files, there are two template files, content will replace the first `` element. Metadata will be inserted into the `` for you automatically. +## Serving Content + +You will need a file server which supports index files with a `.xhtml` +file extension. A sample `.rsync-filter` might be as follows :— + +```rsync-filter +- /.rsync-filter +-s .git +-s .gitignore +-s /README* +-s /build.js +-s index#*.xhtml +-s #*.rdf +``` + +## Customization + +Feel free to add styles, additional content, and whatever else you like +to the template files (as well as the rest of the website). + To modify what content is generated, simply edit `build.js` :) . [CommonMark]: diff --git a/build.js b/build.js index 743de64..3695329 100755 --- a/build.js +++ b/build.js @@ -201,55 +201,17 @@ const applyMetadata = (node, metadata) => { if (hasExpandedName(documentElement, XHTML, "html")) { // This is an XHTML template. const LMN = Lemon.bind({ document }); - const head = Array.from(documentElement.childNodes).find(($) => - hasExpandedName($, XHTML, "head") - ) ?? documentElement.insertBefore( - LMN.head``, - documentElement.childNodes.item(0), - ); - const titleElement = Array.from(head.childNodes).find(($) => - hasExpandedName($, XHTML, "title") - ) ?? head.appendChild(LMN.title``); const { id, title, author, - summary, contributor, published, content, rights, updated, } = metadata; - titleElement.textContent = Object(title) instanceof String - ? title - : Array.from(title ?? []).map(($) => - $.textContent - ).join(""); - for (const person of author) { - // Iterate over authors and add appropriate meta tags. - head.appendChild( - LMN.meta({ name: "author" }) - .content(`${person.name ?? person.uri}`)``, - ); - } - if (summary) { - // The entry has a summary. - head.appendChild( - LMN.meta({ name: "description" }) - .content( - `${ - Object(summary) instanceof String - ? summary - : Array.from(summary).map(($) => $.textContent).join( - "", - ) - }`, - )``, - ); - } else { - /* do nothing */ - } + fillOutHead(document, metadata, "entry"); const contentPlaceholder = document.getElementsByTagNameNS( XHTML, "bjørn-content", @@ -455,6 +417,12 @@ const applyMetadata = (node, metadata) => { // Assume it is an Atom element of some sort and add the // the appropriate metadata as child elements. const { ownerDocument: document } = node; + const alternateLink = node.appendChild( + document.createElement("link"), + ); + alternateLink.setAttribute("rel", "alternate"); + alternateLink.setAttribute("type", "application/xhtml+xml"); + alternateLink.setAttribute("href", metadata.id); for (const [property, values] of Object.entries(metadata)) { for (const value of Array.isArray(values) ? values : [values]) { const propertyNode = document.createElement(property); @@ -582,6 +550,75 @@ const { }; })(); +/** + * Fills out the `head` of the provided H·T·M·L document with the + * appropriate metadata. + */ +const fillOutHead = (document, metadata, type) => { + const { documentElement } = document; + const LMN = Lemon.bind({ document }); + const head = + Array.from(documentElement.childNodes).find(($) => + hasExpandedName($, XHTML, "head") + ) ?? documentElement.insertBefore( + LMN.head``, + documentElement.childNodes.item(0), + ); + const titleElement = + Array.from(head.childNodes).find(($) => + hasExpandedName($, XHTML, "title") + ) ?? head.appendChild(LMN.title``); + const { + title, + author, + summary, + } = metadata; + titleElement.textContent = Object(title) instanceof String + ? title + : Array.from(title ?? []).map(($) => $.textContent).join(""); + for (const person of author) { + // Iterate over authors and add appropriate meta tags. + head.appendChild( + LMN.meta({ + name: "author", + content: person.name ?? person.uri, + })``, + ); + } + head.appendChild( + LMN.meta({ name: "generator", content: "🧸📔 Bjørn" })``, + ); + if (type == "entry") { + // The provided document is an entry document. + if (summary) { + // The entry has a summary. + head.appendChild( + LMN.meta({ + name: "description", + content: Object(summary) instanceof String + ? summary + : Array.from(summary).map(($) => $.textContent).join(""), + })``, + ); + } else { + /* do nothing */ + } + head.appendChild( + LMN.link + .rel("alternate") + .type("application/atom+xml") + .href("../../feed.atom")``, + ); + } else { + head.appendChild( + LMN.link + .rel("alternate") + .type("application/atom+xml") + .href("./feed.atom")``, + ); + } +}; + /** * Returns the language of the provided node, or null if it has no * language. @@ -850,13 +887,7 @@ const validateMetadata = (metadata, type) => { await (async () => { // this is the run script const writes = []; - // Set up the Atom feed. - const document = parser.parseFromString( - ` -`, - "application/xml", - ); - const { documentElement: feed } = document; + // Set up the feed metadata and Atom feed document. const feedMetadata = metadataFromDocument( parser.parseFromString( await Deno.readTextFile(`${basePath}/#feed.rdf`), @@ -864,6 +895,15 @@ await (async () => { // this is the run script ), ); const feedURI = new URL(feedMetadata.id); + const document = parser.parseFromString( + ` +🧸📔 Bjørn`, + "application/xml", + ); + const { documentElement: feed } = document; applyMetadata(feed, feedMetadata); // Set up the index page. @@ -883,9 +923,11 @@ await (async () => { // this is the run script continue; } else { // This is a dated directory. - for await ( - const { name: entryName, isDirectory } of Deno.readDir( - `${basePath}/${date}/`, + for ( + const { name: entryName, isDirectory } of Array.from( + Deno.readDirSync(`${basePath}/${date}/`), + ).sort(({ name: a }, { name: b }) => + a < b ? -1 : a > b ? 1 : 0 ) ) { // Iterate over each directory and process the ones which look @@ -962,34 +1004,13 @@ await (async () => { // this is the run script if (hasExpandedName(feedRoot, XHTML, "html")) { // This is an XHTML template. const LMN = Lemon.bind({ document: feedTemplate }); - const head = Array.from(feedRoot.childNodes).find(($) => - hasExpandedName($, XHTML, "head") - ) ?? feedRoot.insertBefore( - LMN.head``, - feedRoot.childNodes.item(0), - ); - const titleElement = Array.from(head.childNodes).find(($) => - hasExpandedName($, XHTML, "title") - ) ?? head.appendChild(LMN.title``); const { id, title, - author, rights, updated, } = feedMetadata; - titleElement.textContent = Object(title) instanceof String - ? title - : Array.from(title ?? []).map(($) => - $.textContent - ).join(""); - for (const person of author) { - // Iterate over authors and add appropriate meta tags. - head.appendChild( - LMN.meta({ name: "author" }) - .content(`${person.name ?? person.uri}`)``, - ); - } + fillOutHead(feedTemplate, feedMetadata, "feed"); const contentPlaceholder = feedTemplate.getElementsByTagNameNS( XHTML, "bjørn-content",