// Build an immutable runtime from committed files, never the dirty checkout. import { execFileSync } from 'node:child_process'; import { mkdirSync, writeFileSync, readFileSync, cpSync, readdirSync } from 'node:fs'; import { resolve, dirname, posix, join } from 'node:path'; import { createHash } from 'node:crypto'; const [ref, destination, dependencies, nativeBinary] = process.argv.slice(2); if (!ref || !destination || !dependencies || !nativeBinary) throw new Error('Usage: pack.mjs REV NEW_DIRECTORY NODE_MODULES TESTED_NATIVE_BINARY'); const repo = resolve(import.meta.dirname, '../..'); const git = (...args) => execFileSync('git', ['-C', repo, ...args], { maxBuffer: 32 * 1024 * 1024 }); const revision = git('rev-parse', '--verify', ref + '^{commit}').toString().trim(); const root = resolve(destination); mkdirSync(root); // Refuse overwriting an existing immutable release. const hash = bytes => createHash('sha256').update(bytes).digest('hex'); const sha256 = {}, sources = new Set(); function put(path, bytes) { if (path.startsWith('../') || path.startsWith('/')) throw new Error('Invalid release path'); mkdirSync(dirname(join(root, path)), { recursive: true }); writeFileSync(join(root, path), bytes); sha256[path] = hash(bytes); } function source(path, imports = true) { if (sources.has(path)) return; sources.add(path); const bytes = git('show', revision + ':' + path); put(path, bytes); if (!imports || !path.endsWith('.mjs')) return; for (const [, spec] of bytes.toString().matchAll(/(?