108 lines
3.5 KiB
JavaScript
108 lines
3.5 KiB
JavaScript
import { createHash } from "node:crypto";
|
|
import {
|
|
cp,
|
|
mkdtemp,
|
|
mkdir,
|
|
readFile,
|
|
readdir,
|
|
rm,
|
|
writeFile,
|
|
} from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { spawnSync } from "node:child_process";
|
|
|
|
const version = "17.0.3";
|
|
const expectedSha256 =
|
|
"a0855654b633045ae2337537e77f1bb4361162f7fcd910e613eaab1d6d9c5fca";
|
|
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
const outputRoot = join(repositoryRoot, "src/markdown/assets/twemoji");
|
|
const assetMapPath = join(repositoryRoot, "src/markdown/twemoji-assets.ts");
|
|
const temporaryRoot = await mkdtemp(join(tmpdir(), "methanium-twemoji-"));
|
|
|
|
try {
|
|
const archivePath = join(temporaryRoot, `twemoji-${version}.tar.gz`);
|
|
const response = await fetch(
|
|
`https://codeload.github.com/jdecked/twemoji/tar.gz/refs/tags/v${version}`,
|
|
);
|
|
if (!response.ok) {
|
|
throw new Error(`Could not download Twemoji: ${response.status}`);
|
|
}
|
|
|
|
const archive = Buffer.from(await response.arrayBuffer());
|
|
const actualSha256 = createHash("sha256").update(archive).digest("hex");
|
|
if (actualSha256 !== expectedSha256) {
|
|
throw new Error(
|
|
`Twemoji archive checksum mismatch: expected ${expectedSha256}, received ${actualSha256}`,
|
|
);
|
|
}
|
|
await writeFile(archivePath, archive);
|
|
|
|
const extractedRoot = join(temporaryRoot, "extracted");
|
|
await mkdir(extractedRoot);
|
|
const extraction = spawnSync(
|
|
"tar",
|
|
["-xzf", archivePath, "-C", extractedRoot],
|
|
{
|
|
stdio: "inherit",
|
|
},
|
|
);
|
|
if (extraction.status !== 0) throw new Error("Could not extract Twemoji");
|
|
|
|
const sourceRoot = join(extractedRoot, `twemoji-${version}`);
|
|
await rm(outputRoot, { force: true, recursive: true });
|
|
await mkdir(outputRoot, { recursive: true });
|
|
await cp(join(sourceRoot, "assets/svg"), join(outputRoot, "svg"), {
|
|
recursive: true,
|
|
});
|
|
await cp(join(sourceRoot, "LICENSE"), join(outputRoot, "LICENSE"));
|
|
await cp(
|
|
join(sourceRoot, "LICENSE-GRAPHICS"),
|
|
join(outputRoot, "LICENSE-GRAPHICS"),
|
|
);
|
|
for (const licenseName of ["LICENSE", "LICENSE-GRAPHICS"]) {
|
|
const licensePath = join(outputRoot, licenseName);
|
|
const license = await readFile(licensePath, "utf8");
|
|
await writeFile(licensePath, license.replace(/[\t ]+(?=\r?$)/gm, ""));
|
|
}
|
|
await writeFile(
|
|
join(outputRoot, "ATTRIBUTION.md"),
|
|
[
|
|
"# Twemoji Attribution",
|
|
"",
|
|
`Twemoji ${version} graphics are provided by the Twemoji project:`,
|
|
"https://github.com/jdecked/twemoji",
|
|
"",
|
|
"Copyright 2023 Twitter, Inc and other contributors.",
|
|
"Graphics licensed under CC-BY 4.0.",
|
|
"Code licensed under the MIT License.",
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
|
|
const files = (await readdir(join(outputRoot, "svg")))
|
|
.filter((file) => file.endsWith(".svg"))
|
|
.sort();
|
|
const entries = files.map((file) => {
|
|
const hexcode = file.slice(0, -4);
|
|
return ` ${JSON.stringify(hexcode)}: new URL(${JSON.stringify(`./assets/twemoji/svg/${file}`)}, import.meta.url).href,`;
|
|
});
|
|
await writeFile(
|
|
assetMapPath,
|
|
[
|
|
`// Generated by scripts/update-twemoji.mjs from Twemoji ${version}.`,
|
|
"export const twemojiAssetUrls: Readonly<Record<string, string>> = {",
|
|
...entries,
|
|
"};",
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
|
|
const license = await readFile(join(outputRoot, "ATTRIBUTION.md"), "utf8");
|
|
console.log(
|
|
`Vendored ${files.length} Twemoji SVGs. ${license.split("\n")[0]}`,
|
|
);
|
|
} finally {
|
|
await rm(temporaryRoot, { force: true, recursive: true });
|
|
}
|