commit cd65934c8a6780e5651d756ba21fe978128dfae9 Author: sfja.skp Date: Sat May 3 04:06:02 2025 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/deno.jsonc b/deno.jsonc new file mode 100644 index 0000000..808b876 --- /dev/null +++ b/deno.jsonc @@ -0,0 +1,5 @@ +{ + "fmt": { + "indentWidth": 4, + }, +} diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..ee1dc83 --- /dev/null +++ b/deno.lock @@ -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" + } + } +} diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..f2b6ffc --- /dev/null +++ b/main.ts @@ -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
{ + 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) => + `${tag}` + ).join( + ", ", + ), + ) + .replaceAll("$body", article.body) + .replaceAll("$originalLink", article.originalLink) + .replaceAll( + "$navPrevious", + prevId ? `Previous` : "", + ) + .replaceAll( + "$navNext", + nextId ? `Next` : "", + ); +} + +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) => ` + + + + ${article.title} + + + ${article.author} + ${new Date(article.dateAdded).toUTCString()} + ${( + article.tags.map((tag) => ` + + ${tag} + + `).join(", ") + )} + + `).join(""), + ); +} + +const categoriesTemplate = await Deno.readTextFile("templates/categories.html"); + +function renderCategories(articles: ArticleInfo[]): string { + const categories = articles + .reduce>((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]) => ` +
+

${tag}

+
    ${ + articles + .toSorted((a, b) => a.title.localeCompare(b.title)) + .map((article) => ` +
  • + + ${article.title} + +
  • + `).join("") + }
+
+ `).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!"); diff --git a/templates/article.html b/templates/article.html new file mode 100644 index 0000000..534b0be --- /dev/null +++ b/templates/article.html @@ -0,0 +1,62 @@ + + + + + + + + + + +
+ Mirror website of + + PROLETARIANREVOLTION.NET + +
+ +
+

$title

+
    +
  • + Date added: $dateAdded + ($dateAddedRaw) +
  • +
  • + Date downloaded: $downloadDate +
  • +
  • Author: $author
  • +
  • Tags: $tags
  • +
  • + Link to original: $originalLink +
  • +
+
$body
+
+ + + diff --git a/templates/categories.html b/templates/categories.html new file mode 100644 index 0000000..06a7f7e --- /dev/null +++ b/templates/categories.html @@ -0,0 +1,45 @@ + + + + + + + + + + +
+ Mirror website of + + PROLETARIANREVOLTION.NET + +
+ +
+

+ Categories or tags +

+

The articles were downloaded $downloadDate.

+
+ $categoryEntries +
+
+ + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..e679589 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,51 @@ + + + + + + + + + + +
+ Mirror website of + + PROLETARIANREVOLTION.NET + +
+
+

+ All articles (A-Z) +

+

The articles were downloaded $downloadDate.

+

+ See also the list over categories. +

+ + + + + + + + $articleEntries +
TitleAuthorDate addedTags
+
+ +