import { BaseFormatter } from '../classes.baseformatter.js'; import type { IFormatWarning, IPlannedChange } from '../interfaces.format.js'; import * as plugins from '../mod.plugins.js'; import * as paths from '../../paths.js'; import { logger } from '../../gitzone.logging.js'; const INCOMPATIBLE_LICENSES: string[] = ['AGPL', 'GPL', 'SSPL']; export class LicenseFormatter extends BaseFormatter { get name(): string { return 'license'; } get runsWithoutChanges(): boolean { return true; } async analyze(): Promise { // License formatter only checks for incompatible licenses // It does not modify any files, so return empty array // The actual check happens in execute() for reporting purposes return []; } async validate(): Promise { const result = await this.checkLicenses(); if (!result || result.failingModules.length === 0) { return []; } return [ { level: 'error', module: this.name, message: `License check failed for ${result.failingModules.length} module(s): ${result.failingModules .map((failedModule) => `${failedModule.name} (${failedModule.license})`) .join(', ')}`, }, ]; } async execute(changes: IPlannedChange[]): Promise { const startTime = this.stats.moduleStartTime(this.name); this.stats.startModule(this.name); try { const licenseCheckResult = await this.checkLicenses(); if (!licenseCheckResult) { logger.log('warn', 'No node_modules found. Skipping license check'); return; } if (licenseCheckResult.failingModules.length === 0) { logger.log('info', 'License check passed - no incompatible licenses found'); } else { logger.log('error', 'License check failed - incompatible licenses found:'); for (const failedModule of licenseCheckResult.failingModules) { console.log( ` ${failedModule.name} has license ${failedModule.license}`, ); } } } finally { this.stats.endModule(this.name, startTime); } } async applyChange(change: IPlannedChange): Promise { // No file changes for license formatter } private async checkLicenses(): Promise<{ failingModules: Array<{ name: string; license: string }>; } | undefined> { const nodeModulesPath = plugins.path.join(paths.cwd, 'node_modules'); const nodeModulesExists = await plugins.smartfs .directory(nodeModulesPath) .exists(); if (!nodeModulesExists) { return undefined; } const licenseChecker = await plugins.smartlegal.createLicenseChecker(); return await licenseChecker.excludeLicenseWithinPath( paths.cwd, INCOMPATIBLE_LICENSES, ); } }