compilar: Portear a Node.js

This commit is contained in:
Cat /dev/Nulo 2022-12-19 20:18:14 -03:00
parent 507c2239f2
commit c14875b10b
6 changed files with 259 additions and 15 deletions

View file

@ -1,13 +1,15 @@
root = true
[*]
indent_style = tab
tab_width = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
indent_style = tab
tab_width = 4
[*.yml]
indent_style = space
indent_size = 2

2
.gitignore vendored
View file

@ -1,2 +1,2 @@
build/
zig-cache/
node_modules/

View file

@ -1,20 +1,17 @@
pipeline:
build:
image: docker.io/alpine:edge
image: docker.io/alpine:3.17
commands:
- echo "172.17.0.1 alpine.proxy.coso" >> /etc/hosts
- echo "http://alpine.proxy.coso/alpine/edge/main" > /etc/apk/repositories
- echo "http://alpine.proxy.coso/alpine/edge/community" >> /etc/apk/repositories
- echo "http://alpine.proxy.coso/alpine/edge/testing" >> /etc/apk/repositories
- apk add musl-dev cmark-dev lua5.1 zig gcc
- echo "http://alpine.proxy.coso/alpine/v3.17/main" > /etc/apk/repositories
- echo "http://alpine.proxy.coso/alpine/v3.17/community" >> /etc/apk/repositories
- apk add curl nodejs npm lua5.1
- curl -fsSL "https://github.com/pnpm/pnpm/releases/latest/download/pnpm-linuxstatic-x64" -o /bin/pnpm; chmod +x /bin/pnpm
- pnpm set registry http://npm.proxy.coso
- pnpm install
- node compilar.js
- zig run compilar.zig -lc -lcmark
deploy:
image: docker.io/alpine:3.16
commands:
- echo "172.17.0.1 alpine.proxy.coso" >> /etc/hosts
- echo "http://alpine.proxy.coso/alpine/v3.16/main" > /etc/apk/repositories
- echo "http://alpine.proxy.coso/alpine/v3.16/community" >> /etc/apk/repositories
- apk add rsync openssh-client-default
- eval $(ssh-agent -s)

176
compilar.js Normal file
View file

