summaryrefslogtreecommitdiff
path: root/src/build
diff options
context:
space:
mode:
Diffstat (limited to 'src/build')
-rw-r--r--src/build/index.mjs83
-rw-r--r--src/build/list-articles.mjs7
-rw-r--r--src/build/load-svg.mjs17
-rw-r--r--src/build/remove-footnote-header.mjs17
-rw-r--r--src/build/rss.mjs42
-rw-r--r--src/build/special_box.mjs24
-rw-r--r--src/build/to-html.mjs47
7 files changed, 237 insertions, 0 deletions
diff --git a/src/build/index.mjs b/src/build/index.mjs
new file mode 100644
index 0000000..84d4d9a
--- /dev/null
+++ b/src/build/index.mjs
@@ -0,0 +1,83 @@
+import fs from 'node:fs';
+import mustache from 'mustache';
+import {u} from 'unist-builder';
+import {select} from 'hast-util-select';
+import {toString as hastToString} from 'mdast-util-to-string';
+
+import {toHtmlRaw, toString} from './to-html.mjs';
+import loadSVG from './load-svg.mjs';
+import listArticles from './list-articles.mjs';
+import getRSS from './rss.mjs';
+
+const loadMD = (listFile, suffix) => {
+ const listContent = [];
+ for (const file of listFile) {
+ const content = fs.readFileSync(`${suffix}/${file}`, 'utf8');
+ const htmlContent = toHtmlRaw(content);
+ const htmlRender = toString(htmlContent);
+
+ const titleHtml = select('h1', htmlContent);
+ const intro = select('p', htmlContent);
+ const logo = select('img', htmlContent);
+ logo.properties.src = `${suffix}/${logo.properties.src}`;
+ titleHtml.children[0].properties.href = `${suffix}/${file.slice(0, -3)}.html`;
+
+ const title = hastToString(titleHtml);
+ const domTitle = title.replace(/\s+/g, '-').toLowerCase(); // Maybe encodeURI
+ console.log(`Create : ${title}`);
+
+ listContent.push({
+ name: file.slice(0, -3),
+ content: htmlRender,
+ intro: toString(u('root', [titleHtml, intro])),
+ introDesc: hastToString(intro),
+ imageUrl: logo.properties.src,
+ title,
+ domTitle,
+ });
+ }
+
+ return listContent;
+};
+
+const leftPanelTmpl = fs.readFileSync('src/templates/left.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 partials = {
+ header: headerTmpl,
+ leftPanel: leftPanelTmpl,
+};
+
+const svg = loadSVG();
+
+const articles = loadMD(listArticles, 'articles');
+
+for (const article of articles) {
+ const context = {
+ svg,
+ title: `${article.title} - ache`,
+ content: article.content,
+ domTitle: article.domTitle,
+ };
+ const output = mustache.render(articleTmpl, context, partials);
+
+ fs.writeFileSync(`articles/${article.name}.html`, output);
+}
+
+console.log('Create : rss.xml');
+const xmlFeed = getRSS(articles);
+fs.writeFileSync('rss.xml', xmlFeed);
+
+{
+ const context = {
+ title: 'ache: Blog personnel',
+ svg,
+ articles,
+ };
+
+ console.log('Create : Home page');
+ const output = mustache.render(indexTmpl, context, partials);
+ fs.writeFileSync('index.html', output);
+}
diff --git a/src/build/list-articles.mjs b/src/build/list-articles.mjs
new file mode 100644
index 0000000..d327573
--- /dev/null
+++ b/src/build/list-articles.mjs
@@ -0,0 +1,7 @@
+const listArticles = [
+ 'bizarreries-du-langage-c.md',
+ 'retour-sur-laoc-2021-semaine-1.md',
+ '2FA-discord-sur-pc.md',
+ 'duckduckgo-google-en-mieux.md',
+];
+export default listArticles;
diff --git a/src/build/load-svg.mjs b/src/build/load-svg.mjs
new file mode 100644
index 0000000..6bf8db4
--- /dev/null
+++ b/src/build/load-svg.mjs
@@ -0,0 +1,17 @@
+import fs from 'node:fs';
+
+const loadSVG = () => {
+ const svg = {};
+
+ for (const file of fs.readdirSync('s/imgM')) {
+ if (file.endsWith('.svg')) {
+ const contentFile = fs.readFileSync(`s/imgM/${file}`, 'utf8');
+ const name = file.slice(0, -'.svg'.length);
+ svg[name] = contentFile;
+ }
+ }
+
+ return svg;
+};
+
+export default loadSVG;
diff --git a/src/build/remove-footnote-header.mjs b/src/build/remove-footnote-header.mjs
new file mode 100644
index 0000000..a238692
--- /dev/null
+++ b/src/build/remove-footnote-header.mjs
@@ -0,0 +1,17 @@
+import {visit} from 'unist-util-visit';
+
+// This plugin is an example to let users write HTML with directives.
+// It’s informative but rather useless.
+// See below for others examples.
+/** @type {import('unified').Plugin<[], import('mdast').Root>} */
+export default function specialBox() {
+ return tree => {
+ visit(tree, node => {
+ if (node?.tagName === 'h2' && node?.properties?.id === 'footnote-label') {
+ node.tagName = 'hr';
+ node.children = []; // Exposure of children, Roman's way
+ }
+ });
+ };
+}
+
diff --git a/src/build/rss.mjs b/src/build/rss.mjs
new file mode 100644
index 0000000..606cdef
--- /dev/null
+++ b/src/build/rss.mjs
@@ -0,0 +1,42 @@
+import RSS from 'rss';
+
+const siteUrl = 'https://ache.one';
+
+const getRSS = articles => {
+ const rssFeed = new RSS({
+ title: 'ache: Blog personnel',
+ description: 'Programmation, Algorithmique, Système, *pick you poison*',
+ // eslint-disable-next-line camelcase
+ feed_url: `${siteUrl}/feed.xml`,
+ // eslint-disable-next-line camelcase
+ site_url: siteUrl,
+ // eslint-disable-next-line camelcase
+ image_url: `${siteUrl}/ache.svg`,
+ language: 'fr',
+ pubDate: (new Date().toLocaleString()),
+ ttl: '1440',
+ // eslint-disable-next-line camelcase
+ custom_elements: ['<?xml-stylesheet type="text/css" href="http://localhost:8080/s/css/style.css" ?>'],
+ });
+
+ for (const article of articles.slice(0, 10)) {
+ rssFeed.item({
+ title: article.title,
+ description: '<article>' + article.content + '</article>',
+ // eslint-disable-next-line camelcase
+ image_url: article.imageUrl,
+ url: `${siteUrl}/articles/${article.domTitle}`,
+ guid: article.domTitle,
+ author: 'ache',
+ // eslint-disable-next-line camelcase
+ custom_elements: [
+ {logo: article.imageUrl},
+ {intro: article.introDesc},
+ ],
+ });
+ }
+
+ return rssFeed.xml({indent: false}).replace(/<\?xml version="1.0" encoding="UTF-8"\?>/g, '<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="/style.xsl"?>');
+};
+
+export default getRSS;
diff --git a/src/build/special_box.mjs b/src/build/special_box.mjs
new file mode 100644
index 0000000..0b284ec
--- /dev/null
+++ b/src/build/special_box.mjs
@@ -0,0 +1,24 @@
+import {visit} from 'unist-util-visit';
+
+// This plugin is an example to let users write HTML with directives.
+// It’s informative but rather useless.
+// See below for others examples.
+/** @type {import('unified').Plugin<[], import('mdast').Root>} */
+export default function specialBox() {
+ return tree => {
+ visit(tree, node => {
+ if (node.type === 'containerDirective' && (
+ node.name === 'attention'
+ || node.name === 'question'
+ || node.name === 'information')) {
+ const data = node.data || (node.data = {});
+
+ data.hName = 'div';
+ data.hProperties = {
+ className: 'special-box ' + node.name,
+ };
+ }
+ });
+ };
+}
+
diff --git a/src/build/to-html.mjs b/src/build/to-html.mjs
new file mode 100644
index 0000000..d8c3a84
--- /dev/null
+++ b/src/build/to-html.mjs
@@ -0,0 +1,47 @@
+import {unified} from 'unified';
+import remarkParse from 'remark-parse';
+import remarkGfm from 'remark-gfm';
+import remarkToc from 'remark-toc';
+import remarkDirective from 'remark-directive';
+import remarkMath from 'remark-math';
+import remarkRehype from 'remark-rehype';
+import rehypeSlug from 'rehype-slug';
+import rehypeKaTeX from 'rehype-katex';
+import rehypeRaw from 'rehype-raw';
+import rehypeAutolinkHeadings from 'rehype-autolink-headings';
+import rehypeStringify from 'rehype-stringify';
+import rehypeHighlight from 'rehype-highlight';
+import {h} from 'hastscript';
+import remarkSpecialBox from './special_box.mjs';
+import remarkRemoveFootnoteHeader from './remove-footnote-header.mjs';
+
+const autoLinkOption = {
+ behavior: 'wrap',
+ properties: {
+ ariaHidden: true,
+ tabIndex: -1,
+ className: 'anchor',
+ },
+};
+
+const generator = unified()
+ .use(remarkParse)
+ .use(remarkGfm)
+ .use(remarkToc, {heading: 'Sommaire', tight: true, ordered: true})
+ .use(remarkMath)
+ .use(remarkDirective)
+ .use(remarkSpecialBox)
+ .use(remarkRehype, {allowDangerousHtml: true})
+ .use(rehypeRaw)
+ .use(remarkRemoveFootnoteHeader)
+ .use(rehypeKaTeX)
+ .use(rehypeSlug)
+ .use(rehypeHighlight)
+ .use(rehypeAutolinkHeadings, autoLinkOption)
+ .use(rehypeStringify);
+
+const toHtml = content => generator.processSync(content);
+
+export const toHtmlRaw = content => generator.runSync(generator.parse(content));
+export const toString = content => generator.stringify(content);
+export default toHtml;