Compare commits
No commits in common. "1175f0ce74f35065f87b7ab894af90f96c8aa671" and "528825ec9658d0dedef86896c634f93622524f8c" have entirely different histories.
1175f0ce74
...
528825ec96
|
@ -1,7 +1,7 @@
|
||||||
import { Page, PostMetadata } from "../metadata";
|
import { Page, PostMetadata } from "../metadata";
|
||||||
import generatePaginated from "./paginated";
|
import generatePaginated from "./paginated";
|
||||||
|
|
||||||
export default async function(posts: Page[]): Promise<Map<string, Page[]>> {
|
export default async function homepage(posts: Page[]): Promise<Map<string, Page[]>> {
|
||||||
const categories = new Map<string, Page[]>();
|
const categories = new Map<string, Page[]>();
|
||||||
|
|
||||||
for (const post of posts) {
|
for (const post of posts) {
|
||||||
|
@ -12,8 +12,8 @@ export default async function(posts: Page[]): Promise<Map<string, Page[]>> {
|
||||||
categories.get(category)!.push(post);
|
categories.get(category)!.push(post);
|
||||||
}
|
}
|
||||||
|
|
||||||
categories.forEach((categoryPosts, category) => {
|
categories.forEach(async (categoryPosts, category) => {
|
||||||
generatePaginated(categoryPosts, `/${category}/`, "site/category.html.ejs", {
|
await generatePaginated(categoryPosts, `/${category}/`, "site/category.html.ejs", {
|
||||||
category
|
category
|
||||||
}, {
|
}, {
|
||||||
title: `${category} posts`
|
title: `${category} posts`
|
||||||
|
|
|
@ -6,7 +6,6 @@ import homepage from "./homepage";
|
||||||
import posts from "./posts";
|
import posts from "./posts";
|
||||||
import rss from "./rss";
|
import rss from "./rss";
|
||||||
import tutorials from "./tutorials";
|
import tutorials from "./tutorials";
|
||||||
import years from "./years";
|
|
||||||
|
|
||||||
export = {
|
export = {
|
||||||
categories,
|
categories,
|
||||||
|
@ -16,6 +15,5 @@ export = {
|
||||||
homepage,
|
homepage,
|
||||||
posts,
|
posts,
|
||||||
rss,
|
rss,
|
||||||
tutorials,
|
tutorials
|
||||||
years,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,7 @@ import * as metadata from "../metadata";
|
||||||
import { Page, PostMetadata } from "../metadata";
|
import { Page, PostMetadata } from "../metadata";
|
||||||
import layout from "../layout";
|
import layout from "../layout";
|
||||||
|
|
||||||
export default async function generatePaginated(posts: Page[], basePath: string, templatePath: string, extraData?: object, extraMetadata?: object) {
|
export default async function generatePaginted(posts: Page[], basePath: string, templatePath: string, extraData?: object, extraMetadata?: object) {
|
||||||
const page = await metadata.get(templatePath);
|
const page = await metadata.get(templatePath);
|
||||||
|
|
||||||
if (extraMetadata) page.metadata = {...page.metadata, ...extraMetadata};
|
if (extraMetadata) page.metadata = {...page.metadata, ...extraMetadata};
|
||||||
|
|
|
@ -20,7 +20,7 @@ export default async function posts(): Promise<Page[]> {
|
||||||
let postMeta = <PostMetadata>page.metadata;
|
let postMeta = <PostMetadata>page.metadata;
|
||||||
postMeta.date = dateFns.parse(postMeta.date);
|
postMeta.date = dateFns.parse(postMeta.date);
|
||||||
postMeta.slug = postMeta.slug || slugify(postMeta.title);
|
postMeta.slug = postMeta.slug || slugify(postMeta.title);
|
||||||
postMeta.permalink = `/${postMeta.date.getFullYear()}/${postMeta.slug}/`;
|
postMeta.permalink = `/${postMeta.category}/${postMeta.date.getFullYear()}/${postMeta.slug}/`;
|
||||||
}
|
}
|
||||||
|
|
||||||
page.text = util.render(page.text, { metadata: page.metadata }, postPath);
|
page.text = util.render(page.text, { metadata: page.metadata }, postPath);
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
import {Page, PostMetadata} from "../metadata";
|
|
||||||
import generatePaginated from "./paginated";
|
|
||||||
|
|
||||||
export default async function(posts: Page[]) {
|
|
||||||
const years = new Map<number, Page[]>();
|
|
||||||
|
|
||||||
for (const post of posts) {
|
|
||||||
const year = (<Date>(<PostMetadata>post.metadata).date).getFullYear();
|
|
||||||
if (!years.has(year)) {
|
|
||||||
years.set(year, []);
|
|
||||||
}
|
|
||||||
years.get(year)!.push(post)
|
|
||||||
}
|
|
||||||
|
|
||||||
years.forEach((yearPosts, year) => {
|
|
||||||
generatePaginated(yearPosts, `/${year}/`, "site/year.html.ejs", {
|
|
||||||
year
|
|
||||||
}, {
|
|
||||||
title: `Posts from ${year}`
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -20,7 +20,6 @@ async function generate(): Promise<Page[]> {
|
||||||
|
|
||||||
const posts = await generators.posts();
|
const posts = await generators.posts();
|
||||||
generators.homepage(posts);
|
generators.homepage(posts);
|
||||||
generators.years(posts);
|
|
||||||
const categories = await generators.categories(posts);
|
const categories = await generators.categories(posts);
|
||||||
await generators.rss(posts, categories, tutorials);
|
await generators.rss(posts, categories, tutorials);
|
||||||
|
|
||||||
|
|
|
@ -1357,7 +1357,7 @@
|
||||||
},
|
},
|
||||||
"load-json-file": {
|
"load-json-file": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||||
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
|
@ -1494,7 +1494,7 @@
|
||||||
},
|
},
|
||||||
"meow": {
|
"meow": {
|
||||||
"version": "3.7.0",
|
"version": "3.7.0",
|
||||||
"resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
|
||||||
"integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
|
"integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"camelcase-keys": "^2.0.0",
|
"camelcase-keys": "^2.0.0",
|
||||||
|
@ -1547,7 +1547,7 @@
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||||
},
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
|
@ -1638,7 +1638,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
|
||||||
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
|
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1743,12 +1743,12 @@
|
||||||
},
|
},
|
||||||
"os-homedir": {
|
"os-homedir": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
|
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
|
||||||
},
|
},
|
||||||
"os-locale": {
|
"os-locale": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
|
||||||
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
|
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lcid": "^1.0.0"
|
"lcid": "^1.0.0"
|
||||||
|
@ -1756,7 +1756,7 @@
|
||||||
},
|
},
|
||||||
"os-tmpdir": {
|
"os-tmpdir": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
||||||
},
|
},
|
||||||
"osenv": {
|
"osenv": {
|
||||||
|
@ -1933,7 +1933,7 @@
|
||||||
},
|
},
|
||||||
"pify": {
|
"pify": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
|
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
|
||||||
},
|
},
|
||||||
"pinkie": {
|
"pinkie": {
|
||||||
|
@ -2105,7 +2105,7 @@
|
||||||
},
|
},
|
||||||
"readable-stream": {
|
"readable-stream": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||||
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"core-util-is": "~1.0.0",
|
"core-util-is": "~1.0.0",
|
||||||
|
@ -2353,7 +2353,7 @@
|
||||||
},
|
},
|
||||||
"source-map": {
|
"source-map": {
|
||||||
"version": "0.4.4",
|
"version": "0.4.4",
|
||||||
"resolved": "http://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
|
||||||
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
|
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"amdefine": ">=0.0.4"
|
"amdefine": ">=0.0.4"
|
||||||
|
@ -2458,7 +2458,7 @@
|
||||||
},
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
|
@ -2873,7 +2873,7 @@
|
||||||
},
|
},
|
||||||
"wrap-ansi": {
|
"wrap-ansi": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||||
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "^1.0.1",
|
"string-width": "^1.0.1",
|
||||||
|
|
|
@ -9,8 +9,43 @@ metadata.layout = "default.html.ejs"
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<% for (const post of posts) { %>
|
<% for (const post of posts) { %>
|
||||||
<%- include("includes/article-listing.html.ejs", { post }) %>
|
<article itemscope itemtype="https://schema.org/BlogPosting">
|
||||||
|
<h2 class="article-title" itemprop="headline">
|
||||||
|
<a href="<%= post.metadata.permalink %>" itemprop="url mainEntityOfPage">
|
||||||
|
<%= post.metadata.title %>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<%- include("includes/article-meta.html.ejs", { metadata: post.metadata }) %>
|
||||||
|
<div class="article-content" itemprop="description">
|
||||||
|
<%- post.metadata.excerpt %>
|
||||||
|
</div>
|
||||||
|
<p class="read-more-link">
|
||||||
|
<a href="<%= post.metadata.permalink %>">Read more...</a>
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%- include("includes/pagination.html.ejs", { pagination }) %>
|
<div class="pagination">
|
||||||
|
<p>
|
||||||
|
<span class="pagination-link">
|
||||||
|
<% if (pagination.prevLink) { %>
|
||||||
|
<a href="<%= pagination.prevLink %>">
|
||||||
|
<span class="arrow-left"></span> Previous
|
||||||
|
</a>
|
||||||
|
<% } else { %>
|
||||||
|
<span class="arrow-left"></span> Previous
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
Page <%= pagination.current %> of <%= pagination.total %>
|
||||||
|
<span class="pagination-link">
|
||||||
|
<% if (pagination.nextLink) { %>
|
||||||
|
<a href="<%= pagination.nextLink %>">
|
||||||
|
Next <span class="arrow-right"></span>
|
||||||
|
</a>
|
||||||
|
<% } else { %>
|
||||||
|
Next <span class="arrow-right"></span>
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
|
@ -1,14 +0,0 @@
|
||||||
<article itemscope itemtype="https://schema.org/BlogPosting">
|
|
||||||
<h2 class="article-title" itemprop="headline">
|
|
||||||
<a href="<%= post.metadata.permalink %>" itemprop="url mainEntityOfPage">
|
|
||||||
<%= post.metadata.title %>
|
|
||||||
</a>
|
|
||||||
</h2>
|
|
||||||
<%- include("article-meta.html.ejs", { metadata: post.metadata }) %>
|
|
||||||
<div class="article-content" itemprop="description">
|
|
||||||
<%- post.metadata.excerpt %>
|
|
||||||
</div>
|
|
||||||
<p class="read-more-link">
|
|
||||||
<a href="<%= post.metadata.permalink%>">Read more...</a>
|
|
||||||
</p>
|
|
||||||
</article>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<div class="pagination" role="navigation">
|
|
||||||
<p>
|
|
||||||
<span class="pagination-link">
|
|
||||||
<% if (pagination.prevLink) { %>
|
|
||||||
<a href="<%= pagination.prevLink %>">
|
|
||||||
<span class="arrow arrow-left" aria-hidden="true"></span> <span>Previous</span>
|
|
||||||
</a>
|
|
||||||
<% } else { %>
|
|
||||||
<span class="arrow arrow-left" aria-hidden="true"></span> <span>Previous</span>
|
|
||||||
<% } %>
|
|
||||||
</span>
|
|
||||||
Page <%= pagination.current %> of <%= pagination.total %>
|
|
||||||
<span class="pagination-link">
|
|
||||||
<% if (pagination.nextLink) { %>
|
|
||||||
<a href="<%= pagination.nextLink %>">
|
|
||||||
<span>Next</span> <span class="arrow arrow-right" aria-hidden="true"></span>
|
|
||||||
</a>
|
|
||||||
<% } else { %>
|
|
||||||
<span>Next</span> <span class="arrow arrow-right" aria-hidden="true"></span>
|
|
||||||
<% } %>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
|
@ -5,8 +5,43 @@ metadata.layout = "default.html.ejs"
|
||||||
|
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<% for (const post of posts) { %>
|
<% for (const post of posts) { %>
|
||||||
<%- include("includes/article-listing.html.ejs", { post }) %>
|
<article itemscope itemtype="https://schema.org/BlogPosting">
|
||||||
|
<h1 class="article-title" itemprop="headline">
|
||||||
|
<a href="<%= post.metadata.permalink %>" itemprop="url mainEntityOfPage">
|
||||||
|
<%= post.metadata.title %>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
<%- include("includes/article-meta.html.ejs", { metadata: post.metadata }) %>
|
||||||
|
<div class="article-content" itemprop="description">
|
||||||
|
<%- post.metadata.excerpt %>
|
||||||
|
</div>
|
||||||
|
<p class="read-more-link">
|
||||||
|
<a href="<%= post.metadata.permalink %>">Read more...</a>
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
<% } %>
|
<% } %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%- include("includes/pagination.html.ejs", { pagination }) %>
|
<div class="pagination" role="navigation">
|
||||||
|
<p>
|
||||||
|
<span class="pagination-link">
|
||||||
|
<% if (pagination.prevLink) { %>
|
||||||
|
<a href="<%= pagination.prevLink %>">
|
||||||
|
<span class="arrow arrow-left" aria-hidden="true"></span> <span>Previous</span>
|
||||||
|
</a>
|
||||||
|
<% } else { %>
|
||||||
|
<span class="arrow arrow-left" aria-hidden="true"></span> <span>Previous</span>
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
Page <%= pagination.current %> of <%= pagination.total %>
|
||||||
|
<span class="pagination-link">
|
||||||
|
<% if (pagination.nextLink) { %>
|
||||||
|
<a href="<%= pagination.nextLink %>">
|
||||||
|
<span>Next</span> <span class="arrow arrow-right" aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
<% } else { %>
|
||||||
|
<span>Next</span> <span class="arrow arrow-right" aria-hidden="true"></span>
|
||||||
|
<% } %>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
|
@ -17,7 +17,7 @@ metadata.layout = "default.html.ejs"
|
||||||
</summary>
|
</summary>
|
||||||
<p id="comments-info">
|
<p id="comments-info">
|
||||||
Comments powered by ActivityPub. To respond to this post or to another comment, copy its URL into the search interface of your client for Mastodon, Pleroma, or other compatible software.
|
Comments powered by ActivityPub. To respond to this post or to another comment, copy its URL into the search interface of your client for Mastodon, Pleroma, or other compatible software.
|
||||||
<a href="/2019/reincarnation/#activity-pub">Learn more</a>.
|
<a href="/meta/2019/reincarnation/#activity-pub">Learn more</a>.
|
||||||
</p>
|
</p>
|
||||||
<noscript>
|
<noscript>
|
||||||
<p id="comments-js-warning">
|
<p id="comments-js-warning">
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "Hello, World!"
|
metadata.title = "Hello, World!"
|
||||||
metadata.category = "meta"
|
metadata.category = "meta"
|
||||||
metadata.date = "2016-05-06 11:13:18 -0400"
|
metadata.date = "2016-05-06 11:13:18 -0400"
|
||||||
metadata.oldPermalink = ["/meta/2016/06/07/hello-world/", "/meta/2016/hello-world/"]
|
metadata.oldPermalink = "/meta/2016/06/07/hello-world/"
|
||||||
metadata.shortDesc = "Hello again, world! Welcome to the third iteration of my website."
|
metadata.shortDesc = "Hello again, world! Welcome to the third iteration of my website."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "1.9.4 Porting Spree"
|
metadata.title = "1.9.4 Porting Spree"
|
||||||
metadata.category = "minecraft"
|
metadata.category = "minecraft"
|
||||||
metadata.date = "2016-05-21 17:47:18 -0400"
|
metadata.date = "2016-05-21 17:47:18 -0400"
|
||||||
metadata.oldPermalink = ["/mods/2016/05/21/194-porting-spree/", "/minecraft/2016/1-9-4-porting-spree/"]
|
metadata.oldPermalink = "/mods/2016/05/21/194-porting-spree/"
|
||||||
metadata.shortDesc = "Now that Forge for 1.9.4 is out, I've begun the log and arduous process of porting my mods to 1.9.4 (if by long and arduous, you mean short and trivial)."
|
metadata.shortDesc = "Now that Forge for 1.9.4 is out, I've begun the log and arduous process of porting my mods to 1.9.4 (if by long and arduous, you mean short and trivial)."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "Introducing RTFM"
|
metadata.title = "Introducing RTFM"
|
||||||
metadata.category = "minecraft"
|
metadata.category = "minecraft"
|
||||||
metadata.date = "2016-06-29 12:00:00 -0400"
|
metadata.date = "2016-06-29 12:00:00 -0400"
|
||||||
metadata.oldPermalink = ["/meta/2016/06/29/introducing-rtfm/", "/minecraft/2016/introducing-rtfm/"]
|
metadata.oldPermalink = "/meta/2016/06/29/introducing-rtfm/"
|
||||||
metadata.shortDesc = "RTFM is the brand new website that will contain the documentation for all of my projects, currently it only contains documentation for MC mods."
|
metadata.shortDesc = "RTFM is the brand new website that will contain the documentation for all of my projects, currently it only contains documentation for MC mods."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "Forge Modding Tutorials for 1.10.2"
|
metadata.title = "Forge Modding Tutorials for 1.10.2"
|
||||||
metadata.category = "minecraft"
|
metadata.category = "minecraft"
|
||||||
metadata.date = "2016-06-30 10:35:00 -0400"
|
metadata.date = "2016-06-30 10:35:00 -0400"
|
||||||
metadata.oldPermalink = ["/meta/2016/06/30/forge-1102-tutorials/", "/minecraft/2016/forge-modding-tutorials-for-1-10-2"]
|
metadata.oldPermalink = "/meta/2016/06/30/forge-1102-tutorials/"
|
||||||
metadata.shortDesc = "The Forge modding tutorials have all the been updated to MC 1.10.2 as has the GitHub repo."
|
metadata.shortDesc = "The Forge modding tutorials have all the been updated to MC 1.10.2 as has the GitHub repo."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "Introducing Mirror"
|
metadata.title = "Introducing Mirror"
|
||||||
metadata.category = "java"
|
metadata.category = "java"
|
||||||
metadata.date = "2016-07-28 16:45:00 -0400"
|
metadata.date = "2016-07-28 16:45:00 -0400"
|
||||||
metadata.oldPermalink = ["/java/2016/07/28/introducing-mirror/", "/java/2016/introducing-mirror/"]
|
metadata.oldPermalink = "/java/2016/07/28/introducing-mirror/"
|
||||||
metadata.shortDesc = "Allow me to introduce my latest project, Mirror. Mirror is a reflection library for Java designed to take advantage of the streams, lambdas, and optionals introduced in Java 8."
|
metadata.shortDesc = "Allow me to introduce my latest project, Mirror. Mirror is a reflection library for Java designed to take advantage of the streams, lambdas, and optionals introduced in Java 8."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "Kotlin and Minecraft Forge"
|
metadata.title = "Kotlin and Minecraft Forge"
|
||||||
metadata.category = "minecraft"
|
metadata.category = "minecraft"
|
||||||
metadata.date = "2016-08-06 16:45:30 -0400"
|
metadata.date = "2016-08-06 16:45:30 -0400"
|
||||||
metadata.oldPermalink = ["/forge/2016/08/06/kotlin-and-forge/", "/minecraft/2016/kotlin-and-minecraft-forge/"]
|
metadata.oldPermalink = "/forge/2016/08/06/kotlin-and-forge/"
|
||||||
metadata.shortDesc = "So, you wanna use Kotlin in your Forge mod? Well there's good news, I've just released Forgelin, a fork of Emberwalker's Forgelin, a library that provides utilities for using Kotlin with Minecraft/Forge. "
|
metadata.shortDesc = "So, you wanna use Kotlin in your Forge mod? Well there's good news, I've just released Forgelin, a fork of Emberwalker's Forgelin, a library that provides utilities for using Kotlin with Minecraft/Forge. "
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "The Great Redesign"
|
metadata.title = "The Great Redesign"
|
||||||
metadata.category = "meta"
|
metadata.category = "meta"
|
||||||
metadata.date = "2016-08-07 15:39:48 -0400"
|
metadata.date = "2016-08-07 15:39:48 -0400"
|
||||||
metadata.oldPermalink = ["/meta/2016/08/07/the-great-redesign/", "/meta/2016/the-great-redesign/"]
|
metadata.oldPermalink = "/meta/2016/08/07/the-great-redesign/"
|
||||||
metadata.shortDesc = "Welcome to the fourth iteration of my website."
|
metadata.shortDesc = "Welcome to the fourth iteration of my website."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "Type: A FOSS clone of typing.io"
|
metadata.title = "Type: A FOSS clone of typing.io"
|
||||||
metadata.category = "misc"
|
metadata.category = "misc"
|
||||||
metadata.date = "2016-10-08 17:29:42 -0400"
|
metadata.date = "2016-10-08 17:29:42 -0400"
|
||||||
metadata.oldPermalink = ["/misc/2016/10/08/type/", "/misc/2016/type/"]
|
metadata.oldPermalink = "/misc/2016/10/08/type/"
|
||||||
metadata.shortDesc = "I made an awesome FOSS clone of typing.io that you can check out at type.shadowfacts.net."
|
metadata.shortDesc = "I made an awesome FOSS clone of typing.io that you can check out at type.shadowfacts.net."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
metadata.title = "The Pretty Good Minor Update"
|
metadata.title = "The Pretty Good Minor Update"
|
||||||
metadata.category = "meta"
|
metadata.category = "meta"
|
||||||
metadata.date = "2017-02-17 14:30:42 -0400"
|
metadata.date = "2017-02-17 14:30:42 -0400"
|
||||||
metadata.oldPermalink = ["/meta/2017/02/17/the-pretty-good-minor-update/", "/meta/2017/the-pretty-good-minor-update/"]
|
metadata.oldPermalink = "/meta/2017/02/17/the-pretty-good-minor-update/"
|
||||||
metadata.shortDesc = "It's been about six months since the last time I redesigned the site, and while I didn't want to redesign it yet again, I felt it could use a little update to make sure everything's still good."
|
metadata.shortDesc = "It's been about six months since the last time I redesigned the site, and while I didn't want to redesign it yet again, I felt it could use a little update to make sure everything's still good."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
metadata.title = "Comments Powered by GitHub"
|
metadata.title = "Comments Powered by GitHub"
|
||||||
metadata.category = "meta"
|
metadata.category = "meta"
|
||||||
metadata.date = "2017-04-23 09:05:42 -0400"
|
metadata.date = "2017-04-23 09:05:42 -0400"
|
||||||
metadata.oldPermalink = ["/meta/2017/04/23/comments-powered-by-github/", "/meta/2017/comments-powered-by-git-hub/"]
|
metadata.oldPermalink = "/meta/2017/04/23/comments-powered-by-github/"
|
||||||
metadata.shortDesc = "I built a way of commenting on my static website using GitHub to store comments."
|
metadata.shortDesc = "I built a way of commenting on my static website using GitHub to store comments."
|
||||||
```
|
```
|
||||||
|
|
||||||
**NOTE:** This article has been superseded by the [ActivityPub comments system](/2019/reincarnation/#activity-pub).
|
**NOTE:** This article has been superseded by the [ActivityPub comments system](/meta/2019/reincarnation/#activity-pub).
|
||||||
|
|
||||||
After seeing [this article][orig] the other morning about replacing the Disqus comments on a blog powered by a static site generator (like this one) with comments backed by a GitHub issue and some front-end JavaScript to load and display them, I thought it would be fun to implement something similar. First I only built the code for displaying comments, similar to the aforementioned article, but I decided to take it one step further by allowing users to submit comments directly from my site.
|
After seeing [this article][orig] the other morning about replacing the Disqus comments on a blog powered by a static site generator (like this one) with comments backed by a GitHub issue and some front-end JavaScript to load and display them, I thought it would be fun to implement something similar. First I only built the code for displaying comments, similar to the aforementioned article, but I decided to take it one step further by allowing users to submit comments directly from my site.
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ metadata.title = "Reincarnation"
|
||||||
metadata.category = "meta"
|
metadata.category = "meta"
|
||||||
metadata.date = "2019-09-18 10:34:42 -0400"
|
metadata.date = "2019-09-18 10:34:42 -0400"
|
||||||
metadata.shortDesc = "Stand by for reincarnation."
|
metadata.shortDesc = "Stand by for reincarnation."
|
||||||
metadata.oldPermalink = "/meta/2019/reincarnation/"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
|
|
|
@ -3,7 +3,6 @@ metadata.title = "ActivityPub Resources"
|
||||||
metadata.category = "activitypub"
|
metadata.category = "activitypub"
|
||||||
metadata.date = "2019-09-22 17:50:42 -0400"
|
metadata.date = "2019-09-22 17:50:42 -0400"
|
||||||
metadata.shortDesc = "A compilation of resources I found useful in learning/implementing ActivityPub."
|
metadata.shortDesc = "A compilation of resources I found useful in learning/implementing ActivityPub."
|
||||||
metadata.oldPermalink = "/activitypub/2019/activity-pub-resources/"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This isn't really going to be a blog most, but more of a collection of tidbits and resources I found helpful in implenting the [ActivityPub integration](/meta/2019/reincarnation/#activity-pub) for the new version of my blog.
|
This isn't really going to be a blog most, but more of a collection of tidbits and resources I found helpful in implenting the [ActivityPub integration](/meta/2019/reincarnation/#activity-pub) for the new version of my blog.
|
||||||
|
|
|
@ -3,7 +3,6 @@ metadata.title = "Learning Elixir"
|
||||||
metadata.category = "elixir"
|
metadata.category = "elixir"
|
||||||
metadata.date = "2019-10-10 12:29:42 -0400"
|
metadata.date = "2019-10-10 12:29:42 -0400"
|
||||||
metadata.shortDesc = "How I learned Elixir and why I love it."
|
metadata.shortDesc = "How I learned Elixir and why I love it."
|
||||||
metadata.oldPermalink = "/elixir/2019/learning-elixir/"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
About a year ago, I set out to learn the [Elixir](https://elixir-lang.org) programming language. At the time, it was mainly so I could contribute to [Pleroma](https://pleroma.social), but I've since fallen in love with the language.
|
About a year ago, I set out to learn the [Elixir](https://elixir-lang.org) programming language. At the time, it was mainly so I could contribute to [Pleroma](https://pleroma.social), but I've since fallen in love with the language.
|
||||||
|
|
|
@ -3,7 +3,6 @@ metadata.title = "Building a JavaScript-Free Slide-Over Menu"
|
||||||
metadata.category = "web"
|
metadata.category = "web"
|
||||||
metadata.date = "2019-11-11 21:08:42 -0400"
|
metadata.date = "2019-11-11 21:08:42 -0400"
|
||||||
metadata.shortDesc = "Building a slide-over hamburger menu without using JavaScript."
|
metadata.shortDesc = "Building a slide-over hamburger menu without using JavaScript."
|
||||||
metadata.oldPermalink = "/web/2019/js-free-hamburger-menu/"
|
|
||||||
metadata.slug = "js-free-hamburger-menu"
|
metadata.slug = "js-free-hamburger-menu"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
```
|
```
|
||||||
metadata.title = "Mocking HTTP Requests for iOS App UI Tests"
|
metadata.title = "Mocking HTTP Requests for iOS App UI Tests"
|
||||||
metadata.category = "swift"
|
metadata.category = "ios"
|
||||||
metadata.date = "2019-12-22 19:12:42 -0400"
|
metadata.date = "2019-12-22 19:12:42 -0400"
|
||||||
metadata.shortDesc = "Integrating a tiny web server into your Xcode UI test target to mock HTTP requests."
|
metadata.shortDesc = "Integrating a tiny web server into your Xcode UI test target to mock HTTP requests."
|
||||||
metadata.oldPermalink = "/ios/2019/mock-http-ios-ui-testing/"
|
|
||||||
metadata.slug = "mock-http-ios-ui-testing"
|
metadata.slug = "mock-http-ios-ui-testing"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
```
|
|
||||||
metadata.title = "Faking the Mongo Eval Command"
|
|
||||||
metadata.category = "swift"
|
|
||||||
metadata.date = "2020-01-28 19:33:42 -0400"
|
|
||||||
metadata.shortDesc = "MongoDB 4.2 removed the eval command, which is a good security measure, but unfortunate for building database-viewing GUI."
|
|
||||||
metadata.slug = "faking-mongo-eval"
|
|
||||||
```
|
|
||||||
|
|
||||||
One of the changes in MongoDB 4.2 was the removal of the `eval` command. While a reasonable security measure, this is rather annoying if you're building [an app](https://git.shadowfacts.net/shadowfacts/MongoView) for interacting directly with a Mongo database. If you want to be able to run commands directly on the database, you now have to go through the `mongo` shell. This seems straightforward, but actually getting the data back into a format that's usable is a bit of a hassle.
|
|
||||||
|
|
||||||
<!-- excerpt-end -->
|
|
||||||
|
|
||||||
Actually running the command is, surprisingly, the easiest part of this whole endeavor. You can simply launch a [`Process`](https://developer.apple.com/documentation/foundation/process) which invokes the `mongo` shell with a few options as well the command to evaluate:
|
|
||||||
|
|
||||||
```swift
|
|
||||||
let mongoProc = Process()
|
|
||||||
process.launchPath = "/usr/local/bin/mongo"
|
|
||||||
mongoProc.arguments = ["mongodb://localhost:27017/your_database", "--quiet", "--norc", "--eval", command]
|
|
||||||
mongoProc.launch()
|
|
||||||
```
|
|
||||||
|
|
||||||
The `--quiet` option prevents the shell from logging its own messages, making parsing the output a little easier. The `--norc` option prevents it from executing `.monorc.js` on startup, so that the environment our command is running in is entirely standard. The `--eval` option does exactly what it says, it evaluates the following parameter in the shell.
|
|
||||||
|
|
||||||
This bit of code does make the assumption that the mongo shell is installed in or linked to `/usr/local/bin/mongo` (i.e., it's been installed through Homebrew). To do this properly, you would probably want to try and detect where Mongo is installed and use that path, as well as offer the user a way of customizing the path.
|
|
||||||
|
|
||||||
One additional thing to note is that launching an arbitrary executable requires either the App Sandbox be disabled, or Full Disk Access be requested (at least on macOS Catalina), otherwise the process will fail to launch with a message saying "launch path not accessible".
|
|
||||||
|
|
||||||
Getting the output is a little bit more difficult, but still not too complicated.
|
|
||||||
|
|
||||||
```swift
|
|
||||||
let outputPipe = Pipe()
|
|
||||||
|
|
||||||
let mongoProc = Process()
|
|
||||||
// ...
|
|
||||||
mongoProc.standardOutput = outputPipe
|
|
||||||
mongoProc.launch()
|
|
||||||
|
|
||||||
let outputHandle = outputPipe.fileHandleForReading
|
|
||||||
|
|
||||||
var output = ""
|
|
||||||
var data: Data!
|
|
||||||
do {
|
|
||||||
data = outputHandle.availableData
|
|
||||||
output.append(String(data: data, encoding: .utf8))
|
|
||||||
} while (data.count > 0)
|
|
||||||
|
|
||||||
outputHandle.closeFile()
|
|
||||||
```
|
|
||||||
|
|
||||||
We can create a [`Pipe`](https://developer.apple.com/documentation/foundation/pipe) object representing a UNIX pipe. The `mongo` process then uses that pipe as its stdout. We can then read from the pipe's output file handle in order to get the contents of what the shell printed.
|
|
||||||
|
|
||||||
Many StackOverflow posts on the topic of getting the output from a process just call `waitUntilExit` on the process and then read the entirety of the data from the pipe's output file handle. While suitable for small output, this approach does not work for situations where the total output of the command is greater than the buffer size of the pipe (as may very well be the case when running queries against large databases). To solve this, we need to continuously read from the pipe until there's no remaining data (meaning the pipe has closed).
|
|
||||||
|
|
||||||
Now that we've got the output from Mongo, we need to get it into Swift. Unfortunately, parsing it is a bit annoying. The `mongo` shell outputs a non-standardized format that's like JSON, but with a bunch of JavaScript helpers (e.g. `ObjectId("5e00eb48a14888e105a74fda")`) embedded in it. The [MongoSwift](https://github.com/mongodb/mongo-swift-driver) library can't parse this format (nor can anything else, as far as I can tell). So, in order to turn the shell output into the [Extended JSON](https://docs.mongodb.com/manual/reference/mongodb-extended-json/) format that MongoSwift can parse, we'll need to modify the command that we invoke the shell with.
|
|
||||||
|
|
||||||
We'll add some helper code at the beginning of the command we send that defines a function on both the `Object` and `Array` prototypes. This function will take whatever it's invoked on, pass it through `JSON.stringify` to convert it to Extended JSON, and then print it to the console.
|
|
||||||
|
|
||||||
The same function defined on the `Array` prototype will perform the same operations, just for each operation in the array, instead of on the array object as a whole. This isn't strictly necessary, but for my purposes I don't want to deal with top-level arrays, and this will make handling it a bit simpler as top-level array elements will be newline-delimited.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
Object.prototype.printExtJSON = function() { print(JSON.stringify(this)); };
|
|
||||||
Array.prototype.printExtJSON = function() { this.map(JSON.stringify).forEach(it => print(it)); };
|
|
||||||
```
|
|
||||||
|
|
||||||
For the Array helper, we can't just call `.forEach(print)` since `forEach` passes in multiple arguments (the value, the current index, and the whole array) all of which would get printed out if passed directly to `print`.
|
|
||||||
|
|
||||||
We can include these helpers at the beginning of our command and call it on the expression we've been passed in (where `prelude` is a string containing the above JavaScript code):
|
|
||||||
|
|
||||||
```swift
|
|
||||||
let command = "\(prelude)\(command).printExtJSON()"
|
|
||||||
```
|
|
||||||
|
|
||||||
This approach does have a drawback: only the result of the last expression in the user-inputted `command` will be stringified and printed. The results of any statements before will be lost, unless the command specifically calls our `printExtJSON` helper. Again, for my purposes, this is a reasonable trade off.
|
|
||||||
|
|
||||||
Now, back to Swift. We've got the Extended JSON output from the Mongo shell as one giant string and we just need to have MongoSwift parse it into something usable. Because of the way we're printing arrays (separate `print()` calls for each element) and the fact that we've put everything through `JSON.stringify`, it is guaranteed that there will be only one document per line of output. So, to parse each document separately, we can simply split the output we got at newlines and parse each individually:
|
|
||||||
|
|
||||||
```swift
|
|
||||||
let decoder = BSONDecoder()
|
|
||||||
let result = output.components(separatedBy: "\n").compactMap { (json) in
|
|
||||||
try? decoder.decode(BSON.self, from: json)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And there we have it, the data is finally in a form we can understand from the Swift side of things. If you want to see the whole source code for this, it's [part of MongoView](https://git.shadowfacts.net/shadowfacts/MongoView/src/commit/9488c108b693607e827ef77e5bc16f2cdd491f7c/MongoView/MongoEvaluator.swift). As far as I have come up with, that's about the best way of replicating the `eval` command of previous versions of Mongo. If you have any suggestions for how to improve this or make it more robust, let me know!
|
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
@ -1,13 +0,0 @@
|
||||||
```
|
|
||||||
metadata.layout = "default.html.ejs"
|
|
||||||
```
|
|
||||||
|
|
||||||
<div class="main">
|
|
||||||
<h1 class="page-heading">Posts from <%= year %></h1>
|
|
||||||
|
|
||||||
<% for (const post of posts) { %>
|
|
||||||
<%- include("includes/article-listing.html.ejs", { post }) %>
|
|
||||||
<% } %>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%- include("includes/pagination.html.ejs", { pagination }) %>
|
|
Loading…
Reference in New Issue