From c14875b10b6c0a63d5b18f1e2635ea1b477a83dc Mon Sep 17 00:00:00 2001 From: Nulo Date: Mon, 19 Dec 2022 20:18:14 -0300 Subject: [PATCH] compilar: Portear a Node.js --- .editorconfig | 6 +- .gitignore | 2 +- .woodpecker.yml | 21 +++--- compilar.js | 176 ++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 20 ++++++ pnpm-lock.yaml | 49 ++++++++++++++ 6 files changed, 259 insertions(+), 15 deletions(-) create mode 100644 compilar.js create mode 100644 package.json create mode 100644 pnpm-lock.yaml 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` + + `
+

${title}

+ Historial${ + linkConexiones + ? ` / +Conexiones` + : "" + } +
` + ); +} +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") + + ` +`; + 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})

+ +
` + : ""); + + 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