screenshot-antipatterns.js

  1#!/usr/bin/env node
  2
  3/**
  4 * Screenshot Anti-Pattern Examples
  5 *
  6 * Takes 1080x1080 screenshots of each anti-pattern example for LinkedIn sharing.
  7 * Requires the dev server to be running on localhost:3000
  8 */
  9
 10import { chromium } from 'playwright';
 11import path from 'path';
 12import fs from 'fs';
 13import { fileURLToPath } from 'url';
 14
 15const __filename = fileURLToPath(import.meta.url);
 16const __dirname = path.dirname(__filename);
 17const ROOT_DIR = path.resolve(__dirname, '..');
 18const EXAMPLES_DIR = path.join(ROOT_DIR, 'public', 'antipattern-examples');
 19const OUTPUT_DIR = path.join(ROOT_DIR, 'public', 'antipattern-images');
 20
 21async function screenshotAntipatterns() {
 22  // Ensure output directory exists
 23  if (!fs.existsSync(OUTPUT_DIR)) {
 24    fs.mkdirSync(OUTPUT_DIR, { recursive: true });
 25  }
 26
 27  // Get all HTML files in the examples directory
 28  const files = fs.readdirSync(EXAMPLES_DIR)
 29    .filter(f => f.endsWith('.html'));
 30
 31  if (files.length === 0) {
 32    console.log('No HTML files found in', EXAMPLES_DIR);
 33    return;
 34  }
 35
 36  console.log(`📸 Taking screenshots of ${files.length} anti-pattern example(s)...\n`);
 37
 38  const browser = await chromium.launch();
 39  const context = await browser.newContext({
 40    viewport: { width: 1200, height: 1200 },
 41    deviceScaleFactor: 2, // 2x for high-res output
 42  });
 43
 44  for (const file of files) {
 45    const name = path.basename(file, '.html');
 46    const url = `http://localhost:3000/antipattern-examples/${file}`;
 47    const outputPath = path.join(OUTPUT_DIR, `${name}.png`);
 48
 49    console.log(`  ${name}...`);
 50
 51    const page = await context.newPage();
 52    await page.goto(url, { waitUntil: 'networkidle' });
 53
 54    // Wait for fonts to load
 55    await page.waitForTimeout(500);
 56
 57    // Screenshot the .container element (1080x1080)
 58    const container = await page.$('.container');
 59    if (container) {
 60      await container.screenshot({
 61        path: outputPath,
 62        type: 'png',
 63      });
 64      console.log(`    ✓ Saved to ${path.relative(ROOT_DIR, outputPath)}`);
 65    } else {
 66      // Fallback: screenshot full page cropped
 67      await page.screenshot({
 68        path: outputPath,
 69        type: 'png',
 70        clip: { x: 0, y: 0, width: 1080, height: 1080 },
 71      });
 72      console.log(`    ✓ Saved (full page crop) to ${path.relative(ROOT_DIR, outputPath)}`);
 73    }
 74
 75    await page.close();
 76  }
 77
 78  await browser.close();
 79  console.log(`\n✨ Done! Screenshots saved to ${path.relative(ROOT_DIR, OUTPUT_DIR)}/`);
 80}
 81
 82// Check if dev server is running
 83async function checkServer() {
 84  try {
 85    const response = await fetch('http://localhost:3000');
 86    return response.ok;
 87  } catch {
 88    return false;
 89  }
 90}
 91
 92async function main() {
 93  const serverRunning = await checkServer();
 94  if (!serverRunning) {
 95    console.error('❌ Dev server not running. Please start it with: bun run dev');
 96    process.exit(1);
 97  }
 98
 99  await screenshotAntipatterns();
100}
101
102main().catch(console.error);