It's coming together, but we just have a whole bunch of errors to work through.
parent
d5160904ad
commit
8a863d7b0d
|
@ -0,0 +1,12 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"author_name": "Starbeamrainbowlabs",
|
||||||
|
"github_username": "sbrl",
|
||||||
|
"feed_uri": "https://starbeamrainbowlabs.com/blog/feed.php"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author_name": "Edward Charles",
|
||||||
|
"github_username": "closebracket",
|
||||||
|
"feed_uri": "https://love.edwardcharl.es/rss/"
|
||||||
|
}
|
||||||
|
]
|
File diff suppressed because it is too large
Load Diff
|
@ -22,6 +22,13 @@
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@11ty/eleventy": "^0.12.1",
|
"@11ty/eleventy": "^0.12.1",
|
||||||
"feedme": "^2.0.2"
|
"dateformat": "^4.5.1",
|
||||||
|
"feedme": "^2.0.2",
|
||||||
|
"file-type": "^16.5.3",
|
||||||
|
"html-entities": "^2.3.2",
|
||||||
|
"p-reflect": "^2.1.0",
|
||||||
|
"phin": "^3.6.0",
|
||||||
|
"sha3": "^2.1.4",
|
||||||
|
"striptags": "^3.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,53 @@ const os = require("os");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
|
const striptags = require("striptags");
|
||||||
|
const htmlentities = require("html-entities");
|
||||||
|
const { SHA3 } = require("sha3");
|
||||||
|
const filetype = require("file-type");
|
||||||
|
|
||||||
|
const fetch = require("./lib/fetch.js");
|
||||||
|
|
||||||
|
function hash(str) {
|
||||||
|
const hash = new SHA3(128);
|
||||||
|
hash.update(str);
|
||||||
|
return hash.digest("base64")
|
||||||
|
.replaceAll("/", "-")
|
||||||
|
.replaceAll("+", "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function filter_asset(src) {
|
||||||
|
let target_dir = `./_site/img`;
|
||||||
|
if(!fs.existsSync(target_dir))
|
||||||
|
await fs.promises.mkdir(target_dir, { recursive: true });
|
||||||
|
let filename = path.basename(src);
|
||||||
|
|
||||||
|
if(src.search(/https?/)) {
|
||||||
|
let content = await fetch(src, "none");
|
||||||
|
let type = await filetype.fromBuffer(content);
|
||||||
|
// It's a URL - download it
|
||||||
|
filename = `${hash(src)}.${type.ext}`;
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
path.join(target_dir, filename),
|
||||||
|
content
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Generally speaking we optimise PNGs *very* well with oxipng/Zopfli,
|
||||||
|
// and the Image plugin doesn't respect this
|
||||||
|
await fs.promises.copyFile(src, path.join(target_dir, filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
return `/img/${filename}`;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = function(eleventyConfig) {
|
module.exports = function(eleventyConfig) {
|
||||||
|
eleventyConfig.addFilter("striphtml", (value) => striptags(value));
|
||||||
|
eleventyConfig.addFilter("htmlentities", (value) => htmlentities.encode(value));
|
||||||
|
|
||||||
|
eleventyConfig.addFilter("asset", filter_asset);
|
||||||
|
eleventyConfig.addAsyncShortcode("asset", filter_asset);
|
||||||
|
eleventyConfig.addNunjucksAsyncShortcode("asset", filter_asset);
|
||||||
|
|
||||||
// eleventyConfig.addAsyncShortcode("fetch", fetch);
|
// eleventyConfig.addAsyncShortcode("fetch", fetch);
|
||||||
//
|
//
|
||||||
|
@ -15,4 +61,8 @@ module.exports = function(eleventyConfig) {
|
||||||
// eleventyConfig.addAsyncShortcode("image_urlpass", shortcode_image_urlpass);
|
// eleventyConfig.addAsyncShortcode("image_urlpass", shortcode_image_urlpass);
|
||||||
// eleventyConfig.addNunjucksAsyncShortcode("image_urlpass", shortcode_image_urlpass);
|
// eleventyConfig.addNunjucksAsyncShortcode("image_urlpass", shortcode_image_urlpass);
|
||||||
// eleventyConfig.addPairedShortcode("gallerybox", shortcode_gallerybox);
|
// eleventyConfig.addPairedShortcode("gallerybox", shortcode_gallerybox);
|
||||||
|
|
||||||
|
return {
|
||||||
|
htmlTemplateEngine: "njk"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
css/
|
|
@ -0,0 +1,57 @@
|
||||||
|
---
|
||||||
|
site_name: Hull Blogs
|
||||||
|
root_url: https://hullblogs.com
|
||||||
|
description: "Description of hullblogs.com website here"
|
||||||
|
---
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8' />
|
||||||
|
<title>{{ title }} • {{ site_name }}</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/theme.css" />
|
||||||
|
|
||||||
|
<link rel="icon" href="{% asset './favicon-64.png' %}" type="image/png" sizes="64x64" />
|
||||||
|
<link rel="icon" href="{% asset './favicon.png' %}" type="image/png" sizes="540x540" />
|
||||||
|
|
||||||
|
<meta name="theme-color" content="#61b4f4" />
|
||||||
|
|
||||||
|
<!-- OpenGraph -->
|
||||||
|
<meta property="og:title" content="{{ site_name }}" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="{{ root_url }}/" />
|
||||||
|
<meta property="og:image" content="{{ root_url }}{% asset 'images/banner-main.jpeg' %}" />
|
||||||
|
<meta property="og:description" content="{{ description }}" />
|
||||||
|
|
||||||
|
<!-- Twitter Cards -->
|
||||||
|
<meta property="twitter:card" content="summary" />
|
||||||
|
<meta property="twitter:site" content="@SBRLabs" />
|
||||||
|
<meta property="twitter:title" content="{{ site_name }}" />
|
||||||
|
<meta property="twitter:description" content="{{ description }}" />
|
||||||
|
<meta property="twitter:image" content="{{ root_url }}/{% asset '../favicon.png' %}" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="shadow-bottom">
|
||||||
|
<h1><a href="/" class="invisilink">
|
||||||
|
{{ site_name }}
|
||||||
|
</a></h1>
|
||||||
|
<ul>
|
||||||
|
{% for navitem in collections.navigable %}
|
||||||
|
<li {% if page.url == navitem.url %}aria-current="page"{% endif %}>
|
||||||
|
<a href="{{ navitem.url }}" class="nav">{{ navitem.data.title }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
<li><a href="https://github.com/sbrl/Minetest-WorldEditAdditions/" class="nav image"><img src="{% asset 'images/github.svg' %}" alt="GitHub" title="GitHub" /></a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{{ content | safe }}
|
||||||
|
|
||||||
|
<footer class="shadow-top">
|
||||||
|
<p>hullblogs.com built with ❤️ by <a href="https://starbeamrainbowlabs.com/"><img src="{% asset 'https://starbeamrainbowlabs.com/images/sbrl/SBRL-Small-200.png' %}" alt="Starbeamrainbowlabs' logo" aria-hidden="true" />Starbeamrainbowlabs</p>
|
||||||
|
|
||||||
|
<p>Tech: <a href="https://www.11ty.dev/">Eleventy</a> (this website), <a href="https://www.heropatterns.com/">Hero Patterns</a> (background patterns), <a href="https://github.com/shssoichiro/oxipng">Oxipng</a> (PNG image compression), <a href="https://www.npmjs.com/package/feedme"><code>feedme</code></a> (for prsing feeds)</p>
|
||||||
|
<p>Licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache Licence 2.0</a> (<a href="https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)">tldr</a>)</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,64 @@
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const pReflect = require("p-reflect");
|
||||||
|
const dateformat = require("dateformat");
|
||||||
|
const striptags = require("striptags");
|
||||||
|
|
||||||
|
const fetch_feed = require("./lib/fetch_feed.js");
|
||||||
|
|
||||||
|
// The length of auto-generated descriptions if one isn't provided.
|
||||||
|
const DESCRIPTION_LENGTH = 200;
|
||||||
|
|
||||||
|
module.exports = async function() {
|
||||||
|
const feeds = JSON.parse(await fs.promises.readFile("../feeds.json", "utf-8"));
|
||||||
|
|
||||||
|
const feed_data = await Promise.all(feeds.map(async (feed) => { return {
|
||||||
|
author_name: feed.author_name,
|
||||||
|
author_image: `https://avatars.githubusercontent.com/${encodeURIComponent(feed.github_username)}`,
|
||||||
|
data: await pReflect(fetch_feed(feed.feed_uri))
|
||||||
|
} }));
|
||||||
|
const feed_data_ok = feed_data.filter(el => {
|
||||||
|
return el.data.isFulfilled
|
||||||
|
})
|
||||||
|
.map((feed) => {
|
||||||
|
feed.data = feed.data.value;
|
||||||
|
return feed;
|
||||||
|
});
|
||||||
|
const feed_authors_error = feed_data.filter(el => el.data.isRejected)
|
||||||
|
.map(el => el.author_name);
|
||||||
|
|
||||||
|
const feed_items = [].concat(...feed_data_ok.map(feed => feed.data.items.map(item => {
|
||||||
|
// console.log(`FEED ITEM`, item);
|
||||||
|
item.author_name = feed.author_name;
|
||||||
|
item.parent = feed.data;
|
||||||
|
|
||||||
|
if(!item.content) item.content = item["content:encoded"] || "";
|
||||||
|
if(!item.description) item.description = striptags(item.content)
|
||||||
|
.substr(0, DESCRIPTION_LENGTH);
|
||||||
|
|
||||||
|
if(!item.pubdate) item.pubdate = item.published
|
||||||
|
|| item.updated
|
||||||
|
|| new Date("1970-01-01");
|
||||||
|
|
||||||
|
item.pubdate_iso = new Date(item.pubdate).toISOString();
|
||||||
|
item.pubdate_display = dateformat(
|
||||||
|
new Date(item.pubdate),
|
||||||
|
"ddd dS mmmm yyyy h:MM:ss TT Z"
|
||||||
|
);
|
||||||
|
return item;
|
||||||
|
})));
|
||||||
|
|
||||||
|
return {
|
||||||
|
layout: "main.njk",
|
||||||
|
title: "Posts",
|
||||||
|
tags: "navigable",
|
||||||
|
date: "2000-01-01", // For sorting in the navigation bar
|
||||||
|
feed_items: feed_items,
|
||||||
|
feeds_errored: feed_authors_error,
|
||||||
|
pagination: {
|
||||||
|
data: "feed_items",
|
||||||
|
size: 15
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
<section class="posts">
|
||||||
|
|
||||||
|
{% for post in pagination.items %}
|
||||||
|
|
||||||
|
<article class="post">
|
||||||
|
<h2><a href="{{ post.link }}">{{ post.title | striphtml | htmlentities }}</a></h2>
|
||||||
|
<div class="post-extract">
|
||||||
|
{{ post.description | striphtml | htmlentities }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span><a href="{{ post.parent.data.link | htmlentities }}"><img src="{{ post.parent.author_image }}" alt="{{ post.parent.author_name | htmlentities }} avatar" aria-hidden="true" /> {{ post.parent.author_name | htmlentities }}</a></span>
|
||||||
|
<span><time datetime="{{ post.pubdate_iso }}">{{ post.pubdate_display | htmlentities }}</time></span>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<span>{% if page.url != pagination.href.first %}<a href="{{ pagination.href.first }}">First</a>{% else %}First{% endif %}</span>
|
||||||
|
|
||||||
|
<span>{% if pagination.href.previous %}<a href="{{ pagination.href.previous }}">Previous</a>{% else %}Previous{% endif %}</span>
|
||||||
|
|
||||||
|
{% for pageKey in pagination.pages %}
|
||||||
|
<a href="{{ pagination.hrefs[ loop.index0 ] }}"{% if pagination.hrefs[ loop.index0 ] == page.url %} aria-current="page"{% endif %}>{{ loop.index }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<span>{% if pagination.href.next %}<a href="{{ pagination.href.next }}">Next</a>{% else %}Next{% endif %}</span>
|
||||||
|
|
||||||
|
<span>{% if page.url != pagination.href.last %}<a href="{{ pagination.href.last }}">Last</a>{% else %}Last{% endif %}</span>
|
||||||
|
</nav>
|
|
@ -0,0 +1,23 @@
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const os = require("os");
|
||||||
|
|
||||||
|
const phin = require("phin");
|
||||||
|
|
||||||
|
async function fetch(url, format = "string") {
|
||||||
|
let package = JSON.parse(await fs.promises.readFile(
|
||||||
|
path.join(path.dirname(path.dirname(__dirname)), "package.json"), "utf8"
|
||||||
|
));
|
||||||
|
|
||||||
|
return (await phin({
|
||||||
|
url,
|
||||||
|
headers: {
|
||||||
|
"user-agent": `HullBlogsStaticBuilder/${package.version} (Node.js/${process.version}; ${os.platform()} ${os.arch()}) eleventy/${package.dependencies["@11ty/eleventy"].replace(/\^/, "")}`
|
||||||
|
},
|
||||||
|
followRedirects: true,
|
||||||
|
parse: format
|
||||||
|
})).body;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = fetch;
|
|
@ -0,0 +1,27 @@
|
||||||
|
const fetch_stream = require("./fetch_stream.js");
|
||||||
|
const FeedMe = require("feedme");
|
||||||
|
const events = require("events");
|
||||||
|
|
||||||
|
function do_parse(stream) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
const parser = new FeedMe(true);
|
||||||
|
parser.once("error", reject);
|
||||||
|
stream.pipe(parser);
|
||||||
|
events.once(parser, "end").then(() => {
|
||||||
|
parser.off("error", reject);
|
||||||
|
resolve(parser.done());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetch_feed(url) {
|
||||||
|
let start = new Date();
|
||||||
|
let result = await do_parse(
|
||||||
|
await fetch_stream(url)
|
||||||
|
);
|
||||||
|
console.log(`FETCH ${new Date() - start}ms ${url}`);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = fetch_feed;
|
|
@ -0,0 +1,23 @@
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const os = require("os");
|
||||||
|
|
||||||
|
const phin = require("phin");
|
||||||
|
|
||||||
|
async function fetch_stream(url) {
|
||||||
|
let package = JSON.parse(await fs.promises.readFile(
|
||||||
|
path.join(path.dirname(path.dirname(__dirname)), "package.json"), "utf8"
|
||||||
|
));
|
||||||
|
|
||||||
|
return (await phin({
|
||||||
|
url,
|
||||||
|
headers: {
|
||||||
|
"user-agent": `HullBlogsStaticBuilder/${package.version} (Node.js/${process.version}; ${os.platform()} ${os.arch()}) eleventy/${package.dependencies["@11ty/eleventy"].replace(/\^/, "")}`
|
||||||
|
},
|
||||||
|
followRedirects: true,
|
||||||
|
stream: true
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = fetch_stream;
|
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
permalink: theme.css
|
||||||
|
---
|
||||||
|
|
||||||
|
{% include "css/theme.css" %}
|
||||||
|
{% include "css/smallscreens.css" %}
|
||||||
|
|
||||||
|
|
||||||
|
{# {% fetch "https://unpkg.com/prismjs/themes/prism-okaidia.css" %} #}
|
||||||
|
{# {% fetch "https://raw.githubusercontent.com/PrismJS/prism-themes/master/themes/prism-shades-of-purple.css" %} #}
|
||||||
|
{# {% fetch "https://raw.githubusercontent.com/PrismJS/prism-themes/master/themes/prism-material-light.css" %} #}
|
Loading…
Reference in New Issue