blob: 3fc6e71430ff35b907807a0f02cc61dc1ba3ca01 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/** Render the social preview images from the built, localized pages. */
import { execFileSync } from 'node:child_process';
import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
import http from 'node:http';
import { extname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { chromium } from '@playwright/test';
import { headlineText, heroText } from './hero-text.mjs';
const dist = fileURLToPath(new URL('../dist/', import.meta.url));
const social = fileURLToPath(new URL('../src/assets/', import.meta.url));
const types = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'text/javascript',
'.woff2': 'font/woff2',
'.png': 'image/png',
'.svg': 'image/svg+xml',
};
// The built pages reference /_astro/... absolutely, so serve dist over HTTP.
const server = http.createServer((request, response) => {
let path = join(dist, decodeURIComponent(new URL(request.url, 'http://x').pathname));
if (existsSync(path) && statSync(path).isDirectory()) path = join(path, 'index.html');
if (!existsSync(path)) {
response.writeHead(404);
response.end();
return;
}
response.writeHead(200, { 'content-type': types[extname(path)] ?? 'application/octet-stream' });
response.end(readFileSync(path));
});
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
const origin = `http://127.0.0.1:${server.address().port}`;
// The scene only: the README carries the headline, the lede and the links as
// its own text, and reduced motion shows every event of the turn at once.
// The social preview: the brand, the headline and the scene, centred in the
// 1200×630 frame link previews are cut to. Light only, since a preview shows
// on the sharing site's own background, and the sections below the hero are
// dropped so the frame ends where the scene does.
const socialOnly = `
.lede, .cta, .fine, .hero ~ *, footer { display: none !important; }
.nav > :not(.brand) { display: none !important; }
html, body { overflow: hidden !important; }
.hero {
box-sizing: border-box !important;
min-height: 570px !important;
padding: 0 0 12px !important;
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
}
.scene { margin-top: 28px !important; }
`;
// npm ci installs the Playwright package but not a browser, so a clean
// checkout has to be able to fetch one before this command can run.
const executable = (() => {
try {
return chromium.executablePath();
} catch {
return undefined;
}
})();
if (!executable || !existsSync(executable)) {
execFileSync(
process.platform === 'win32' ? 'npx.cmd' : 'npx',
['playwright', 'install', 'chromium'],
{
stdio: 'inherit',
},
);
}
const manifest = {};
const headlines = {};
const browser = await chromium.launch();
try {
for (const locale of ['en', 'zh-CN']) {
const html = readFileSync(join(dist, locale, 'index.html'), 'utf8');
manifest[locale] = heroText(html);
headlines[locale] = headlineText(html);
const page = await browser.newPage({
viewport: { width: 1200, height: 630 },
deviceScaleFactor: 2,
colorScheme: 'light',
reducedMotion: 'reduce',
});
await page.goto(`${origin}/${locale}/`);
await page.addStyleTag({ content: socialOnly });
await page.evaluate(() => document.fonts.ready);
const path = join(social, `social.${locale}.png`);
await page.screenshot({ path });
console.log(path);
await page.close();
}
// The copy these images were made from, so the site test can tell when the
// pages have moved on and the committed images have not.
manifest.headline = headlines;
const path = join(social, 'social-preview.json');
writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\n`);
console.log(path);
} finally {
await browser.close();
server.close();
}