Files

80 lines
2.9 KiB
TypeScript

// Packages built extensions for store upload (run a build first -- the npm zip scripts do):
//
// tsx scripts/zip.ts chrome firefox edge sources
//
// -> dist/zips/thingport-grab-<target>.zip with manifest.json at the root, and
// thingport-grab-sources.zip, the source AMO asks for to reproduce the build.
import { existsSync } from "node:fs";
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { zipSync, type Zippable } from "fflate";
import { TARGETS, isTarget } from "./manifest";
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const OUT = path.join(ROOT, "dist", "zips");
// Fixed timestamp for byte-identical zips.
const MTIME = new Date("2020-01-01T00:00:00Z");
const SOURCE_ENTRIES = [
"package.json",
"package-lock.json",
"tsconfig.json",
".oxlintrc.json",
"README.md",
"scripts",
"src",
"public",
];
const IGNORED = new Set([".DS_Store", "Thumbs.db"]);
async function collect(dir: string, base: string, files: Zippable): Promise<void> {
for (const entry of (await readdir(dir, { withFileTypes: true })).toSorted((a, b) => a.name.localeCompare(b.name))) {
if (IGNORED.has(entry.name)) continue;
const full = path.join(dir, entry.name);
const rel = path.posix.join(base, entry.name);
if (entry.isDirectory()) await collect(full, rel, files);
else files[rel] = [await readFile(full), { mtime: MTIME }];
}
}
async function writeZip(name: string, files: Zippable): Promise<void> {
await mkdir(OUT, { recursive: true });
const out = path.join(OUT, name);
await writeFile(out, zipSync(files, { level: 9 }));
console.log(
`${path.relative(ROOT, out)} ${((await stat(out)).size / 1024).toFixed(1)} KB, ${Object.keys(files).length} files`,
);
}
async function zipTarget(target: string): Promise<void> {
const dir = path.join(ROOT, "dist", target);
if (!existsSync(path.join(dir, "manifest.json")))
throw new Error(`dist/${target} isn't built -- run \`npm run build:${target}\` first`);
const files: Zippable = {};
await collect(dir, "", files);
await writeZip(`thingport-grab-${target}.zip`, files);
}
async function zipSources(): Promise<void> {
const files: Zippable = {};
for (const entry of SOURCE_ENTRIES) {
const full = path.join(ROOT, entry);
if ((await stat(full)).isDirectory()) await collect(full, entry, files);
else files[entry] = [await readFile(full), { mtime: MTIME }];
}
await writeZip("thingport-grab-sources.zip", files);
}
const requested = process.argv.slice(2);
const valid = [...TARGETS, "sources"];
const unknown = requested.filter((arg) => !valid.includes(arg));
if (!requested.length || unknown.length) {
console.error(`Usage: tsx scripts/zip.ts <${valid.join("|")}>...`);
process.exit(1);
}
for (const target of requested) {
if (target === "sources") await zipSources();
else if (isTarget(target)) await zipTarget(target);
}