import { resolve, basename } from "path"; import * as fs from "fs"; import { execSync } from "child_process"; const root = resolve(import.meta.dirname, ".."); const dry = process.argv.includes("--dry"); const bytes2utf8 = new TextDecoder(); /** * Base64 to UTF-8 * @param encoded Base64 encoded string * @returns UTF-8 string */ export const base64Decode = (encoded) => bytes2utf8.decode(Uint8Array.from(atob(encoded), (m) => m.charCodeAt(0))); const yarn = (cmd, stdio = "inherit") => { const script = `yarn run ${cmd}`; if (dry) { return script; } return execSync(script, { stdio }); }; const typlite = (input, output = "-") => { if (output === "-") { // return stdout const res = yarn(`--silent typlite ${input} -`, "pipe"); return res.toString(); } return yarn(`typlite ${input} ${output}`); }; const convert = async (inp, out, opts) => { const input = resolve(root, inp); const output = resolve(root, out); const { before } = opts || {}; const res = typlite(input).trim(); if (dry) { console.log(res); return; } const assetsDir = resolve(output, "../assets/images"); fs.mkdirSync(assetsDir, { recursive: true }); // outline all data "data:image/svg+xml;base64," to render on GitHub correctly const inputName = basename(input); let imageCnt = 0; const outputContent = `\n${before || ""}${res}\n`.replace( /\"data\:image\/svg\+xml\;base64,([^\"]*)\"/g, (_, content) => { const fileName = `${inputName}-inlined${imageCnt}.svg`; imageCnt += 1; fs.writeFileSync(resolve(assetsDir, fileName), base64Decode(content)); return `"./assets/images/${fileName}"`; }, ); await fs.promises.writeFile(output, outputContent); }; // todo: generate me using typlite. const maintainerMd = async () => { const maintainers = JSON.parse(yarn(`--silent maintainers --input=action=maintainers`, "pipe")); const features = JSON.parse(yarn(`--silent maintainers --input=action=features`, "pipe")); const output = []; output.push("\n"); output.push("# Tinymist Maintainers\n\n"); output.push( "Tinymist [ˈtaɪni mɪst] is an integrated language service for [Typst](https://typst.app/) [taɪpst].", ); output.push( "\nThis page is generated from [./MAINTAINERS.typ](./MAINTAINERS.typ) and renders information of [maintainers](#maintainers) and [features.](#features)\n", ); output.push("## Maintainers\n"); const italicIt = (it) => `*${it}*`; const featureLink = (it) => { const feature = features.find((f) => f.name === it); if (feature) { return `[${it}](#${it.replace(/\s+/g, "-").toLowerCase()})`; } return it; }; const fsPath = (it) => { if (!fs.existsSync(it)) { throw new Error(`Path ${it} does not exist!`); } return `[\`${it}\`](./${it})`; }; for (const maintainer of maintainers) { output.push(`- [**${maintainer["name"]}**](https://github.com/${maintainer["github-name"]})`); output.push(` - Email: ${maintainer.email}`); if (maintainer.maintains.length > 0) { const rendered = maintainer.maintains.map(featureLink).map(italicIt); if (rendered.length > 1) { const last = rendered.pop(); output.push(` - Maintains: ${rendered.join(", ")}, and ${last}`); } else { output.push(` - Maintains: ${rendered.join(", ")}`); } } output.push(""); } output.push("## Features\n"); for (const feature of features) { output.push(`### ${feature.name}`); output.push(`${feature.description}`); output.push(`- Scope: ${feature.scope.map(fsPath).join(", ")}`); } const outPath = resolve(root, "MAINTAINERS.md"); const outputContent = output.join("\n"); if (dry) { console.log(content); return; } await fs.promises.writeFile(outPath, outputContent); }; const main = async () => { await Promise.all([ convert("docs/tinymist/introduction.typ", "README.md", { before: "# Tinymist\n\n", }), convert("docs/tinymist/release-instruction.typ", "docs/release-instruction.md", { before: "# Release Instructions\n\n", }), convert("docs/tinymist/frontend/emacs.typ", "editors/emacs/README.md", { before: "# Tinymist Emacs Support for Typst\n\n", }), convert("docs/tinymist/frontend/helix.typ", "editors/helix/README.md", { before: "# Tinymist Helix Support for Typst\n\n", }), convert("docs/tinymist/frontend/neovim.typ", "editors/neovim/README.md", { before: "# Tinymist Neovim Support for Typst\n\n", }), convert("docs/tinymist/frontend/sublime-text.typ", "editors/sublime-text/README.md", { before: "# Tinymist Sublime Support for Typst\n\n", }), convert("docs/tinymist/frontend/vscode.typ", "editors/vscode/README.md", { before: "# Tinymist Typst VS Code Extension\n\n", }), convert("docs/tinymist/frontend/zed.typ", "editors/zed/README.md", { before: "# Tinymist Zed Support for Typst\n\n", }), maintainerMd(), ]); }; main().catch(console.error);