73 lines
4.1 KiB
JavaScript
73 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Render a Sitesmith JSON response (a recorded fixture, or a live one saved to a file) through the
|
|
* repo's own exporter into HTML, then take Firefox screenshots so a human can compare runs.
|
|
*
|
|
* node scripts/render-sitesmith-response.mjs --response resp.json --out outdir [--design design.json]
|
|
* [--page 0] [--assets dir] [--width 1280] [--mobile 390] [--no-shots]
|
|
*
|
|
* --design JSON of SiteDesign overrides (primaryColor, headingFont, ...), i.e. the values the
|
|
* SITE DESIGN prompt block carried. Defaults to the editor's default design.
|
|
* --assets directory to expose as ./assets/ next to the HTML (symlinked) so relUrl images load.
|
|
* Screenshots: Playwright module and Firefox binary come from $SITESMITH_PLAYWRIGHT / $SITESMITH_FIREFOX
|
|
* (defaults below). Only type=replace responses render (patches need a canvas).
|
|
*/
|
|
import { build } from 'esbuild';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { createRequire } from 'node:module';
|
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
|
const craftRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const args = process.argv.slice(2);
|
|
const opt = (name, def) => { const i = args.indexOf('--' + name); return i >= 0 ? args[i + 1] : def; };
|
|
const flag = (name) => args.includes('--' + name);
|
|
const respPath = opt('response'), outDir = opt('out');
|
|
if (!respPath || !outDir) { console.error('usage: render-sitesmith-response.mjs --response resp.json --out dir [--design design.json] [--assets dir]'); process.exit(2); }
|
|
|
|
const PLAYWRIGHT = process.env.SITESMITH_PLAYWRIGHT || '/tmp/claude-1000/-workspace/edbe144d-d5fe-40a1-88a3-fb8ed9709df9/scratchpad/repro/node_modules/playwright/index.mjs';
|
|
const FIREFOX = process.env.SITESMITH_FIREFOX || '/home/claude/.cache/ms-playwright/firefox-1538/firefox/firefox';
|
|
|
|
const bundle = await build({
|
|
entryPoints: [path.join(craftRoot, 'scripts/sitesmith-catalog/render-entry.ts')],
|
|
bundle: true, write: false, format: 'cjs', platform: 'node', jsx: 'automatic', logLevel: 'error',
|
|
define: { 'process.env.NODE_ENV': '"production"' },
|
|
});
|
|
const tmp = fs.mkdtempSync(path.join(process.env.TMPDIR || '/tmp', 'ssrender-'));
|
|
fs.writeFileSync(path.join(tmp, 'r.cjs'), bundle.outputFiles[0].text);
|
|
const { renderResponse } = createRequire(import.meta.url)(path.join(tmp, 'r.cjs'));
|
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
|
|
const resp = JSON.parse(fs.readFileSync(respPath, 'utf8'));
|
|
const design = opt('design') ? JSON.parse(fs.readFileSync(opt('design'), 'utf8')) : {};
|
|
const html = renderResponse(resp, design, Number(opt('page', 0)));
|
|
fs.mkdirSync(outDir, { recursive: true });
|
|
const htmlPath = path.join(outDir, 'page.html');
|
|
fs.writeFileSync(htmlPath, html);
|
|
if (opt('assets')) {
|
|
const link = path.join(outDir, 'assets');
|
|
fs.rmSync(link, { recursive: true, force: true });
|
|
fs.symlinkSync(path.resolve(opt('assets')), link);
|
|
}
|
|
console.log(`html: ${htmlPath} (${html.length} bytes)`);
|
|
|
|
if (!flag('no-shots')) {
|
|
const { firefox } = await import(pathToFileURL(PLAYWRIGHT).href);
|
|
const browser = await firefox.launch({ executablePath: FIREFOX, headless: true });
|
|
for (const [label, width, height] of [['desktop', Number(opt('width', 1280)), 900], ['mobile', Number(opt('mobile', 390)), 844]]) {
|
|
const page = await browser.newPage({ viewport: { width, height } });
|
|
const errors = [];
|
|
page.on('pageerror', (e) => errors.push(String(e)));
|
|
await page.goto(pathToFileURL(htmlPath).href, { waitUntil: 'networkidle', timeout: 60000 }).catch((e) => errors.push(String(e)));
|
|
await page.evaluate(() => document.fonts && document.fonts.ready);
|
|
// reveal entrance animations deterministically for the capture
|
|
await page.addStyleTag({ content: '[data-animation]{opacity:1!important;animation:none!important}' });
|
|
const shot = path.join(outDir, `${label}.png`);
|
|
await page.screenshot({ path: shot, fullPage: true });
|
|
const overflow = await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth);
|
|
console.log(`${label}: ${shot} horizontal-overflow-px=${overflow}${errors.length ? ' errors=' + JSON.stringify(errors) : ''}`);
|
|
await page.close();
|
|
}
|
|
await browser.close();
|
|
}
|