UNPKG

11.5 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\";\nimport { toReadonlyMap } from \"./lib/toReadonlyMap\";\n\n/* eslint-disable @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports */\n\nimport type { AkteApp } from \"./AkteApp\";\n\n/* eslint-enable @typescript-eslint/no-unused-vars, unused-imports/no-unused-imports */\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(\"defined %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\tdata?: TData;\n\t}): Promise<string> {\n\t\tconst data = args.data || (await this.getData(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.getBulkData(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._dataMapCache.clear();\n\t\tthis._bulkDataCache = undefined;\n\t}\n\n\t/**\n\t * Readonly cache of files' definition `data` method.\n\t *\n\t * @experimental Programmatic API might still change not following SemVer.\n\t */\n\tget dataMapCache(): ReadonlyMap<string, Awaitable<TData>> {\n\t\treturn toReadonlyMap(this._dataMapCache);\n\t}\n\n\tprivate _dataMapCache: Map<string, Awaitable<TData>> = new Map();\n\n\t/**\n\t * Retrieves data from files' definition `data` method with given context.\n\t *\n\t * @param context - Context to get data with.\n\t * @returns Retrieved data.\n\t * @remark Returned data may come from cache.\n\t * @experimental Programmatic API might still change not following SemVer.\n\t */\n\tgetData: FilesDataFn<TGlobalData, TParams, TData> = (context) => {\n\t\tconst maybePromise = this._dataMapCache.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\tconst bulkData = await this.getBulkData({\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._dataMapCache.set(context.path, promise);\n\n\t\treturn promise;\n\t};\n\n\t/**\n\t * Readonly cache of files' definition `bulkData` method.\n\t *\n\t * @experimental Programmatic API might still change not following SemVer.\n\t */\n\tget bulkDataCache(): Awaitable<Record<string, TData>> | undefined {\n\t\treturn this._bulkDataCache;\n\t}\n\n\tprivate _bulkDataCache: Awaitable<Record<string, TData>> | undefined;\n\n\t/**\n\t * Retrieves data from files' definition `bulkData` method with given context.\n\t *\n\t * @param context - Context to get bulk data with.\n\t * @returns Retrieved bulk data.\n\t * @remark Returned bulk data may come from cache.\n\t * @experimental Programmatic API might still change not following SemVer.\n\t */\n\tgetBulkData: FilesBulkDataFn<TGlobalData, TData> = (context) => {\n\t\tif (!this._bulkDataCache) {\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._bulkDataCache = bulkDataPromise;\n\t\t} else {\n\t\t\tdebugCache(\"using cached bulk data %o\", this.path);\n\t\t}\n\n\t\treturn this._bulkDataCache;\n\t};\n}\n"],"names":["createDebugger","NotFoundError","pathToFilePath","toReadonlyMap"],"mappings":"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"}
\No newline at end of file