UNPKG

10.1 kBSource Map (JSON)View Raw
1{"version":3,"file":"AkteFiles.cjs","sources":["../../src/AkteFiles.ts"],"sourcesContent":["import { NotFoundError } from \"./errors\";\nimport { type Awaitable } from \"./types\";\n\nimport { createDebugger } from \"./lib/createDebugger\";\nimport { pathToFilePath } from \"./lib/pathToFilePath\";\n\n/* eslint-disable @typescript-eslint/no-unused-vars */\n\nimport type { AkteApp } from \"./AkteApp\";\n\n/* eslint-enable @typescript-eslint/no-unused-vars */\n\ntype Path<\n\tTParams extends string[],\n\tTPrefix extends string = string,\n> = TParams extends []\n\t? \"\"\n\t: TParams extends [string]\n\t? `${TPrefix}:${TParams[0]}${string}`\n\t: TParams extends readonly [string, ...infer Rest extends string[]]\n\t? Path<Rest, `${TPrefix}:${TParams[0]}${string}`>\n\t: string;\n\n/**\n * A function responsible for fetching the data required to render a given file\n * at the provided path. Used for optimization like server side rendering or\n * serverless.\n */\nexport type FilesDataFn<\n\tTGlobalData,\n\tTParams extends string[],\n\tTData,\n> = (context: {\n\t/** Path to get data for. */\n\tpath: string;\n\n\t/** Path parameters if any. */\n\tparams: Record<TParams[number], string>;\n\n\t/** Akte app global data. */\n\tglobalData: TGlobalData;\n}) => Awaitable<TData>;\n\n/** A function responsible for fetching all the data required to render files. */\nexport type FilesBulkDataFn<TGlobalData, TData> = (context: {\n\t/** Akte app global data. */\n\tglobalData: TGlobalData;\n}) => Awaitable<Record<string, TData>>;\n\nexport type FilesDefinition<TGlobalData, TParams extends string[], TData> = {\n\t/**\n\t * Path pattern for the Akte files.\n\t *\n\t * @example\n\t * \t\"/\";\n\t * \t\"/foo\";\n\t * \t\"/bar.json\";\n\t * \t\"/posts/:slug\";\n\t * \t\"/posts/:taxonomy/:slug\";\n\t * \t\"/pages/**\";\n\t * \t\"/assets/**.json\";\n\t */\n\tpath: Path<TParams>;\n\n\t/**\n\t * A function responsible for fetching the data required to render a given\n\t * file. Used for optimization like server side rendering or serverless.\n\t *\n\t * Throwing a {@link NotFoundError} makes the file at path to be treated as a\n\t * 404, any other error makes it treated as a 500.\n\t */\n\tdata?: FilesDataFn<TGlobalData, TParams, TData>;\n\n\t/** A function responsible for fetching all the data required to render files. */\n\tbulkData?: FilesBulkDataFn<TGlobalData, TData>;\n\n\t/**\n\t * A function responsible for rendering the file.\n\t *\n\t * @param context - Resolved file path, app global data, and data to render\n\t * the file.\n\t * @returns Rendered file.\n\t */\n\trender: (context: {\n\t\t/** Path to render. */\n\t\tpath: string;\n\n\t\t/** Akte app global data. */\n\t\tglobalData: TGlobalData;\n\n\t\t/** File data for path. */\n\t\tdata: TData;\n\t}) => Awaitable<string>;\n};\n\nconst debug = createDebugger(\"akte:files\");\nconst debugRender = createDebugger(\"akte:files:render\");\nconst debugCache = createDebugger(\"akte:files:cache\");\n\n/** An Akte files, managing its data cascade and rendering process. */\nexport class AkteFiles<\n\tTGlobalData = unknown,\n\tTParams extends string[] = string[],\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tTData = any,\n> {\n\tprotected definition: FilesDefinition<TGlobalData, TParams, TData>;\n\n\t/** Path pattern of this Akte files. */\n\tget path(): string {\n\t\treturn this.definition.path;\n\t}\n\n\tconstructor(definition: FilesDefinition<TGlobalData, TParams, TData>) {\n\t\tthis.definition = definition;\n\n\t\tdebug(\"created %o\", this.path);\n\t}\n\n\t/** @internal Prefer {@link AkteApp.render} or use at your own risks. */\n\tasync render(args: {\n\t\tpath: string;\n\t\tparams: Record<TParams[number], string>;\n\t\tglobalData: TGlobalData;\n\t}): Promise<string> {\n\t\tconst data = await this.getDataPromise(args);\n\n\t\treturn this.definition.render({\n\t\t\tpath: args.path,\n\t\t\tglobalData: args.globalData,\n\t\t\tdata,\n\t\t});\n\t}\n\n\t/** @internal Prefer {@link AkteApp.renderAll} or use at your own risks. */\n\tasync renderAll(args: {\n\t\tglobalData: TGlobalData;\n\t}): Promise<Record<string, string>> {\n\t\tif (!this.definition.bulkData) {\n\t\t\tdebugRender(\"no files to render %o\", this.path);\n\n\t\t\treturn {};\n\t\t}\n\n\t\tdebugRender(\"rendering files... %o\", this.path);\n\n\t\tconst bulkData = await this.getBulkDataPromise(args);\n\n\t\tconst render = async (\n\t\t\tpath: string,\n\t\t\tdata: TData,\n\t\t): Promise<[string, string]> => {\n\t\t\tconst content = await this.definition.render({\n\t\t\t\tpath,\n\t\t\t\tglobalData: args.globalData,\n\t\t\t\tdata,\n\t\t\t});\n\n\t\t\tdebugRender(\"rendered %o\", path);\n\n\t\t\treturn [pathToFilePath(path), content];\n\t\t};\n\n\t\tconst promises: Awaitable<[string, string]>[] = [];\n\t\tfor (const path in bulkData) {\n\t\t\tconst data = bulkData[path];\n\n\t\t\tpromises.push(render(path, data));\n\t\t}\n\n\t\tconst fileEntries = await Promise.all(Object.values(promises));\n\n\t\tdebugRender(\n\t\t\t`rendered %o ${fileEntries.length > 1 ? \"files\" : \"file\"} %o`,\n\t\t\tfileEntries.length,\n\t\t\tthis.path,\n\t\t);\n\n\t\treturn Object.fromEntries(fileEntries);\n\t}\n\n\t/** @internal Prefer {@link AkteApp.clearCache} or use at your own risks. */\n\tclearCache(): void {\n\t\tthis._dataPromiseMap = new Map();\n\t\tthis._bulkDataPromise = undefined;\n\t}\n\n\tprivate _dataPromiseMap: Map<string, Awaitable<TData>> = new Map();\n\n\tprotected getDataPromise: FilesDataFn<TGlobalData, TParams, TData> = (\n\t\tcontext,\n\t) => {\n\t\tconst maybePromise = this._dataPromiseMap.get(context.path);\n\t\tif (maybePromise) {\n\t\t\tdebugCache(\"using cached data %o\", context.path);\n\n\t\t\treturn maybePromise;\n\t\t}\n\n\t\tdebugCache(\"retrieving data... %o\", context.path);\n\n\t\tlet promise: Awaitable<TData>;\n\t\tif (this.definition.data) {\n\t\t\tpromise = this.definition.data(context);\n\t\t} else if (this.definition.bulkData) {\n\t\t\tconst dataFromBulkData = async (path: string): Promise<TData> => {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tconst bulkData = await this.definition.bulkData!({\n\t\t\t\t\tglobalData: context.globalData,\n\t\t\t\t});\n\n\t\t\t\tif (path in bulkData) {\n\t\t\t\t\treturn bulkData[path];\n\t\t\t\t}\n\n\t\t\t\tthrow new NotFoundError(path);\n\t\t\t};\n\n\t\t\tpromise = dataFromBulkData(context.path);\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot render file for path \\`${context.path}\\`, no \\`data\\` or \\`bulkData\\` function available`,\n\t\t\t);\n\t\t}\n\n\t\tif (promise instanceof Promise) {\n\t\t\tpromise\n\t\t\t\t.then(() => {\n\t\t\t\t\tdebugCache(\"retrieved data %o\", context.path);\n\t\t\t\t})\n\t\t\t\t.catch(() => {});\n\t\t} else {\n\t\t\tdebugCache(\"retrieved data %o\", context.path);\n\t\t}\n\n\t\tthis._dataPromiseMap.set(context.path, promise);\n\n\t\treturn promise;\n\t};\n\n\tprivate _bulkDataPromise: Awaitable<Record<string, TData>> | undefined;\n\n\tprotected getBulkDataPromise: FilesBulkDataFn<TGlobalData, TData> = (\n\t\tcontext,\n\t) => {\n\t\tif (!this._bulkDataPromise) {\n\t\t\tdebugCache(\"retrieving bulk data... %o\", this.path);\n\n\t\t\tconst bulkDataPromise =\n\t\t\t\tthis.definition.bulkData?.(context) || ({} as Record<string, TData>);\n\n\t\t\tif (bulkDataPromise instanceof Promise) {\n\t\t\t\tbulkDataPromise.then(() => {\n\t\t\t\t\tdebugCache(\"retrieved bulk data %o\", this.path);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tdebugCache(\"retrieved bulk data %o\", this.path);\n\t\t\t}\n\n\t\t\tthis._bulkDataPromise = bulkDataPromise;\n\t\t} else {\n\t\t\tdebugCache(\"using cached bulk data %o\", this.path);\n\t\t}\n\n\t\treturn this._bulkDataPromise;\n\t};\n}\n"],"names":["createDebugger","NotFoundError","pathToFilePath"],"mappings":"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"}
\No newline at end of file