import { resolve, basename, relative } from "path"; import * as fs from "fs"; import * as util from "util"; import { execSync } from "child_process"; const exec = util.promisify(execSync); const root = resolve(import.meta.dirname, ".."); const dry = process.argv.includes("--dry"); const yarn = (cmd, stdio = "inherit") => { const script = `yarn run ${cmd}`; if (dry) { return script; } return execSync(script, { stdio }); }; const typlite = (input, output) => { const assets_flag = dry ? "" : `--assets-path ${relative(root, resolve(output, "../assets/images/", basename(input.slice(0, -4))))}`; // return stdout const res = yarn(`--silent typlite ${assets_flag} --root ${root} ${input} -`, "pipe"); return res.toString(); }; const isCheck = process.argv.includes("--check"); const convert = async ({ input: inp, output: out }) => { const input = resolve(root, inp); const output = resolve(root, out); const outputContent = typlite(input, output).trim(); if (dry) { console.log(outputContent); return; } await fs.promises.writeFile(output, outputContent.trim() + "\n"); if (isCheck) { const gitStatus = ( await exec(`git status --porcelain ${output}`, { encoding: "utf-8", }) ).trim(); if (gitStatus) { throw new Error( `The file ${out} is not up to date. Please run \`node scripts/link-docs.mjs\` locally to update it.`, ); } } }; // 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 tasks = [ { input: "docs/tinymist/introduction.typ", output: "README.md", }, { input: "docs/tinymist/release-instruction.typ", output: "docs/release-instruction.md", }, { input: "docs/tinymist/crates/typlite.typ", output: "crates/typlite/README.md", }, { input: "docs/tinymist/frontend/emacs.typ", output: "editors/emacs/README.md", }, { input: "docs/tinymist/frontend/helix.typ", output: "editors/helix/README.md", }, { input: "docs/tinymist/frontend/neovim.typ", output: "editors/neovim/README.md", }, { input: "docs/tinymist/frontend/sublime-text.typ", output: "editors/sublime-text/README.md", }, { input: "docs/tinymist/frontend/vscode.typ", output: "editors/vscode/README.md", }, { input: "docs/tinymist/frontend/zed.typ", output: "editors/zed/README.md", }, ]; const main = async () => await Promise.all([...tasks.map(convert), maintainerMd()]); main();