// License file generator import { BaseService } from '@/core/base/BaseService.js'; import type { LicenseData, FontLicenseData } from '@/modules/docs/types.js'; export class LicenseGenerator extends BaseService { constructor() { super('LicenseGenerator'); } /** * Generate markdown license file */ async generate(licenseData: LicenseData): Promise { const { generatedAt, generator, version, fonts, compliance } = licenseData; const content = [ '# Font Licenses', '', `Generated by ${generator} on ${new Date(generatedAt).toISOString()}`, `Version: ${version}`, '', '## Overview', '', `This document contains license information for ${ Object.keys(fonts).length } fonts used in this project.`, '', '## Font Licenses', '', ]; // Add each font's license information for (const fontData of Object.values(fonts)) { content.push(...this.generateFontSection(fontData)); content.push(''); } // Add compliance section if available if (compliance.checked) { content.push('## License Compliance'); content.push(''); if (compliance.issues.length === 0) { content.push('✅ All licenses are compliant for web use.'); } else { content.push('⚠️ License compliance issues found:'); content.push(''); for (const issue of compliance.issues) { content.push(`- ${issue}`); } } content.push(''); } // Add footer content.push('## Important Notes'); content.push(''); content.push( '- Please review individual license terms before redistributing fonts' ); content.push('- Some licenses may have specific attribution requirements'); content.push('- Commercial use restrictions may apply to certain fonts'); content.push('- Always include original license files when redistributing'); content.push(''); content.push('---'); content.push(`Generated on ${new Date(generatedAt).toISOString()}`); return content.join('\n'); } /** * Generate plain text version */ async generatePlainText(licenseData: LicenseData): Promise { const markdown = await this.generate(licenseData); // Simple markdown-to-text conversion return markdown .replace(/#{1,6}\s+/g, '') // Remove headers .replace(/\*\*([^*]+)\*\*/g, '$1') // Remove bold .replace(/\*([^*]+)\*/g, '$1') // Remove italic .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Remove links, keep text .replace(/---+/g, '========================================'); } /** * Generate HTML version */ async generateHtml(licenseData: LicenseData): Promise { const markdown = await this.generate(licenseData); // Simple markdown-to-HTML conversion let html = markdown .replace(/^# (.+)$/gm, '

$1

') .replace(/^## (.+)$/gm, '

$1

') .replace(/^### (.+)$/gm, '

$1

') .replace(/\*\*([^*]+)\*\*/g, '$1') .replace(/\*([^*]+)\*/g, '$1') .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1') .replace(/^- (.+)$/gm, '
  • $1
  • ') .replace(/\n\n/g, '

    ') .replace(/---+/g, '


    '); // Wrap list items html = html.replace(/(
  • .*<\/li>)/gs, '
      $1
    '); // Wrap in paragraphs html = '

    ' + html + '

    '; // Clean up extra paragraph tags html = html .replace(/

    <\/p>/g, '') .replace(/

    ()/g, '$1') .replace(/(<\/h[1-6]>)<\/p>/g, '$1'); return ` Font Licenses ${html} `; } /** * Generate JSON license file */ async generateJson(licenseData: LicenseData): Promise { // Create a clean, formatted JSON structure const jsonOutput = { metadata: { generatedAt: licenseData.generatedAt, generator: licenseData.generator, version: licenseData.version, totalFonts: Object.keys(licenseData.fonts).length, }, compliance: licenseData.compliance, fonts: this.formatFontsForJson(licenseData.fonts), }; return JSON.stringify(jsonOutput, null, 2); } /** * Format fonts data for JSON output */ private formatFontsForJson( fonts: Record ): Record { const formatted: Record = {}; for (const [fontId, fontData] of Object.entries(fonts)) { formatted[fontId] = { name: fontData.name, displayName: fontData.displayName, description: fontData.description, source: { type: fontData.source.type, repository: `${fontData.source.owner}/${fontData.source.repo}`, url: fontData.source.url, }, license: { type: fontData.license.type, url: fontData.license.url, attribution: fontData.license.attribution, requirements: fontData.license.requirements ?? [], permissions: { webUse: this.checkPermission(fontData.license.type, 'web'), commercialUse: this.checkPermission( fontData.license.type, 'commercial' ), modification: this.checkPermission( fontData.license.type, 'modification' ), }, }, }; } return formatted; } /** * Check specific license permissions */ private checkPermission( licenseType: string, permission: 'web' | 'commercial' | 'modification' ): boolean { const type = licenseType.toLowerCase(); const permissions = { web: [ 'sil open font license 1.1', 'ofl-1.1', 'apache license 2.0', 'apache-2.0', 'mit', 'ipa font license agreement v1.0', 'ipa', ], commercial: [ 'sil open font license 1.1', 'ofl-1.1', 'apache license 2.0', 'apache-2.0', 'mit', ], modification: [ 'sil open font license 1.1', 'ofl-1.1', 'apache license 2.0', 'apache-2.0', 'mit', 'ipa font license agreement v1.0', 'ipa', ], }; return permissions[permission].includes(type); } /** * Generate license section for a single font */ private generateFontSection(fontData: FontLicenseData): string[] { const { fontId, name, displayName, source, license, description } = fontData; const section = [ `### ${displayName}`, '', `**Font ID**: \`${fontId}\``, `**Internal Name**: \`${name}\``, ]; if (description) { section.push(`**Description**: ${description}`); } section.push( '', '**Source**:', `- Repository: [${source.owner}/${source.repo}](${source.url})`, `- Type: ${source.type}`, '', '**License**:', `- Type: ${license.type}`, `- URL: [License](${license.url})` ); if (license.attribution) { section.push(`- Attribution: ${license.attribution}`); } if (license.requirements && license.requirements.length > 0) { section.push('- Requirements:'); for (const requirement of license.requirements) { section.push(` - ${requirement}`); } } return section; } }