// 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-.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 { 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 { 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 { 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 { 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); }