pkg.ts

1.8 kB · typescript · 54 lines

1import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";2import { dirname, join, resolve } from "node:path";3import { client, list, PROD_BUCKET } from "../../aws/s3.ts";45/* HASH */67function crawl(dir: string, at: string, out: string[]) {8  for (const entry of readdirSync(join(dir, at), { withFileTypes: true })) {9    const rel = at ? `${at}/${entry.name}` : entry.name;10    if (entry.isDirectory()) crawl(dir, rel, out);11    else out.push(rel);12  }13}1415export function pkgFiles(dir: string): string[] {16  const out: string[] = [];17  crawl(dir, "", out);18  return out.sort((a, b) => Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8")));19}2021export function pkgHash(dir: string): string {22  const manifest = new Bun.CryptoHasher("sha256");23  for (const rel of pkgFiles(dir)) {24    const one = new Bun.CryptoHasher("sha256");25    one.update(readFileSync(join(dir, rel)));26    manifest.update(`${one.digest("hex")}  ${rel}\n`);27  }28  return manifest.digest("hex");29}3031/* FETCH */3233export async function ensurePkg(root = resolve(import.meta.dir, "..")): Promise<string> {34  const dir = join(root, "pkg");35  if (existsSync(dir) && pkgFiles(dir).length > 0) return dir;36  const hash = readFileSync(join(root, "pkg.lock"), "utf8").trim();37  const prefix = `pkg/${hash}/`;38  const s3 = client(PROD_BUCKET);39  const keys = (await list(s3, prefix)).filter((key) => key.length > prefix.length);40  for (const key of keys) {41    const to = join(dir, key.slice(prefix.length));42    mkdirSync(dirname(to), { recursive: true });43    writeFileSync(to, Buffer.from(await s3.file(key).arrayBuffer()));44  }45  if (keys.length === 0) throw new Error(`no objects at s3://${PROD_BUCKET}/${prefix}`);46  return dir;47}4849/* MAIN */5051if (import.meta.main) {52  const dir = await ensurePkg();53  console.log(`${pkgHash(dir)} ${pkgFiles(dir).length} ${dir}`);54}