@ -0,0 +1,176 @@
import { copyFile, mkdir, opendir, readFile, writeFile } from "fs/promises";
import { basename, extname, join } from "path";
import { execFile as execFileCallback } from "child_process";
import { promisify } from "util";
import * as commonmark from "commonmark";
const execFile = promisify(execFileCallback);
const reader = new commonmark.Parser({ smart: true });
const writer = new commonmark.HtmlRenderer({ safe: false, smart: true });
const config = {
sourcePath: ".",
buildPath: "build",
};
function head(title, outputName) {
// TODO: deshardcodear og:url
return `<!doctype html>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1.0">
<meta name=author content=Nulo>
<meta property=og:title content="${title}">
<meta property=og:type content=website>
<meta property=og:url content="https://nulo.in/${outputName}.html">
<meta property=og:image content=cowboy.svg>
<link rel=stylesheet href=drip.css>
<link rel=icon href=cowboy.svg>
<title>${title}</title>
`;
}
function header(title, sourceCodePath, linkConexiones = false) {
return (
`<a href=.>☚ Volver al inicio</a>` +
`<header>
<h1>${title}</h1>
<a href="https://gitea.nulo.in/Nulo/sitio/commits/branch/ANTIFASCISTA/${sourceCodePath}">Historial</a>${
linkConexiones
? ` /
<a href="#conexiones">Conexiones</a>`
: ""
}
</header>`
);
}
const wikilinkExp = /\[\[(.+?)\]\]/giu;
async function scanForConnections(sourcePath) {
const dir = await opendir(sourcePath);
let connections = [];
for await (const entry of dir) {
const extension = extname(entry.name);
if (extension === ".md") {
const name = basename(entry.name, ".md");
const file = await readFile(join(config.sourcePath, entry.name), "utf-8");
for (const [, linked] of file.matchAll(wikilinkExp)) {
connections.push({ linked, linker: name });
}
}
}
return connections;
}
function hackilyTransformHtml(html) {
return html
.replaceAll("<a h", '<a rel="noopener noreferrer" h')
.replaceAll(wikilinkExp, `<a href="$1.html">$1</a>`);
}
const connections = await scanForConnections(config.sourcePath);
await mkdir(config.buildPath, { recursive: true });
const dir = await opendir(config.sourcePath);
let pageList = [];
let promises = [];
for await (const entry of dir) {
if (!entry.isFile()) continue;
promises.push(compileFile(entry.name));
}
await Promise.all(promises);
await compilePageList(config, pageList);
async function compileFile(name) {
const extension = extname(name);
if (
[".js", ".md", ".css", ".png", ".jpg", ".mp4", ".svg", ".html"].includes(
extension
)
) {
await copyFile(join(config.sourcePath, name), join(config.buildPath, name));
}
if ([".md", ".gen"].includes(extension)) {
pageList.push(basename(name, extension));
}
if (extension === ".md") await compileMarkdown(config, name);
else if (extension === ".gen") await compileExecutable(config, name);
}
async function compilePageList(config, pageList) {
const name = "Lista de páginas";
const outputPath = join(config.buildPath, name + ".html");
const html =
head(name, name) +
header(name, "compilar.js") +
`<ul>
${pageList
.map((name) => `<li><a href="${name}.html">${name}</a></li>`)
.join("\n")}
</ul>
`;
await writeFile(outputPath, html);
}
async function compileMarkdown(config, sourceFileName) {
const name = basename(sourceFileName, ".md");
const markdown = await readFile(
join(config.sourcePath, sourceFileName),
"utf-8"
);
const markdownHtml = renderMarkdown(markdown);
const fileConnections = connections.filter(({ linked }) => linked === name);
const isIndex = sourceFileName === "index.md";
const title = isIndex ? "nulo.in" : name;
const html =
head(title, sourceFileName) +
(isIndex ? "" : header(title, sourceFileName, fileConnections.length > 0)) +
hackilyTransformHtml(markdownHtml) +
(fileConnections.length > 0
? `
<section id=conexiones>
<h2> Conexiones (${fileConnections.length})</h2>
<ul>
${fileConnections
.map(({ linker }) => `<li><a href="${linker}.html">${linker}</a></li>`)
.join("\n")}
</ul>
</section>`
: "");
const outputPath = join(
config.buildPath,
basename(sourceFileName, ".md") + ".html"
);
await writeFile(outputPath, html);
}
async function compileExecutable(config, sourceFileName) {
const name = basename(sourceFileName, ".gen");
const { stdout, stderr } = await execFile(
"./" + join(config.sourcePath, sourceFileName)
);
if (stderr.length > 0) console.error(`${sourceFileName} stderr: ${stderr}`);
const html = head(name, name) + header(name, sourceFileName) + stdout;
const outputPath = join(
config.buildPath,
basename(sourceFileName, ".gen") + ".html"
);
await writeFile(outputPath, html);
}
// ==============================================
// Markdown utils
// ==============================================
function renderMarkdown(markdown) {
const parsed = reader.parse(markdown);
return writer.render(parsed);
}

20
package.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "sitio",
"type": "module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/commonmark": "^0.27.5",
"@types/node": "^18.11.17"
},
"dependencies": {
"commonmark": "^0.30.0"
}
}

49
pnpm-lock.yaml Normal file
View file

@ -0,0 +1,49 @@
lockfileVersion: 5.4
specifiers:
'@types/commonmark': ^0.27.5
'@types/node': ^18.11.17
commonmark: ^0.30.0
dependencies:
commonmark: 0.30.0
devDependencies:
'@types/commonmark': 0.27.5
'@types/node': 18.11.17
packages:
/@types/commonmark/0.27.5:
resolution: {integrity: sha512-vIqgmHyLsc8Or3EWLz6QkhI8/v61FNeH0yxRupA7VqSbA2eFMoHHJAhZSHudplAV89wqg1CKSmShE016ziRXuw==}
dev: true
/@types/node/18.11.17:
resolution: {integrity: sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==}
dev: true
/commonmark/0.30.0:
resolution: {integrity: sha512-j1yoUo4gxPND1JWV9xj5ELih0yMv1iCWDG6eEQIPLSWLxzCXiFoyS7kvB+WwU+tZMf4snwJMMtaubV0laFpiBA==}
hasBin: true
dependencies:
entities: 2.0.3
mdurl: 1.0.1
minimist: 1.2.7
string.prototype.repeat: 0.2.0
dev: false
/entities/2.0.3:
resolution: {integrity: sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==}
dev: false
/mdurl/1.0.1:
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
dev: false
/minimist/1.2.7:
resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
dev: false
/string.prototype.repeat/0.2.0:
resolution: {integrity: sha512-1BH+X+1hSthZFW+X+JaUkjkkUPwIlLEMJBLANN3hOob3RhEk5snLWNECDnYbgn/m5c5JV7Ersu1Yubaf+05cIA==}
dev: false