"use strict"; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const path = require("node:path"); const fs = require("node:fs/promises"); const radix3 = require("radix3"); const errors = require("./errors.cjs"); const runCLI = require("./runCLI.cjs"); const akteWelcome = require("./akteWelcome.cjs"); const __PRODUCTION__ = require("./lib/__PRODUCTION__.cjs"); const createDebugger = require("./lib/createDebugger.cjs"); const pathToRouterPath = require("./lib/pathToRouterPath.cjs"); const isCLI = require("./lib/isCLI.cjs"); const debug = createDebugger.createDebugger("akte:app"); const debugWrite = createDebugger.createDebugger("akte:app:write"); const debugRender = createDebugger.createDebugger("akte:app:render"); const debugRouter = createDebugger.createDebugger("akte:app:router"); const debugCache = createDebugger.createDebugger("akte:app:cache"); class AkteApp { constructor(config) { __publicField(this, "config"); __publicField(this, "_globalDataPromise"); __publicField(this, "_router"); if (!__PRODUCTION__.__PRODUCTION__) { if (config.files.length === 0 && akteWelcome.akteWelcome) { config.files.push(akteWelcome.akteWelcome); } } this.config = config; debug("created with %o files", this.config.files.length); if (isCLI.isCLI) { runCLI.runCLI(this); } } /** * Looks up the Akte file responsible for rendering the path. * * @param path - Path to lookup, e.g. "/foo" * @returns A match featuring the path, the path parameters if any, and the * Akte file. * @throws {@link NotFoundError} When no Akte file is found for handling * looked up path. * @experimental Programmatic API might still change not following SemVer. */ lookup(path2) { const pathWithExtension = pathToRouterPath.pathToRouterPath(path2); debugRouter("looking up %o (%o)", path2, pathWithExtension); const maybeMatch = this.getRouter().lookup(pathWithExtension); if (!maybeMatch || !maybeMatch.file) { debugRouter("not found %o", path2); throw new errors.NotFoundError(path2); } return { ...maybeMatch, path: path2 }; } /** * Renders a match from {@link lookup}. * * @param match - Match to render. * @returns Rendered file. * @throws {@link NotFoundError} When the Akte file could not render the match * (404), with an optional `cause` attached to it for uncaught errors (500) * @experimental Programmatic API might still change not following SemVer. */ async render(match) { debugRender("rendering %o...", match.path); const params = match.params || {}; const globalData = await this.getGlobalDataPromise(); try { const content = await match.file.render({ path: match.path, params, globalData }); debugRender("rendered %o", match.path); return content; } catch (error) { if (error instanceof errors.NotFoundError) { throw error; } debugRender("could not render %o", match.path); throw new errors.NotFoundError(match.path, { cause: error }); } } /** * Renders all Akte files. * * @returns Rendered files map. * @experimental Programmatic API might still change not following SemVer. */ async renderAll() { debugRender("rendering all files..."); const globalData = await this.getGlobalDataPromise(); const renderAll = async (akteFiles) => { try { const files2 = await akteFiles.renderAll({ globalData }); return files2; } catch (error) { debug.error("Akte → Failed to build %o\n", akteFiles.path); throw error; } }; const promises = []; for (const akteFiles of this.config.files) { promises.push(renderAll(akteFiles)); } const rawFilesArray = await Promise.all(promises); const files = {}; for (const rawFiles of rawFilesArray) { for (const path2 in rawFiles) { if (path2 in files) { debug.warn(" Multiple files built %o, only the first one is preserved", path2); continue; } files[path2] = rawFiles[path2]; } } const rendered = Object.keys(files).length; debugRender(`done, %o ${rendered > 1 ? "files" : "file"} rendered`, rendered); return files; } /** * Writes a map of rendered Akte files to the specified `outDir`, or the app * specified one (defaults to `"dist"`). * * @param args - A map of rendered Akte files, and an optional `outDir` * @experimental Programmatic API might still change not following SemVer. */ async writeAll(args) { var _a; debugWrite("writing all files..."); const outDir = args.outDir ?? ((_a = this.config.build) == null ? void 0 : _a.outDir) ?? "dist"; const outDirPath = path.resolve(outDir); const controller = new AbortController(); const write = async (path$1, content) => { const filePath = path.join(outDirPath, path$1); const fileDir = path.dirname(filePath); try { await fs.mkdir(fileDir, { recursive: true }); await fs.writeFile(filePath, content, { encoding: "utf-8", signal: controller.signal }); } catch (error) { if (controller.signal.aborted) { return; } controller.abort(); debug.error("Akte → Failed to write %o\n", path$1); throw error; } debugWrite("%o", path$1); debugWrite.log(" %o", path$1); }; const promises = []; for (const path2 in args.files) { promises.push(write(path2, args.files[path2])); } await Promise.all(promises); debugWrite(`done, %o ${promises.length > 1 ? "files" : "file"} written`, promises.length); } /** * Build (renders and write) all Akte files to the specified `outDir`, or the * app specified one (defaults to `"dist"`). * * @param args - An optional `outDir` * @returns Built files array. * @experimental Programmatic API might still change not following SemVer. */ async buildAll(args) { const files = await this.renderAll(); await this.writeAll({ ...args, files }); return Object.keys(files); } /** * Akte caches all `globalData`, `data`, `bulkData` calls for performance. * This method can be used to clear the cache. * * @param alsoClearFileCache - Also clear cache on all registered Akte files. * @experimental Programmatic API might still change not following SemVer. */ clearCache(alsoClearFileCache = false) { debugCache("clearing..."); this._globalDataPromise = void 0; this._router = void 0; if (alsoClearFileCache) { for (const file of this.config.files) { file.clearCache(); } } debugCache("cleared"); } getGlobalDataPromise() { var _a, _b; if (!this._globalDataPromise) { debugCache("retrieving global data..."); const globalDataPromise = ((_b = (_a = this.config).globalData) == null ? void 0 : _b.call(_a)) ?? void 0; if (globalDataPromise instanceof Promise) { globalDataPromise.then(() => { debugCache("retrieved global data"); }); } else { debugCache("retrieved global data"); } this._globalDataPromise = globalDataPromise; } else { debugCache("using cached global data"); } return this._globalDataPromise; } getRouter() { if (!this._router) { debugCache("creating router..."); const router = radix3.createRouter(); for (const file of this.config.files) { const path2 = pathToRouterPath.pathToRouterPath(file.path); router.insert(pathToRouterPath.pathToRouterPath(file.path), { file }); debugRouter("registered %o", path2); if (file.path.endsWith("/**")) { const catchAllPath = pathToRouterPath.pathToRouterPath(file.path.replace(/\/\*\*$/, "")); router.insert(catchAllPath, { file }); debugRouter("registered %o", catchAllPath); debugCache(pathToRouterPath.pathToRouterPath(file.path.replace(/\/\*\*$/, ""))); } } this._router = router; debugCache("created router"); } else { debugCache("using cached router"); } return this._router; } } exports.AkteApp = AkteApp; //# sourceMappingURL=AkteApp.cjs.map