summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorache <ache@ache.one>2025-01-22 09:17:52 +0100
committerache <ache@ache.one>2025-01-22 09:17:52 +0100
commit591e05350749c59170969cb13408bf10d143c3f0 (patch)
tree24ac2b82a3cf318f8e936037bbde1054575a7155 /src
parentFix english: business => visiting (diff)
Add a anti-website
Diffstat (limited to 'src')
-rw-r--r--src/build/anti-index.mjs309
-rw-r--r--src/build/index.mjs2
-rw-r--r--src/build/loadMD.mjs15
3 files changed, 318 insertions, 8 deletions
diff --git a/src/build/anti-index.mjs b/src/build/anti-index.mjs
new file mode 100644
index 0000000..fb750a2
--- /dev/null
+++ b/src/build/anti-index.mjs
@@ -0,0 +1,309 @@
+import process from "node:process";
+import fs from "node:fs";
+import mustache from "mustache";
+import loadSVG from "./load-svg.mjs";
+import getRSS from "./rss.mjs";
+import loadMD from "./loadMD.mjs";
+import { cmpArticles } from "./article.mjs";
+import i18n from "./i18n.mjs";
+import { addDescription } from "./i18n.mjs";
+import { minifyHTML } from "./utils.mjs";
+
+import { SitemapStream, streamToPromise } from "sitemap";
+import { Readable } from "stream";
+
+const leftPanelTmpl = fs.readFileSync("src/templates/left.tmpl", "utf8");
+const likesTmpl = fs.readFileSync("src/templates/likes.tmpl", "utf8");
+const headerTmpl = fs.readFileSync("src/templates/header.tmpl", "utf8");
+const articleTmpl = fs.readFileSync("src/templates/article.tmpl", "utf8");
+const indexTmpl = fs.readFileSync("src/templates/index.tmpl", "utf8");
+const notesTmpl = fs.readFileSync("src/templates/notes.tmpl", "utf8");
+const legalTmpl = fs.readFileSync("src/templates/legal.tmpl", "utf8");
+const tagTmpl = fs.readFileSync("src/templates/tag.tmpl", "utf8");
+const hidTmpl = fs.readFileSync("src/templates/hid.tmpl", "utf8");
+const style_xslTmpl = fs.readFileSync("src/templates/style_xsl.tmpl", "utf8");
+const baseUrl = "https://ache.one/";
+
+const partials = {
+ header: headerTmpl,
+ leftPanel: leftPanelTmpl,
+ likesButton: likesTmpl,
+ hid: hidTmpl,
+};
+
+// Load global variables
+const svg = loadSVG();
+const antiDir = ".anti";
+
+let links = [];
+
+const listNotes = fs
+ .readdirSync("anti-notes")
+ .filter((name) => name.endsWith(".md"));
+const notesAll = loadMD(listNotes, "notes"); // lang is determined from the note
+const legalPages = loadMD(["legal.md", "legal-en.md"], "src/pages");
+
+for (const lang in i18n) {
+ const tagsArticle = new Map();
+ const filter = process.argv.slice(2);
+ const listArticles =
+ filter.length > 0
+ ? i18n[lang].articles.filter((article) => filter.includes(article.name))
+ : i18n[lang].articles;
+ const articles = loadMD(listArticles, "anti-articles", "articles", lang);
+
+ // Same for notes
+ const notes = notesAll.filter((note) => note.metaData.lang == lang);
+ const material = [...articles, ...notes];
+
+ // Make notes directory
+ try {
+ fs.mkdirSync(`${antiDir}/${lang}/notes`, { recursive: true });
+ } catch {
+ fs.rmSync(`${antiDir}/${lang}/notes`, { force: true, recursive: true });
+ fs.mkdirSync(`${antiDir}/${lang}/notes`, { recursive: true });
+ }
+
+ for (const post of material) {
+ const context = {
+ svg,
+ page_title: `${post.title} - ache`,
+ title: i18n[lang].title,
+ intro: i18n[lang].intro,
+ lang,
+ canonical: `${baseUrl}${post.url.slice(1)}`,
+ content: post.content,
+ metaData: post.metaData,
+ alt_lang: addDescription(post.metaData.alt_lang),
+ description: post.intro,
+
+ like_title: i18n[lang].like_title,
+ like_text: i18n[lang].like_text,
+ };
+ const output = mustache.render(articleTmpl, context, partials);
+
+ console.log(`Create : ${post.title}`);
+ const type = post.metaData?.type || "article";
+ fs.writeFileSync(
+ `${antiDir}/${type}s/${post.name}.html`,
+ minifyHTML(output),
+ );
+ links.push({
+ url: context.canonical,
+ changefreq: "yearly",
+ priority: 0.6,
+ img: [{ url: post?.imageUrl }],
+ });
+
+ for (const tag of post.metaData.tags) {
+ // Insert the tag, if it already exists add it, otherwise create a object for it.
+ if (tagsArticle.has(tag)) {
+ tagsArticle.get(tag).push(post);
+ } else {
+ tagsArticle.set(tag, [post]);
+ }
+ }
+ }
+
+ // Set of pages language dependant
+ try {
+ fs.mkdirSync(`${antiDir}/${lang}/tag`, { recursive: true });
+ } catch {
+ fs.rmSync(`${antiDir}/${lang}/tag`, { force: true, recursive: true });
+ fs.mkdirSync(`${antiDir}/${lang}/tag`, { recursive: true });
+ }
+
+ for (const [tag, material] of tagsArticle.entries()) {
+ console.log(`Create tag page : ${lang}/${tag}.html`);
+ material.sort(cmpArticles);
+
+ const articles = material.filter((item) => item.metaData.type != "note");
+ const notes = material.filter((item) => item.metaData.type == "note");
+
+ const context = {
+ svg,
+ page_title: `ache - Tag: ${tag}`,
+ title: i18n[lang].title,
+ intro: i18n[lang].intro,
+ lang,
+ tag,
+ articles,
+ notes,
+ description: `${i18n[lang]["tag_desc"]} ${tag}.`,
+ };
+
+ const output = mustache.render(tagTmpl, context, partials);
+ fs.writeFileSync(`${antiDir}/${lang}/tag/${tag}.html`, minifyHTML(output));
+ links.push({
+ url: `${baseUrl}${lang}/tag/${tag}`,
+ changefreq: "monthly",
+ priority: 0.3,
+ });
+ }
+
+ {
+ material.sort(cmpArticles);
+ console.log(`Create RSS Flux: ${lang}/rss.xml`);
+ const xmlFeed = getRSS(material, baseUrl, lang);
+ fs.writeFileSync(`${antiDir}/${lang}/rss.xml`, xmlFeed);
+ links.push({
+ url: `${baseUrl}${lang}/rss.xml`,
+ changefreq: "monthly",
+ priority: 0.3,
+ });
+ }
+
+ {
+ console.log(`Create RSS Flux: ${lang}/rss-main.xml`);
+ const xmlFeed = getRSS(articles, baseUrl, lang, "rss");
+ fs.writeFileSync(`${antiDir}/${lang}/rss-main.xml`, xmlFeed);
+ links.push({
+ url: `${baseUrl}${lang}/rss-main.xml`,
+ changefreq: "monthly",
+ priority: 0.3,
+ });
+ }
+
+ // Notes page: A note with a list of every note in that language
+ {
+ // Pages to redirect to when switching languages.
+ const alt_lang = {
+ fr: {
+ lang: "en",
+ url: "/en/notes",
+ },
+ en: {
+ lang: "fr",
+ url: "/fr/notes",
+ },
+ };
+ const context = {
+ page_title: i18n[lang].title.notes,
+ title: i18n[lang].title,
+ intro: i18n[lang].intro,
+ lang,
+ canonical: `${baseUrl}${lang}`,
+ svg,
+ notes,
+ alt_lang: [alt_lang[lang]],
+ description: i18n[lang].index_desc,
+ };
+
+ console.log(`Create : Notes page ${lang}`);
+ const output = mustache.render(notesTmpl, context, partials);
+ fs.writeFileSync(`${antiDir}/${lang}/notes/index.html`, minifyHTML(output));
+ links.push({
+ url: `${baseUrl}${lang}/notes`,
+ changefreq: "monthly",
+ priority: 0.7,
+ img: [{ url: "https://ache.one/res/ache.svg" }],
+ });
+ }
+
+ // Legal page
+ {
+ const legal_page = legalPages.filter(
+ (page) => page.metaData.lang == lang,
+ )[0];
+ // Pages to redirect to when switching languages.
+ const alt_lang = {
+ fr: {
+ lang: "en",
+ url: "/en/legal",
+ },
+ en: {
+ lang: "fr",
+ url: "/fr/legal",
+ },
+ };
+ const context = {
+ page_title: i18n[lang].title.legal,
+ title: i18n[lang].title,
+ intro: i18n[lang].intro,
+ lang,
+ canonical: `${baseUrl}${lang}/legal`,
+ svg,
+ content: legal_page.content,
+ alt_lang: [alt_lang[lang]],
+ description: i18n[lang].index_desc,
+ };
+
+ console.log(`Create : Legal page ${lang}`);
+ const output = mustache.render(legalTmpl, context, partials);
+
+ fs.writeFileSync(`${antiDir}/${lang}/legal.html`, minifyHTML(output));
+ links.push({
+ url: `${lang}/legal.html`,
+ changefreq: "yearly",
+ priority: 0.1,
+ });
+ }
+
+ // Home page
+ {
+ // Pages to redirect to when switching languages.
+ const alt_lang = {
+ fr: {
+ lang: "en",
+ url: "/en/",
+ },
+ en: {
+ lang: "fr",
+ url: "/fr/",
+ },
+ };
+ const context = {
+ page_title: i18n[lang].title.main,
+ title: i18n[lang].title,
+ intro: i18n[lang].intro,
+ lang,
+ canonical: `${baseUrl}${lang}`,
+ svg,
+ articles,
+ alt_lang: [alt_lang[lang]],
+ description: i18n[lang].index_desc,
+ };
+
+ console.log(`Create : Home page ${lang}`);
+ const output = mustache.render(indexTmpl, context, partials);
+ fs.writeFileSync(`${antiDir}/${lang}/index.html`, minifyHTML(output));
+ links.push({
+ url: `${baseUrl}${lang}`,
+ changefreq: "monthly",
+ priority: 0.7,
+ img: [{ url: "https://ache.one/res/ache.svg" }],
+ });
+ }
+
+ // style.xsl
+ {
+ /* Because firefox doesn't support disable-output-escaping="yes" (it doesn't do anything)
+ * The description must be in the xsl file non escaped. 🤷
+ * So the xsl file is made from a template to insert the description.
+ * Even if the goal of xsl is to grab that information from the initial page
+ */
+ let context = {
+ lang,
+ svg,
+ description: i18n[lang].rss_full.description,
+ };
+
+ console.log(`Create : Style.xsl (${lang})`);
+ let output = mustache.render(style_xslTmpl, context, partials);
+ fs.writeFileSync(`${antiDir}/${lang}/style.xsl`, output);
+
+ context.description = i18n[lang].rss.description;
+ console.log(`Create : Style.xsl (${lang})`);
+ output = mustache.render(style_xslTmpl, context, partials);
+ fs.writeFileSync(`${antiDir}/${lang}/style_main.xsl`, output);
+ }
+}
+
+console.log(`Create: sitemap.xml`);
+// Create a stream to write to
+const stream = new SitemapStream({ hostname: "https://ache.one" });
+
+// Return a promise that resolves with your XML string
+streamToPromise(Readable.from(links).pipe(stream))
+ .then((data) => data.toString())
+ .then((sitemapXML) => fs.writeFileSync(`${antiDir}/sitemap.xml`, sitemapXML));
diff --git a/src/build/index.mjs b/src/build/index.mjs
index 8baeeef..fda873f 100644
--- a/src/build/index.mjs
+++ b/src/build/index.mjs
@@ -49,7 +49,7 @@ for (const lang in i18n) {
filter.length > 0
? i18n[lang].articles.filter((article) => filter.includes(article.name))
: i18n[lang].articles;
- const articles = loadMD(listArticles, "articles", lang);
+ const articles = loadMD(listArticles, "articles", "articles", lang);
// Same for notes
const notes = notesAll.filter((note) => note.metaData.lang == lang);
diff --git a/src/build/loadMD.mjs b/src/build/loadMD.mjs
index 8153d37..ed31196 100644
--- a/src/build/loadMD.mjs
+++ b/src/build/loadMD.mjs
@@ -10,17 +10,18 @@ import { toString, toMdRaw, mdToHtmlRaw } from "./to-html.mjs";
import { getArticleYear } from "./article.mjs";
import i18n from "./i18n.mjs";
-const loadMD = (listFile, suffix, lang) => {
+const loadMD = (listFile, dirSuffix, pathLinkSuffix, lang) => {
const listContent = [];
for (const file of listFile) {
console.log(`Working on ${file}`);
- const content = fs.readFileSync(`${suffix}/${file}`, "utf8");
+ const content = fs.readFileSync(`${dirSuffix}/${file}`, "utf8");
const mdRaw = toMdRaw(content);
const tomlStringValue = mdRaw.children[0].value;
const metaData = toml.parse(tomlStringValue);
const newHTML = mdToHtmlRaw(mdRaw);
const langR = lang || metaData.lang;
+ const linkSuffix = pathLinkSuffix || dirSuffix;
const htmlContent = newHTML;
const htmlRender = toString(htmlContent);
@@ -32,7 +33,7 @@ const loadMD = (listFile, suffix, lang) => {
const logo = select("img", intro);
if (logo && logo?.properties) {
if (logo.properties.src[0] != "/") {
- logo.properties.src = `/${suffix}/${logo.properties.src}`;
+ logo.properties.src = `/${linkSuffix}/${logo.properties.src}`;
}
logo.properties.height = "150";
logo.properties.width = "150";
@@ -40,10 +41,10 @@ const loadMD = (listFile, suffix, lang) => {
const logoP = select("source", intro);
if (logoP !== null) {
- logoP.properties.srcSet = `/${suffix}/${logoP.properties.srcSet}`;
+ logoP.properties.srcSet = `/${linkSuffix}/${logoP.properties.srcSet}`;
}
- titleHtml.children[0].properties.href = `/${suffix}/${file.slice(0, -3)}`;
+ titleHtml.children[0].properties.href = `/${linkSuffix}/${file.slice(0, -3)}`;
const title = hastToString(titleHtml);
const domTitle = cssesc(
@@ -56,7 +57,7 @@ const loadMD = (listFile, suffix, lang) => {
console.log(langR);
const readMore = h("a", i18n[langR]["read_more"]);
- readMore.properties.href = `/${suffix}/${file.slice(0, -3)}`;
+ readMore.properties.href = `/${linkSuffix}/${file.slice(0, -3)}`;
const pubYear = getArticleYear({ metaData });
if (metaData.pubDate) {
@@ -79,7 +80,7 @@ const loadMD = (listFile, suffix, lang) => {
pubYear,
title,
domTitle,
- url: `/${suffix}/${file.slice(0, -3)}`,
+ url: `/${linkSuffix}/${file.slice(0, -3)}`,
});
}