PackFormatter/index.ts

165 lines
5.1 KiB
TypeScript

import AdmZip from "adm-zip";
import prettyBytes from "pretty-bytes";
import { parseArgs } from "node:util";
import {
access,
copyFile,
cp,
lstat,
mkdir,
mkdtemp,
rm,
stat,
writeFile
} from "node:fs/promises";
import type { PathLike } from "node:fs";
import { tmpdir } from "node:os";
import { cwd } from "node:process";
import { resolve } from "node:path";
interface Override {
common?: string;
}
interface Info {
description: string;
exports: Array<["all"] | Array<string>>;
name: string;
url: string;
versions: Array<[mcVersion: string, packFormat: number]>;
overrides?: Record<string, Override>;
}
const { values: { indir: inDir, common: commonDir, revision: rev, outdir: outDir, info: infoPath } } = parseArgs({
args: Bun.argv,
options: {
indir: {
type: "string",
short: "i",
default: cwd()
},
common: {
type: "string",
short: "c",
default: "common"
},
revision: {
type: "string",
short: "r"
},
outdir: {
type: "string",
short: "o",
default: "dist"
},
info: {
type: "string",
default: "info.json"
},
},
strict: true,
allowPositionals: true
});
const directoryExists = (path: PathLike) => access(path).then(() => lstat(path).then(d => d.isDirectory())).catch(() => false);
const dir = resolve(inDir!);
console.log("Building packs for:", dir);
if (!dir) {
console.log("No directory specified");
process.exit(1);
}
if (!await directoryExists(dir)) {
console.log(`Specified input directory "${inDir}" (${dir}) does not exist.`);
process.exit(1);
}
const info = await Bun.file(infoPath ?? "info.json").json() as Info;
const common = resolve(dir, commonDir!);
const dist = resolve(dir, outDir!);
if (!await directoryExists(common)) {
console.log(`Specified common directory "${commonDir}" (${common}) does not exist.`);
process.exit(1);
}
let revision = rev;
if (!revision) {
if (await directoryExists(`${dir}/.git`)) {
revision = (await Bun.$`git rev-parse --short HEAD`.cwd(dir).text().catch(() => "")).slice(0, -1);
if (!revision) {
console.log("Failed to get git revision.");
process.exit(1);
}
} else {
console.log("No revision specified and no git directory found.");
process.exit(1);
}
}
const tempDir = await mkdtemp(`${tmpdir()}/${info.name.toLowerCase().replaceAll(" ", "-")}`);
console.log("Temporary Directory:", tempDir);
let totalSize = 0;
async function createVersion(mcVersion: string, packFormat: number) {
let commonDir = common;
if (info.overrides) {
const override = info.overrides[mcVersion];
if (override?.common) {
commonDir = resolve(dir, override.common);
if (!await directoryExists(commonDir)) {
console.log(`[Override:${mcVersion}] Specified common directory "${override.common}" (${commonDir}) does not exist.`);
process.exit(1);
}
}
}
console.log("Building %s (pf: %d)", mcVersion, packFormat);
const mcmeta = {
pack: {
pack_format: packFormat,
description: info.description,
version: {
git: revision,
mc: mcVersion,
url: info.url
}
}
};
await mkdir(`${tempDir}/${mcVersion}`);
await writeFile(`${tempDir}/${mcVersion}/pack.mcmeta`, JSON.stringify(mcmeta, null, 4));
await copyFile(`${commonDir}/pack.png`, `${tempDir}/${mcVersion}/pack.png`);
await cp(`${commonDir}/assets`, `${tempDir}/${mcVersion}/assets`, { recursive: true });
const zip = new AdmZip();
await zip.addLocalFolderPromise(`${tempDir}/${mcVersion}`, {});
await zip.writeZipPromise(`${dist}/${info.name.replaceAll(" ", "")}-${mcVersion}.zip`);
const { size } = await stat(`${dist}/${info.name.replaceAll(" ", "")}-${mcVersion}.zip`);
totalSize += size;
console.log("%s built, size: %s", mcVersion, prettyBytes(size));
}
if (await directoryExists(dist)) {
await rm(dist, { recursive: true, force: true });
}
await mkdir(dist);
for (const [mcVersion, packFormat] of info.versions) {
await createVersion(mcVersion, packFormat);
}
await rm(tempDir, { recursive: true, force: true });
console.log("Total size: %s", prettyBytes(totalSize));
for (const ex of info.exports) {
const name = (ex[0] === "all" ? info.name : `${info.name}-${ex.join("-")}`).replaceAll(" ", "");
console.log("Exporting %s", name);
const zip = new AdmZip();
const versions = ex[0] === "all" ? info.versions.map(([mcVersion]) => mcVersion) : ex;
for (const version of versions) {
zip.addLocalFile(`${dist}/${info.name.replaceAll(" ", "")}-${version}.zip`);
}
await zip.writeZipPromise(`${dist}/${name}.zip`);
const { size } = await stat(`${dist}/${name}.zip`);
console.log("%s exported, size: %s", name, prettyBytes(size));
}
console.log("Done");