mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-07-08 13:35:02 +00:00
189 lines
5.4 KiB
JavaScript
189 lines
5.4 KiB
JavaScript
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();
|