UNPKG

license-kit

Version:

Aggregate license notes of OSS libraries used in your Node.js project, analyze & visualize OSS licenses with AI-turbocharged tooling

139 lines (138 loc) 8.24 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = analyzeCommandSetup; const node_fs_1 = __importDefault(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); const node_process_1 = __importDefault(require("node:process")); const licenses_1 = require("@callstack/licenses"); const colorette_1 = require("colorette"); const table_1 = require("table"); const scanOptionsUtils_1 = require("../scanOptionsUtils"); const commandUtils_1 = require("../utils/commandUtils"); const tableConfig = { border: (0, table_1.getBorderCharacters)('norc'), }; const categoryToEmojiMapping = { [licenses_1.LicenseCategory.PERMISSIVE]: '🔓', [licenses_1.LicenseCategory.WEAK_COPYLEFT]: '🟡', [licenses_1.LicenseCategory.STRONG_COPYLEFT]: '🔒', [licenses_1.LicenseCategory.UNKNOWN]: '❓', }; function getLicenseColor(license) { if (license === 'unknown') return (0, colorette_1.yellow)(license); if (licenses_1.WEAK_COPYLEFT_LICENSES_LOWERCASE.has(license)) return (0, colorette_1.yellow)(license); if (licenses_1.STRONG_COPYLEFT_LICENSES_LOWERCASE.has(license)) return (0, colorette_1.red)(license); return license; } function analyzeCommandSetup(program) { return (0, commandUtils_1.curryCommonScanOptions)(program .command('analyze') .description('Scan licenses & report the insights: summary, top license types, optionally unknowns & breakdown of licenses by different features.') .option('--root [path]', 'Path to the root of your project', '.') .option('--list-unknown', 'List unknown licenses', false) .option('--show-breakdown', 'Show breakdown of licenses by category and type', false)).action((options) => { const repoRootPath = node_path_1.default.resolve(node_process_1.default.cwd(), options.root); const packageJsonPath = node_path_1.default.join(repoRootPath, 'package.json'); if (!node_fs_1.default.existsSync(packageJsonPath)) { console.error(`package.json not found at ${packageJsonPath}`); node_process_1.default.exit(1); } const { name = null } = JSON.parse(node_fs_1.default.readFileSync(packageJsonPath, 'utf8')); if (name) { console.log((0, colorette_1.underline)(`💼 Project: ${(0, colorette_1.whiteBright)(name)}`)); } console.log(); const licenses = (0, licenses_1.scanDependencies)(packageJsonPath, (0, scanOptionsUtils_1.createScanOptionsFactory)(options)); const { byCategory, byLicense, categorizedLicenses, total, description, categoriesPresence } = (0, licenses_1.analyzeLicenses)(licenses); console.log(); const summaryColor = categoriesPresence.hasAllPermissive ? colorette_1.green : categoriesPresence.hasAnyUnknown ? colorette_1.yellowBright : categoriesPresence.hasAnyStrongCopyleft ? colorette_1.red : colorette_1.yellow; console.log(`📦 ${(0, colorette_1.bold)('Total packages')}: ${(0, colorette_1.whiteBright)(total)}`); console.log(`🔓 ${summaryColor('Graph state summary')}: ${description}`); const byLicenseEntries = Object.entries(byLicense); console.log(`🧮 ${(0, colorette_1.underline)('Top 5')} licenses in your project: ${byLicenseEntries .sort(([, a], [, b]) => b - a) .slice(0, 5) .map(([license, count]) => `${(0, colorette_1.whiteBright)(license)} (${count})`) .join(', ')} ${byLicenseEntries.length > 5 ? (0, colorette_1.italic)((0, colorette_1.yellow)(` + ${byLicenseEntries.length - 5} more (${options.showBreakdown ? 'see below' : 'pass --show-breakdown to view'})`)) : ''}`); console.log(); // breakdown of categories checklist const unknownCount = byCategory[licenses_1.LicenseCategory.UNKNOWN]; const hasUnknown = unknownCount > 0; const copyleftCount = byCategory[licenses_1.LicenseCategory.STRONG_COPYLEFT]; const hasCopyleft = copyleftCount > 0; const weakCopyleftCount = byCategory[licenses_1.LicenseCategory.WEAK_COPYLEFT]; const hasWeakCopyleft = weakCopyleftCount > 0; console.log(`${categoryToEmojiMapping[licenses_1.LicenseCategory.STRONG_COPYLEFT]} Copyleft licenses: ${(hasCopyleft ? colorette_1.red : colorette_1.green)(copyleftCount)} ${hasCopyleft ? '⚠️' : '✅'}`); console.log(`${categoryToEmojiMapping[licenses_1.LicenseCategory.WEAK_COPYLEFT]} Weak copyleft licenses: ${(hasWeakCopyleft ? colorette_1.yellow : colorette_1.green)(weakCopyleftCount)} ${hasWeakCopyleft ? '⚠️' : '✅'}`); console.log(`${categoryToEmojiMapping[licenses_1.LicenseCategory.UNKNOWN]} Unknown licenses: ${(hasUnknown ? colorette_1.yellow : colorette_1.green)(unknownCount)} ${hasUnknown ? '⚠️' : '✅'}`); console.log(`${categoryToEmojiMapping[licenses_1.LicenseCategory.PERMISSIVE]} Permissive licenses: ${(0, colorette_1.green)(byCategory[licenses_1.LicenseCategory.PERMISSIVE])}`); if (options.listUnknown) { console.log(); console.log('―'.repeat(node_process_1.default.stdout.columns)); console.log(); console.log(`🔍 ${(0, colorette_1.whiteBright)('Unknown licenses')}`); console.log((0, table_1.table)(Object.entries(licenses) .filter(([_packageKey, license]) => (0, licenses_1.categorizeLicense)(license.type) === licenses_1.LicenseCategory.UNKNOWN) .map(([packageKey]) => [packageKey]), tableConfig)); } if (options.showBreakdown) { console.log(); console.log('―'.repeat(node_process_1.default.stdout.columns)); console.log(); // licenses by category const byCategoryTable = [['Category', 'Count', 'Percentage']]; Object.entries(byCategory).forEach(([category, count]) => { byCategoryTable.push([category, count, Number(((count / total) * 100).toFixed(2))]); }); console.log(`🗂️ Licenses by ${(0, colorette_1.whiteBright)('category')}`); console.log((0, table_1.table)(byCategoryTable, tableConfig)); console.log(); // licenses by type const byLicenseTable = [['License', 'Count', 'Percentage']]; byLicenseEntries .sort(([, a], [, b]) => b - a) .forEach(([license, count]) => { byLicenseTable.push([ license === 'unknown' ? (0, colorette_1.yellow)(license) : getLicenseColor(license), count, Number(((count / total) * 100).toFixed(2)), ]); }); console.log(`🏷️ Licenses by ${(0, colorette_1.whiteBright)('type')}`); console.log((0, table_1.table)(byLicenseTable, tableConfig)); console.log(); const nonFullyPermissiveObj = [ ['License', 'Category'], ...Object.entries(categorizedLicenses) .filter(([_license, category]) => category !== licenses_1.LicenseCategory.PERMISSIVE) .map(([license, category]) => [license, `${category} ${categoryToEmojiMapping[category]}`]), ]; // non-permissive licenses if (nonFullyPermissiveObj.length > 0) { console.log(`⚠️ ${(0, colorette_1.whiteBright)('Non-fully-permissive')} licenses`); console.log((0, table_1.table)(nonFullyPermissiveObj, tableConfig)); } else { console.log(`✅ ${(0, colorette_1.whiteBright)('All licenses are fully-permissive')}`); } } console.log(); console.log((0, colorette_1.italic)('Remember that all data presented by the tool require manual verification. The presented information may be inaccurate or incomplete.')); }); }