// 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, '');
// 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;
}
}