49 lines
2.7 KiB
JavaScript
49 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
|
// Generate a reproducible third-party inventory from the committed npm lockfile.
|
|
// This is an inventory, not legal advice; every release must regenerate and review it.
|
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
import { dirname, resolve } from 'node:path';
|
|
|
|
const [output = 'docs/commercial/THIRD_PARTY_SOFTWARE.json'] = process.argv.slice(2);
|
|
const lock = JSON.parse(readFileSync('package-lock.json', 'utf8'));
|
|
const exceptionsPath = 'docs/commercial/LICENSE_EXCEPTIONS.json';
|
|
const exceptions = existsSync(exceptionsPath) ? JSON.parse(readFileSync(exceptionsPath, 'utf8')).exceptions ?? {} : {};
|
|
const workspaceLicenses = new Map(Object.entries(lock.packages ?? {})
|
|
.filter(([path, value]) => path && !path.startsWith('node_modules/') && value && typeof value === 'object' && typeof value.name === 'string')
|
|
.map(([, value]) => [value.name, typeof value.license === 'string' ? value.license : 'UNKNOWN']));
|
|
const rows = Object.entries(lock.packages ?? {})
|
|
.filter(([path, value]) => path.startsWith('node_modules/') && value && typeof value === 'object')
|
|
.map(([path, value]) => {
|
|
const name = path.slice('node_modules/'.length);
|
|
const key = `${name}@${String(value.version ?? 'unknown')}`;
|
|
const declared = typeof value.license === 'string' ? value.license : (workspaceLicenses.get(name) ?? 'UNKNOWN');
|
|
const exception = exceptions[key];
|
|
return {
|
|
name,
|
|
version: String(value.version ?? 'unknown'),
|
|
declared_license: declared,
|
|
license: typeof exception?.reviewed_license === 'string' ? exception.reviewed_license : declared,
|
|
review_evidence: typeof exception?.evidence === 'string' ? exception.evidence : null,
|
|
resolved: typeof value.resolved === 'string' ? value.resolved : null,
|
|
integrity: typeof value.integrity === 'string' ? value.integrity : null,
|
|
};
|
|
})
|
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
const licenses = {};
|
|
for (const row of rows) licenses[row.license] = (licenses[row.license] ?? 0) + 1;
|
|
const inventory = {
|
|
schema_version: '1.0',
|
|
generated_from: 'package-lock.json',
|
|
generated_at: new Date().toISOString(),
|
|
scope: 'npm dependencies recorded in the root lockfile; Python, container base images, model and dataset licenses require separate release review.',
|
|
exceptions_source: existsSync(exceptionsPath) ? exceptionsPath : null,
|
|
package_count: rows.length,
|
|
licenses,
|
|
packages: rows,
|
|
};
|
|
const destination = resolve(output);
|
|
mkdirSync(dirname(destination), { recursive: true });
|
|
writeFileSync(destination, `${JSON.stringify(inventory, null, 2)}\n`);
|
|
console.log(`LICENSE_INVENTORY_CREATED path=${output} packages=${rows.length} licenses=${Object.keys(licenses).length}`);
|