It's coming together, but we just have a whole bunch of errors to work through.

pull/1/head
Starbeamrainbowlabs 2021-08-09 01:49:18 +01:00
parent d5160904ad
commit 8a863d7b0d
No known key found for this signature in database
GPG Key ID: 1BE5172E637709C2
14 changed files with 6772 additions and 6087 deletions

12
feeds.json Normal file
View File

@ -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/"
}
]

12549
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,13 @@
"license": "Apache-2.0",
"dependencies": {
"@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"
}
}

View File

@ -2,7 +2,53 @@ const os = require("os");
const fs = require("fs");
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) {
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);
//
@ -15,4 +61,8 @@ module.exports = function(eleventyConfig) {
// eleventyConfig.addAsyncShortcode("image_urlpass", shortcode_image_urlpass);
// eleventyConfig.addNunjucksAsyncShortcode("image_urlpass", shortcode_image_urlpass);
// eleventyConfig.addPairedShortcode("gallerybox", shortcode_gallerybox);
return {
htmlTemplateEngine: "njk"
};
}

1
src/.eleventyignore Normal file
View File

@ -0,0 +1 @@
css/

57
src/_includes/main.njk Normal file
View File

@ -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
src/css/smallscreens.css Normal file
View File

0
src/css/theme.css Normal file
View File

64
src/index.11tydata.js Normal file
View File

@ -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
}
};
}

View File

@ -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>

23
src/lib/fetch.js Normal file
View File

@ -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;

27
src/lib/fetch_feed.js Normal file
View File

@ -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;

23
src/lib/fetch_stream.js Normal file
View File

@ -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;

11
src/theme.njk Normal file
View File

@ -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" %} #}