diff --git a/.editorconfig b/.editorconfig
index 9f66fe5..1ee12df 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -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
diff --git a/.gitignore b/.gitignore
index 3714436..3e2e84b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
build/
-zig-cache/
+node_modules/
diff --git a/.woodpecker.yml b/.woodpecker.yml
index f9be5cc..41801de 100644
--- a/.woodpecker.yml
+++ b/.woodpecker.yml
@@ -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)
diff --git a/compilar.js b/compilar.js
new file mode 100644
index 0000000..63027d1
--- /dev/null
+++ b/compilar.js
@@ -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 `
+
+
+
+
+
+
+
+
+
+
${title}
+`;
+}
+
+function header(title, sourceCodePath, linkConexiones = false) {
+ return (
+ `☚ Volver al inicio` +
+ ``
+ );
+}
+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("$1`);
+}
+
+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") +
+ `
+ ${pageList
+ .map((name) => `- ${name}
`)
+ .join("\n")}
+
+`;
+ 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
+ ? `
+
+ ⥆ Conexiones (${fileConnections.length})
+
+ ${fileConnections
+ .map(({ linker }) => `- ${linker}
`)
+ .join("\n")}
+
+`
+ : "");
+
+ 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);
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..04d0af2
--- /dev/null
+++ b/package.json
@@ -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"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..a4b06ab
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -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