import { cp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const destination = path.join(root, "public"); const required = [ "LICENSE", "README.md", "CHANGELOG.md", "SOURCE.md", "SECURITY.md", "THIRD_PARTY_NOTICES.md", ]; await mkdir(destination, { recursive: true }); for (const name of required) { await readFile(path.join(root, name)); await cp(path.join(root, name), path.join(destination, name)); } const publicLicenses = path.join(destination, "LICENSES"); await rm(publicLicenses, { recursive: true, force: true }); await cp(path.join(root, "LICENSES"), publicLicenses, { recursive: true }); const lock = JSON.parse( await readFile(path.join(root, "package-lock.json"), "utf8"), ); const sections = []; for (const [location, locked] of Object.entries(lock.packages ?? {}).sort( ([left], [right]) => left.localeCompare(right), )) { if (!location.includes("node_modules/") || locked.dev === true) continue; const packageDirectory = path.join(root, location); const details = JSON.parse( await readFile(path.join(packageDirectory, "package.json"), "utf8"), ); const candidates = (await readdir(packageDirectory)) .filter((name) => /^(?:licen[cs]e|copying|notice)(?:\.|$)/iu.test(name)) .sort(); const texts = []; for (const candidate of candidates) { try { texts.push( `--- ${candidate} ---\n${await readFile(path.join(packageDirectory, candidate), "utf8")}`, ); } catch { /* Ignore directories and non-text aliases. */ } } sections.push( [ "=".repeat(78), `${details.name}@${details.version}`, `Declared licence: ${details.license ?? locked.license ?? "See upstream"}`, `Installed from: ${location}`, "=".repeat(78), texts.join("\n\n") || "No package-local licence file was present; see THIRD_PARTY_NOTICES.md.", ].join("\n"), ); } await writeFile( path.join(publicLicenses, "npm-runtime-licenses.txt"), `${sections.join("\n\n")}\n`, ); console.log("Prepared static release notices");