tinymist/scripts/link-docs.mjs
Myriad-Dreamin d7424abed8
docs: add sponsoring section to readme (#1620)
* docs: add sponsoring section to readme

* build: generate readme

* docs: wording

* docs: wording also mention contributors

* docs: wording also mention contributors
2025-04-02 15:20:53 +08:00

162 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 =
`<!-- This file is generated by scripts/link-docs.mjs from ${inp}. Do not edit manually. -->\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("<!-- This file is generated by scripts/link-docs.mjs. Do not edit manually. -->\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);