import { Canvas, Image, SKRSContext2D } from '@napi-rs/canvas'; import { PackageMetadata } from '../../../nx-dev/models-package/src/lib/package.models'; import { ensureDir, readFile, readJSONSync, writeFileSync } from 'fs-extra'; import { resolve } from 'path'; const mapJson = readJSONSync('./docs/map.json', 'utf8').content; const documents: any[] = [ ...mapJson .find((x) => x.id === 'nx-documentation') ?.['itemList'].map((item) => { item.sidebarId = ''; return item; }), ...mapJson .find((x) => x.id === 'extending-nx') ?.['itemList'].map((item) => { item.sidebarId = 'extending-nx'; return item; }), ...mapJson .find((x) => x.id === 'ci') ?.['itemList'].map((item) => { item.sidebarId = 'ci'; return item; }), ].filter(Boolean); const packages: PackageMetadata[] = readJSONSync( resolve(__dirname, '../../../', `./docs/generated/packages-metadata.json`) ); const targetFolder: string = resolve( __dirname, '../../../', `./nx-dev/nx-dev/public/images/open-graph` ); const data: { title: string; content: string; filename: string }[] = []; documents.forEach((category) => { data.push({ title: category.name, content: category.description, filename: [category.sidebarId, category.id].filter(Boolean).join('-'), }); category.itemList.forEach((item) => { data.push({ title: item.name, content: item.description || category.name, filename: [category.sidebarId, category.id, item.id] .filter(Boolean) .join('-'), }); item.itemList?.forEach((subItem) => { data.push({ title: subItem.name, content: subItem.description || category.name, filename: [category.sidebarId, category.id, item.id, subItem.id] .filter(Boolean) .join('-'), }); }); }); }); packages.map((pkg) => { data.push({ title: pkg.packageName, content: 'Package details', filename: ['packages', pkg.name].join('-'), }); pkg.documents.map((document) => { data.push({ title: document.name, content: pkg.packageName, filename: ['packages', pkg.name, 'documents', document.id].join('-'), }); }); pkg.executors.map((executor) => { data.push({ title: executor.name, content: pkg.packageName, filename: ['packages', pkg.name, 'executors', executor.name].join('-'), }); }); pkg.generators.map((generator) => { data.push({ title: generator.name, content: pkg.packageName, filename: ['packages', pkg.name, 'generators', generator.name].join('-'), }); }); }); const TITLE_LINE_HEIGHT = 60; const SUB_LINE_HEIGHT = 38; function createOpenGraphImage( backgroundImagePath: string, targetFolder: string, title: string, content: string, filename: string ): Promise { const addBackground = readFile(backgroundImagePath).then((content) => { const image = new Image(); image.src = content; image.width = 1200; image.height = 630; return image; }); return addBackground.then((image) => { const canvas = new Canvas(1200, 630); const context = canvas.getContext('2d'); context.drawImage(image, 0, 0, 1200, 630); context.font = 'bold 50px system-ui'; context.textAlign = 'center'; context.textBaseline = 'top'; context.fillStyle = '#0F172A'; const titleLines = splitLines(context, title.toUpperCase(), 1100); titleLines.forEach((line, index) => { context.fillText(line, 600, 220 + index * TITLE_LINE_HEIGHT); }); context.font = 'normal 32px system-ui'; context.textAlign = 'center'; context.textBaseline = 'top'; context.fillStyle = '#334155'; const lines = splitLines(context, content, 1100); lines.forEach((line, index) => { context.fillText( line, 600, 310 + index * SUB_LINE_HEIGHT + titleLines.length * TITLE_LINE_HEIGHT ); }); console.log('Generating: ', `${filename}.jpg`); return writeFileSync( resolve(targetFolder + `/${filename}.jpg`), canvas.toBuffer('image/jpeg') ); }); } function splitLines( context: SKRSContext2D, text: string, maxWidth: number ): string[] { // calculate line splits const words = text.split(' '); if (words.length <= 1) { return words; } const lines: string[] = []; let currentLine = words[0]; for (let i = 1; i < words.length; i++) { const word = words[i]; const newLine = `${currentLine} ${word}`; if (context.measureText(newLine).width < maxWidth) { currentLine = newLine; } else { lines.push(currentLine); currentLine = word; } } lines.push(currentLine); return lines; } console.log( 'Generated images will be on this path:\n', resolve(targetFolder, '\n\n') ); ensureDir(targetFolder).then(() => data.map((item) => createOpenGraphImage( resolve(__dirname, './media.jpg'), targetFolder, item.title, item.content, item.filename ) ) );