UNPKG

@raven-js/glean

Version:

Glean documentation gold from your codebase - JSDoc parsing, validation, and beautiful doc generation

175 lines (149 loc) 5.93 kB
/** * @author Anonyfox <max@anonyfox.com> * @license MIT * @see {@link https://github.com/Anonyfox/ravenjs} * @see {@link https://ravenjs.dev} * @see {@link https://anonyfox.com} */ /** * Documentation router creation and configuration. * * Creates a Wings router instance with all documentation routes, * static asset serving, and middleware configured. This is the core * routing logic extracted for reuse in both server and resolver modes. */ import path from "node:path"; import { fileURLToPath } from "node:url"; import { Router } from "@raven-js/wings"; import { Assets, Logger } from "@raven-js/wings/server"; import { AssetRegistry, createAssetMiddleware } from "../assets/index.js"; import { discover } from "../discover/index.js"; import { extract } from "../extract/index.js"; import { createEntityPageHandler } from "./routes/entity-page.js"; import { createModuleDirectoryHandler } from "./routes/module-directory.js"; import { createModuleOverviewHandler } from "./routes/module-overview.js"; import { createPackageOverviewHandler } from "./routes/package-overview.js"; import { createSitemapHandler } from "./routes/sitemap.js"; /** * Creates documentation router instance with routes, static assets, and middleware. * * Loads and processes package data once at initialization, then creates Wings router * with all documentation routes configured. This router can be used directly for * resolver-based static generation or wrapped in a server for HTTP serving. * * @param {string} packagePath - Path to package to document * @param {Object} options - Configuration options * @param {string} [options.domain] - Base domain for canonical URLs (optional) * @param {boolean} [options.enableLogging] - Enable request logging (default: false) * @returns {Router} Wings router instance ready to handle requests * * @example * // Create documentation router for resolver mode * const router = createDocumentationRouter('./my-package'); * * // Use in resolver function * const resolver = async (path) => { * const ctx = new Context('GET', new URL(`http://localhost${path}`), new Headers()); * await router.handleRequest(ctx); * return ctx.toResponse(); * }; * * @example * // Create router with custom options * const router = createDocumentationRouter('./my-package', { * domain: 'docs.mypackage.com', * enableLogging: true * }); */ export function createDocumentationRouter(packagePath, options = {}) { const { domain, enableLogging = false } = options; // Validate required parameters if (!packagePath || typeof packagePath !== "string") { throw new Error("packagePath is required and must be a string"); } // Load package data once at initialization const packageMetadata = discover(packagePath); const packageInstance = extract(packageMetadata); // Validate extracted package data if (!packageInstance || !packageInstance.name) { throw new Error( `Failed to extract valid package data from: ${packagePath}`, ); } // Create asset registry and register discovered image assets const assetRegistry = new AssetRegistry(); // Register package-level image assets (from discovery phase) if (packageMetadata.imageAssets) { for (const asset of packageMetadata.imageAssets) { assetRegistry.register(/** @type {any} */ (asset)); } } // Register module-level image assets (from discovery phase) for (const module of packageMetadata.modules) { if (module.imageAssets) { for (const asset of module.imageAssets) { assetRegistry.register(/** @type {any} */ (asset)); } } } // Create Wings router instance const router = new Router(); // Add Wings middlewares // Optional request logging middleware if (enableLogging) { router.useEarly(new Logger()); } // Static assets middleware (serves from glean package static/ directory) // Calculate absolute path to static directory within glean package const currentFileUrl = import.meta.url; const currentFilePath = fileURLToPath(currentFileUrl); const currentDir = path.dirname(currentFilePath); // Different paths for development vs production (bundled) // Development: lib/server/router.js -> ../../static // Production: bin/glean.min.js -> ../static const isBundled = currentFilePath.includes("bin/") && currentFilePath.includes(".min.js"); const staticDir = path.resolve( currentDir, isBundled ? "../static" : "../../static", ); router.use(new Assets({ assetsDir: staticDir })); // Register asset serving route for local image assets router.get( "/assets/:filename", /** @type {any} */ (createAssetMiddleware(assetRegistry)), ); // Create route handlers with package data and asset registry const packageOverviewHandler = createPackageOverviewHandler( packageInstance, assetRegistry, ); const moduleDirectoryHandler = createModuleDirectoryHandler(packageInstance); const moduleOverviewHandler = createModuleOverviewHandler( packageInstance, assetRegistry, ); const entityPageHandler = createEntityPageHandler(packageInstance); const sitemapHandler = createSitemapHandler(packageInstance, { baseUrl: domain ? `https://${domain}` : "https://docs.example.com", }); // Register all documentation routes // Package overview route (homepage) router.get("/", /** @type {any} */ (packageOverviewHandler)); // Module directory route (all modules overview) router.get("/modules/", /** @type {any} */ (moduleDirectoryHandler)); // Module overview route (specific module documentation) router.get( "/modules/:moduleName/", /** @type {any} */ (moduleOverviewHandler), ); // Entity documentation route (specific API documentation) router.get( "/modules/:moduleName/:entityName/", /** @type {any} */ (entityPageHandler), ); // Sitemap route (SEO optimization) router.get("/sitemap.xml", /** @type {any} */ (sitemapHandler)); // Wings router handles 404s automatically when no routes match return router; }