This commit is contained in:
Simon 2025-05-03 04:06:02 +02:00
commit cd65934c8a
7 changed files with 385 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

5
deno.jsonc Normal file
View File

@ -0,0 +1,5 @@
{
"fmt": {
"indentWidth": 4,
},
}

11
deno.lock Normal file
View File

@ -0,0 +1,11 @@
{
"version": "4",
"specifiers": {
"jsr:@b-fuze/deno-dom@*": "0.1.49"
},
"jsr": {
"@b-fuze/deno-dom@0.1.49": {
"integrity": "45c40175fdd1e74ab2d4b54c4fdaabbad6e5b76ee28df12a48e076b3fa7901a9"
}
}
}

210
main.ts Normal file
View File

@ -0,0 +1,210 @@
import { DOMParser } from "jsr:@b-fuze/deno-dom";
const downloadDate = new Date();
type ArticleInfo = {
id: number;
title: string;
dateAdded: string;
author: string;
tags: string[];
originalLink: string;
};
type Article = ArticleInfo & {
body: string;
};
async function downloadArticle(id: number): Promise<Article> {
const url =
`https://proletarianrevolution.net/index.php?page=news&type=view&id=${id}`;
console.log(`Fetching '${url}'...`);
const contentText = await fetch(url)
.then((res) => res.text());
// await Deno.writeTextFile("test_article.html", contentText);
// const contentText = await Deno.readTextFile("test_article.html");
const doc = new DOMParser().parseFromString(contentText, "text/html");
const article = doc.querySelector("article");
const title = article
?.querySelector("h1.screen_title")
?.innerText
.trim()!;
const dateAdded = article
?.querySelector("article ul.meta_details_list")
?.children[1]
.children[0]
.attributes
.getNamedItem("datetime")
?.value!;
const author = article
?.querySelector("article ul.meta_details_list")
?.children[2]
.children[1]
.innerText!;
const tags = [article]
.map((article) =>
article?.querySelectorAll(`span[itemprop="keywords"] a`)
)
.map((nodeList) => [...nodeList!])
.map((children) => children.map((aElem) => aElem.innerText))[0];
const body = article
?.querySelector(`div[itemprop="articleBody"]`)
?.innerHTML!;
return { id, title, dateAdded, author, tags, originalLink: url, body };
}
const articleTemplate = await Deno.readTextFile("templates/article.html");
function renderArticle(
article: Article,
prevId?: number,
nextId?: number,
): string {
return articleTemplate
.slice()
.replaceAll("$title", article.title)
.replaceAll("$dateAddedRaw", article.dateAdded)
.replaceAll(
"$dateAdded",
new Date(article.dateAdded).toUTCString(),
)
.replaceAll("$downloadDate", downloadDate.toUTCString())
.replaceAll("$author", article.author)
.replaceAll(
"$tags",
article.tags.map((tag) =>
`<a href="categories.html#${tag}"><span class="tag">${tag}</span></a>`
).join(
", ",
),
)
.replaceAll("$body", article.body)
.replaceAll("$originalLink", article.originalLink)
.replaceAll(
"$navPrevious",
prevId ? `<a href="article-${prevId}.html">Previous</a>` : "",
)
.replaceAll(
"$navNext",
nextId ? `<a href="article-${nextId}.html">Next</a>` : "",
);
}
const indexTemplate = await Deno.readTextFile("templates/index.html");
function renderIndex(articles: ArticleInfo[]): string {
return indexTemplate
.slice()
.replaceAll("$downloadDate", downloadDate.toUTCString())
.replaceAll(
"$articleEntries",
articles
.toSorted((a, b) => a.title.localeCompare(b.title))
.map((article) => `
<tr>
<td>
<a href="article-${article.id}.html">
${article.title}
</a>
</td>
<td>${article.author}</td>
<td>${new Date(article.dateAdded).toUTCString()}</td>
<td>${(
article.tags.map((tag) => `
<a href="categories.html#${tag}">
<span class="tag">${tag}</span>
</a>
`).join(", ")
)}</td>
</tr>
`).join(""),
);
}
const categoriesTemplate = await Deno.readTextFile("templates/categories.html");
function renderCategories(articles: ArticleInfo[]): string {
const categories = articles
.reduce<Map<string, ArticleInfo[]>>((categories, article) => {
for (const tag of article.tags) {
if (!categories.has(tag)) {
categories.set(tag, []);
}
categories.get(tag)!.push(article);
}
return categories;
}, new Map());
return categoriesTemplate
.slice()
.replaceAll("$downloadDate", downloadDate.toUTCString())
.replaceAll(
"$categoryEntries",
categories
.entries()
.toArray()
.toSorted((a, b) => a[0].localeCompare(b[0]))
.map(([tag, articles]) => `
<div id="${tag}">
<h2>${tag}</h2>
<ul>${
articles
.toSorted((a, b) => a.title.localeCompare(b.title))
.map((article) => `
<li>
<a href="article-${article.id}.html">
${article.title}
</a>
</li>
`).join("")
}</ul>
</div>
`).join(""),
);
}
await Deno.mkdir("build/articles", { recursive: true });
const articlesIdMin = 1;
const articlesIdMax = 87;
const articles: ArticleInfo[] = [];
for (let id = articlesIdMin; id <= articlesIdMax; ++id) {
const prevId = id > articlesIdMin ? id : undefined;
const nextId = id < articlesIdMax ? id : undefined;
console.log(`Downloading article ${id}...`);
const article = await downloadArticle(id);
if (article.title === "Log in") {
console.log(`Article ${id} invalid. Skipping!`);
continue;
}
const html = renderArticle(article, prevId, nextId);
const filepath = `build/articles/article-${id}.html`;
await Deno.writeTextFile(filepath, html);
console.log(`Article ${id} written to '${filepath}'!`);
const { title, dateAdded, author, tags, originalLink } = article;
articles.push({ id, title, dateAdded, author, tags, originalLink });
}
console.log("Building index...");
const indexHtml = renderIndex(articles);
await Deno.writeTextFile("build/articles/index.html", indexHtml);
console.log("Index written to 'build/articles/index.html'!");
console.log("Building categories...");
const categoriesHtml = renderCategories(articles);
await Deno.writeTextFile("build/articles/categories.html", categoriesHtml);
console.log("Categories written to 'build/articles/categories.html'!");
console.log("Done!");

