-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathbuild.ts
More file actions
executable file
·116 lines (93 loc) · 3.48 KB
/
build.ts
File metadata and controls
executable file
·116 lines (93 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/env bun
import { SHA1, type BunFile } from "bun";
import { zipSync, type Zippable } from "fflate";
import { mkdir, readdir } from "node:fs/promises";
import { basename, dirname, join } from "node:path";
const rootDir = dirname(import.meta.dir); // the `scripts` dir's parent dir path - which is the project root
const outDir = join(rootDir, "out");
const licenseFile = Bun.file(join(rootDir, "LICENSE"));
const creditsFile = Bun.file(join(rootDir, "CREDITS.txt"));
if (!(await licenseFile.exists()))
throw new Error("`LICENSE` file does not exist in the working directory!");
if (!(await creditsFile.exists()))
throw new Error("`CREDITS.txt` file does not exist in the working directory!");
const JAVA_DIR = "java";
const BEDROCK_DIR = "bedrock";
// A pack folder is any directory that contains a "java" or "bedrock" subfolder
const allEntries = await readdir(rootDir, { withFileTypes: true });
const packDirs = (
await Promise.all(
allEntries
.filter((e) => e.isDirectory())
.map(async (e) => {
const sub = await readdir(join(rootDir, e.name), {
recursive: false,
});
const hasJava = sub.includes(JAVA_DIR);
const hasBedrock = sub.includes(BEDROCK_DIR);
return hasJava || hasBedrock ? { name: e.name, hasJava, hasBedrock } : null;
}),
)
).filter((x) => x !== null);
if (packDirs.length < 1)
throw new Error(
"Working directory contains no pack folders. Each pack folder must have a `java` and/or `bedrock` subfolder.",
);
makeOutputDir: {
try {
await mkdir(outDir);
} catch (err) {
if (Error.isError(err) && "code" in err && err.code === "EEXIST") break makeOutputDir;
else throw err;
}
}
const CompressionLevel = {
NONE: 0,
LESS: 1,
DEFAULT: 6,
HIGHEST: 9,
} as const;
type CompressionLevel = 0 | 1 | 6 | 9;
async function addFile(zip: Zippable, path: string, file: BunFile) {
zip[path.replaceAll("\\", "/")] = await file.bytes();
}
async function buildZip(sourceDir: string): Promise<Zippable> {
const contents: Zippable = {};
await addFile(contents, basename(licenseFile.name!), licenseFile);
await addFile(contents, basename(creditsFile.name!), creditsFile);
for (const filePath of await readdir(sourceDir, { recursive: true })) {
try {
await addFile(contents, filePath, Bun.file(join(sourceDir, filePath)));
} catch (err) {
if (Error.isError(err) && "code" in err && err.code === "EISDIR") continue;
else throw err;
}
}
return contents;
}
const hashes: Record<string, string> = {};
for (const { name: packName, hasJava, hasBedrock } of packDirs) {
const packRoot = join(rootDir, packName);
const variants: { subDir: string; ext: string }[] = [];
if (hasJava) variants.push({ subDir: JAVA_DIR, ext: "zip" });
if (hasBedrock) variants.push({ subDir: BEDROCK_DIR, ext: "mcpack" });
for (const { subDir, ext } of variants) {
const sourceDir = join(packRoot, subDir);
const zipPath = join(outDir, `${packName}.${ext}`);
const contents = await buildZip(sourceDir);
const zip = zipSync(contents, { level: CompressionLevel.DEFAULT });
await Bun.write(Bun.file(zipPath), zip);
const hash = SHA1.hash(await Bun.file(zipPath).arrayBuffer(), "hex");
hashes[basename(zipPath)] = hash;
console.info(`Saved ${packName}.${ext} (${hash})`);
}
}
const longestFilenameLength = Object.keys(hashes)
.map((filename) => filename.length)
.toSorted((a, b) => b - a)[0]!;
await Bun.write(
Bun.file(join(outDir, "checksums.txt")),
Object.entries(hashes).map(
([filename, hash]) => `${filename.padEnd(longestFilenameLength, " ")} ${hash}\n`,
),
);