import { merge } from "../core/utils.ts";import onDemand, { getRouter } from "../middlewares/on_demand.ts";import { extname } from "../deps/path.ts";
import type { Logger, Page, Site } from "../core.ts";
export interface Options { routesPath: string;
preloadPath: string;}
export const defaults: Options = { routesPath: "/_routes.json", preloadPath: "/_preload.ts",};
export default function (userOptions?: Partial<Options>) { const options = merge(defaults, userOptions);
return (site: Site) => { const routesFile = site.root(options.routesPath); const preloadFile = site.root(options.preloadPath); const collector = new JsonRouterCollector(routesFile, preloadFile);
site.addEventListener("beforeSave", async () => { collector.collectRoutes(site.onDemandPages); await collector.saveRoutes(site.logger); });
site.options.watcher.ignore.push(options.routesPath); site.options.watcher.ignore.push(options.preloadPath);
site.options.server.middlewares ||= []; site.options.server.middlewares.push(onDemand({ site, router: getRouter(collector.routes), })); };}
export class JsonRouterCollector { #routesFile: string; #preloadFile: string;
routes = new Map<string, string>();
constructor(routesFile: string, preloadFile: string) { this.#routesFile = routesFile; this.#preloadFile = preloadFile; }
collectRoutes(pages: Page[]): void { this.routes.clear();
pages.forEach((page) => { this.routes.set( page.data.url as string, page.src.path + page.src.ext, ); }); }
async saveRoutes(logger: Logger): Promise<void> { if (!this.routes.size) { return; }
const data: Record<string, string> = {}; const preloaded = new Set<string>();
this.routes.forEach((path, url) => { data[url] = path;
switch (extname(path)) { case ".js": case ".jsx": case ".ts": case ".tsx": case ".mjs": preloaded.add(path); } });
await Deno.writeTextFile( this.#routesFile, JSON.stringify(data, null, 2) + "\n", ); logger.log(`Routes saved at <dim>${this.#routesFile}</dim>`);
if (preloaded.size) { await Deno.writeTextFile( this.#preloadFile, generatePreloadCode(preloaded) + "\n", ); logger.log(`Preloader saved at <dim>${this.#preloadFile}</dim>`); } }}
function generatePreloadCode(paths: Set<string>): string { const imports: string[] = []; const caches: string[] = [];
Array.from(paths).map((path, index) => { imports.push(`import * as $${index} from ".${path}";`); caches.push(` site.cacheFile("${path}", toData($${index}));`); });
return `import { toData } from "lume/core/loaders/module.ts";${imports.join("\n")}
import type { Site } from "lume/core.ts";
export default function (site: Site) {${caches.join("\n")}}`;}