62
templates/article.html Normal file
View File

@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="$author">
<meta name="description" content="$title">
<style>
:root {
color-scheme: light dark;
}
body {
margin: 0 auto;
padding: 2rem;
max-width: 1000px;
line-height: 1.6em;
}
table {
border-collapse: collapse;
}
</style>
</head>
<body>
<header>
Mirror website of
<a href="https://proletarianrevolution.net/">
PROLETARIANREVOLTION.NET
</a>
</header>
<nav>
<a href="index.html">Index</a>
<a href="categories.html">Categories</a>
$navPrevious $navNext
</nav>
<main>
<h1 id="title">$title</h1>
<ul>
<li>
Date added: <span id="date-added">$dateAdded</span>
<span id="date-added-raw">($dateAddedRaw)</span>
</li>
<li>
Date downloaded: <span id="date-downloaded"
>$downloadDate</span>
</li>
<li>Author: <span id="author">$author</span></li>
<li>Tags: <span id="tags">$tags</span></li>
<li>
Link to original: <a href="$originalLink">$originalLink</a>
</li>
</ul>
<article id="article-body">$body</article>
</main>
<nav>
<a href="index.html">Index</a>
<a href="categories.html">Categories</a>
$navPrevious $navNext
</nav>
</body>
</html>

45
templates/categories.html Normal file
View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="PROLETARIANREVOLUTION.NET">
<meta name="description" content="Categories">
<style>
:root {
color-scheme: light dark;
}
body {
margin: 0 auto;
padding: 2rem;
max-width: 1400px;
line-height: 1.6em;
}
table {
border-collapse: collapse;
}
</style>
</head>
<body>
<header>
Mirror website of
<a href="https://proletarianrevolution.net/">
PROLETARIANREVOLTION.NET
</a>
</header>
<nav>
<a href="index.html">Index</a>
</nav>
<main>
<h1 id="title">
Categories or tags
</h1>
<p>The articles were downloaded $downloadDate.</p>
<div id="categories">
$categoryEntries
</div>
</main>
</body>
</html>

51
templates/index.html Normal file
View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="PROLETARIANREVOLUTION.NET">
<meta name="description" content="All articles (A-Z)">
<style>
:root {
color-scheme: light dark;
}
body {
margin: 0 auto;
padding: 2rem;
max-width: 1400px;
line-height: 1.6em;
}
table {
border-collapse: collapse;
}
</style>
</head>
<body>
<header>
Mirror website of
<a href="https://proletarianrevolution.net/">
PROLETARIANREVOLTION.NET
</a>
</header>
<main>
<h1 id="title">
All articles (A-Z)
</h1>
<p>The articles were downloaded $downloadDate.</p>
<p>
See also the <a href="categories.html">list over categories</a>.
</p>
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th>Date added</th>
<th>Tags</th>
</tr>
$articleEntries
</table>
</main>
</body>
</html>