tinymist/scripts/link-docs.mjs
Myriad-Dreamin 1478280a07
Some checks failed
tinymist::ci / Duplicate Actions Detection (push) Has been cancelled
tinymist::ci / Check Clippy, Formatting, Completion, Documentation, and Tests (Linux) (push) Has been cancelled
tinymist::ci / Check Minimum Rust version and Tests (Windows) (push) Has been cancelled
tinymist::ci / prepare-build (push) Has been cancelled
tinymist::gh_pages / build-gh-pages (push) Has been cancelled
tinymist::ci / E2E Tests (darwin-arm64 on macos-latest) (push) Has been cancelled
tinymist::ci / E2E Tests (linux-x64 on ubuntu-22.04) (push) Has been cancelled
tinymist::ci / E2E Tests (linux-x64 on ubuntu-latest) (push) Has been cancelled
tinymist::ci / E2E Tests (win32-x64 on windows-2022) (push) Has been cancelled
tinymist::ci / E2E Tests (win32-x64 on windows-latest) (push) Has been cancelled
tinymist::ci / build-binary (push) Has been cancelled
tinymist::ci / build-vsc-assets (push) Has been cancelled
tinymist::ci / build-vscode (push) Has been cancelled
tinymist::ci / build-vscode-others (push) Has been cancelled
tinymist::ci / publish-vscode (push) Has been cancelled
docs: generate typlite readme (#1868)
* docs: generate typltie readme

* dev(ci): add docs check

* fix: don't cache error

* dev(ci): install typst

* docs: update docs

* feat: mark shell scripts
2025-07-03 01:41:19 +08:00

189 lines
5.4 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, relative } 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) => {
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 convert = async (inp, out, opts) => {
const input = resolve(root, inp);
const output = resolve(root, out);
const { before } = opts || {};
const res = typlite(input, output).trim();
if (dry) {
console.log(res);
return;
}
const outputContent = `<!-- This file is generated by scripts/link-docs.mjs from ${inp}. Do not edit manually. -->\n${before || ""}${res}\n`;
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 isCheck = process.argv.includes("--check");
const tasks = [
{
input: "docs/tinymist/introduction.typ",
output: "README.md",
title: "Tinymist",
},
{
input: "docs/tinymist/release-instruction.typ",
output: "docs/release-instruction.md",
title: "Release Instructions",
},
{
input: "docs/tinymist/crates/typlite.typ",
output: "crates/typlite/README.md",
title: "Typlite",
},
{
input: "docs/tinymist/frontend/emacs.typ",
output: "editors/emacs/README.md",
title: "Tinymist Emacs Support for Typst",
},
{
input: "docs/tinymist/frontend/helix.typ",
output: "editors/helix/README.md",
title: "Tinymist Helix Support for Typst",
},
{
input: "docs/tinymist/frontend/neovim.typ",
output: "editors/neovim/README.md",
title: "Tinymist Neovim Support for Typst",
},
{
input: "docs/tinymist/frontend/sublime-text.typ",
output: "editors/sublime-text/README.md",
title: "Tinymist Sublime Support for Typst",
},
{
input: "docs/tinymist/frontend/vscode.typ",
output: "editors/vscode/README.md",
title: "Tinymist Typst VS Code Extension",
},
{
input: "docs/tinymist/frontend/zed.typ",
output: "editors/zed/README.md",
title: "Tinymist Zed Support for Typst",
},
];
const main = async () => {
await Promise.all([
...tasks.map((task) => convert(task.input, task.output, { before: `# ${task.title}\n\n` })),
maintainerMd(),
]);
if (isCheck) {
// any dirty git files?
await Promise.all(
tasks.map(async (task) => {
const gitStatus = execSync(`git status --porcelain ${task.output}`, {
encoding: "utf-8",
}).trim();
if (gitStatus) {
throw new Error(
`The file ${task.output} is not up to date. Please run \`node scripts/link-docs.mjs\` to update it.`,
);
}
}),
);
}
};
main();