UNPKG

@raven-js/fledge

Version:

From nestling to flight-ready - Build & bundle tool for modern JavaScript apps

136 lines (117 loc) 3.95 kB
/** * @author Anonyfox <max@anonyfox.com> * @license MIT * @see {@link https://ravenjs.dev} * @see {@link https://github.com/Anonyfox/ravenjs} * @see {@link https://anonyfox.com} */ /** * @file Static site generation module - builds static HTML/CSS/JS sites from applications. * * This module handles the generation of static websites and single-page applications * optimized for edge deployment and CDN distribution. */ import { buildBundles, validateBundleConfig } from "./bundler.js"; import { Crawler } from "./crawler.js"; /** * @typedef {Object} StaticGenerationOptions * @property {string} outputDir - Output directory path * @property {boolean} [verbose] - Enable verbose logging */ /** * @typedef {Object} StaticGenerationResult * @property {number} totalFiles - Total number of resources found * @property {number} savedFiles - Number of files successfully saved * @property {number} totalTime - Total generation time in milliseconds * @property {number} resourcesCount - Number of resources crawled * @property {number} errorsCount - Number of errors encountered * @property {string} outputDir - Absolute path to output directory * @property {Array<{url: string, error: string}>} errors - Details of any errors * @property {Array<{url: string, path: string}>} [savedPaths] - Saved file details (if verbose) */ export { Config } from "./config/config.js"; /** * Generate static site from configuration * @param {import("./config/config.js").Config} config - Validated configuration * @param {StaticGenerationOptions} options - Generation options * @returns {Promise<StaticGenerationResult>} Generation results * @throws {Error} If static generation fails */ export async function generateStaticSite(config, options) { const { outputDir, verbose = false } = options; const errors = []; /** @type {Array<{url: string, path: string}> | undefined} */ const savedPaths = verbose ? [] : undefined; const crawler = new Crawler(config); try { // Build bundles first if configured const bundlesConfig = config.getBundles(); if (Object.keys(bundlesConfig).length > 0) { // Validate bundle configuration validateBundleConfig(bundlesConfig); // Build all bundles in-memory const resolver = config.getResolver(); const baseUrl = resolver ? new URL("http://localhost:3000") // Dummy URL for resolver mode : (() => { const serverConfig = config.getServer(); return new URL( typeof serverConfig === "string" ? serverConfig : "http://localhost:3000", ); })(); const bundleResources = await buildBundles(bundlesConfig, baseUrl); // Pre-populate crawler with bundle resources for (const [mountPath, resource] of bundleResources) { crawler.addVisitedResource(mountPath, resource); } } // Start crawling await crawler.start(); await crawler.crawl(); // Get crawled resources const resources = crawler.getResources(); // Save all resources to files let savedCount = 0; for (const resource of resources) { try { const savedPath = await resource.saveToFile( outputDir, config.getBasePath(), ); if (verbose && savedPaths) { const url = resource.getUrl(); savedPaths.push({ url: url.pathname, path: savedPath, }); } savedCount++; } catch (error) { const url = resource.getUrl(); const errorDetail = { url: url.href, error: /** @type {Error} */ (error).message, }; errors.push(errorDetail); } } // Get final statistics const stats = /** @type {any} */ (crawler.getStatistics()); await crawler.stop(); return { totalFiles: resources.length, savedFiles: savedCount, totalTime: stats.totalTime, resourcesCount: stats.resourcesCount, errorsCount: errors.length, outputDir, errors, ...(savedPaths && { savedPaths }), }; } catch (error) { await crawler.stop(); throw error; } }