nx/scripts/documentation/open-graph/generate-images.ts
Isaac Mann 12eb5df469
docs(core): powerpack docs (#27904)
-  Activate powerpack recipe
-  Powerpack owners documentation
- [x] Powerpack custom remote cache documentation
- [x] Powerpack conformance documentation

Infrastructure for powerpack docs

- Adds the ability to generate API docs from ocean packages

To generate API documentation for plugins in the ocean repository, run
the `nx documentation` command with the `NX_OCEAN_RELATIVE_PATH`
environment variable set to the relative path to your checked out copy
of the ocean repo.

```
NX_OCEAN_RELATIVE_PATH=../ocean nx documentation
```

This will create generated API documentation in the
`docs/external-generated` folder. This API will be merged into the
normal `docs/generated` documentation when the docs site is built.

Because there are two separate output folders, if someone runs `nx
documentation` without the `NX_OCEAN_RELATIVE_PATH` environment
variable, the ocean documentation will not be overwritten. The ocean
documentation will only be updated or deleted when someone explicitly
chooses to do so.

---------

Co-authored-by: Juri Strumpflohner <juri.strumpflohner@gmail.com>
2024-09-25 10:15:47 -04:00

233 lines
5.7 KiB
TypeScript

import { Canvas, Image, SKRSContext2D } from '@napi-rs/canvas';
import { PackageMetadata } from '../../../nx-dev/models-package/src/lib/package.models';
import {
ensureDir,
readFile,
readJSONSync,
writeFileSync,
copyFileSync,
} 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`)
),
...readJSONSync(
resolve(
__dirname,
'../../../',
`./docs/external-generated/packages-metadata.json`
)
),
];
const targetFolder: string = resolve(
__dirname,
'../../../',
`./nx-dev/nx-dev/public/images/open-graph`
);
const data: {
title: string;
content: string;
mediaImage?: 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,
mediaImage: item.mediaImage,
filename: [category.sidebarId, category.id, item.id]
.filter(Boolean)
.join('-'),
});
item.itemList?.forEach((subItem) => {
data.push({
title: subItem.name,
content: subItem.description || category.name,
mediaImage: subItem.mediaImage,
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<void> {
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 = '#FFFFFF';
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 = '#F8FAFC';
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 copyImage(
backgroundImagePath: string,
targetFolder: string,
filename: string
) {
const splits = backgroundImagePath.split('.');
const extension = splits[splits.length - 1];
copyFileSync(
backgroundImagePath,
resolve(targetFolder, `./${filename}.${extension}`)
);
}
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) =>
item.mediaImage
? copyImage(
resolve(__dirname, '../../../docs/' + item.mediaImage),
targetFolder,
item.filename
)
: createOpenGraphImage(
resolve(__dirname, './media.jpg'),
targetFolder,
item.title,
item.content,
item.filename
)
)
);