@twocaretcat/astro-snapshot
Version:
An Astro integration for generating screenshots of your pages automatically at build time. Perfect for creating social images, content previews, dynamic icons, and more!
147 lines (146 loc) • 6.03 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = snapshot;
/**
* Astro integration for generating automated page snapshots using Puppeteer.
*
* This module provides an integration that runs after the build process,
* starts a local preview server, and captures screenshots for configured
* routes using a headless browser.
*
* @module
*/
const node_util_1 = require("node:util");
const astro_1 = require("astro");
const puppeteer_1 = require("puppeteer");
const node_url_1 = require("node:url");
const promises_1 = require("node:fs/promises");
const node_path_1 = require("node:path");
const utils_js_1 = require("./utils.js");
/**
* Creates an Astro integration that captures screenshots of specified pages
* during the `astro:build:done` lifecycle event.
*
* @param config - Integration configuration, including page mappings, defaults,
* and Puppeteer settings.
* @returns The configured Astro integration.
*/
function snapshot(config) {
// Resolved config options
const pages = config.pages;
const defaults = {
...config.defaults,
};
const launchOptions = {
headless: true,
...config.launchOptions,
};
const port = config.port ?? 4322;
let astroConfig;
let rootDir;
/**
* Merges per-page configuration with defaults and resolves
* the final screenshot configuration.
*
* @param pageConfig - Configuration for a specific page.
* @returns Fully resolved configuration for Puppeteer.
*/
const resolveScreenshotConfig = (pageConfig) => {
const outputPath = pageConfig.outputPath;
return {
// Or operator is used to ignore 0
width: pageConfig.width || defaults.width || 1200,
height: pageConfig.height || defaults.height || 630,
overwrite: pageConfig.overwrite ?? defaults.overwrite ?? false,
goToOptions: {
waitUntil: 'networkidle2',
...defaults.gotoOptions,
...pageConfig.gotoOptions,
},
outputPath,
screenshotOptions: {
path: outputPath,
type: (0, utils_js_1.getFormat)(outputPath),
fullPage: false,
...defaults.screenshotOptions,
...pageConfig.screenshotOptions,
},
setViewportOptions: {
...defaults.setViewportOptions,
...pageConfig.setViewportOptions,
},
};
};
/**
* Handles the `astro:config:done` lifecycle event.
* Stores the Astro configuration and root directory for later use.
*
* @param param0 - Object containing the resolved Astro config.
*/
const handleConfigDone = ({ config }) => {
astroConfig = config;
rootDir = (0, node_url_1.fileURLToPath)(astroConfig.root);
};
/**
* Handles the `astro:build:done` lifecycle event.
* Launches a local preview server and uses Puppeteer to generate
* screenshots for all configured pages.
*
* @param param0 - Object containing the Astro logger instance.
*/
const handleBuildDone = async ({ logger }) => {
const startTime = performance.now();
logger.info('🔭 Integration loaded.');
const pageEntries = Object.entries(pages);
if (pageEntries.length === 0) {
logger.warn('No pages configured for screenshot generation. Skipping...');
return;
}
// Start local server to render pages
const previewServer = await (0, astro_1.preview)({
root: rootDir,
server: { port },
});
// Launch Puppeteer
const browser = await (0, puppeteer_1.launch)(launchOptions);
logger.info('🔭 Generating screenshots...');
logger.label = '';
try {
for (const [pagePath, screenshotConfigs] of pageEntries) {
const normalizedPagePath = pagePath.startsWith('/') ? pagePath : `/${pagePath}`;
const pageUrl = `http://localhost:${port}${normalizedPagePath}`;
for (const screenshotConfig of screenshotConfigs) {
const { width, height, overwrite, goToOptions, outputPath, screenshotOptions, setViewportOptions } = resolveScreenshotConfig(screenshotConfig);
const absoluteOutputPath = (0, node_path_1.resolve)(rootDir, outputPath);
const relativePath = (0, node_path_1.relative)(rootDir, absoluteOutputPath);
const doesFileExist = await (0, utils_js_1.fileExists)(absoluteOutputPath);
if (doesFileExist && !overwrite) {
(0, utils_js_1.logStatus)(logger, normalizedPagePath, relativePath, 'skipped');
continue;
}
// Ensure output directory exists
await (0, promises_1.mkdir)((0, node_path_1.dirname)(absoluteOutputPath), { recursive: true });
// Create page and take screenshot
const page = await browser.newPage();
await page.setViewport({ width, height, ...setViewportOptions });
await page.goto(pageUrl, goToOptions);
await page.screenshot(screenshotOptions);
await page.close();
(0, utils_js_1.logStatus)(logger, normalizedPagePath, relativePath, doesFileExist && overwrite ? 'overwritten' : undefined);
}
}
}
finally {
await browser.close();
await previewServer.stop();
}
logger.info((0, node_util_1.styleText)('green', `✓ Completed in ${(0, utils_js_1.formatDuration)(performance.now() - startTime)}.`));
};
return {
name: 'astro-snapshot',
hooks: {
'astro:config:done': handleConfigDone,
'astro:build:done': handleBuildDone,
},
};
}