import { readFile, readdir } from 'node:fs/promises'; import path from 'node:path'; import { zipSync } from 'fflate'; const LICENSE_FILE = /^(?:licen[cs]e|copying|notice)(?:[._-].*)?$/iu; export function compareCodePoints(left, right) { return left < right ? -1 : left > right ? 1 : 0; } export function createDeterministicZip(files) { const orderedFiles = Object.fromEntries( Object.entries(files).sort(([left], [right]) => compareCodePoints(left, right) ) ); return zipSync(orderedFiles, { level: 9, mtime: new Date(1980, 0, 1, 0, 0, 0), }); } export async function collectDirectoryFiles(directory, prefix = '') { const result = {}; const entries = await readdir(directory, { withFileTypes: true }); entries.sort((left, right) => compareCodePoints(left.name, right.name)); for (const entry of entries) { const absolutePath = path.join(directory, entry.name); const relativePath = path.posix.join(prefix, entry.name); if (entry.isDirectory()) { Object.assign( result, await collectDirectoryFiles(absolutePath, relativePath) ); } else if (entry.isFile()) { result[relativePath] = await readFile(absolutePath); } else { throw new Error( `Unsupported filesystem entry in release input: ${relativePath}` ); } } return result; } export async function createThirdPartyLicenseBundle(projectRoot, packageNames) { const sections = []; for (const packageName of [...new Set(packageNames)].sort( compareCodePoints )) { const packageDirectory = path.join( projectRoot, 'node_modules', ...packageName.split('/') ); const packageJson = JSON.parse( await readFile(path.join(packageDirectory, 'package.json'), 'utf8') ); const licenseFiles = await collectLicenseFiles(packageDirectory); if (licenseFiles.length === 0) { throw new Error(`No license text found for ${packageName}.`); } const contents = []; for (const licenseFile of licenseFiles) { const text = await readFile( path.join(packageDirectory, ...licenseFile.split('/')), 'utf8' ); contents.push( `--- ${licenseFile} ---\n${text.replaceAll('\r\n', '\n').trim()}\n` ); } sections.push( [ '================================================================================', `${packageJson.name ?? packageName}@${packageJson.version ?? 'unknown'}`, `Declared license: ${String(packageJson.license ?? 'unspecified')}`, '================================================================================', ...contents, ].join('\n') ); } return Buffer.from( [ 'Runtime third-party software license texts bundled with OneNote Tools', 'Generated deterministically from installed production packages.', '', ...sections, '', ].join('\n'), 'utf8' ); } async function collectLicenseFiles(directory, prefix = '') { const matches = []; const entries = await readdir(directory, { withFileTypes: true }); entries.sort((left, right) => compareCodePoints(left.name, right.name)); for (const entry of entries) { if (entry.name === 'node_modules') continue; const relativePath = path.posix.join(prefix, entry.name); const absolutePath = path.join(directory, entry.name); if (entry.isDirectory()) { matches.push(...(await collectLicenseFiles(absolutePath, relativePath))); } else if (entry.isFile() && LICENSE_FILE.test(entry.name)) { matches.push(relativePath); } else if (!entry.isFile()) { throw new Error( `Unsupported filesystem entry in license input: ${relativePath}` ); } } return matches; }