feat: add local OneNote and ONEPKG reader
This commit is contained in:
118
scripts/release-archive.mjs
Normal file
118
scripts/release-archive.mjs
Normal file
@@ -0,0 +1,118 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user