init
This commit is contained in:
commit
cd65934c8a
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build/
|
5
deno.jsonc
Normal file
5
deno.jsonc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"fmt": {
|
||||
"indentWidth": 4,
|
||||
},
|
||||
}
|
11
deno.lock
Normal file
11
deno.lock
Normal 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
210
main.ts
Normal 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
62
templates/article.html
Normal 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
45
templates/categories.html
Normal 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
51
templates/index.html
Normal 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>
|
Loading…
Reference in New Issue
Block a user